From 724ada8439be709de2757d93d7a5186e3934cdaa Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 3 Apr 2023 18:35:00 -0400 Subject: [PATCH 001/253] fix codegen diff for codegen-client and codegen-server (#2532) --- tools/ci-scripts/codegen-diff/diff_lib.py | 27 ++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/tools/ci-scripts/codegen-diff/diff_lib.py b/tools/ci-scripts/codegen-diff/diff_lib.py index f48cd53ae1..42068040ca 100644 --- a/tools/ci-scripts/codegen-diff/diff_lib.py +++ b/tools/ci-scripts/codegen-diff/diff_lib.py @@ -15,31 +15,37 @@ CDN_URL = "https://d2luzm2xt3nokh.cloudfront.net" +target_codegen_client = 'codegen-client-test' +target_codegen_server = 'codegen-server-test' +target_aws_sdk = 'aws:sdk' + def generate_and_commit_generated_code(revision_sha, targets=None): - targets = targets or ['codegen-client-test', 'codegen-server-test', 'aws:sdk'] + targets = targets or [target_codegen_client, target_codegen_server, target_aws_sdk] # Clean the build artifacts before continuing get_cmd_output("rm -rf aws/sdk/build") - if 'codegen-server-test' in targets: + if target_codegen_server in targets: get_cmd_output("cd rust-runtime/aws-smithy-http-server-python/examples && make distclean", shell=True) get_cmd_output("./gradlew codegen-core:clean codegen-client:clean codegen-server:clean aws:sdk-codegen:clean") # Generate code tasks = ' '.join([f'{t}:assemble' for t in targets]) get_cmd_output(f"./gradlew --rerun-tasks {tasks}") - if 'codegen-server-test' in targets: + if target_codegen_server in targets: get_cmd_output("cd rust-runtime/aws-smithy-http-server-python/examples && make build", shell=True, check=False) # Move generated code into codegen-diff/ directory get_cmd_output(f"rm -rf {OUTPUT_PATH}") get_cmd_output(f"mkdir {OUTPUT_PATH}") - if 'aws:sdk' in targets: + if target_aws_sdk in targets: get_cmd_output(f"mv aws/sdk/build/aws-sdk {OUTPUT_PATH}/") - for target in ['codegen-client', 'codegen-server']: + for target in [target_codegen_client, target_codegen_server]: if target in targets: get_cmd_output(f"mv {target}/build/smithyprojections/{target} {OUTPUT_PATH}/") - if target == 'codegen-server-test': - get_cmd_output(f"mv rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-server-sdk/ {OUTPUT_PATH}/codegen-server-test-python/", check=False) + if target == target_codegen_server: + get_cmd_output( + f"mv rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-server-sdk/ {OUTPUT_PATH}/codegen-server-test-python/", + check=False) # Clean up the SDK directory get_cmd_output(f"rm -f {OUTPUT_PATH}/aws-sdk/versions.toml") @@ -58,9 +64,9 @@ def generate_and_commit_generated_code(revision_sha, targets=None): get_cmd_output(f"git add -f {OUTPUT_PATH}") get_cmd_output(f"git -c 'user.name=GitHub Action (generated code preview)' " - f"-c 'user.name={COMMIT_AUTHOR_NAME}' " - f"-c 'user.email={COMMIT_AUTHOR_EMAIL}' " - f"commit --no-verify -m 'Generated code for {revision_sha}' --allow-empty") + f"-c 'user.name={COMMIT_AUTHOR_NAME}' " + f"-c 'user.email={COMMIT_AUTHOR_EMAIL}' " + f"commit --no-verify -m 'Generated code for {revision_sha}' --allow-empty") def make_diff(title, path_to_diff, base_commit_sha, head_commit_sha, suffix, whitespace): @@ -167,6 +173,7 @@ def get_cmd_output(command, cwd=None, check=True, **kwargs): return result.returncode, stdout, stderr + # Runs a shell command and returns its exit status def get_cmd_status(command): return subprocess.run(command, capture_output=True, shell=True).returncode From c901c0b1c69ddbae350529c0f8116c4ab06fd348 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Mon, 3 Apr 2023 20:51:37 -0500 Subject: [PATCH 002/253] Exclude examples from the workspace (#2535) This commit automates the fix made in https://github.com/awslabs/aws-sdk-rust/pull/767. When `aws-sdk-rust` is generated, `examples` will be excluded from the workspace. Co-authored-by: Yuki Saito --- aws/sdk/build.gradle.kts | 5 ++--- .../src/main/kotlin/aws/sdk/ServiceLoader.kt | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index 8db1cdc0ee..29c7e83254 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -5,7 +5,6 @@ import aws.sdk.AwsServices import aws.sdk.Membership -import aws.sdk.RootTest import aws.sdk.discoverServices import aws.sdk.docsLandingPage import aws.sdk.parseMembership @@ -302,9 +301,9 @@ tasks.register("relocateChangelog") { fun generateCargoWorkspace(services: AwsServices): String { return """ |[workspace] - |exclude = [${"\n"}${services.rootTests.map(RootTest::manifestName).joinToString(",\n") { "| \"$it\"" }} + |exclude = [${"\n"}${services.excludedFromWorkspace().joinToString(",\n") { "| \"$it\"" }} |] - |members = [${"\n"}${services.allModules.joinToString(",\n") { "| \"$it\"" }} + |members = [${"\n"}${services.includedInWorkspace().joinToString(",\n") { "| \"$it\"" }} |] """.trimMargin() } diff --git a/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt b/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt index 44510d1b11..a1cbe0dc99 100644 --- a/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt +++ b/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt @@ -36,10 +36,10 @@ class AwsServices( ( services.map(AwsService::module).map { "sdk/$it" } + CrateSet.AWS_SDK_SMITHY_RUNTIME.map { "sdk/$it" } + - CrateSet.AWS_SDK_RUNTIME.map { "sdk/$it" } + - examples + CrateSet.AWS_SDK_RUNTIME.map { "sdk/$it" } // Root tests should not be included since they can't be part of the root Cargo workspace - // in order to test differences in Cargo features. + // in order to test differences in Cargo features. Examples should not be included either + // because each example itself is a workspace. ).toSortedSet() } @@ -78,6 +78,16 @@ class AwsServices( false } } + + /** + * Returns a sorted set of members included in the workspace. + */ + fun includedInWorkspace() = allModules + + /** + * Returns a list of crates excluded from the workspace. + */ + fun excludedFromWorkspace() = examples + rootTests.map(RootTest::manifestName) } /** From f2abe7c695f8757e98d7000f01a5060e4c4c2c83 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 4 Apr 2023 09:58:24 +0200 Subject: [PATCH 003/253] Improve invalid HTTP status code responses failure mode handling (#2522) If the model only uses the `@http` trait to define the response code, Smithy already checks that this is a valid status code, so the conversion cannot fail, and we can avoid rejecting with `ResponseRejection::InvalidHttpStatusCode`. This commit also removes this variant from RPC protocols, where HTTP binding traits are not allowed, and so this failure mode can never happen. --- .../ServerHttpBoundProtocolGenerator.kt | 51 +++++++++---------- .../src/proto/aws_json/rejection.rs | 1 - .../src/proto/rest_json_1/rejection.rs | 4 +- .../src/proto/rest_xml/rejection.rs | 4 +- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index eb2fd10a3c..1cf2debcba 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -439,17 +439,18 @@ class ServerHttpBoundProtocolTraitImplGenerator( Attribute.AllowUnusedMut.render(this) rustTemplate("let mut builder = #{http}::Response::builder();", *codegenScope) serverRenderResponseHeaders(operationShape) - // Fallback to the default code of `@http`, 200. + // Fallback to the default code of `@http`, which should be 200. val httpTraitDefaultStatusCode = HttpTrait .builder().method("GET").uri(UriPattern.parse("/")) /* Required to build */ .build() .code + check(httpTraitDefaultStatusCode == 200) val httpTraitStatusCode = operationShape.getTrait()?.code ?: httpTraitDefaultStatusCode bindings.find { it.location == HttpLocation.RESPONSE_CODE } ?.let { serverRenderResponseCodeBinding(it, httpTraitStatusCode)(this) } - // no binding, use http's + // No binding, use `@http`. ?: serverRenderHttpResponseCode(httpTraitStatusCode)(this) operationShape.outputShape(model).findStreamingMember(model)?.let { @@ -555,46 +556,42 @@ class ServerHttpBoundProtocolTraitImplGenerator( ) } - private fun serverRenderHttpResponseCode( - defaultCode: Int, - ): Writable { - return writable { - rustTemplate( - """ - let status = $defaultCode; - let http_status: u16 = status.try_into() - .map_err(|_| #{ResponseRejection}::InvalidHttpStatusCode)?; - builder = builder.status(http_status); - """.trimIndent(), - *codegenScope, - ) + private fun serverRenderHttpResponseCode(defaultCode: Int) = writable { + check(defaultCode in 100..999) { + """ + Smithy library lied to us. According to https://smithy.io/2.0/spec/http-bindings.html#http-trait, + "The provided value SHOULD be between 100 and 599, and it MUST be between 100 and 999". + """.replace("\n", "").trimIndent() } + rustTemplate( + """ + let http_status: u16 = $defaultCode; + builder = builder.status(http_status); + """, + *codegenScope, + ) } private fun serverRenderResponseCodeBinding( binding: HttpBindingDescriptor, + /** This is the status code to fall back on if the member shape bound with `@httpResponseCode` is not + * `@required` and the user did not provide a value for it at runtime. **/ fallbackStatusCode: Int, ): Writable { check(binding.location == HttpLocation.RESPONSE_CODE) return writable { val memberName = symbolProvider.toMemberName(binding.member) - rust("let status = output.$memberName") - if (symbolProvider.toSymbol(binding.member).isOptional()) { - rustTemplate( - """ - .unwrap_or($fallbackStatusCode) - """.trimIndent(), - *codegenScope, - ) + withBlock("let status = output.$memberName", ";") { + if (symbolProvider.toSymbol(binding.member).isOptional()) { + rust(".unwrap_or($fallbackStatusCode)") + } } rustTemplate( """ - ; - let http_status: u16 = status.try_into() - .map_err(|_| #{ResponseRejection}::InvalidHttpStatusCode)?; + let http_status: u16 = status.try_into().map_err(#{ResponseRejection}::InvalidHttpStatusCode)?; builder = builder.status(http_status); - """.trimIndent(), + """, *codegenScope, ) } diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs index c47057f661..1a74168f4a 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs @@ -9,7 +9,6 @@ use crate::rejection::MissingContentTypeReason; #[derive(Debug, Display)] pub enum ResponseRejection { - InvalidHttpStatusCode, Serialization(crate::Error), Http(crate::Error), } diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs index 9c3ffc20f7..6f521880af 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs @@ -47,6 +47,8 @@ //! //! Consult `crate::proto::$protocolName::rejection` for rejection types for other protocols. +use std::num::TryFromIntError; + use strum_macros::Display; use crate::rejection::MissingContentTypeReason; @@ -58,7 +60,7 @@ pub enum ResponseRejection { /// Used when the service implementer provides an integer outside the 100-999 range for a /// member targeted by `httpResponseCode`. /// See . - InvalidHttpStatusCode, + InvalidHttpStatusCode(TryFromIntError), /// Used when an invalid HTTP header value (a value that cannot be parsed as an /// `[http::header::HeaderValue]`) is provided for a shape member bound to an HTTP header with diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs index 2e3366f0b6..272f69f7b6 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs @@ -7,11 +7,13 @@ //! [`crate::proto::rest_json_1::rejection::RequestRejection::JsonDeserialize`] is swapped for //! [`RequestRejection::XmlDeserialize`]. +use std::num::TryFromIntError; + use strum_macros::Display; #[derive(Debug, Display)] pub enum ResponseRejection { - InvalidHttpStatusCode, + InvalidHttpStatusCode(TryFromIntError), Build(crate::Error), Serialization(crate::Error), Http(crate::Error), From 421488ef491b66e8f7621b8c22d96e23e9137825 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 4 Apr 2023 09:58:29 +0200 Subject: [PATCH 004/253] Error log server response rejections (#2524) All `ResponseRejection`s are errors; the service owners are to blame. So we centrally log them here to let them know. --- .../protocols/ServerHttpBoundProtocolGenerator.kt | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 1cf2debcba..6bf8dba7ba 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -135,6 +135,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( "ResponseRejection" to protocol.responseRejection(runtimeConfig), "PinProjectLite" to ServerCargoDependency.PinProjectLite.toType(), "http" to RuntimeType.Http, + "Tracing" to RuntimeType.Tracing, ) fun generateTraitImpls(operationWriter: RustWriter, operationShape: OperationShape, customizations: List) { @@ -251,17 +252,22 @@ class ServerHttpBoundProtocolTraitImplGenerator( // Implement `into_response` for output types. val errorSymbol = symbolProvider.symbolForOperationError(operationShape) + // All `ResponseRejection`s are errors; the service owners are to blame. So we centrally log them here + // to let them know. rustTemplate( """ impl #{SmithyHttpServer}::response::IntoResponse<#{Marker}> for #{O} { fn into_response(self) -> #{SmithyHttpServer}::response::Response { match #{serialize_response}(self) { Ok(response) => response, - Err(e) => #{SmithyHttpServer}::response::IntoResponse::<#{Marker}>::into_response(#{RuntimeError}::from(e)) + Err(e) => { + #{Tracing}::error!(error = %e, "failed to serialize response"); + #{SmithyHttpServer}::response::IntoResponse::<#{Marker}>::into_response(#{RuntimeError}::from(e)) + } } } } - """.trimIndent(), + """, *codegenScope, "O" to outputSymbol, "Marker" to protocol.markerStruct(), @@ -278,7 +284,10 @@ class ServerHttpBoundProtocolTraitImplGenerator( response.extensions_mut().insert(#{SmithyHttpServer}::extension::ModeledErrorExtension::new(self.name())); response }, - Err(e) => #{SmithyHttpServer}::response::IntoResponse::<#{Marker}>::into_response(#{RuntimeError}::from(e)) + Err(e) => { + #{Tracing}::error!(error = %e, "failed to serialize response"); + #{SmithyHttpServer}::response::IntoResponse::<#{Marker}>::into_response(#{RuntimeError}::from(e)) + } } } } From 8bc93fc04dd8c8d7447bfe3f5196a75cba0b10ba Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 4 Apr 2023 14:25:38 +0200 Subject: [PATCH 005/253] Move `RestRequestSpecGenerator` into `codegen-server` (#2537) Move `RestRequestSpecGenerator` from `codegen-core` to `codegen-server`. This can now be done thanks to the `ServerProtocol` interface. --- .../smithy/generators/http/RestRequestSpecGenerator.kt | 6 +----- .../server/smithy/generators/protocol/ServerProtocol.kt | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) rename {codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core => codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server}/smithy/generators/http/RestRequestSpecGenerator.kt (89%) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RestRequestSpecGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/RestRequestSpecGenerator.kt similarity index 89% rename from codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RestRequestSpecGenerator.kt rename to codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/RestRequestSpecGenerator.kt index 46890998a2..b5c0cd052b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/RestRequestSpecGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/RestRequestSpecGenerator.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.core.smithy.generators.http +package software.amazon.smithy.rust.codegen.server.smithy.generators.http import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -15,10 +15,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingReso /** * [RestRequestSpecGenerator] generates a restJson1 or restXml specific `RequestSpec`. Both protocols are routed the same. - * - * This class has to live in the `codegen-core` subproject instead of in the `codegen-server` subproject because it is used - * by the implementations of the `serverRouterRequestSpec` of the [Protocol] interface, which is used by both subprojects - * (even though only the `codegen-server` subproject calls `serverRouterRequestSpec`). */ class RestRequestSpecGenerator( private val httpBindingResolver: HttpBindingResolver, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index eb41a35e51..9abe7db0c3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -14,7 +14,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.http.RestRequestSpecGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol @@ -32,6 +31,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape +import software.amazon.smithy.rust.codegen.server.smithy.generators.http.RestRequestSpecGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerAwsJsonSerializerGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerRestJsonSerializerGenerator import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape From 7e6f2c97ee6e2c3148fcd35b554d2a4bf8485da5 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 4 Apr 2023 18:23:53 +0200 Subject: [PATCH 006/253] Remove parameter from `Protocol`s `structuredDataParser`, `structuredDataSerializer` (#2536) No implementation of the `Protocol` interface makes use of the `OperationShape` parameter in the `structuredDataParser` and `structuredDataSerializer` methods. --- .../protocol/ProtocolParserGenerator.kt | 2 +- .../codegen/core/smithy/protocols/AwsJson.kt | 7 +- .../codegen/core/smithy/protocols/AwsQuery.kt | 4 +- .../smithy/protocols/AwsQueryCompatible.kt | 8 +-- .../codegen/core/smithy/protocols/Ec2Query.kt | 7 +- .../HttpBoundProtocolPayloadGenerator.kt | 6 +- .../codegen/core/smithy/protocols/Protocol.kt | 4 +- .../codegen/core/smithy/protocols/RestJson.kt | 7 +- .../codegen/core/smithy/protocols/RestXml.kt | 10 ++- .../parse/EventStreamUnmarshallerGenerator.kt | 6 +- .../generators/protocol/ServerProtocol.kt | 71 +++++++++---------- .../ServerHttpBoundProtocolGenerator.kt | 4 +- 12 files changed, 63 insertions(+), 73 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt index 616325ee3e..8b247cd917 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt @@ -232,7 +232,7 @@ class ProtocolParserGenerator( customizations: List, ) { val httpBindingGenerator = ResponseBindingGenerator(protocol, codegenContext, operationShape) - val structuredDataParser = protocol.structuredDataParser(operationShape) + val structuredDataParser = protocol.structuredDataParser() Attribute.AllowUnusedMut.render(this) rust("let mut output = #T::default();", symbolProvider.symbolForBuilder(outputShape)) if (outputShape.id == operationShape.output.get()) { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt index 7812f917bb..0a53422552 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsJson.kt @@ -143,15 +143,14 @@ open class AwsJson( override fun additionalRequestHeaders(operationShape: OperationShape): List> = listOf("x-amz-target" to "${codegenContext.serviceShape.id.name}.${operationShape.id.name}") - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - return JsonParserGenerator( + override fun structuredDataParser(): StructuredDataParserGenerator = + JsonParserGenerator( codegenContext, httpBindingResolver, ::awsJsonFieldName, ) - } - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = AwsJsonSerializerGenerator(codegenContext, httpBindingResolver) override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt index 9d12cfbb62..934b703eba 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQuery.kt @@ -51,10 +51,10 @@ class AwsQueryProtocol(private val codegenContext: CodegenContext) : Protocol { override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator = + override fun structuredDataParser(): StructuredDataParserGenerator = AwsQueryParserGenerator(codegenContext, awsQueryErrors) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = AwsQuerySerializerGenerator(codegenContext) override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQueryCompatible.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQueryCompatible.kt index 88fb69d015..489f1de582 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQueryCompatible.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/AwsQueryCompatible.kt @@ -64,11 +64,11 @@ class AwsQueryCompatible( override val defaultTimestampFormat = awsJson.defaultTimestampFormat - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator = - awsJson.structuredDataParser(operationShape) + override fun structuredDataParser(): StructuredDataParserGenerator = + awsJson.structuredDataParser() - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = - awsJson.structuredDataSerializer(operationShape) + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = + awsJson.structuredDataSerializer() override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt index 01a530d46a..215865f4cb 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Ec2Query.kt @@ -42,11 +42,10 @@ class Ec2QueryProtocol(private val codegenContext: CodegenContext) : Protocol { override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - return Ec2QueryParserGenerator(codegenContext, ec2QueryErrors) - } + override fun structuredDataParser(): StructuredDataParserGenerator = + Ec2QueryParserGenerator(codegenContext, ec2QueryErrors) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = Ec2QuerySerializerGenerator(codegenContext) override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt index bb55883a11..10d4b83cec 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt @@ -102,7 +102,7 @@ class HttpBoundProtocolPayloadGenerator( val payloadMemberName = httpBindingResolver.requestMembers(operationShape, HttpLocation.PAYLOAD).firstOrNull()?.memberName if (payloadMemberName == null) { - val serializerGenerator = protocol.structuredDataSerializer(operationShape) + val serializerGenerator = protocol.structuredDataSerializer() generateStructureSerializer(writer, self, serializerGenerator.operationInputSerializer(operationShape)) } else { generatePayloadMemberSerializer(writer, self, operationShape, payloadMemberName) @@ -113,7 +113,7 @@ class HttpBoundProtocolPayloadGenerator( val payloadMemberName = httpBindingResolver.responseMembers(operationShape, HttpLocation.PAYLOAD).firstOrNull()?.memberName if (payloadMemberName == null) { - val serializerGenerator = protocol.structuredDataSerializer(operationShape) + val serializerGenerator = protocol.structuredDataSerializer() generateStructureSerializer(writer, self, serializerGenerator.operationOutputSerializer(operationShape)) } else { generatePayloadMemberSerializer(writer, self, operationShape, payloadMemberName) @@ -126,7 +126,7 @@ class HttpBoundProtocolPayloadGenerator( operationShape: OperationShape, payloadMemberName: String, ) { - val serializerGenerator = protocol.structuredDataSerializer(operationShape) + val serializerGenerator = protocol.structuredDataSerializer() if (operationShape.isEventStream(model)) { if (operationShape.isInputEventStream(model) && target == CodegenTarget.CLIENT) { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt index ec9b944e3e..4a1339ca9a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/Protocol.kt @@ -38,10 +38,10 @@ interface Protocol { fun additionalErrorResponseHeaders(errorShape: StructureShape): List> = emptyList() /** Returns a deserialization code generator for this protocol */ - fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator + fun structuredDataParser(): StructuredDataParserGenerator /** Returns a serialization code generator for this protocol */ - fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator + fun structuredDataSerializer(): StructuredDataSerializerGenerator /** * Generates a function signature like the following: diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt index 675a0b2126..9a3d12a993 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt @@ -89,11 +89,10 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { override fun additionalErrorResponseHeaders(errorShape: StructureShape): List> = listOf("x-amzn-errortype" to errorShape.id.toString()) - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - return JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) - } + override fun structuredDataParser(): StructuredDataParserGenerator = + JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = JsonSerializerGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt index 3045cf0268..41df9fd52d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestXml.kt @@ -40,13 +40,11 @@ open class RestXml(val codegenContext: CodegenContext) : Protocol { override val defaultTimestampFormat: TimestampFormatTrait.Format = TimestampFormatTrait.Format.DATE_TIME - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - return RestXmlParserGenerator(codegenContext, restXmlErrors) - } + override fun structuredDataParser(): StructuredDataParserGenerator = + RestXmlParserGenerator(codegenContext, restXmlErrors) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator { - return XmlBindingTraitSerializerGenerator(codegenContext, httpBindingResolver) - } + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = + XmlBindingTraitSerializerGenerator(codegenContext, httpBindingResolver) override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType = ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 4fa98fa6ee..6e9826f054 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -294,7 +294,7 @@ class EventStreamUnmarshallerGenerator( private fun RustWriter.renderParseProtocolPayload(member: MemberShape) { val memberName = symbolProvider.toMemberName(member) - val parser = protocol.structuredDataParser(operationShape).payloadParser(member) + val parser = protocol.structuredDataParser().payloadParser(member) rustTemplate( """ #{parser}(&message.payload()[..]) @@ -341,7 +341,7 @@ class EventStreamUnmarshallerGenerator( when (codegenTarget) { CodegenTarget.CLIENT -> { val target = model.expectShape(member.target, StructureShape::class.java) - val parser = protocol.structuredDataParser(operationShape).errorParser(target) + val parser = protocol.structuredDataParser().errorParser(target) if (parser != null) { rust("let mut builder = #T::default();", symbolProvider.symbolForBuilder(target)) rustTemplate( @@ -363,7 +363,7 @@ class EventStreamUnmarshallerGenerator( CodegenTarget.SERVER -> { val target = model.expectShape(member.target, StructureShape::class.java) - val parser = protocol.structuredDataParser(operationShape).errorParser(target) + val parser = protocol.structuredDataParser().errorParser(target) val mut = if (parser != null) { " mut" } else { "" } rust("let$mut builder = #T::default();", symbolProvider.symbolForBuilder(target)) if (parser != null) { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index 9abe7db0c3..0b466f95c2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators.protocol +import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.StructureShape @@ -16,6 +17,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion +import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestXml @@ -90,6 +92,31 @@ interface ServerProtocol : Protocol { .toType().resolve("proto::$protocolModulePath::runtime_error::RuntimeError") } +fun returnSymbolToParseFn(codegenContext: ServerCodegenContext): (Shape) -> ReturnSymbolToParse { + fun returnSymbolToParse(shape: Shape): ReturnSymbolToParse = + if (shape.canReachConstrainedShape(codegenContext.model, codegenContext.symbolProvider)) { + ReturnSymbolToParse(codegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape), true) + } else { + ReturnSymbolToParse(codegenContext.symbolProvider.toSymbol(shape), false) + } + return ::returnSymbolToParse +} + +fun jsonParserGenerator( + codegenContext: ServerCodegenContext, + httpBindingResolver: HttpBindingResolver, + jsonName: (MemberShape) -> String, +): JsonParserGenerator = + JsonParserGenerator( + codegenContext, + httpBindingResolver, + jsonName, + returnSymbolToParseFn(codegenContext), + listOf( + ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization(codegenContext), + ), + ) + class ServerAwsJsonProtocol( private val serverCodegenContext: ServerCodegenContext, awsJsonVersion: AwsJsonVersion, @@ -102,25 +129,10 @@ class ServerAwsJsonProtocol( is AwsJsonVersion.Json11 -> "aws_json_11" } - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - fun returnSymbolToParse(shape: Shape): ReturnSymbolToParse = - if (shape.canReachConstrainedShape(codegenContext.model, serverCodegenContext.symbolProvider)) { - ReturnSymbolToParse(serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape), true) - } else { - ReturnSymbolToParse(codegenContext.symbolProvider.toSymbol(shape), false) - } - return JsonParserGenerator( - codegenContext, - httpBindingResolver, - ::awsJsonFieldName, - ::returnSymbolToParse, - listOf( - ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization(serverCodegenContext), - ), - ) - } + override fun structuredDataParser(): StructuredDataParserGenerator = + jsonParserGenerator(serverCodegenContext, httpBindingResolver, ::awsJsonFieldName) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = ServerAwsJsonSerializerGenerator(serverCodegenContext, httpBindingResolver, awsJsonVersion) override fun markerStruct(): RuntimeType { @@ -176,27 +188,10 @@ class ServerRestJsonProtocol( override val protocolModulePath: String = "rest_json_1" - override fun structuredDataParser(operationShape: OperationShape): StructuredDataParserGenerator { - fun returnSymbolToParse(shape: Shape): ReturnSymbolToParse = - if (shape.canReachConstrainedShape(codegenContext.model, codegenContext.symbolProvider)) { - ReturnSymbolToParse(serverCodegenContext.unconstrainedShapeSymbolProvider.toSymbol(shape), true) - } else { - ReturnSymbolToParse(serverCodegenContext.symbolProvider.toSymbol(shape), false) - } - return JsonParserGenerator( - codegenContext, - httpBindingResolver, - ::restJsonFieldName, - ::returnSymbolToParse, - listOf( - ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization( - serverCodegenContext, - ), - ), - ) - } + override fun structuredDataParser(): StructuredDataParserGenerator = + jsonParserGenerator(serverCodegenContext, httpBindingResolver, ::restJsonFieldName) - override fun structuredDataSerializer(operationShape: OperationShape): StructuredDataSerializerGenerator = + override fun structuredDataSerializer(): StructuredDataSerializerGenerator = ServerRestJsonSerializerGenerator(serverCodegenContext, httpBindingResolver) override fun markerStruct() = ServerRuntimeType.protocol("RestJson1", protocolModulePath, runtimeConfig) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 6bf8dba7ba..9a7e45f112 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -390,7 +390,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( errorSymbol: Symbol, ) { val operationName = symbolProvider.toSymbol(operationShape).name - val structuredDataSerializer = protocol.structuredDataSerializer(operationShape) + val structuredDataSerializer = protocol.structuredDataSerializer() withBlock("match error {", "}") { val errors = operationShape.operationErrors(model) errors.forEach { @@ -612,7 +612,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( bindings: List, ) { val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape) - val structuredDataParser = protocol.structuredDataParser(operationShape) + val structuredDataParser = protocol.structuredDataParser() Attribute.AllowUnusedMut.render(this) rust( "let mut input = #T::default();", From 035c99e771980c23be20607eac8f51758aaeb083 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 5 Apr 2023 10:48:12 +0200 Subject: [PATCH 007/253] `#[allow(unused_variables)]` in JSON serializers when structure has no members (#2538) Which is more informational than generating artificial code to suppress the Clippy warning. --- .../serialize/JsonSerializerGenerator.kt | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt index 33b06973b0..efe178db8d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -26,12 +26,14 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.TimestampShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.TimestampFormatTrait.Format.EPOCH_SECONDS +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock +import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider @@ -312,21 +314,25 @@ class JsonSerializerGenerator( context: StructContext, includedMembers: List? = null, ) { - val structureSymbol = symbolProvider.toSymbol(context.shape) val structureSerializer = protocolFunctions.serializeFn(context.shape) { fnName -> + val inner = context.copy(objectName = "object", localName = "input") + val members = includedMembers ?: inner.shape.members() + val allowUnusedVariables = writable { + if (members.isEmpty()) { Attribute.AllowUnusedVariables.render(this) } + } rustBlockTemplate( - "pub fn $fnName(object: &mut #{JsonObjectWriter}, input: &#{Input}) -> Result<(), #{Error}>", - "Input" to structureSymbol, + """ + pub fn $fnName( + #{AllowUnusedVariables:W} object: &mut #{JsonObjectWriter}, + #{AllowUnusedVariables:W} input: &#{StructureSymbol}, + ) -> Result<(), #{Error}> + """, + "StructureSymbol" to symbolProvider.toSymbol(context.shape), + "AllowUnusedVariables" to allowUnusedVariables, *codegenScope, ) { - context.copy(objectName = "object", localName = "input").also { inner -> - val members = includedMembers ?: inner.shape.members() - if (members.isEmpty()) { - rust("let (_, _) = (object, input);") // Suppress unused argument warnings - } - for (member in members) { - serializeMember(MemberContext.structMember(inner, member, symbolProvider, jsonName)) - } + for (member in members) { + serializeMember(MemberContext.structMember(inner, member, symbolProvider, jsonName)) } rust("Ok(())") } From c7c82855986673fc950c532d4cbdfa6cee4cc201 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 5 Apr 2023 12:38:22 +0200 Subject: [PATCH 008/253] Tighten server rejection types (#2542) Rejection types used to be loose, but now that they are protocol-specific (see #2517), they can be tightened up to be more useful. Where possible, variants now no longer take in the dynamic `crate::Error`, instead taking in concrete error types arising from particular fallible invocations in the generated SDKs.This makes it easier to see how errors arise, and allows for more specific and helpful error messages that can be logged. We `#[derive(thiserror::Error)]` on rejection types to cut down on boilerplate code. This commit removes the dependency on `strum_macros`. --- .../aws-smithy-http-server/Cargo.toml | 1 - .../aws-smithy-http-server/src/macros.rs | 10 -- .../src/proto/aws_json/rejection.rs | 43 +++---- .../src/proto/rest_json_1/rejection.rs | 108 ++++++++---------- .../src/proto/rest_xml/rejection.rs | 83 +++++--------- .../aws-smithy-http-server/src/rejection.rs | 10 +- 6 files changed, 102 insertions(+), 153 deletions(-) diff --git a/rust-runtime/aws-smithy-http-server/Cargo.toml b/rust-runtime/aws-smithy-http-server/Cargo.toml index e5226e910a..96ee56f79b 100644 --- a/rust-runtime/aws-smithy-http-server/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server/Cargo.toml @@ -35,7 +35,6 @@ pin-project-lite = "0.2" once_cell = "1.13" regex = "1.5.5" serde_urlencoded = "0.7" -strum_macros = "0.24" thiserror = "1.0.0" tracing = "0.1.35" tokio = { version = "1.23.1", features = ["full"] } diff --git a/rust-runtime/aws-smithy-http-server/src/macros.rs b/rust-runtime/aws-smithy-http-server/src/macros.rs index b7df36188d..7f70ba5562 100644 --- a/rust-runtime/aws-smithy-http-server/src/macros.rs +++ b/rust-runtime/aws-smithy-http-server/src/macros.rs @@ -94,13 +94,3 @@ macro_rules! convert_to_request_rejection { } }; } - -macro_rules! convert_to_response_rejection { - ($from:ty, $to:ident) => { - impl From<$from> for ResponseRejection { - fn from(err: $from) -> Self { - Self::$to(crate::Error::new(err)) - } - } - }; -} diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs index 1a74168f4a..10b94964df 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs @@ -3,45 +3,34 @@ * SPDX-License-Identifier: Apache-2.0 */ -use strum_macros::Display; - use crate::rejection::MissingContentTypeReason; +use thiserror::Error; -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum ResponseRejection { - Serialization(crate::Error), - Http(crate::Error), + #[error("error serializing JSON-encoded body: {0}")] + Serialization(#[from] aws_smithy_http::operation::error::SerializationError), + #[error("error building HTTP response: {0}")] + HttpBuild(#[from] http::Error), } -impl std::error::Error for ResponseRejection {} - -convert_to_response_rejection!(aws_smithy_http::operation::error::SerializationError, Serialization); -convert_to_response_rejection!(http::Error, Http); - -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum RequestRejection { - HttpBody(crate::Error), - MissingContentType(MissingContentTypeReason), - JsonDeserialize(crate::Error), + #[error("error converting non-streaming body to bytes: {0}")] + BufferHttpBodyBytes(crate::Error), + #[error("expected `Content-Type` header not found: {0}")] + MissingContentType(#[from] MissingContentTypeReason), + #[error("error deserializing request HTTP body as JSON: {0}")] + JsonDeserialize(#[from] aws_smithy_json::deserialize::error::DeserializeError), + #[error("request does not adhere to modeled constraints: {0}")] ConstraintViolation(String), } -impl std::error::Error for RequestRejection {} - impl From for RequestRejection { fn from(_err: std::convert::Infallible) -> Self { match _err {} } } -impl From for RequestRejection { - fn from(e: MissingContentTypeReason) -> Self { - Self::MissingContentType(e) - } -} - -convert_to_request_rejection!(aws_smithy_json::deserialize::error::DeserializeError, JsonDeserialize); - -convert_to_request_rejection!(hyper::Error, HttpBody); - -convert_to_request_rejection!(Box, HttpBody); +convert_to_request_rejection!(hyper::Error, BufferHttpBodyBytes); +convert_to_request_rejection!(Box, BufferHttpBodyBytes); diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs index 6f521880af..87925e2f03 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs @@ -47,30 +47,36 @@ //! //! Consult `crate::proto::$protocolName::rejection` for rejection types for other protocols. -use std::num::TryFromIntError; - -use strum_macros::Display; - use crate::rejection::MissingContentTypeReason; +use std::num::TryFromIntError; +use thiserror::Error; /// Errors that can occur when serializing the operation output provided by the service implementer /// into an HTTP response. -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum ResponseRejection { /// Used when the service implementer provides an integer outside the 100-999 range for a /// member targeted by `httpResponseCode`. /// See . + #[error("invalid bound HTTP status code; status codes must be inside the 100-999 range: {0}")] InvalidHttpStatusCode(TryFromIntError), - /// Used when an invalid HTTP header value (a value that cannot be parsed as an - /// `[http::header::HeaderValue]`) is provided for a shape member bound to an HTTP header with + /// Used when an invalid HTTP header name (a value that cannot be parsed as an + /// [`http::header::HeaderName`]) or HTTP header value (a value that cannot be parsed as an + /// [`http::header::HeaderValue`]) is provided for a shape member bound to an HTTP header with /// `httpHeader` or `httpPrefixHeaders`. /// Used when failing to serialize an `httpPayload`-bound struct into an HTTP response body. - Build(crate::Error), - - /// Used when failing to serialize a struct into a `String` for the HTTP response body (for - /// example, converting a struct into a JSON-encoded `String`). - Serialization(crate::Error), + #[error("error building HTTP response: {0}")] + Build(#[from] aws_smithy_http::operation::error::BuildError), + + /// Used when failing to serialize a struct into a `String` for the JSON-encoded HTTP response + /// body. + /// Fun fact: as of writing, this can only happen when date formatting + /// (`aws_smithy_types::date_time::DateTime:fmt`) fails, which can only happen if the + /// supplied timestamp is outside of the valid range when formatting using RFC-3339, i.e. a + /// date outside the `0001-01-01T00:00:00.000Z`-`9999-12-31T23:59:59.999Z` range is supplied. + #[error("error serializing JSON-encoded body: {0}")] + Serialization(#[from] aws_smithy_http::operation::error::SerializationError), /// Used when consuming an [`http::response::Builder`] into the constructed [`http::Response`] /// when calling [`http::response::Builder::body`]. @@ -78,15 +84,10 @@ pub enum ResponseRejection { /// `[http::header::HeaderValue]`) is used for the protocol-specific response `Content-Type` /// header, or for additional protocol-specific headers (like `X-Amzn-Errortype` to signal /// errors in RestJson1). - Http(crate::Error), + #[error("error building HTTP response: {0}")] + HttpBuild(#[from] http::Error), } -impl std::error::Error for ResponseRejection {} - -convert_to_response_rejection!(aws_smithy_http::operation::error::BuildError, Build); -convert_to_response_rejection!(aws_smithy_http::operation::error::SerializationError, Serialization); -convert_to_response_rejection!(http::Error, Http); - /// Errors that can occur when deserializing an HTTP request into an _operation input_, the input /// that is passed as the first argument to operation handlers. /// @@ -98,59 +99,65 @@ convert_to_response_rejection!(http::Error, Http); /// deliberate design choice to keep code generation simple. After all, this type is an inner /// detail of the framework the service implementer does not interact with. /// -/// If a variant takes in a value, it represents the underlying cause of the error. This inner -/// value should be of the type-erased boxed error type `[crate::Error]`. In practice, some of the -/// variants that take in a value are only instantiated with errors of a single type in the -/// generated code. For example, `UriPatternMismatch` is only instantiated with an error coming -/// from a `nom` parser, `nom::Err>`. This is reflected in the converters -/// below that convert from one of these very specific error types into one of the variants. For -/// example, the `RequestRejection` implements `From` to construct the `HttpBody` -/// variant. This is a deliberate design choice to make the code simpler and less prone to changes. +/// If a variant takes in a value, it represents the underlying cause of the error. /// /// The variants are _roughly_ sorted in the order in which the HTTP request is processed. -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum RequestRejection { /// Used when failing to convert non-streaming requests into a byte slab with /// `hyper::body::to_bytes`. - HttpBody(crate::Error), + #[error("error converting non-streaming body to bytes: {0}")] + BufferHttpBodyBytes(crate::Error), /// Used when checking the `Content-Type` header. - MissingContentType(MissingContentTypeReason), + #[error("expected `Content-Type` header not found: {0}")] + MissingContentType(#[from] MissingContentTypeReason), /// Used when failing to deserialize the HTTP body's bytes into a JSON document conforming to /// the modeled input it should represent. - JsonDeserialize(crate::Error), + #[error("error deserializing request HTTP body as JSON: {0}")] + JsonDeserialize(#[from] aws_smithy_json::deserialize::error::DeserializeError), /// Used when failing to parse HTTP headers that are bound to input members with the `httpHeader` /// or the `httpPrefixHeaders` traits. - HeaderParse(crate::Error), + #[error("error binding request HTTP headers: {0}")] + HeaderParse(#[from] aws_smithy_http::header::ParseError), + // In theory, the next two errors should never happen because the router should have already + // rejected the request. /// Used when the URI pattern has a literal after the greedy label, and it is not found in the /// request's URL. + #[error("request URI does not match pattern because of literal suffix after greedy label was not found")] UriPatternGreedyLabelPostfixNotFound, /// Used when the `nom` parser's input does not match the URI pattern. + #[error("request URI does not match `@http` URI pattern: {0}")] UriPatternMismatch(crate::Error), /// Used when percent-decoding URL query string. /// Used when percent-decoding URI path label. - InvalidUtf8(crate::Error), + /// This is caused when calling + /// [`percent_encoding::percent_decode_str`](https://docs.rs/percent-encoding/latest/percent_encoding/fn.percent_decode_str.html). + /// This can happen when the percent-encoded data decodes to bytes that are + /// not a well-formed UTF-8 string. + #[error("request URI cannot be percent decoded into valid UTF-8")] + PercentEncodedUriNotValidUtf8(#[from] core::str::Utf8Error), /// Used when failing to deserialize strings from a URL query string and from URI path labels /// into an [`aws_smithy_types::DateTime`]. - DateTimeParse(crate::Error), + #[error("error parsing timestamp from request URI: {0}")] + DateTimeParse(#[from] aws_smithy_types::date_time::DateTimeParseError), /// Used when failing to deserialize strings from a URL query string and from URI path labels /// into "primitive" types. - PrimitiveParse(crate::Error), + #[error("error parsing primitive type from request URI: {0}")] + PrimitiveParse(#[from] aws_smithy_types::primitive::PrimitiveParseError), /// Used when consuming the input struct builder, and constraint violations occur. - // Unlike the rejections above, this does not take in `crate::Error`, since it is constructed - // directly in the code-generated SDK instead of in this crate. + // This rejection is constructed directly in the code-generated SDK instead of in this crate. + #[error("request does not adhere to modeled constraints: {0}")] ConstraintViolation(String), } -impl std::error::Error for RequestRejection {} - // Consider a conversion between `T` and `U` followed by a bubbling up of the conversion error // through `Result<_, RequestRejection>`. This [`From`] implementation accomodates the special case // where `T` and `U` are equal, in such cases `T`/`U` a enjoy `TryFrom` with @@ -170,43 +177,24 @@ impl From for RequestRejection { } } -impl From for RequestRejection { - fn from(e: MissingContentTypeReason) -> Self { - Self::MissingContentType(e) - } -} - // These converters are solely to make code-generation simpler. They convert from a specific error // type (from a runtime/third-party crate or the standard library) into a variant of the // [`crate::rejection::RequestRejection`] enum holding the type-erased boxed [`crate::Error`] // type. Generated functions that use [crate::rejection::RequestRejection] can thus use `?` to // bubble up instead of having to sprinkle things like [`Result::map_err`] everywhere. -convert_to_request_rejection!(aws_smithy_json::deserialize::error::DeserializeError, JsonDeserialize); -convert_to_request_rejection!(aws_smithy_http::header::ParseError, HeaderParse); -convert_to_request_rejection!(aws_smithy_types::date_time::DateTimeParseError, DateTimeParse); -convert_to_request_rejection!(aws_smithy_types::primitive::PrimitiveParseError, PrimitiveParse); -convert_to_request_rejection!(serde_urlencoded::de::Error, InvalidUtf8); - impl From>> for RequestRejection { fn from(err: nom::Err>) -> Self { Self::UriPatternMismatch(crate::Error::new(err.to_owned())) } } -// Used when calling -// [`percent_encoding::percent_decode_str`](https://docs.rs/percent-encoding/latest/percent_encoding/fn.percent_decode_str.html) -// and bubbling up. -// This can happen when the percent-encoded data in e.g. a query string decodes to bytes that are -// not a well-formed UTF-8 string. -convert_to_request_rejection!(std::str::Utf8Error, InvalidUtf8); - // `[crate::body::Body]` is `[hyper::Body]`, whose associated `Error` type is `[hyper::Error]`. We // need this converter for when we convert the body into bytes in the framework, since protocol // tests use `[crate::body::Body]` as their body type when constructing requests (and almost // everyone will run a Hyper-based server in their services). -convert_to_request_rejection!(hyper::Error, HttpBody); +convert_to_request_rejection!(hyper::Error, BufferHttpBodyBytes); // Useful in general, but it also required in order to accept Lambda HTTP requests using // `Router` since `lambda_http::Error` is a type alias for `Box`. -convert_to_request_rejection!(Box, HttpBody); +convert_to_request_rejection!(Box, BufferHttpBodyBytes); diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs index 272f69f7b6..829d330dfe 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs @@ -3,92 +3,71 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! This module hosts _exactly_ the same as [`crate::proto::rest_json_1::rejection`], expect that +//! This module hosts _exactly_ the same as [`crate::proto::rest_json_1::rejection`], except that //! [`crate::proto::rest_json_1::rejection::RequestRejection::JsonDeserialize`] is swapped for //! [`RequestRejection::XmlDeserialize`]. +use crate::rejection::MissingContentTypeReason; use std::num::TryFromIntError; +use thiserror::Error; -use strum_macros::Display; - -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum ResponseRejection { + #[error("invalid bound HTTP status code; status codes must be inside the 100-999 range: {0}")] InvalidHttpStatusCode(TryFromIntError), - Build(crate::Error), - Serialization(crate::Error), - Http(crate::Error), + #[error("error building HTTP response: {0}")] + Build(#[from] aws_smithy_http::operation::error::BuildError), + #[error("error serializing XML-encoded body: {0}")] + Serialization(#[from] aws_smithy_http::operation::error::SerializationError), + #[error("error building HTTP response: {0}")] + HttpBuild(#[from] http::Error), } -impl std::error::Error for ResponseRejection {} - -convert_to_response_rejection!(aws_smithy_http::operation::error::BuildError, Build); -convert_to_response_rejection!(aws_smithy_http::operation::error::SerializationError, Serialization); -convert_to_response_rejection!(http::Error, Http); - -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum RequestRejection { - HttpBody(crate::Error), + #[error("error converting non-streaming body to bytes: {0}")] + BufferHttpBodyBytes(crate::Error), - MissingContentType(MissingContentTypeReason), + #[error("expected `Content-Type` header not found: {0}")] + MissingContentType(#[from] MissingContentTypeReason), /// Used when failing to deserialize the HTTP body's bytes into a XML conforming to the modeled /// input it should represent. - XmlDeserialize(crate::Error), + #[error("error deserializing request HTTP body as XML: {0}")] + XmlDeserialize(#[from] aws_smithy_xml::decode::XmlDecodeError), - HeaderParse(crate::Error), + #[error("error binding request HTTP headers: {0}")] + HeaderParse(#[from] aws_smithy_http::header::ParseError), + #[error("request URI does not match pattern because of literal suffix after greedy label was not found")] UriPatternGreedyLabelPostfixNotFound, + #[error("request URI does not match `@http` URI pattern: {0}")] UriPatternMismatch(crate::Error), - InvalidUtf8(crate::Error), + #[error("request URI cannot be percent decoded into valid UTF-8")] + PercentEncodedUriNotValidUtf8(#[from] core::str::Utf8Error), - DateTimeParse(crate::Error), + #[error("error parsing timestamp from request URI: {0}")] + DateTimeParse(#[from] aws_smithy_types::date_time::DateTimeParseError), - PrimitiveParse(crate::Error), + #[error("error parsing primitive type from request URI: {0}")] + PrimitiveParse(#[from] aws_smithy_types::primitive::PrimitiveParseError), + #[error("request does not adhere to modeled constraints: {0}")] ConstraintViolation(String), } -#[derive(Debug, Display)] -pub enum MissingContentTypeReason { - HeadersTakenByAnotherExtractor, - NoContentTypeHeader, - ToStrError(http::header::ToStrError), - MimeParseError(mime::FromStrError), - UnexpectedMimeType { - expected_mime: Option, - found_mime: Option, - }, -} - -impl std::error::Error for RequestRejection {} - impl From for RequestRejection { fn from(_err: std::convert::Infallible) -> Self { match _err {} } } -impl From for RequestRejection { - fn from(e: MissingContentTypeReason) -> Self { - Self::MissingContentType(e) - } -} - -convert_to_request_rejection!(aws_smithy_xml::decode::XmlDecodeError, XmlDeserialize); -convert_to_request_rejection!(aws_smithy_http::header::ParseError, HeaderParse); -convert_to_request_rejection!(aws_smithy_types::date_time::DateTimeParseError, DateTimeParse); -convert_to_request_rejection!(aws_smithy_types::primitive::PrimitiveParseError, PrimitiveParse); -convert_to_request_rejection!(serde_urlencoded::de::Error, InvalidUtf8); - impl From>> for RequestRejection { fn from(err: nom::Err>) -> Self { Self::UriPatternMismatch(crate::Error::new(err.to_owned())) } } -convert_to_request_rejection!(std::str::Utf8Error, InvalidUtf8); - -convert_to_request_rejection!(hyper::Error, HttpBody); - -convert_to_request_rejection!(Box, HttpBody); +convert_to_request_rejection!(hyper::Error, BufferHttpBodyBytes); +convert_to_request_rejection!(Box, BufferHttpBodyBytes); diff --git a/rust-runtime/aws-smithy-http-server/src/rejection.rs b/rust-runtime/aws-smithy-http-server/src/rejection.rs index 209a6721f3..1f1e247435 100644 --- a/rust-runtime/aws-smithy-http-server/src/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/rejection.rs @@ -3,17 +3,21 @@ * SPDX-License-Identifier: Apache-2.0 */ -use strum_macros::Display; - use crate::response::IntoResponse; +use thiserror::Error; // This is used across different protocol-specific `rejection` modules. -#[derive(Debug, Display)] +#[derive(Debug, Error)] pub enum MissingContentTypeReason { + #[error("headers taken by another extractor")] HeadersTakenByAnotherExtractor, + #[error("no `Content-Type` header")] NoContentTypeHeader, + #[error("`Content-Type` header value is not a valid HTTP header value: {0}")] ToStrError(http::header::ToStrError), + #[error("invalid `Content-Type` header value mime type: {0}")] MimeParseError(mime::FromStrError), + #[error("unexpected `Content-Type` header value; expected {expected_mime:?}, found {found_mime:?}")] UnexpectedMimeType { expected_mime: Option, found_mime: Option, From 9006fe8775007c7451c86602101184c0820cc7e6 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 5 Apr 2023 10:33:03 -0700 Subject: [PATCH 009/253] Add SigV4 support to the orchestrator (#2533) * Create the `aws-runtime` and `aws-runtime-api` crates * Implement SigV4 in the orchestrator * CI fixes * Connect the service config http connector to the orchestrator * Make it possible to register interceptors, and get signing to pass tests * Fix allowed types lint * Add missing docs.rs metadata --- aws/rust-runtime/Cargo.toml | 4 +- aws/rust-runtime/aws-runtime-api/Cargo.toml | 16 + aws/rust-runtime/aws-runtime-api/LICENSE | 175 ++++++++++ aws/rust-runtime/aws-runtime-api/README.md | 8 + .../aws-runtime-api/external-types.toml | 2 + aws/rust-runtime/aws-runtime-api/src/lib.rs | 14 + aws/rust-runtime/aws-runtime/Cargo.toml | 25 ++ aws/rust-runtime/aws-runtime/LICENSE | 175 ++++++++++ aws/rust-runtime/aws-runtime/README.md | 8 + .../aws-runtime/external-types.toml | 6 + aws/rust-runtime/aws-runtime/src/auth.rs | 300 ++++++++++++++++++ aws/rust-runtime/aws-runtime/src/identity.rs | 38 +++ aws/rust-runtime/aws-runtime/src/lib.rs | 20 ++ .../smithy/rustsdk/AwsCargoDependency.kt | 4 +- .../smithy/rustsdk/AwsCodegenDecorator.kt | 1 + .../amazon/smithy/rustsdk/AwsRuntimeType.kt | 3 + .../smithy/rustsdk/CredentialProviders.kt | 38 +++ .../smithy/rustsdk/SigV4AuthDecorator.kt | 164 ++++++++++ .../smithy/rustsdk/SigV4SigningDecorator.kt | 1 + .../integration-tests/aws-sdk-s3/Cargo.toml | 9 +- .../aws-sdk-s3/tests/sra_test.rs | 215 ++++++++++++- buildSrc/src/main/kotlin/CrateSet.kt | 2 + .../client/smithy/ClientCodegenVisitor.kt | 2 +- .../customize/ClientCodegenDecorator.kt | 34 ++ .../OperationRuntimePluginGenerator.kt | 46 ++- .../smithy/generators/ServiceGenerator.kt | 19 +- .../ServiceRuntimePluginGenerator.kt | 120 +++++-- .../protocol/ClientProtocolGenerator.kt | 30 +- rust-runtime/aws-smithy-client/src/erase.rs | 10 + .../aws-smithy-runtime-api/Cargo.toml | 5 +- .../aws-smithy-runtime-api/src/client/auth.rs | 1 - .../src/client/auth/identity_resolver.rs | 26 -- .../src/client/auth/option_resolver.rs | 27 +- .../src/client/endpoints.rs | 20 +- .../src/client/identity.rs | 38 ++- .../src/client/interceptors.rs | 118 +++++-- .../src/client/interceptors/context.rs | 7 + .../src/client/orchestrator.rs | 30 +- rust-runtime/aws-smithy-runtime/Cargo.toml | 2 +- .../aws-smithy-runtime/external-types.toml | 1 + .../src/client/connections.rs | 29 ++ .../src/client/connections/test_connection.rs | 27 +- .../src/client/orchestrator.rs | 16 +- .../src/client/orchestrator/auth.rs | 48 ++- .../src/client/orchestrator/phase.rs | 2 +- 45 files changed, 1692 insertions(+), 194 deletions(-) create mode 100644 aws/rust-runtime/aws-runtime-api/Cargo.toml create mode 100644 aws/rust-runtime/aws-runtime-api/LICENSE create mode 100644 aws/rust-runtime/aws-runtime-api/README.md create mode 100644 aws/rust-runtime/aws-runtime-api/external-types.toml create mode 100644 aws/rust-runtime/aws-runtime-api/src/lib.rs create mode 100644 aws/rust-runtime/aws-runtime/Cargo.toml create mode 100644 aws/rust-runtime/aws-runtime/LICENSE create mode 100644 aws/rust-runtime/aws-runtime/README.md create mode 100644 aws/rust-runtime/aws-runtime/external-types.toml create mode 100644 aws/rust-runtime/aws-runtime/src/auth.rs create mode 100644 aws/rust-runtime/aws-runtime/src/identity.rs create mode 100644 aws/rust-runtime/aws-runtime/src/lib.rs create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt delete mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/auth/identity_resolver.rs diff --git a/aws/rust-runtime/Cargo.toml b/aws/rust-runtime/Cargo.toml index b4e4670550..0e32e03bfc 100644 --- a/aws/rust-runtime/Cargo.toml +++ b/aws/rust-runtime/Cargo.toml @@ -8,9 +8,11 @@ members = [ "aws-http", "aws-hyper", "aws-inlineable", + "aws-runtime", + "aws-runtime-api", "aws-sig-auth", + "aws-sigv4", "aws-types", - "aws-sigv4" ] exclude = ["aws-config"] diff --git a/aws/rust-runtime/aws-runtime-api/Cargo.toml b/aws/rust-runtime/aws-runtime-api/Cargo.toml new file mode 100644 index 0000000000..49353b7d08 --- /dev/null +++ b/aws/rust-runtime/aws-runtime-api/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "aws-runtime-api" +version = "0.0.0-smithy-rs-head" +authors = ["AWS Rust SDK Team "] +description = "Runtime support code for the AWS SDK. This isn't intended to be used directly." +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" + +[dependencies] + +[package.metadata.docs.rs] +all-features = true +targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = ["--cfg", "docsrs"] +# End of docs.rs metadata diff --git a/aws/rust-runtime/aws-runtime-api/LICENSE b/aws/rust-runtime/aws-runtime-api/LICENSE new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/aws/rust-runtime/aws-runtime-api/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/aws/rust-runtime/aws-runtime-api/README.md b/aws/rust-runtime/aws-runtime-api/README.md new file mode 100644 index 0000000000..3eb3bd4193 --- /dev/null +++ b/aws/rust-runtime/aws-runtime-api/README.md @@ -0,0 +1,8 @@ +aws-runtime-api +=============== + +Runtime support code for the AWS SDK. This crate isn't intended to be used directly. + + +This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. + diff --git a/aws/rust-runtime/aws-runtime-api/external-types.toml b/aws/rust-runtime/aws-runtime-api/external-types.toml new file mode 100644 index 0000000000..ff30ccf5ad --- /dev/null +++ b/aws/rust-runtime/aws-runtime-api/external-types.toml @@ -0,0 +1,2 @@ +allowed_external_types = [ +] diff --git a/aws/rust-runtime/aws-runtime-api/src/lib.rs b/aws/rust-runtime/aws-runtime-api/src/lib.rs new file mode 100644 index 0000000000..c66728daf4 --- /dev/null +++ b/aws/rust-runtime/aws-runtime-api/src/lib.rs @@ -0,0 +1,14 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Runtime support code for the AWS SDK. This crate isn't intended to be used directly. + +#![warn( + missing_docs, + rustdoc::missing_crate_level_docs, + missing_debug_implementations, + rust_2018_idioms, + unreachable_pub +)] diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml new file mode 100644 index 0000000000..f2e4ab2bf9 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "aws-runtime" +version = "0.0.0-smithy-rs-head" +authors = ["AWS Rust SDK Team "] +description = "Runtime support code for the AWS SDK. This crate isn't intended to be used directly." +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" + +[dependencies] +aws-types = { path = "../aws-types" } +aws-credential-types = { path = "../aws-credential-types" } +aws-sigv4 = { path = "../aws-sigv4" } +aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } +aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } +tracing = "0.1" + +[dev-dependencies] +tracing-test = "0.2.1" + +[package.metadata.docs.rs] +all-features = true +targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = ["--cfg", "docsrs"] +# End of docs.rs metadata diff --git a/aws/rust-runtime/aws-runtime/LICENSE b/aws/rust-runtime/aws-runtime/LICENSE new file mode 100644 index 0000000000..67db858821 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/aws/rust-runtime/aws-runtime/README.md b/aws/rust-runtime/aws-runtime/README.md new file mode 100644 index 0000000000..65b01de9e2 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/README.md @@ -0,0 +1,8 @@ +aws-runtime +=========== + +Runtime support code for the AWS SDK. This crate isn't intended to be used directly. + + +This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. + diff --git a/aws/rust-runtime/aws-runtime/external-types.toml b/aws/rust-runtime/aws-runtime/external-types.toml new file mode 100644 index 0000000000..3d92860f16 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/external-types.toml @@ -0,0 +1,6 @@ +allowed_external_types = [ + "aws_credential_types::*", + "aws_sigv4::*", + "aws_smithy_runtime_api::*", + "aws_types::*", +] diff --git a/aws/rust-runtime/aws-runtime/src/auth.rs b/aws/rust-runtime/aws-runtime/src/auth.rs new file mode 100644 index 0000000000..4546d58aee --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/auth.rs @@ -0,0 +1,300 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Auth implementations for SigV4. +pub mod sigv4 { + use aws_credential_types::Credentials; + use aws_sigv4::http_request::{ + sign, PayloadChecksumKind, PercentEncodingMode, SessionTokenMode, SignableBody, + SignableRequest, SignatureLocation, SigningParams, SigningSettings, + UriPathNormalizationMode, + }; + use aws_smithy_runtime_api::client::identity::Identity; + use aws_smithy_runtime_api::client::orchestrator::{ + BoxError, HttpAuthScheme, HttpRequest, HttpRequestSigner, IdentityResolver, + IdentityResolvers, + }; + use aws_smithy_runtime_api::config_bag::ConfigBag; + use aws_types::region::SigningRegion; + use aws_types::SigningService; + use std::time::{Duration, SystemTime}; + + const EXPIRATION_WARNING: &str = "Presigned request will expire before the given \ + `expires_in` duration because the credentials used to sign it will expire first."; + + /// Auth scheme ID for SigV4. + pub const SCHEME_ID: &str = "sigv4"; + + /// SigV4 auth scheme. + #[derive(Debug, Default)] + pub struct SigV4HttpAuthScheme { + signer: SigV4HttpRequestSigner, + } + + impl SigV4HttpAuthScheme { + /// Creates a new `SigV4HttpAuthScheme`. + pub fn new() -> Self { + Default::default() + } + } + + impl HttpAuthScheme for SigV4HttpAuthScheme { + fn scheme_id(&self) -> &'static str { + SCHEME_ID + } + + fn identity_resolver<'a>( + &self, + identity_resolvers: &'a IdentityResolvers, + ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + &self.signer + } + } + + /// Type of SigV4 signature. + #[derive(Debug, Eq, PartialEq, Clone, Copy)] + pub enum HttpSignatureType { + /// A signature for a full http request should be computed, with header updates applied to the signing result. + HttpRequestHeaders, + + /// A signature for a full http request should be computed, with query param updates applied to the signing result. + /// + /// This is typically used for presigned URLs. + HttpRequestQueryParams, + } + + /// Signing options for SigV4. + #[derive(Clone, Debug, Eq, PartialEq)] + #[non_exhaustive] + pub struct SigningOptions { + /// Apply URI encoding twice. + pub double_uri_encode: bool, + /// Apply a SHA-256 payload checksum. + pub content_sha256_header: bool, + /// Normalize the URI path before signing. + pub normalize_uri_path: bool, + /// Omit the session token from the signature. + pub omit_session_token: bool, + /// Optional override for the payload to be used in signing. + pub payload_override: Option>, + /// Signature type. + pub signature_type: HttpSignatureType, + /// Whether or not the signature is optional. + pub signing_optional: bool, + /// Optional expiration (for presigning) + pub expires_in: Option, + /// Timestamp to sign with. + pub request_timestamp: SystemTime, + } + + impl Default for SigningOptions { + fn default() -> Self { + Self { + double_uri_encode: true, + content_sha256_header: false, + normalize_uri_path: true, + omit_session_token: false, + payload_override: None, + signature_type: HttpSignatureType::HttpRequestHeaders, + signing_optional: false, + expires_in: None, + request_timestamp: SystemTime::now(), + } + } + } + + /// SigV4 signing configuration for an operation + /// + /// Although these fields MAY be customized on a per request basis, they are generally static + /// for a given operation + #[derive(Clone, Debug, PartialEq, Eq)] + pub struct SigV4OperationSigningConfig { + /// AWS Region to sign for. + pub region: SigningRegion, + /// AWS Service to sign for. + pub service: SigningService, + /// Signing options. + pub signing_options: SigningOptions, + } + + /// SigV4 HTTP request signer. + #[derive(Debug, Default)] + pub struct SigV4HttpRequestSigner; + + impl SigV4HttpRequestSigner { + /// Creates a new signer instance. + pub fn new() -> Self { + Self + } + + fn settings(operation_config: &SigV4OperationSigningConfig) -> SigningSettings { + let mut settings = SigningSettings::default(); + settings.percent_encoding_mode = if operation_config.signing_options.double_uri_encode { + PercentEncodingMode::Double + } else { + PercentEncodingMode::Single + }; + settings.payload_checksum_kind = + if operation_config.signing_options.content_sha256_header { + PayloadChecksumKind::XAmzSha256 + } else { + PayloadChecksumKind::NoHeader + }; + settings.uri_path_normalization_mode = + if operation_config.signing_options.normalize_uri_path { + UriPathNormalizationMode::Enabled + } else { + UriPathNormalizationMode::Disabled + }; + settings.session_token_mode = if operation_config.signing_options.omit_session_token { + SessionTokenMode::Exclude + } else { + SessionTokenMode::Include + }; + settings.signature_location = match operation_config.signing_options.signature_type { + HttpSignatureType::HttpRequestHeaders => SignatureLocation::Headers, + HttpSignatureType::HttpRequestQueryParams => SignatureLocation::QueryParams, + }; + settings.expires_in = operation_config.signing_options.expires_in; + settings + } + + fn signing_params<'a>( + settings: SigningSettings, + credentials: &'a Credentials, + operation_config: &'a SigV4OperationSigningConfig, + ) -> SigningParams<'a> { + if let Some(expires_in) = settings.expires_in { + if let Some(creds_expires_time) = credentials.expiry() { + let presigned_expires_time = + operation_config.signing_options.request_timestamp + expires_in; + if presigned_expires_time > creds_expires_time { + tracing::warn!(EXPIRATION_WARNING); + } + } + } + + let mut builder = SigningParams::builder() + .access_key(credentials.access_key_id()) + .secret_key(credentials.secret_access_key()) + .region(operation_config.region.as_ref()) + .service_name(operation_config.service.as_ref()) + .time(operation_config.signing_options.request_timestamp) + .settings(settings); + builder.set_security_token(credentials.session_token()); + builder.build().expect("all required fields set") + } + } + + impl HttpRequestSigner for SigV4HttpRequestSigner { + fn sign_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + cfg: &ConfigBag, + ) -> Result<(), BoxError> { + let operation_config = cfg + .get::() + .ok_or("missing operation signing config for SigV4")?; + + let credentials = if let Some(creds) = identity.data::() { + creds + } else if operation_config.signing_options.signing_optional { + tracing::debug!("skipped SigV4 signing since signing is optional for this operation and there are no credentials"); + return Ok(()); + } else { + return Err(format!("wrong identity type for SigV4: {identity:?}").into()); + }; + + let settings = Self::settings(operation_config); + let signing_params = Self::signing_params(settings, credentials, operation_config); + + let (signing_instructions, _signature) = { + // A body that is already in memory can be signed directly. A body that is not in memory + // (any sort of streaming body or presigned request) will be signed via UNSIGNED-PAYLOAD. + let signable_body = operation_config + .signing_options + .payload_override + .as_ref() + // the payload_override is a cheap clone because it contains either a + // reference or a short checksum (we're not cloning the entire body) + .cloned() + .unwrap_or_else(|| { + request + .body() + .bytes() + .map(SignableBody::Bytes) + .unwrap_or(SignableBody::UnsignedPayload) + }); + + let signable_request = SignableRequest::new( + request.method(), + request.uri(), + request.headers(), + signable_body, + ); + sign(signable_request, &signing_params)? + } + .into_parts(); + + signing_instructions.apply_to_request(request); + Ok(()) + } + } + + #[cfg(test)] + mod tests { + use super::*; + use aws_credential_types::Credentials; + use aws_sigv4::http_request::SigningSettings; + use std::time::{Duration, SystemTime}; + use tracing_test::traced_test; + + #[test] + #[traced_test] + fn expiration_warning() { + let now = SystemTime::UNIX_EPOCH + Duration::from_secs(1000); + let creds_expire_in = Duration::from_secs(100); + + let mut settings = SigningSettings::default(); + settings.expires_in = Some(creds_expire_in - Duration::from_secs(10)); + + let credentials = Credentials::new( + "test-access-key", + "test-secret-key", + Some("test-session-token".into()), + Some(now + creds_expire_in), + "test", + ); + let operation_config = SigV4OperationSigningConfig { + region: SigningRegion::from_static("test"), + service: SigningService::from_static("test"), + signing_options: SigningOptions { + double_uri_encode: true, + content_sha256_header: true, + normalize_uri_path: true, + omit_session_token: true, + signature_type: HttpSignatureType::HttpRequestHeaders, + signing_optional: false, + expires_in: None, + request_timestamp: now, + payload_override: None, + }, + }; + SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config); + assert!(!logs_contain(EXPIRATION_WARNING)); + + let mut settings = SigningSettings::default(); + settings.expires_in = Some(creds_expire_in + Duration::from_secs(10)); + + SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config); + assert!(logs_contain(EXPIRATION_WARNING)); + } + } +} diff --git a/aws/rust-runtime/aws-runtime/src/identity.rs b/aws/rust-runtime/aws-runtime/src/identity.rs new file mode 100644 index 0000000000..0cf5b5101e --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/identity.rs @@ -0,0 +1,38 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Credentials-based identity support. +pub mod credentials { + use aws_credential_types::cache::SharedCredentialsCache; + use aws_smithy_http::property_bag::PropertyBag; + use aws_smithy_runtime_api::client::identity::Identity; + use aws_smithy_runtime_api::client::orchestrator::{ + BoxError, BoxFallibleFut, IdentityResolver, + }; + + /// Smithy identity resolver for AWS credentials. + #[derive(Debug)] + pub struct CredentialsIdentityResolver { + credentials_cache: SharedCredentialsCache, + } + + impl CredentialsIdentityResolver { + /// Creates a new `CredentialsIdentityResolver`. + pub fn new(credentials_cache: SharedCredentialsCache) -> Self { + Self { credentials_cache } + } + } + + impl IdentityResolver for CredentialsIdentityResolver { + fn resolve_identity(&self, _identity_properties: &PropertyBag) -> BoxFallibleFut { + let cache = self.credentials_cache.clone(); + Box::pin(async move { + let credentials = cache.as_ref().provide_cached_credentials().await?; + let expiration = credentials.expiry(); + Result::<_, BoxError>::Ok(Identity::new(credentials, expiration)) + }) + } + } +} diff --git a/aws/rust-runtime/aws-runtime/src/lib.rs b/aws/rust-runtime/aws-runtime/src/lib.rs new file mode 100644 index 0000000000..4070e43e33 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/lib.rs @@ -0,0 +1,20 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Runtime support code for the AWS SDK. This crate isn't intended to be used directly. + +#![warn( + missing_docs, + rustdoc::missing_crate_level_docs, + missing_debug_implementations, + rust_2018_idioms, + unreachable_pub +)] + +/// Supporting code for authentication in the AWS SDK. +pub mod auth; + +/// Supporting code for identity in the AWS SDK. +pub mod identity; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCargoDependency.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCargoDependency.kt index 655efd8b8a..c0127ae1e9 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCargoDependency.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCargoDependency.kt @@ -13,10 +13,12 @@ fun RuntimeConfig.awsRuntimeCrate(name: String, features: Set = setOf()) CargoDependency(name, awsRoot().crateLocation(null), features = features) object AwsCargoDependency { - fun awsCredentialTypes(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-credential-types") fun awsConfig(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-config") + fun awsCredentialTypes(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-credential-types") fun awsEndpoint(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-endpoint") fun awsHttp(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-http") + fun awsRuntime(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-runtime") + fun awsRuntimeApi(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-runtime-api") fun awsSigAuth(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-sig-auth") fun awsSigAuthEventStream(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-sig-auth", setOf("sign-eventstream")) fun awsSigv4(runtimeConfig: RuntimeConfig) = runtimeConfig.awsRuntimeCrate("aws-sigv4") 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 802b55b04a..a5ce0c3354 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 @@ -32,6 +32,7 @@ val DECORATORS: List = listOf( RegionDecorator(), RequireEndpointRules(), UserAgentDecorator(), + SigV4AuthDecorator(), SigV4SigningDecorator(), HttpRequestChecksumDecorator(), HttpResponseChecksumDecorator(), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt index 60bf14f9ea..e11f696d58 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt @@ -70,4 +70,7 @@ object AwsRuntimeType { fun awsSigv4(runtimeConfig: RuntimeConfig) = AwsCargoDependency.awsSigv4(runtimeConfig).toType() fun awsTypes(runtimeConfig: RuntimeConfig) = AwsCargoDependency.awsTypes(runtimeConfig).toType() + + fun awsRuntime(runtimeConfig: RuntimeConfig) = AwsCargoDependency.awsRuntime(runtimeConfig).toType() + fun awsRuntimeApi(runtimeConfig: RuntimeConfig) = AwsCargoDependency.awsRuntimeApi(runtimeConfig).toType() } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index 8b2e65a4b3..c07590f1e2 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -9,8 +9,11 @@ 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.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +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 @@ -19,11 +22,20 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization +import software.amazon.smithy.rust.codegen.core.util.letIf class CredentialsProviderDecorator : ClientCodegenDecorator { override val name: String = "CredentialsProvider" override val order: Byte = 0 + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + it + listOf(CredentialsIdentityResolverRegistration(codegenContext)) + } + override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -93,5 +105,31 @@ class CredentialProviderConfig(runtimeConfig: RuntimeConfig) : ConfigCustomizati } } +class CredentialsIdentityResolverRegistration( + codegenContext: ClientCodegenContext, +) : ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + when (section) { + is ServiceRuntimePluginSection.IdentityResolver -> { + rustTemplate( + """ + .identity_resolver( + #{SIGV4_SCHEME_ID}, + #{CredentialsIdentityResolver}::new(self.handle.conf.credentials_cache()) + ) + """, + "SIGV4_SCHEME_ID" to AwsRuntimeType.awsRuntime(runtimeConfig) + .resolve("auth::sigv4::SCHEME_ID"), + "CredentialsIdentityResolver" to AwsRuntimeType.awsRuntime(runtimeConfig) + .resolve("identity::credentials::CredentialsIdentityResolver"), + ) + } + else -> {} + } + } +} + fun defaultProvider() = RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("no_credentials")).resolve("NoCredentials") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt new file mode 100644 index 0000000000..01e0173372 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -0,0 +1,164 @@ +/* + * 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.aws.traits.auth.SigV4Trait +import software.amazon.smithy.aws.traits.auth.UnsignedPayloadTrait +import software.amazon.smithy.model.knowledge.ServiceIndex +import software.amazon.smithy.model.traits.OptionalAuthTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.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.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.isInputEventStream +import software.amazon.smithy.rust.codegen.core.util.letIf + +class SigV4AuthDecorator : ClientCodegenDecorator { + override val name: String get() = "SigV4AuthDecorator" + override val order: Byte = 0 + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + it + listOf(AuthServiceRuntimePluginCustomization(codegenContext)) + } + + override fun operationRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + it + listOf(AuthOperationRuntimePluginCustomization(codegenContext)) + } +} + +private class AuthServiceRuntimePluginCustomization(codegenContext: ClientCodegenContext) : + ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val codegenScope by lazy { + val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + arrayOf( + "SIGV4_SCHEME_ID" to awsRuntime.resolve("auth::sigv4::SCHEME_ID"), + "SigV4HttpAuthScheme" to awsRuntime.resolve("auth::sigv4::SigV4HttpAuthScheme"), + "SigningRegion" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::SigningRegion"), + "SigningService" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("SigningService"), + ) + } + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + when (section) { + is ServiceRuntimePluginSection.HttpAuthScheme -> { + rustTemplate( + """ + .auth_scheme(#{SIGV4_SCHEME_ID}, #{SigV4HttpAuthScheme}::new()) + """, + *codegenScope, + ) + } + + is ServiceRuntimePluginSection.AdditionalConfig -> { + rustTemplate( + """ + cfg.put(#{SigningService}::from_static(self.handle.conf.signing_service())); + if let Some(region) = self.handle.conf.region() { + cfg.put(#{SigningRegion}::from(region.clone())); + } + """, + *codegenScope, + ) + } + + else -> {} + } + } +} + +private class AuthOperationRuntimePluginCustomization(private val codegenContext: ClientCodegenContext) : + OperationRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val codegenScope by lazy { + val runtimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) + val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + arrayOf( + "AuthOptionListResolver" to runtimeApi.resolve("client::auth::option_resolver::AuthOptionListResolver"), + "HttpAuthOption" to runtimeApi.resolve("client::orchestrator::HttpAuthOption"), + "HttpSignatureType" to awsRuntime.resolve("auth::sigv4::HttpSignatureType"), + "PropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::PropertyBag"), + "SIGV4_SCHEME_ID" to awsRuntime.resolve("auth::sigv4::SCHEME_ID"), + "SigV4OperationSigningConfig" to awsRuntime.resolve("auth::sigv4::SigV4OperationSigningConfig"), + "SigningOptions" to awsRuntime.resolve("auth::sigv4::SigningOptions"), + "SigningRegion" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::SigningRegion"), + "SigningService" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("SigningService"), + "SignableBody" to AwsRuntimeType.awsSigv4(runtimeConfig).resolve("http_request::SignableBody"), + ) + } + private val serviceIndex = ServiceIndex.of(codegenContext.model) + + override fun section(section: OperationRuntimePluginSection): Writable = writable { + when (section) { + is OperationRuntimePluginSection.AdditionalConfig -> { + val authSchemes = serviceIndex.getEffectiveAuthSchemes(codegenContext.serviceShape, section.operationShape) + if (authSchemes.containsKey(SigV4Trait.ID)) { + rustTemplate( + """ + let auth_option_resolver = #{AuthOptionListResolver}::new( + vec![#{HttpAuthOption}::new(#{SIGV4_SCHEME_ID}, std::sync::Arc::new(#{PropertyBag}::new()))] + ); + ${section.configBagName}.set_auth_option_resolver(auth_option_resolver); + """, + *codegenScope, + ) + + val unsignedPayload = section.operationShape.hasTrait() + val doubleUriEncode = unsignedPayload || !disableDoubleEncode(codegenContext.serviceShape) + val contentSha256Header = needsAmzSha256(codegenContext.serviceShape) + val normalizeUrlPath = !disableUriPathNormalization(codegenContext.serviceShape) + val signingOptional = section.operationShape.hasTrait() + rustTemplate( + """ + let signing_region = cfg.get::<#{SigningRegion}>().expect("region required for signing").clone(); + let signing_service = cfg.get::<#{SigningService}>().expect("service required for signing").clone(); + let mut signing_options = #{SigningOptions}::default(); + signing_options.double_uri_encode = $doubleUriEncode; + signing_options.content_sha256_header = $contentSha256Header; + signing_options.normalize_uri_path = $normalizeUrlPath; + signing_options.signing_optional = $signingOptional; + signing_options.payload_override = #{payload_override}; + ${section.configBagName}.put(#{SigV4OperationSigningConfig} { + region: signing_region, + service: signing_service, + signing_options, + }); + """, + *codegenScope, + "payload_override" to writable { + if (unsignedPayload) { + rustTemplate("Some(#{SignableBody}::UnsignedPayload)", *codegenScope) + } else if (section.operationShape.isInputEventStream(codegenContext.model)) { + // TODO(EventStream): Is this actually correct for all Event Stream operations? + rustTemplate("Some(#{SignableBody}::Bytes(&[]))", *codegenScope) + } else { + rust("None") + } + }, + ) + } + } + + else -> {} + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt index 0676db868a..81400d6597 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt @@ -32,6 +32,7 @@ import software.amazon.smithy.rust.codegen.core.util.hasEventStreamOperations import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.isInputEventStream +// TODO(enableNewSmithyRuntime): Remove this decorator (superseded by SigV4AuthDecorator) /** * The SigV4SigningDecorator: * - adds a `signing_service()` method to `config` to return the default signing service diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml index cdc7a56ab5..65cfa7528d 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml +++ b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml @@ -8,15 +8,16 @@ publish = false [dependencies] aws-credential-types = { path = "../../../rust-runtime/aws-credential-types", features = ["test-util"] } aws-http = { path = "../../../rust-runtime/aws-http" } +aws-runtime = { path = "../../../rust-runtime/aws-runtime" } +aws-sdk-s3 = { path = "../../build/sdk/aws-sdk-s3", features = ["test-util"] } aws-sigv4 = { path = "../../../rust-runtime/aws-sigv4" } aws-types = { path = "../../../rust-runtime/aws-types" } -aws-sdk-s3 = { path = "../../build/sdk/aws-sdk-s3" } aws-smithy-async = { path = "../../../../rust-runtime/aws-smithy-async", features = ["rt-tokio"] } -aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client" } +aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client", features = ["test-util"] } aws-smithy-types = { path = "../../../../rust-runtime/aws-smithy-types" } aws-smithy-http = { path = "../../../../rust-runtime/aws-smithy-http" } -aws-smithy-runtime = { path = "../../../../rust-runtime/aws-smithy-runtime" } -aws-smithy-runtime-api = { path = "../../../../rust-runtime/aws-smithy-runtime-api", features = ["test-util"] } +aws-smithy-runtime = { path = "../../../../rust-runtime/aws-smithy-runtime", features = ["test-util"] } +aws-smithy-runtime-api = { path = "../../../../rust-runtime/aws-smithy-runtime-api" } tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3.15", features = ["env-filter", "json"] } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index caa1b5bba9..1a50e2a8cd 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -3,22 +3,227 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache}; +use aws_credential_types::provider::SharedCredentialsProvider; +use aws_http::user_agent::AwsUserAgent; +use aws_runtime::auth::sigv4::SigV4OperationSigningConfig; +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::operation::list_objects_v2::{ + ListObjectsV2Error, ListObjectsV2Input, ListObjectsV2Output, +}; +use aws_sdk_s3::primitives::SdkBody; +use aws_smithy_client::erase::DynConnector; +use aws_smithy_client::test_connection::TestConnection; +use aws_smithy_runtime::client::connections::adapter::DynConnectorAdapter; +use aws_smithy_runtime_api::client::endpoints::StaticUriEndpointResolver; +use aws_smithy_runtime_api::client::interceptors::{ + Interceptor, InterceptorContext, InterceptorError, Interceptors, +}; +use aws_smithy_runtime_api::client::orchestrator::{ + BoxError, ConfigBagAccessors, Connection, HttpRequest, HttpResponse, TraceProbe, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_runtime_api::type_erasure::TypedBox; +use aws_types::region::SigningRegion; +use aws_types::SigningService; +use http::{HeaderValue, Uri}; +use std::sync::Arc; +use std::time::{Duration, UNIX_EPOCH}; + mod interceptors; +// TODO(orchestrator-test): unignore +#[ignore] #[tokio::test] async fn sra_test() { tracing_subscriber::fmt::init(); - // TODO(orchestrator-testing): Replace the connector with a fake request/response - let config = aws_sdk_s3::Config::builder().build(); + let conn = TestConnection::new(vec![( + http::Request::builder() + .header("authorization", "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=ae78f74d26b6b0c3a403d9e8cc7ec3829d6264a2b33db672bf2b151bbb901786") + .uri("https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~") + .body(SdkBody::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = aws_sdk_s3::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .http_connector(conn.clone()) + .build(); let client = aws_sdk_s3::Client::from_conf(config); let _ = dbg!( client - .get_object() - .bucket("zhessler-test-bucket") - .key("1000-lines.txt") + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") .send_v2() .await ); + + conn.assert_requests_match(&[]); +} + +// TODO(orchestrator-test): replace with the above once runtime plugin config works +#[tokio::test] +async fn sra_manual_test() { + tracing_subscriber::fmt::init(); + + struct ManualServiceRuntimePlugin(TestConnection<&'static str>); + + impl RuntimePlugin for ManualServiceRuntimePlugin { + fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { + let identity_resolvers = + aws_smithy_runtime_api::client::orchestrator::IdentityResolvers::builder() + .identity_resolver( + aws_runtime::auth::sigv4::SCHEME_ID, + aws_runtime::identity::credentials::CredentialsIdentityResolver::new( + SharedCredentialsCache::new(CredentialsCache::lazy().create_cache( + SharedCredentialsProvider::new(Credentials::for_tests()), + )), + ), + ) + .identity_resolver( + "anonymous", + aws_smithy_runtime_api::client::identity::AnonymousIdentityResolver::new(), + ) + .build(); + cfg.set_identity_resolvers(identity_resolvers); + + let http_auth_schemes = + aws_smithy_runtime_api::client::orchestrator::HttpAuthSchemes::builder() + .auth_scheme( + aws_runtime::auth::sigv4::SCHEME_ID, + aws_runtime::auth::sigv4::SigV4HttpAuthScheme::new(), + ) + .build(); + cfg.set_http_auth_schemes(http_auth_schemes); + + cfg.set_auth_option_resolver( + aws_smithy_runtime_api::client::auth::option_resolver::AuthOptionListResolver::new( + Vec::new(), + ), + ); + + cfg.set_endpoint_resolver(StaticUriEndpointResolver::uri(Uri::from_static( + "https://test-bucket.s3.us-east-1.amazonaws.com/", + ))); + + cfg.set_retry_strategy( + aws_smithy_runtime_api::client::retries::NeverRetryStrategy::new(), + ); + + let connection: Box = + Box::new(DynConnectorAdapter::new(DynConnector::new(self.0.clone()))); + cfg.set_connection(connection); + + cfg.set_trace_probe({ + #[derive(Debug)] + struct StubTraceProbe; + impl TraceProbe for StubTraceProbe { + fn dispatch_events(&self) { + // no-op + } + } + StubTraceProbe + }); + + cfg.put(SigningService::from_static("s3")); + cfg.put(SigningRegion::from(Region::from_static("us-east-1"))); + + #[derive(Debug)] + struct UserAgentInterceptor; + impl Interceptor for UserAgentInterceptor { + fn modify_before_signing( + &self, + context: &mut InterceptorContext, + _cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let ua = AwsUserAgent::for_tests(); + + context.request_mut().unwrap().headers_mut().append( + "x-amz-user-agent", + HeaderValue::from_str(&ua.aws_ua_header()).unwrap(), + ); + Ok(()) + } + } + + #[derive(Debug)] + struct OverrideSigningTimeInterceptor; + impl Interceptor for OverrideSigningTimeInterceptor { + fn read_before_signing( + &self, + _context: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let mut signing_config = + cfg.get::().unwrap().clone(); + signing_config.signing_options.request_timestamp = + UNIX_EPOCH + Duration::from_secs(1624036048); + cfg.put(signing_config); + Ok(()) + } + } + + cfg.get::>() + .expect("interceptors set") + .register_client_interceptor(Arc::new(UserAgentInterceptor) as _) + .register_client_interceptor(Arc::new(OverrideSigningTimeInterceptor) as _); + Ok(()) + } + } + + let conn = TestConnection::new(vec![( + http::Request::builder() + .header("authorization", "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=ae78f74d26b6b0c3a403d9e8cc7ec3829d6264a2b33db672bf2b151bbb901786") + .uri("https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~") + .body(SdkBody::empty()) + .unwrap(), + http::Response::builder().status(200).body(r#" + + test-bucket + prefix~ + 1 + 1000 + false + + some-file.file + 2009-10-12T17:50:30.000Z + 434234 + STANDARD + + +"#).unwrap(), + )]); + + let runtime_plugins = aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins::new() + .with_client_plugin(ManualServiceRuntimePlugin(conn.clone())) + .with_operation_plugin(aws_sdk_s3::operation::list_objects_v2::ListObjectsV2::new()); + + let input = ListObjectsV2Input::builder() + .bucket("test-bucket") + .prefix("prefix~") + .build() + .unwrap(); + let input = TypedBox::new(input).erase(); + let output = aws_smithy_runtime::client::orchestrator::invoke(input, &runtime_plugins) + .await + .map_err(|err| { + err.map_service_error(|err| { + TypedBox::::assume_from(err) + .expect("correct error type") + .unwrap() + }) + }) + .unwrap(); + let output = TypedBox::::assume_from(output) + .expect("correct output type") + .unwrap(); + dbg!(output); + + conn.assert_requests_match(&[]); } diff --git a/buildSrc/src/main/kotlin/CrateSet.kt b/buildSrc/src/main/kotlin/CrateSet.kt index 47ff406b71..70a8f42acc 100644 --- a/buildSrc/src/main/kotlin/CrateSet.kt +++ b/buildSrc/src/main/kotlin/CrateSet.kt @@ -10,6 +10,8 @@ object CrateSet { "aws-endpoint", "aws-http", "aws-hyper", + "aws-runtime", + "aws-runtime-api", "aws-sig-auth", "aws-sigv4", "aws-types", diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index d7ce6440e4..f5d0334067 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -307,7 +307,7 @@ class ClientCodegenVisitor( this@operationWriter, this@inputWriter, operationShape, - codegenDecorator.operationCustomizations(codegenContext, operationShape, listOf()), + codegenDecorator, ) // render protocol tests into `operation.rs` (note operationWriter vs. inputWriter) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt index 6949fc8b5d..d7066256a7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt @@ -10,6 +10,8 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator @@ -58,6 +60,22 @@ interface ClientCodegenDecorator : CoreCodegenDecorator { * Hook to customize client construction documentation. */ fun clientConstructionDocs(codegenContext: ClientCodegenContext, baseDocs: Writable): Writable = baseDocs + + /** + * Hooks to register additional service-level runtime plugins at codegen time + */ + fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + + /** + * Hooks to register additional operation-level runtime plugins at codegen time + */ + fun operationRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations } /** @@ -107,6 +125,22 @@ open class CombinedClientCodegenDecorator(decorators: List, + ): List = + combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.serviceRuntimePluginCustomizations(codegenContext, customizations) + } + + override fun operationRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + combineCustomizations(baseCustomizations) { decorator, customizations -> + decorator.operationRuntimePluginCustomizations(codegenContext, customizations) + } + companion object { fun fromClasspath( context: PluginContext, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index fb0a7f6e17..fb3e9f84b4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -5,10 +5,27 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators +import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.smithy.customize.Section +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations + +sealed class OperationRuntimePluginSection(name: String) : Section(name) { + /** + * Hook for adding additional things to config inside operation runtime plugins. + */ + data class AdditionalConfig( + val configBagName: String, + val operationShape: OperationShape, + ) : OperationRuntimePluginSection("AdditionalConfig") +} + +typealias OperationRuntimePluginCustomization = NamedCustomization /** * Generates operation-level runtime plugins @@ -17,15 +34,23 @@ class OperationRuntimePluginGenerator( codegenContext: ClientCodegenContext, ) { private val codegenScope = codegenContext.runtimeConfig.let { rc -> + val runtimeApi = RuntimeType.smithyRuntimeApi(rc) arrayOf( - "BoxError" to RuntimeType.smithyRuntimeApi(rc).resolve("client::runtime_plugin::BoxError"), - "ConfigBag" to RuntimeType.smithyRuntimeApi(rc).resolve("config_bag::ConfigBag"), - "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(rc).resolve("client::orchestrator::ConfigBagAccessors"), - "RuntimePlugin" to RuntimeType.smithyRuntimeApi(rc).resolve("client::runtime_plugin::RuntimePlugin"), + "AuthOptionListResolverParams" to runtimeApi.resolve("client::auth::option_resolver::AuthOptionListResolverParams"), + "AuthOptionResolverParams" to runtimeApi.resolve("client::orchestrator::AuthOptionResolverParams"), + "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), + "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), + "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), + "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), ) } - fun render(writer: RustWriter, operationStructName: String) { + fun render( + writer: RustWriter, + operationShape: OperationShape, + operationStructName: String, + customizations: List, + ) { writer.rustTemplate( """ impl #{RuntimePlugin} for $operationStructName { @@ -33,11 +58,22 @@ class OperationRuntimePluginGenerator( use #{ConfigBagAccessors} as _; cfg.set_request_serializer(${operationStructName}RequestSerializer); cfg.set_response_deserializer(${operationStructName}ResponseDeserializer); + + ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} + cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{AuthOptionListResolverParams}::new())); + + #{additional_config} Ok(()) } } """, *codegenScope, + "additional_config" to writable { + writeCustomizations( + customizations, + OperationRuntimePluginSection.AdditionalConfig("cfg", operationShape), + ) + }, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index b28a329fc4..3be412dfee 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -22,31 +22,32 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustCrate */ class ServiceGenerator( private val rustCrate: RustCrate, - private val clientCodegenContext: ClientCodegenContext, + private val codegenContext: ClientCodegenContext, private val decorator: ClientCodegenDecorator, ) { - private val index = TopDownIndex.of(clientCodegenContext.model) + private val index = TopDownIndex.of(codegenContext.model) /** * Render Service-specific code. Code will end up in different files via `useShapeWriter`. See `SymbolVisitor.kt` * which assigns a symbol location to each shape. */ fun render() { - val operations = index.getContainedOperations(clientCodegenContext.serviceShape).sortedBy { it.id } + val operations = index.getContainedOperations(codegenContext.serviceShape).sortedBy { it.id } ServiceErrorGenerator( - clientCodegenContext, + codegenContext, operations, - decorator.errorCustomizations(clientCodegenContext, emptyList()), + decorator.errorCustomizations(codegenContext, emptyList()), ).render(rustCrate) rustCrate.withModule(ClientRustModule.Config) { ServiceConfigGenerator.withBaseBehavior( - clientCodegenContext, - extraCustomizations = decorator.configCustomizations(clientCodegenContext, listOf()), + codegenContext, + extraCustomizations = decorator.configCustomizations(codegenContext, listOf()), ).render(this) - if (clientCodegenContext.settings.codegenConfig.enableNewSmithyRuntime) { - ServiceRuntimePluginGenerator(clientCodegenContext).render(this) + if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + ServiceRuntimePluginGenerator(codegenContext) + .render(this, decorator.serviceRuntimePluginCustomizations(codegenContext, emptyList())) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 5c35eed98b..52a6519b65 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -8,7 +8,39 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.smithy.customize.Section +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations + +sealed class ServiceRuntimePluginSection(name: String) : Section(name) { + /** + * Hook for adding identity resolvers. + * + * Should emit code that looks like the following: + * ``` + * .identity_resolver("name", path::to::MyIdentityResolver::new()) + * ``` + */ + data class IdentityResolver(val configBagName: String) : ServiceRuntimePluginSection("IdentityResolver") + + /** + * Hook for adding HTTP auth schemes. + * + * Should emit code that looks like the following: + * ``` + * .auth_scheme("name", path::to::MyAuthScheme::new()) + * ``` + */ + data class HttpAuthScheme(val configBagName: String) : ServiceRuntimePluginSection("HttpAuthScheme") + + /** + * Hook for adding additional things to config inside service runtime plugins. + */ + data class AdditionalConfig(val configBagName: String) : ServiceRuntimePluginSection("AdditionalConfig") +} +typealias ServiceRuntimePluginCustomization = NamedCustomization /** * Generates the service-level runtime plugin @@ -17,22 +49,28 @@ class ServiceRuntimePluginGenerator( codegenContext: ClientCodegenContext, ) { private val codegenScope = codegenContext.runtimeConfig.let { rc -> + val runtimeApi = RuntimeType.smithyRuntimeApi(rc) + val runtime = RuntimeType.smithyRuntime(rc) arrayOf( - "BoxError" to RuntimeType.smithyRuntimeApi(rc).resolve("client::runtime_plugin::BoxError"), - "NeverRetryStrategy" to RuntimeType.smithyRuntimeApi(rc).resolve("client::retries::NeverRetryStrategy"), - "ConfigBag" to RuntimeType.smithyRuntimeApi(rc).resolve("config_bag::ConfigBag"), - "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(rc).resolve("client::orchestrator::ConfigBagAccessors"), - "TestConnection" to RuntimeType.smithyRuntimeApi(rc).resolve("client::connections::test_connection::TestConnection"), - "RuntimePlugin" to RuntimeType.smithyRuntimeApi(rc).resolve("client::runtime_plugin::RuntimePlugin"), - "StaticUriEndpointResolver" to RuntimeType.smithyRuntimeApi(rc).resolve("client::endpoints::StaticUriEndpointResolver"), - "StubAuthOptionResolver" to RuntimeType.smithyRuntimeApi(rc).resolve("client::auth::option_resolver::StubAuthOptionResolver"), - "StubAuthOptionResolverParams" to RuntimeType.smithyRuntimeApi(rc).resolve("client::auth::option_resolver::StubAuthOptionResolverParams"), - "AuthOptionResolverParams" to RuntimeType.smithyRuntimeApi(rc).resolve("client::orchestrator::AuthOptionResolverParams"), - "IdentityResolvers" to RuntimeType.smithyRuntimeApi(rc).resolve("client::orchestrator::IdentityResolvers"), + "AnonymousIdentityResolver" to runtimeApi.resolve("client::identity::AnonymousIdentityResolver"), + "AuthOptionListResolver" to runtimeApi.resolve("client::auth::option_resolver::AuthOptionListResolver"), + "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), + "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), + "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), + "Connection" to runtimeApi.resolve("client::orchestrator::Connection"), + "ConnectorSettings" to RuntimeType.smithyClient(rc).resolve("http_connector::ConnectorSettings"), + "DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"), + "HttpAuthSchemes" to runtimeApi.resolve("client::orchestrator::HttpAuthSchemes"), + "IdentityResolvers" to runtimeApi.resolve("client::orchestrator::IdentityResolvers"), + "NeverRetryStrategy" to runtimeApi.resolve("client::retries::NeverRetryStrategy"), + "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), + "StaticUriEndpointResolver" to runtimeApi.resolve("client::endpoints::StaticUriEndpointResolver"), + "TestConnection" to runtime.resolve("client::connections::test_connection::TestConnection"), + "TraceProbe" to runtimeApi.resolve("client::orchestrator::TraceProbe"), ) } - fun render(writer: RustWriter) { + fun render(writer: RustWriter, customizations: List) { writer.rustTemplate( """ pub(crate) struct ServiceRuntimePlugin { @@ -40,26 +78,70 @@ class ServiceRuntimePluginGenerator( } impl ServiceRuntimePlugin { - pub fn new(handle: std::sync::Arc) -> Self { Self { handle } } + pub fn new(handle: std::sync::Arc) -> Self { + Self { handle } + } } impl #{RuntimePlugin} for ServiceRuntimePlugin { fn configure(&self, cfg: &mut #{ConfigBag}) -> Result<(), #{BoxError}> { use #{ConfigBagAccessors}; - cfg.set_identity_resolvers(#{IdentityResolvers}::builder().build()); - cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StubAuthOptionResolverParams}::new())); - cfg.set_auth_option_resolver(#{StubAuthOptionResolver}::new()); - cfg.set_endpoint_resolver(#{StaticUriEndpointResolver}::default()); + let identity_resolvers = #{IdentityResolvers}::builder() + #{identity_resolver_customizations} + .identity_resolver("anonymous", #{AnonymousIdentityResolver}::new()) + .build(); + cfg.set_identity_resolvers(identity_resolvers); + + let http_auth_schemes = #{HttpAuthSchemes}::builder() + #{http_auth_scheme_customizations} + .build(); + cfg.set_http_auth_schemes(http_auth_schemes); + + // Set an empty auth option resolver to be overridden by operations that need auth. + cfg.set_auth_option_resolver(#{AuthOptionListResolver}::new(Vec::new())); + + // TODO(RuntimePlugins): Resolve the correct endpoint + cfg.set_endpoint_resolver(#{StaticUriEndpointResolver}::http_localhost(1234)); + + // TODO(RuntimePlugins): Wire up standard retry cfg.set_retry_strategy(#{NeverRetryStrategy}::new()); - cfg.set_connection(#{TestConnection}::new(vec![])); - // TODO(RuntimePlugins): Add the HttpAuthSchemes to the config bag + + // TODO(RuntimePlugins): Replace this with the correct long-term solution + let sleep_impl = self.handle.conf.sleep_impl(); + let connection: Box = self.handle.conf.http_connector() + .and_then(move |c| c.connector(&#{ConnectorSettings}::default(), sleep_impl)) + .map(|c| Box::new(#{DynConnectorAdapter}::new(c)) as _) + .unwrap_or_else(|| Box::new(#{TestConnection}::new(vec![])) as _); + cfg.set_connection(connection); + // TODO(RuntimePlugins): Add the TraceProbe to the config bag + cfg.set_trace_probe({ + ##[derive(Debug)] + struct StubTraceProbe; + impl #{TraceProbe} for StubTraceProbe { + fn dispatch_events(&self) { + // no-op + } + } + StubTraceProbe + }); + + #{additional_config} Ok(()) } } """, *codegenScope, + "identity_resolver_customizations" to writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.IdentityResolver("cfg")) + }, + "http_auth_scheme_customizations" to writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.HttpAuthScheme("cfg")) + }, + "additional_config" to writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg")) + }, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt index de19834721..ebe387d4d6 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBoundProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute @@ -50,26 +51,28 @@ open class ClientProtocolGenerator( // TODO(enableNewSmithyRuntime): Remove the `inputWriter` since `make_operation` generation is going away inputWriter: RustWriter, operationShape: OperationShape, - customizations: List, + codegenDecorator: ClientCodegenDecorator, ) { + val operationCustomizations = codegenDecorator.operationCustomizations(codegenContext, operationShape, emptyList()) val inputShape = operationShape.inputShape(model) // impl OperationInputShape { ... } inputWriter.implBlock(symbolProvider.toSymbol(inputShape)) { writeCustomizations( - customizations, - OperationSection.InputImpl(customizations, operationShape, inputShape, protocol), + operationCustomizations, + OperationSection.InputImpl(operationCustomizations, operationShape, inputShape, protocol), ) - makeOperationGenerator.generateMakeOperation(this, operationShape, customizations) + makeOperationGenerator.generateMakeOperation(this, operationShape, operationCustomizations) } - renderOperationStruct(operationWriter, operationShape, customizations) + renderOperationStruct(operationWriter, operationShape, operationCustomizations, codegenDecorator) } private fun renderOperationStruct( operationWriter: RustWriter, operationShape: OperationShape, - customizations: List, + operationCustomizations: List, + codegenDecorator: ClientCodegenDecorator, ) { val operationName = symbolProvider.toSymbol(operationShape).name @@ -89,17 +92,22 @@ open class ClientProtocolGenerator( rust("Self") } - writeCustomizations(customizations, OperationSection.OperationImplBlock(customizations)) + writeCustomizations(operationCustomizations, OperationSection.OperationImplBlock(operationCustomizations)) } - traitGenerator.generateTraitImpls(operationWriter, operationShape, customizations) + traitGenerator.generateTraitImpls(operationWriter, operationShape, operationCustomizations) if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { - OperationRuntimePluginGenerator(codegenContext).render(operationWriter, operationName) + OperationRuntimePluginGenerator(codegenContext).render( + operationWriter, + operationShape, + operationName, + codegenDecorator.operationRuntimePluginCustomizations(codegenContext, emptyList()), + ) ResponseDeserializerGenerator(codegenContext, protocol) - .render(operationWriter, operationShape, customizations) + .render(operationWriter, operationShape, operationCustomizations) RequestSerializerGenerator(codegenContext, protocol, bodyGenerator) - .render(operationWriter, operationShape, customizations) + .render(operationWriter, operationShape, operationCustomizations) } } } diff --git a/rust-runtime/aws-smithy-client/src/erase.rs b/rust-runtime/aws-smithy-client/src/erase.rs index 648562192c..d966664b3b 100644 --- a/rust-runtime/aws-smithy-client/src/erase.rs +++ b/rust-runtime/aws-smithy-client/src/erase.rs @@ -164,6 +164,16 @@ impl DynConnector { { Self(BoxCloneService::new(connector.map_err(|e| e.into()))) } + + #[doc(hidden)] + pub fn call_lite( + &mut self, + req: http::Request, + ) -> BoxFuture, Box> + { + let future = Service::call(self, req); + Box::pin(async move { future.await.map_err(|err| Box::new(err) as _) }) + } } impl Service> for DynConnector { diff --git a/rust-runtime/aws-smithy-runtime-api/Cargo.toml b/rust-runtime/aws-smithy-runtime-api/Cargo.toml index 9c6d96f52f..68e2005e93 100644 --- a/rust-runtime/aws-smithy-runtime-api/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime-api/Cargo.toml @@ -6,15 +6,14 @@ description = "Smithy runtime types." edition = "2021" license = "Apache-2.0" repository = "https://github.com/awslabs/smithy-rs" -publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -aws-smithy-types = { path = "../aws-smithy-types" } aws-smithy-http = { path = "../aws-smithy-http" } -tokio = { version = "1.25", features = ["sync"] } +aws-smithy-types = { path = "../aws-smithy-types" } http = "0.2.3" +tokio = { version = "1.25", features = ["sync"] } [package.metadata.docs.rs] all-features = true diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 8454d2f91c..278e7e0410 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -3,5 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 */ -pub mod identity_resolver; pub mod option_resolver; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/identity_resolver.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/identity_resolver.rs deleted file mode 100644 index 01e85ce27b..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth/identity_resolver.rs +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use crate::client::identity::Identity; -use crate::client::orchestrator::{BoxError, IdentityResolver}; -use crate::config_bag::ConfigBag; - -#[derive(Debug)] -pub struct AnonymousIdentity {} - -impl AnonymousIdentity { - pub fn new() -> Self { - Self {} - } -} - -#[derive(Debug)] -pub struct StubIdentityResolver {} - -impl IdentityResolver for StubIdentityResolver { - fn resolve_identity(&self, _cfg: &ConfigBag) -> Result { - Ok(Identity::new(AnonymousIdentity::new(), None)) - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs index cad736df48..66ef4fd10d 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs @@ -7,28 +7,37 @@ use crate::client::orchestrator::{ AuthOptionResolver, AuthOptionResolverParams, BoxError, HttpAuthOption, }; +/// New-type around a `Vec` that implements `AuthOptionResolver`. +/// +/// This is useful for clients that don't require `AuthOptionResolverParams` to resolve auth options. #[derive(Debug)] -pub struct StubAuthOptionResolver {} +pub struct AuthOptionListResolver { + auth_options: Vec, +} -impl StubAuthOptionResolver { - pub fn new() -> Self { - Self {} +impl AuthOptionListResolver { + /// Creates a new instance of `AuthOptionListResolver`. + pub fn new(auth_options: Vec) -> Self { + Self { auth_options } } } -impl AuthOptionResolver for StubAuthOptionResolver { +impl AuthOptionResolver for AuthOptionListResolver { fn resolve_auth_options( &self, _params: &AuthOptionResolverParams, ) -> Result, BoxError> { - Ok(Vec::new()) + Ok(self.auth_options.clone()) } } -pub struct StubAuthOptionResolverParams {} +/// Empty params to be used with [`AuthOptionListResolver`]. +#[derive(Debug)] +pub struct AuthOptionListResolverParams; -impl StubAuthOptionResolverParams { +impl AuthOptionListResolverParams { + /// Creates new `AuthOptionListResolverParams`. pub fn new() -> Self { - Self {} + Self } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs b/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs index 03a9ea083c..1e823e713a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs @@ -4,38 +4,32 @@ */ use crate::client::orchestrator::{BoxError, EndpointResolver, HttpRequest}; +use aws_smithy_http::endpoint::apply_endpoint; use http::Uri; use std::fmt::Debug; use std::str::FromStr; #[derive(Debug, Clone)] pub struct StaticUriEndpointResolver { - uri: Uri, + endpoint: Uri, } impl StaticUriEndpointResolver { - pub fn localhost(port: u16) -> Self { + pub fn http_localhost(port: u16) -> Self { Self { - uri: Uri::from_str(&format!("https://localhost:{port}")) + endpoint: Uri::from_str(&format!("http://localhost:{port}")) .expect("all u16 values are valid ports"), } } - pub fn uri(uri: Uri) -> Self { - Self { uri } - } -} - -impl Default for StaticUriEndpointResolver { - fn default() -> Self { - StaticUriEndpointResolver::localhost(3000) + pub fn uri(endpoint: Uri) -> Self { + Self { endpoint } } } impl EndpointResolver for StaticUriEndpointResolver { fn resolve_and_apply_endpoint(&self, request: &mut HttpRequest) -> Result<(), BoxError> { - *request.uri_mut() = self.uri.clone(); - + apply_endpoint(request.uri_mut(), &self.endpoint, None)?; Ok(()) } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index a5dbe262ed..3d9c7e829d 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -3,19 +3,21 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_types::DateTime; +use super::orchestrator::{BoxFallibleFut, IdentityResolver}; +use aws_smithy_http::property_bag::PropertyBag; use std::any::Any; use std::fmt::Debug; use std::sync::Arc; +use std::time::SystemTime; #[derive(Clone, Debug)] pub struct Identity { data: Arc, - expiration: Option, + expiration: Option, } impl Identity { - pub fn new(data: impl Any + Send + Sync, expiration: Option) -> Self { + pub fn new(data: impl Any + Send + Sync, expiration: Option) -> Self { Self { data: Arc::new(data), expiration, @@ -26,15 +28,38 @@ impl Identity { self.data.downcast_ref() } - pub fn expiration(&self) -> Option<&DateTime> { + pub fn expiration(&self) -> Option<&SystemTime> { self.expiration.as_ref() } } +#[derive(Debug)] +pub struct AnonymousIdentity; + +impl AnonymousIdentity { + pub fn new() -> Self { + Self + } +} + +#[derive(Debug)] +pub struct AnonymousIdentityResolver; + +impl AnonymousIdentityResolver { + pub fn new() -> Self { + AnonymousIdentityResolver + } +} + +impl IdentityResolver for AnonymousIdentityResolver { + fn resolve_identity(&self, _: &PropertyBag) -> BoxFallibleFut { + Box::pin(async { Ok(Identity::new(AnonymousIdentity::new(), None)) }) + } +} + #[cfg(test)] mod tests { use super::*; - use aws_smithy_types::date_time::Format; #[test] fn check_send_sync() { @@ -50,8 +75,7 @@ mod tests { last: String, } - let expiration = - DateTime::from_str("2023-03-15T00:00:00.000Z", Format::DateTimeWithOffset).unwrap(); + let expiration = SystemTime::now(); let identity = Identity::new( MyIdentityData { first: "foo".into(), diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index f584c3752c..c1734d58c2 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -9,12 +9,13 @@ pub mod error; use crate::config_bag::ConfigBag; pub use context::InterceptorContext; pub use error::InterceptorError; +use std::sync::{Arc, Mutex}; macro_rules! interceptor_trait_fn { ($name:ident, $docs:tt) => { #[doc = $docs] fn $name( - &mut self, + &self, context: &InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { @@ -23,6 +24,18 @@ macro_rules! interceptor_trait_fn { Ok(()) } }; + (mut $name:ident, $docs:tt) => { + #[doc = $docs] + fn $name( + &self, + context: &mut InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let _ctx = context; + let _cfg = cfg; + Ok(()) + } + }; } /// An interceptor allows injecting code into the SDK ’s request execution pipeline. @@ -35,7 +48,7 @@ macro_rules! interceptor_trait_fn { /// of the SDK ’s request execution pipeline. Hooks are either "read" hooks, which make it possible /// to read in-flight request or response messages, or "read/write" hooks, which make it possible /// to modify in-flight request or output messages. -pub trait Interceptor { +pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_execution, " @@ -60,7 +73,7 @@ pub trait Interceptor { ); interceptor_trait_fn!( - modify_before_serialization, + mut modify_before_serialization, " A hook called before the input message is marshalled into a transport message. @@ -129,7 +142,7 @@ pub trait Interceptor { ); interceptor_trait_fn!( - modify_before_retry_loop, + mut modify_before_retry_loop, " A hook called before the retry loop is entered. This method has the ability to modify and return a new transport request @@ -176,7 +189,7 @@ pub trait Interceptor { ); interceptor_trait_fn!( - modify_before_signing, + mut modify_before_signing, " A hook called before the transport request message is signed. This method has the ability to modify and return a new transport @@ -253,7 +266,7 @@ pub trait Interceptor { ); interceptor_trait_fn!( - modify_before_transmit, + mut modify_before_transmit, " /// A hook called before the transport request message is sent to the /// service. This method has the ability to modify and return @@ -338,7 +351,7 @@ pub trait Interceptor { ); interceptor_trait_fn!( - modify_before_deserialization, + mut modify_before_deserialization, " A hook called before the transport response message is unmarshalled. This method has the ability to modify and return a new transport @@ -421,7 +434,7 @@ pub trait Interceptor { ); interceptor_trait_fn!( - modify_before_attempt_completion, + mut modify_before_attempt_completion, " A hook called when an attempt is completed. This method has the ability to modify and return a new output message or error @@ -477,7 +490,7 @@ pub trait Interceptor { ); interceptor_trait_fn!( - modify_before_completion, + mut modify_before_completion, " A hook called when an execution is completed. This method has the ability to modify and return a new @@ -527,16 +540,45 @@ pub trait Interceptor { ); } +pub type SharedInterceptor = Arc + Send + Sync>; + +#[derive(Debug)] +struct Inner { + client_interceptors: Vec + Send + Sync>>, + operation_interceptors: Vec + Send + Sync>>, +} + +// The compiler isn't smart enough to realize that TxReq and TxRes don't need to implement `Clone` +impl Clone for Inner { + fn clone(&self) -> Self { + Self { + client_interceptors: self.client_interceptors.clone(), + operation_interceptors: self.operation_interceptors.clone(), + } + } +} + +#[derive(Debug)] pub struct Interceptors { - client_interceptors: Vec>>, - operation_interceptors: Vec>>, + inner: Arc>>, +} + +// The compiler isn't smart enough to realize that TxReq and TxRes don't need to implement `Clone` +impl Clone for Interceptors { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } } impl Default for Interceptors { fn default() -> Self { Self { - client_interceptors: Vec::new(), - operation_interceptors: Vec::new(), + inner: Arc::new(Mutex::new(Inner { + client_interceptors: Vec::new(), + operation_interceptors: Vec::new(), + })), } } } @@ -550,11 +592,18 @@ macro_rules! interceptor_impl_fn { }; (context, $outer_name:ident, $inner_name:ident) => { pub fn $outer_name( - &mut self, + &self, context: &InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { - for interceptor in self.client_interceptors.iter_mut() { + // Since interceptors can modify the interceptor list (since its in the config bag), copy the list ahead of time. + // This should be cheap since the interceptors inside the list are Arcs. + let client_interceptors = self.inner.lock().unwrap().client_interceptors.clone(); + for interceptor in client_interceptors { + interceptor.$inner_name(context, cfg)?; + } + let operation_interceptors = self.inner.lock().unwrap().operation_interceptors.clone(); + for interceptor in operation_interceptors { interceptor.$inner_name(context, cfg)?; } Ok(()) @@ -562,11 +611,18 @@ macro_rules! interceptor_impl_fn { }; (mut context, $outer_name:ident, $inner_name:ident) => { pub fn $outer_name( - &mut self, + &self, context: &mut InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { - for interceptor in self.client_interceptors.iter_mut() { + // Since interceptors can modify the interceptor list (since its in the config bag), copy the list ahead of time. + // This should be cheap since the interceptors inside the list are Arcs. + let client_interceptors = self.inner.lock().unwrap().client_interceptors.clone(); + for interceptor in client_interceptors { + interceptor.$inner_name(context, cfg)?; + } + let operation_interceptors = self.inner.lock().unwrap().operation_interceptors.clone(); + for interceptor in operation_interceptors { interceptor.$inner_name(context, cfg)?; } Ok(()) @@ -579,19 +635,27 @@ impl Interceptors { Self::default() } - pub fn with_client_interceptor( - &mut self, - interceptor: impl Interceptor + 'static, - ) -> &mut Self { - self.client_interceptors.push(Box::new(interceptor)); + pub fn register_client_interceptor( + &self, + interceptor: SharedInterceptor, + ) -> &Self { + self.inner + .lock() + .unwrap() + .client_interceptors + .push(interceptor); self } - pub fn with_operation_interceptor( - &mut self, - interceptor: impl Interceptor + 'static, - ) -> &mut Self { - self.operation_interceptors.push(Box::new(interceptor)); + pub fn register_operation_interceptor( + &self, + interceptor: SharedInterceptor, + ) -> &Self { + self.inner + .lock() + .unwrap() + .operation_interceptors + .push(interceptor); self } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 4a3cb7bfe7..60e6461fb8 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -46,6 +46,7 @@ impl InterceptorContext { } /// Takes ownership of the input. + #[doc(hidden)] pub fn take_input(&mut self) -> Option { self.input.take() } @@ -66,6 +67,12 @@ impl InterceptorContext { .ok_or_else(InterceptorError::invalid_request_access) } + /// Takes ownership of the request. + #[doc(hidden)] + pub fn take_request(&mut self) -> Option { + self.request.take() + } + /// Retrieve the response to the transmittable response for the operation /// being invoked. This will only be available once transmission has /// completed. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index e38b284859..1cb5f34006 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -22,7 +22,7 @@ pub type BoxError = Box; pub type BoxFallibleFut = Pin>>>; pub trait TraceProbe: Send + Sync + Debug { - fn dispatch_events(&self) -> BoxFallibleFut<()>; + fn dispatch_events(&self); } pub trait RequestSerializer: Send + Sync + Debug { @@ -39,7 +39,13 @@ pub trait ResponseDeserializer: Send + Sync + Debug { } pub trait Connection: Send + Sync + Debug { - fn call(&self, request: &mut HttpRequest, cfg: &ConfigBag) -> BoxFallibleFut; + fn call(&self, request: HttpRequest) -> BoxFallibleFut; +} + +impl Connection for Box { + fn call(&self, request: HttpRequest) -> BoxFallibleFut { + (**self).call(request) + } } pub trait RetryStrategy: Send + Sync + Debug { @@ -72,6 +78,15 @@ pub trait AuthOptionResolver: Send + Sync + Debug { ) -> Result, BoxError>; } +impl AuthOptionResolver for Box { + fn resolve_auth_options( + &self, + params: &AuthOptionResolverParams, + ) -> Result, BoxError> { + (**self).resolve_auth_options(params) + } +} + #[derive(Clone, Debug)] pub struct HttpAuthOption { scheme_id: &'static str, @@ -96,7 +111,7 @@ impl HttpAuthOption { } pub trait IdentityResolver: Send + Sync + Debug { - fn resolve_identity(&self, cfg: &ConfigBag) -> Result; + fn resolve_identity(&self, identity_properties: &PropertyBag) -> BoxFallibleFut; } #[derive(Debug)] @@ -143,7 +158,10 @@ impl HttpAuthSchemes { pub trait HttpAuthScheme: Send + Sync + Debug { fn scheme_id(&self) -> &'static str; - fn identity_resolver(&self, identity_resolvers: &IdentityResolvers) -> &dyn IdentityResolver; + fn identity_resolver<'a>( + &self, + identity_resolvers: &'a IdentityResolvers, + ) -> Option<&'a dyn IdentityResolver>; fn request_signer(&self) -> &dyn HttpRequestSigner; } @@ -154,10 +172,10 @@ pub trait HttpRequestSigner: Send + Sync + Debug { /// If the provided identity is incompatible with this signer, an error must be returned. fn sign_request( &self, - request: &HttpRequest, + request: &mut HttpRequest, identity: &Identity, cfg: &ConfigBag, - ) -> Result; + ) -> Result<(), BoxError>; } pub trait EndpointResolver: Send + Sync + Debug { diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 5227691cbb..4ff7e7613d 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -6,7 +6,6 @@ description = "The new smithy runtime crate" edition = "2021" license = "Apache-2.0" repository = "https://github.com/awslabs/smithy-rs" -publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -14,6 +13,7 @@ publish = false test-util = ["dep:aws-smithy-protocol-test"] [dependencies] +aws-smithy-client = { path = "../aws-smithy-client" } aws-smithy-http = { path = "../aws-smithy-http" } aws-smithy-protocol-test = { path = "../aws-smithy-protocol-test", optional = true } aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api" } diff --git a/rust-runtime/aws-smithy-runtime/external-types.toml b/rust-runtime/aws-smithy-runtime/external-types.toml index 45467fc294..5ecb3a376b 100644 --- a/rust-runtime/aws-smithy-runtime/external-types.toml +++ b/rust-runtime/aws-smithy-runtime/external-types.toml @@ -1,6 +1,7 @@ allowed_external_types = [ "aws_smithy_runtime_api::*", "aws_smithy_http::*", + "aws_smithy_client::erase::DynConnector", # TODO(audit-external-type-usage) We should newtype these or otherwise avoid exposing them "http::header::name::HeaderName", "http::request::Request", diff --git a/rust-runtime/aws-smithy-runtime/src/client/connections.rs b/rust-runtime/aws-smithy-runtime/src/client/connections.rs index a46518d15e..d1d8b55658 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connections.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connections.rs @@ -5,3 +5,32 @@ #[cfg(feature = "test-util")] pub mod test_connection; + +pub mod adapter { + use aws_smithy_client::erase::DynConnector; + use aws_smithy_runtime_api::client::orchestrator::{ + BoxFallibleFut, Connection, HttpRequest, HttpResponse, + }; + use std::sync::{Arc, Mutex}; + + #[derive(Debug)] + pub struct DynConnectorAdapter { + // `DynConnector` requires `&mut self`, so we need interior mutability to adapt to it + dyn_connector: Arc>, + } + + impl DynConnectorAdapter { + pub fn new(dyn_connector: DynConnector) -> Self { + Self { + dyn_connector: Arc::new(Mutex::new(dyn_connector)), + } + } + } + + impl Connection for DynConnectorAdapter { + fn call(&self, request: HttpRequest) -> BoxFallibleFut { + let future = self.dyn_connector.lock().unwrap().call_lite(request); + future + } + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs b/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs index eb3c29daa6..7366615ab8 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs @@ -11,7 +11,6 @@ use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; use aws_smithy_runtime_api::client::orchestrator::{ BoxFallibleFut, Connection, HttpRequest, HttpResponse, }; -use aws_smithy_runtime_api::config_bag::ConfigBag; use http::header::{HeaderName, CONTENT_TYPE}; use std::fmt::Debug; use std::future::ready; @@ -188,15 +187,14 @@ impl TestConnection { } impl Connection for TestConnection { - fn call(&self, request: &mut HttpRequest, _cfg: &ConfigBag) -> BoxFallibleFut { + fn call(&self, request: HttpRequest) -> BoxFallibleFut { // TODO(orchestrator) Validate request let res = if let Some((expected, resp)) = self.data.lock().unwrap().pop() { - let actual = try_clone_http_request(request).expect("test request is cloneable"); - self.requests - .lock() - .unwrap() - .push(ValidateRequest { expected, actual }); + self.requests.lock().unwrap().push(ValidateRequest { + expected, + actual: request, + }); Ok(resp.map(SdkBody::from)) } else { Err(ConnectorError::other("No more data".into(), None).into()) @@ -205,18 +203,3 @@ impl Connection for TestConnection { Box::pin(ready(res)) } } - -pub fn try_clone_http_request(req: &http::Request) -> Option> { - let cloned_body = req.body().try_clone()?; - let mut cloned_request = http::Request::builder() - .uri(req.uri().clone()) - .method(req.method()); - *cloned_request - .headers_mut() - .expect("builder has not been modified, headers must be valid") = req.headers().clone(); - let req = cloned_request - .body(cloned_body) - .expect("a clone of a valid request should be a valid request"); - - Some(req) -} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index d97ca1cdf5..fbc83cadfb 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -26,8 +26,9 @@ pub async fn invoke( ) -> Result> { let mut cfg = ConfigBag::base(); let cfg = &mut cfg; - let mut interceptors = Interceptors::new(); - let interceptors = &mut interceptors; + + let interceptors = Interceptors::new(); + cfg.put(interceptors.clone()); let context = Phase::construction(InterceptorContext::new(input)) // Client configuration @@ -66,7 +67,7 @@ pub async fn invoke( let mut context = context; let handling_phase = loop { let dispatch_phase = Phase::dispatch(context); - context = make_an_attempt(dispatch_phase, cfg, interceptors) + context = make_an_attempt(dispatch_phase, cfg, &interceptors) .await? .include(|ctx| interceptors.read_after_attempt(ctx, cfg))? .include_mut(|ctx| interceptors.modify_before_attempt_completion(ctx, cfg))? @@ -86,8 +87,7 @@ pub async fn invoke( let handling_phase = Phase::response_handling(context) .include_mut(|ctx| interceptors.modify_before_completion(ctx, cfg))?; - let trace_probe = cfg.trace_probe(); - trace_probe.dispatch_events(); + cfg.trace_probe().dispatch_events(); break handling_phase.include(|ctx| interceptors.read_after_execution(ctx, cfg))?; }; @@ -101,7 +101,7 @@ pub async fn invoke( async fn make_an_attempt( dispatch_phase: Phase, cfg: &mut ConfigBag, - interceptors: &mut Interceptors, + interceptors: &Interceptors, ) -> Result> { let dispatch_phase = dispatch_phase .include(|ctx| interceptors.read_before_attempt(ctx, cfg))? @@ -125,9 +125,9 @@ async fn make_an_attempt( // The connection consumes the request but we need to keep a copy of it // within the interceptor context, so we clone it here. let call_result = { - let tx_req = context.request_mut().expect("request has been set"); + let request = context.take_request().expect("request has been set"); let connection = cfg.connection(); - connection.call(tx_req, cfg).await + connection.call(request).await }; let mut context = Phase::dispatch(context) diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 8f15051a9b..705ca9f44e 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -13,24 +13,44 @@ pub(super) async fn orchestrate_auth( dispatch_phase: Phase, cfg: &ConfigBag, ) -> Result> { - dispatch_phase.include_mut(|ctx| { - let params = cfg.auth_option_resolver_params(); - let auth_options = cfg.auth_option_resolver().resolve_auth_options(params)?; - let identity_resolvers = cfg.identity_resolvers(); + fn construction_failure(err: impl Into) -> SdkError { + SdkError::construction_failure(err) + } - for option in auth_options { - let scheme_id = option.scheme_id(); - if let Some(auth_scheme) = cfg.http_auth_schemes().scheme(scheme_id) { - let identity_resolver = auth_scheme.identity_resolver(identity_resolvers); - let request_signer = auth_scheme.request_signer(); + let params = cfg.auth_option_resolver_params(); + let auth_options = cfg + .auth_option_resolver() + .resolve_auth_options(params) + .map_err(construction_failure)?; + let identity_resolvers = cfg.identity_resolvers(); - let identity = identity_resolver.resolve_identity(cfg)?; + for option in auth_options { + let scheme_id = option.scheme_id(); + let scheme_properties = option.properties(); + if let Some(auth_scheme) = cfg.http_auth_schemes().scheme(scheme_id) { + let identity_resolver = auth_scheme + .identity_resolver(identity_resolvers) + .ok_or_else(|| { + construction_failure(format!( + "no identity resolver found for auth scheme {id}. This is a bug. Please file an issue.", + id = auth_scheme.scheme_id() + )) + })?; + let request_signer = auth_scheme.request_signer(); + + let identity = identity_resolver + .resolve_identity(scheme_properties) + .await + .map_err(construction_failure)?; + return dispatch_phase.include_mut(|ctx| { let request = ctx.request_mut()?; request_signer.sign_request(request, &identity, cfg)?; - return Result::<_, BoxError>::Ok(()); - } + Result::<_, BoxError>::Ok(()) + }); } + } - Err("No auth scheme matched auth options. This is a bug. Please file an issue.".into()) - }) + Err(construction_failure( + "no auth scheme matched auth options. This is a bug. Please file an issue.", + )) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs index d163b5c743..f8af01c4b5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs @@ -39,7 +39,7 @@ impl Phase { ) -> Self { match phase { OrchestrationPhase::Construction => {} - OrchestrationPhase::Dispatch => debug_assert!(context.request().is_ok()), + OrchestrationPhase::Dispatch => {} OrchestrationPhase::ResponseHandling => debug_assert!(context.response().is_ok()), } Self { phase, context } From 3862ca0a3767967628913405c728a568757a5595 Mon Sep 17 00:00:00 2001 From: Matteo Bigoi <1781140+crisidev@users.noreply.github.com> Date: Thu, 6 Apr 2023 16:27:17 +0100 Subject: [PATCH 010/253] Initial implementation of Typescript server bindings (#2277) * A barely working code generation in typescript * Extract shared socket into feature inside aws-smithy-http-server * Building a fully functional application, I think * Add NAPI build.rs * Refactor all names to use typescript instead of js * Add (hopefully) the PR bot for TS * Clippy fixes * Fix documentation Signed-off-by: Daniele Ahmed * set_reuse_port in socket not on windows Signed-off-by: Daniele Ahmed * Add example implementation * Allow the new application to build * Remove all occurrences of Python * Simplify README * Fix issue with the codegen-diff-revision script * Try to prevent the ci-lint to bother us with TODO at this stage of development * Remove codegen-client from typescript dependencies Signed-off-by: Bigo <1781140+crisidev@users.noreply.github.com> * Add CODEOWNERS and fix some other linting issues * Add license * Prevent from running tests on typescript in Windows Signed-off-by: Bigo <1781140+crisidev@users.noreply.github.com> * Initial work to remove error from this PR * Update to call_async Signed-off-by: Daniele Ahmed * types/node in package.json Signed-off-by: Daniele Ahmed * Generate app.ts Signed-off-by: Daniele Ahmed * Improve makefile * Adapting code to the latest changes and removing runtime dependency (for now). * Removing rust-runtime/aws-smithy-http-server-typescript. * Making CI happy. * Restoring ServerCodegenDecorator to be like main. * Adding back the aws-smithy-http-server-typescript crate back. * Removing index.js file. --------- Signed-off-by: Daniele Ahmed Signed-off-by: Bigo <1781140+crisidev@users.noreply.github.com> Co-authored-by: 82marbag <69267416+82marbag@users.noreply.github.com> Co-authored-by: Alberto Pose --- .github/workflows/ci.yml | 17 +- .github/workflows/pull-request-bot.yml | 4 +- CODEOWNERS | 61 ++-- buildSrc/src/main/kotlin/CrateSet.kt | 1 + .../codegen/core/rustlang/CargoDependency.kt | 1 + .../rust/codegen/core/rustlang/RustWriter.kt | 4 +- .../smithy/generators/CargoTomlGenerator.kt | 2 + .../typescript/build.gradle.kts | 60 ++++ .../typescript/model/pokemon-common.smithy | 1 + .../typescript/model/pokemon.smithy | 25 ++ codegen-server/typescript/build.gradle.kts | 86 +++++ .../smithy/RustServerCodegenTsPlugin.kt | 105 ++++++ .../smithy/TsServerCargoDependency.kt | 33 ++ .../smithy/TsServerCodegenVisitor.kt | 213 ++++++++++++ .../smithy/TsServerModuleDocProvider.kt | 29 ++ .../typescript/smithy/TsServerRuntimeType.kt | 28 ++ .../smithy/TsServerSymbolProvider.kt | 135 ++++++++ .../TsServerCodegenDecorator.kt | 114 +++++++ .../generators/TsApplicationGenerator.kt | 314 ++++++++++++++++++ .../generators/TsServerEnumGenerator.kt | 52 +++ .../TsServerOperationErrorGenerator.kt | 48 +++ .../TsServerOperationHandlerGenerator.kt | 58 ++++ .../generators/TsServerStructureGenerator.kt | 50 +++ ...ware.amazon.smithy.build.SmithyBuildPlugin | 5 + .../Cargo.toml | 23 ++ .../aws-smithy-http-server-typescript/LICENSE | 202 +++++++++++ .../README.md | 7 + .../examples/.gitignore | 93 ++++++ .../examples/Cargo.toml | 9 + .../examples/Makefile | 44 +++ .../examples/README.md | 30 ++ .../examples/package.json | 5 + .../examples/pokemon-service.ts | 97 ++++++ .../src/lib.rs | 4 + settings.gradle.kts | 2 + tools/ci-build/publisher/src/package.rs | 1 + .../ci-build/sdk-lints/src/lint_cargo_toml.rs | 6 +- tools/ci-scripts/codegen-diff/diff_lib.py | 20 +- 38 files changed, 1947 insertions(+), 42 deletions(-) create mode 100644 codegen-server-test/typescript/build.gradle.kts create mode 120000 codegen-server-test/typescript/model/pokemon-common.smithy create mode 100644 codegen-server-test/typescript/model/pokemon.smithy create mode 100644 codegen-server/typescript/build.gradle.kts create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/RustServerCodegenTsPlugin.kt create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCargoDependency.kt create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerModuleDocProvider.kt create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerRuntimeType.kt create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerSymbolProvider.kt create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/customizations/TsServerCodegenDecorator.kt create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsApplicationGenerator.kt create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerEnumGenerator.kt create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationErrorGenerator.kt create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationHandlerGenerator.kt create mode 100644 codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerStructureGenerator.kt create mode 100644 codegen-server/typescript/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin create mode 100644 rust-runtime/aws-smithy-http-server-typescript/Cargo.toml create mode 100644 rust-runtime/aws-smithy-http-server-typescript/LICENSE create mode 100644 rust-runtime/aws-smithy-http-server-typescript/README.md create mode 100644 rust-runtime/aws-smithy-http-server-typescript/examples/.gitignore create mode 100644 rust-runtime/aws-smithy-http-server-typescript/examples/Cargo.toml create mode 100644 rust-runtime/aws-smithy-http-server-typescript/examples/Makefile create mode 100644 rust-runtime/aws-smithy-http-server-typescript/examples/README.md create mode 100644 rust-runtime/aws-smithy-http-server-typescript/examples/package.json create mode 100644 rust-runtime/aws-smithy-http-server-typescript/examples/pokemon-service.ts create mode 100644 rust-runtime/aws-smithy-http-server-typescript/src/lib.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8814fdd364..941298298d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -189,8 +189,9 @@ jobs: pushd "${runtime_path}" &>/dev/null # aws-smithy-http-server-python cannot be compiled on Windows since it uses the `signal-hook` crate # which is not really yet fully supported on the platform. - cargo test --all-features --workspace --exclude aws-smithy-http-server-python - cargo doc --no-deps --document-private-items --all-features --workspace --exclude aws-smithy-http-server-python + # aws-smithy-http-server-typescript cannot be compiled right now on Windows. + cargo test --all-features --workspace --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript + cargo doc --no-deps --document-private-items --all-features --workspace --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript popd &>/dev/null done @@ -210,24 +211,24 @@ jobs: - target: i686-unknown-linux-gnu build_smithy_rs_features: --all-features build_aws_exclude: '' - build_smithy_rs_exclude: --exclude aws-smithy-http-server-python + build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript test_smithy_rs_features: --all-features test_aws_exclude: '' - test_smithy_rs_exclude: --exclude aws-smithy-http-server-python + test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript - target: powerpc-unknown-linux-gnu build_smithy_rs_features: --features native-tls build_aws_exclude: --exclude aws-inlineable - build_smithy_rs_exclude: --exclude aws-smithy-http-server-python + build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript test_smithy_rs_features: --features native-tls test_aws_exclude: --exclude aws-inlineable - test_smithy_rs_exclude: --exclude aws-smithy-http-server-python + test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript - target: powerpc64-unknown-linux-gnu build_smithy_rs_features: --features native-tls build_aws_exclude: --exclude aws-inlineable - build_smithy_rs_exclude: --exclude aws-smithy-http-server-python + build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript test_smithy_rs_features: --features native-tls test_aws_exclude: --exclude aws-inlineable - test_smithy_rs_exclude: --exclude aws-smithy-http-server-python + test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript env: CROSS_CONFIG: Cross.toml steps: diff --git a/.github/workflows/pull-request-bot.yml b/.github/workflows/pull-request-bot.yml index 6d22213eb5..221f520f1c 100644 --- a/.github/workflows/pull-request-bot.yml +++ b/.github/workflows/pull-request-bot.yml @@ -110,7 +110,7 @@ jobs: ./gradlew -Paws.services=+sts,+sso,+transcribestreaming,+dynamodb :aws:sdk:assemble # Copy the Server runtime crate(s) in - cp -r rust-runtime/aws-smithy-http-server rust-runtime/aws-smithy-http-server-python aws/sdk/build/aws-sdk/sdk + cp -r rust-runtime/aws-smithy-http-server rust-runtime/aws-smithy-http-server-python rust-runtime/aws-smithy-http-server-typescript aws/sdk/build/aws-sdk/sdk pushd aws/sdk/build/aws-sdk @@ -118,7 +118,7 @@ jobs: sed -i '/examples/d' Cargo.toml # Add server runtime crates to the workspace - sed -i 's/"sdk\/sts",/"sdk\/sts","sdk\/aws-smithy-http-server","sdk\/aws-smithy-http-server-python",/' Cargo.toml + sed -i 's/"sdk\/sts",/"sdk\/sts","sdk\/aws-smithy-http-server","sdk\/aws-smithy-http-server-python","sdk\/aws-smithy-http-server-typescript",/' Cargo.toml cargo doc --no-deps --all-features popd diff --git a/CODEOWNERS b/CODEOWNERS index 9671079212..44bcc8d692 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,34 +1,39 @@ -* @awslabs/rust-sdk-owners +* @awslabs/rust-sdk-owners # Server -/codegen-server-test/ @awslabs/smithy-rs-server -/codegen-server/ @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-http-server/ @awslabs/smithy-rs-server +/codegen-server-test/ @awslabs/smithy-rs-server +/codegen-server/ @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-http-server/ @awslabs/smithy-rs-server # Python Server -/codegen-server-test/python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server -/codegen-server/python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-http-server-python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server +/codegen-server-test/python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server +/codegen-server/python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-http-server-python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server + +# Typescript Server +/codegen-server-test/typescript/ @awslabs/smithy-rs-typescript-server @awslabs/smithy-rs-server +/codegen-server/typescript/ @awslabs/smithy-rs-typescript-server @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-http-server-typescript/ @awslabs/smithy-rs-typescript-server @awslabs/smithy-rs-server # Shared ownership -/.github/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/CHANGELOG.md @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/CHANGELOG.next.toml @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/README.md @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/build.gradle.kts @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/buildSrc/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/codegen-core/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/design/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/gradle.properties @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/tools/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-async/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-eventstream/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-http/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-json/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-protocol-test/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-types/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-types-convert/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/aws-smithy-xml/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/inlineable/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/build.gradle.kts @awslabs/rust-sdk-owners @awslabs/smithy-rs-server -/rust-runtime/Cargo.toml @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/.github/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/CHANGELOG.md @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/CHANGELOG.next.toml @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/README.md @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/build.gradle.kts @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/buildSrc/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/codegen-core/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/design/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/gradle.properties @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/tools/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-async/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-eventstream/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-http/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-json/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-protocol-test/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-types/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-types-convert/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/aws-smithy-xml/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/inlineable/ @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/build.gradle.kts @awslabs/rust-sdk-owners @awslabs/smithy-rs-server +/rust-runtime/Cargo.toml @awslabs/rust-sdk-owners @awslabs/smithy-rs-server diff --git a/buildSrc/src/main/kotlin/CrateSet.kt b/buildSrc/src/main/kotlin/CrateSet.kt index 70a8f42acc..132246cf39 100644 --- a/buildSrc/src/main/kotlin/CrateSet.kt +++ b/buildSrc/src/main/kotlin/CrateSet.kt @@ -40,6 +40,7 @@ object CrateSet { val SERVER_SMITHY_RUNTIME = SMITHY_RUNTIME_COMMON + listOf( "aws-smithy-http-server", "aws-smithy-http-server-python", + "aws-smithy-http-server-typescript", ) val ENTIRE_SMITHY_RUNTIME = (AWS_SDK_SMITHY_RUNTIME + SERVER_SMITHY_RUNTIME).toSortedSet() diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index f10b828cfe..2e372b4989 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -16,6 +16,7 @@ import java.nio.file.Path sealed class DependencyScope { object Dev : DependencyScope() object Compile : DependencyScope() + object Build : DependencyScope() } sealed class DependencyLocation diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt index 7fd296db83..cf2ff4b5d5 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt @@ -468,7 +468,7 @@ class RustWriter private constructor( debugMode = debugMode, devDependenciesOnly = true, ) - + fileName == "package.json" -> rawWriter(fileName, debugMode = debugMode) else -> RustWriter(fileName, namespace, debugMode = debugMode) } } @@ -514,7 +514,7 @@ class RustWriter private constructor( init { expressionStart = '#' if (filename.endsWith(".rs")) { - require(namespace.startsWith("crate") || filename.startsWith("tests/")) { + require(namespace.startsWith("crate") || filename.startsWith("tests/") || filename == "build.rs") { "We can only write into files in the crate (got $namespace)" } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt index cceced878b..86c76c521a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt @@ -72,6 +72,8 @@ class CargoTomlGenerator( ).toMap(), "dependencies" to dependencies.filter { it.scope == DependencyScope.Compile } .associate { it.name to it.toMap() }, + "build-dependencies" to dependencies.filter { it.scope == DependencyScope.Build } + .associate { it.name to it.toMap() }, "dev-dependencies" to dependencies.filter { it.scope == DependencyScope.Dev } .associate { it.name to it.toMap() }, "features" to cargoFeatures.toMap(), diff --git a/codegen-server-test/typescript/build.gradle.kts b/codegen-server-test/typescript/build.gradle.kts new file mode 100644 index 0000000000..8b65d53239 --- /dev/null +++ b/codegen-server-test/typescript/build.gradle.kts @@ -0,0 +1,60 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +description = "Generates Rust/Typescript code from Smithy models and runs the protocol tests" +extra["displayName"] = "Smithy :: Rust :: Codegen :: Server :: Typescript :: Test" +extra["moduleName"] = "software.amazon.smithy.rust.kotlin.codegen.server.typescript.test" + +tasks["jar"].enabled = false + +plugins { + id("software.amazon.smithy") +} + +val smithyVersion: String by project +val defaultRustDocFlags: String by project +val properties = PropertyRetriever(rootProject, project) + +val pluginName = "rust-server-codegen-typescript" +val workingDirUnderBuildDir = "smithyprojections/codegen-server-test-typescript/" + +configure { + outputDirectory = file("$buildDir/$workingDirUnderBuildDir") +} + +buildscript { + val smithyVersion: String by project + dependencies { + classpath("software.amazon.smithy:smithy-cli:$smithyVersion") + } +} + +dependencies { + implementation(project(":codegen-server:typescript")) + implementation("software.amazon.smithy:smithy-aws-protocol-tests:$smithyVersion") + implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") + implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") +} + +val allCodegenTests = "../../codegen-core/common-test-models".let { commonModels -> + listOf( + CodegenTest("com.amazonaws.simple#SimpleService", "simple", imports = listOf("$commonModels/simple.smithy")), + CodegenTest("com.aws.example.ts#PokemonService", "pokemon-service-server-sdk"), + ) +} + +project.registerGenerateSmithyBuildTask(rootProject, pluginName, allCodegenTests) +project.registerGenerateCargoWorkspaceTask(rootProject, pluginName, allCodegenTests, workingDirUnderBuildDir) +project.registerGenerateCargoConfigTomlTask(buildDir.resolve(workingDirUnderBuildDir)) + +tasks["smithyBuildJar"].dependsOn("generateSmithyBuild") +tasks["assemble"].finalizedBy("generateCargoWorkspace") + +project.registerModifyMtimeTask() +project.registerCargoCommandsTasks(buildDir.resolve(workingDirUnderBuildDir), defaultRustDocFlags) + +tasks["test"].finalizedBy(cargoCommands(properties).map { it.toString }) + +tasks["clean"].doFirst { delete("smithy-build.json") } diff --git a/codegen-server-test/typescript/model/pokemon-common.smithy b/codegen-server-test/typescript/model/pokemon-common.smithy new file mode 120000 index 0000000000..31ad0d9f44 --- /dev/null +++ b/codegen-server-test/typescript/model/pokemon-common.smithy @@ -0,0 +1 @@ +../../../codegen-core/common-test-models/pokemon-common.smithy \ No newline at end of file diff --git a/codegen-server-test/typescript/model/pokemon.smithy b/codegen-server-test/typescript/model/pokemon.smithy new file mode 100644 index 0000000000..06e46906e7 --- /dev/null +++ b/codegen-server-test/typescript/model/pokemon.smithy @@ -0,0 +1,25 @@ +/// TODO(https://github.com/awslabs/smithy-rs/issues/1508) +/// reconcile this model with the main one living inside codegen-server-test/model/pokemon.smithy +/// once the Typescript implementation supports Streaming and Union shapes. +$version: "1.0" + +namespace com.aws.example.ts + +use aws.protocols#restJson1 +use com.aws.example#PokemonSpecies +use com.aws.example#GetServerStatistics +use com.aws.example#DoNothing +use com.aws.example#CheckHealth + +/// The Pokémon Service allows you to retrieve information about Pokémon species. +@title("Pokémon Service") +@restJson1 +service PokemonService { + version: "2021-12-01", + resources: [PokemonSpecies], + operations: [ + GetServerStatistics, + DoNothing, + CheckHealth, + ], +} diff --git a/codegen-server/typescript/build.gradle.kts b/codegen-server/typescript/build.gradle.kts new file mode 100644 index 0000000000..b6dfa39a8f --- /dev/null +++ b/codegen-server/typescript/build.gradle.kts @@ -0,0 +1,86 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import org.gradle.api.tasks.testing.logging.TestExceptionFormat + +plugins { + kotlin("jvm") + `maven-publish` +} + +description = "Generates Rust/Node server-side code from Smithy models" + +extra["displayName"] = "Smithy :: Rust :: Codegen :: Server :: Typescript" + +extra["moduleName"] = "software.amazon.smithy.rust.codegen.server.typescript" + +group = "software.amazon.smithy.rust.codegen.server.typescript.smithy" + +version = "0.1.0" + +val smithyVersion: String by project + +dependencies { + implementation(project(":codegen-core")) + implementation(project(":codegen-server")) + implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") + implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") +} + +tasks.compileKotlin { kotlinOptions.jvmTarget = "1.8" } + +// Reusable license copySpec +val licenseSpec = copySpec { + from("${project.rootDir}/LICENSE") + from("${project.rootDir}/NOTICE") +} + +// Configure jars to include license related info +tasks.jar { + metaInf.with(licenseSpec) + inputs.property("moduleName", project.name) + manifest { attributes["Automatic-Module-Name"] = project.name } +} + +val sourcesJar by tasks.creating(Jar::class) { + group = "publishing" + description = "Assembles Kotlin sources jar" + archiveClassifier.set("sources") + from(sourceSets.getByName("main").allSource) +} + +val isTestingEnabled: String by project +if (isTestingEnabled.toBoolean()) { + val kotestVersion: String by project + + dependencies { + testImplementation("org.junit.jupiter:junit-jupiter:5.6.1") + testImplementation("io.kotest:kotest-assertions-core-jvm:$kotestVersion") + } + + tasks.compileTestKotlin { kotlinOptions.jvmTarget = "1.8" } + + tasks.test { + useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + exceptionFormat = TestExceptionFormat.FULL + showCauses = true + showExceptions = true + showStackTraces = true + showStandardStreams = true + } + } +} + +publishing { + publications { + create("default") { + from(components["java"]) + artifact(sourcesJar) + } + } + repositories { maven { url = uri("$buildDir/repository") } } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/RustServerCodegenTsPlugin.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/RustServerCodegenTsPlugin.kt new file mode 100644 index 0000000000..10aafec81b --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/RustServerCodegenTsPlugin.kt @@ -0,0 +1,105 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.build.PluginContext +import software.amazon.smithy.build.SmithyBuildPlugin +import software.amazon.smithy.codegen.core.ReservedWordSymbolProvider +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.BaseSymbolMetadataProvider +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.smithy.EventStreamSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig +import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor +import software.amazon.smithy.rust.codegen.server.smithy.ConstrainedShapeSymbolMetadataProvider +import software.amazon.smithy.rust.codegen.server.smithy.ConstrainedShapeSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.DeriveEqAndHashSymbolMetadataProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerReservedWords +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings +import software.amazon.smithy.rust.codegen.server.smithy.customizations.CustomValidationExceptionWithReasonDecorator +import software.amazon.smithy.rust.codegen.server.smithy.customizations.ServerRequiredCustomizations +import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionDecorator +import software.amazon.smithy.rust.codegen.server.smithy.customize.CombinedServerCodegenDecorator +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.customizations.DECORATORS +import java.util.logging.Level +import java.util.logging.Logger + +/** + * Rust with Typescript bindings Codegen Plugin. + * This is the entrypoint for code generation, triggered by the smithy-build plugin. + * `resources/META-INF.services/software.amazon.smithy.build.SmithyBuildPlugin` refers to this class by name which + * enables the smithy-build plugin to invoke `execute` with all of the Smithy plugin context + models. + */ +class RustServerCodegenTsPlugin : SmithyBuildPlugin { + private val logger = Logger.getLogger(javaClass.name) + + override fun getName(): String = "rust-server-codegen-typescript" + + override fun execute(context: PluginContext) { + // Suppress extremely noisy logs about reserved words + Logger.getLogger(ReservedWordSymbolProvider::class.java.name).level = Level.OFF + // Discover [RustCodegenDecorators] on the classpath. [RustCodegenDecorator] return different types of + // customization. A customization is a function of: + // - location (e.g. the mutate section of an operation) + // - context (e.g. the of the operation) + // - writer: The active RustWriter at the given location + val codegenDecorator: CombinedServerCodegenDecorator = + CombinedServerCodegenDecorator.fromClasspath( + context, + ServerRequiredCustomizations(), + SmithyValidationExceptionDecorator(), + CustomValidationExceptionWithReasonDecorator(), + *DECORATORS, + ) + + // TsServerCodegenVisitor is the main driver of code generation that traverses the model and generates code + logger.info("Loaded plugin to generate Rust/Node bindings for the server SSDK for projection ${context.projectionName}") + TsServerCodegenVisitor(context, codegenDecorator).execute() + } + + companion object { + /** + * When generating code, smithy types need to be converted into Rust types—that is the core role of the symbol provider + * + * The Symbol provider is composed of a base [SymbolVisitor] which handles the core functionality, then is layered + * with other symbol providers, documented inline, to handle the full scope of Smithy types. + */ + fun baseSymbolProvider( + settings: ServerRustSettings, + model: Model, + serviceShape: ServiceShape, + rustSymbolProviderConfig: RustSymbolProviderConfig, + constrainedTypes: Boolean = true, + includeConstrainedShapeProvider: Boolean = true, + codegenDecorator: ServerCodegenDecorator, + ) = + TsServerSymbolVisitor(settings, model, serviceShape = serviceShape, config = rustSymbolProviderConfig) + // Generate public constrained types for directly constrained shapes. + // In the Typescript server project, this is only done to generate constrained types for simple shapes (e.g. + // a `string` shape with the `length` trait), but these always remain `pub(crate)`. + .let { + if (includeConstrainedShapeProvider) ConstrainedShapeSymbolProvider(it, serviceShape, constrainedTypes) else it + } + // Generate different types for EventStream shapes (e.g. transcribe streaming) + .let { EventStreamSymbolProvider(rustSymbolProviderConfig.runtimeConfig, it, CodegenTarget.SERVER) } + // Add Rust attributes (like `#[derive(PartialEq)]`) to generated shapes + .let { BaseSymbolMetadataProvider(it, additionalAttributes = listOf()) } + // Constrained shapes generate newtypes that need the same derives we place on types generated from aggregate shapes. + .let { ConstrainedShapeSymbolMetadataProvider(it, constrainedTypes) } + // Streaming shapes need different derives (e.g. they cannot derive Eq) + .let { TsStreamingShapeMetadataProvider(it) } + // Derive `Eq` and `Hash` if possible. + .let { DeriveEqAndHashSymbolMetadataProvider(it) } + // Rename shapes that clash with Rust reserved words & and other SDK specific features e.g. `send()` cannot + // be the name of an operation input + .let { RustReservedWordSymbolProvider(it, ServerReservedWords) } + // Allows decorators to inject a custom symbol provider + .let { codegenDecorator.symbolProvider(it) } + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCargoDependency.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCargoDependency.kt new file mode 100644 index 0000000000..5c3b4c2209 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCargoDependency.kt @@ -0,0 +1,33 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.CratesIo +import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig + +/** + * Object used *exclusively* in the runtime of the Typescript server, for separation concerns. + * Analogous to the companion object in [CargoDependency] and [software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency]; see its documentation for details. + * For a dependency that is used in the client, or in both the client and the server, use [CargoDependency] directly. + */ +object TsServerCargoDependency { + val Napi: CargoDependency = CargoDependency("napi", CratesIo("2.11"), features = setOf("tokio_rt", "napi8")) + val NapiDerive: CargoDependency = CargoDependency("napi-derive", CratesIo("2.11")) + val NapiBuild: CargoDependency = CargoDependency("napi-build", CratesIo("2.0"), DependencyScope.Build) + val Tokio: CargoDependency = CargoDependency("tokio", CratesIo("1.20.1"), features = setOf("full")) + val Tracing: CargoDependency = CargoDependency("tracing", CratesIo("0.1")) + val Tower: CargoDependency = CargoDependency("tower", CratesIo("0.4")) + val TowerHttp: CargoDependency = CargoDependency("tower-http", CratesIo("0.3"), features = setOf("trace")) + val Hyper: CargoDependency = CargoDependency("hyper", CratesIo("0.14.12"), features = setOf("server", "http1", "http2", "tcp", "stream")) + val NumCpus: CargoDependency = CargoDependency("num_cpus", CratesIo("1.13")) + val ParkingLot: CargoDependency = CargoDependency("parking_lot", CratesIo("0.12")) + val Socket2: CargoDependency = CargoDependency("socket2", CratesIo("0.4")) + + fun smithyHttpServer(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-http-server") + fun smithyHttpServerTs(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-http-server-typescript") +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt new file mode 100644 index 0000000000..a5593d1847 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt @@ -0,0 +1,213 @@ + +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.build.PluginContext +import software.amazon.smithy.codegen.core.CodegenException +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.knowledge.NullableIndex +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.model.traits.EnumTrait +import software.amazon.smithy.model.traits.ErrorTrait +import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig +import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator +import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenVisitor +import software.amazon.smithy.rust.codegen.server.smithy.ServerModuleDocProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerModuleProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings +import software.amazon.smithy.rust.codegen.server.smithy.ServerSymbolProviders +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator +import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol +import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader +import software.amazon.smithy.rust.codegen.server.typescript.smithy.generators.TsApplicationGenerator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.generators.TsServerEnumGenerator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.generators.TsServerOperationErrorGenerator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.generators.TsServerOperationHandlerGenerator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.generators.TsServerStructureGenerator + +/** + * Entrypoint for Typescript server-side code generation. This class will walk the in-memory model and + * generate all the needed types by calling the accept() function on the available shapes. + * + * This class inherits from [ServerCodegenVisitor] since it uses most of the functionalities of the super class + * and have to override the symbol provider with [TsServerSymbolProvider]. + */ +class TsServerCodegenVisitor( + context: PluginContext, + private val codegenDecorator: ServerCodegenDecorator, +) : ServerCodegenVisitor(context, codegenDecorator) { + + init { + val symbolVisitorConfig = + RustSymbolProviderConfig( + runtimeConfig = settings.runtimeConfig, + renameExceptions = false, + nullabilityCheckMode = NullableIndex.CheckMode.SERVER, + moduleProvider = ServerModuleProvider, + ) + val baseModel = baselineTransform(context.model) + val service = settings.getService(baseModel) + val (protocol, generator) = + ServerProtocolLoader( + codegenDecorator.protocols( + service.id, + ServerProtocolLoader.DefaultProtocols, + ), + ) + .protocolFor(context.model, service) + protocolGeneratorFactory = generator + + model = codegenDecorator.transformModel(service, baseModel) + + // `publicConstrainedTypes` must always be `false` for the Typescript server, since Typescript generates its own + // wrapper newtypes. + settings = settings.copy(codegenConfig = settings.codegenConfig.copy(publicConstrainedTypes = false)) + + fun baseSymbolProviderFactory( + settings: ServerRustSettings, + model: Model, + serviceShape: ServiceShape, + rustSymbolProviderConfig: RustSymbolProviderConfig, + publicConstrainedTypes: Boolean, + includeConstraintShapeProvider: Boolean, + codegenDecorator: ServerCodegenDecorator, + ) = RustServerCodegenTsPlugin.baseSymbolProvider(settings, model, serviceShape, rustSymbolProviderConfig, publicConstrainedTypes, includeConstraintShapeProvider, codegenDecorator) + + val serverSymbolProviders = ServerSymbolProviders.from( + settings, + model, + service, + symbolVisitorConfig, + settings.codegenConfig.publicConstrainedTypes, + codegenDecorator, + ::baseSymbolProviderFactory, + ) + + // Override `codegenContext` which carries the various symbol providers. + codegenContext = + ServerCodegenContext( + model, + serverSymbolProviders.symbolProvider, + null, + service, + protocol, + settings, + serverSymbolProviders.unconstrainedShapeSymbolProvider, + serverSymbolProviders.constrainedShapeSymbolProvider, + serverSymbolProviders.constraintViolationSymbolProvider, + serverSymbolProviders.pubCrateConstrainedShapeSymbolProvider, + ) + + codegenContext = codegenContext.copy( + moduleDocProvider = codegenDecorator.moduleDocumentationCustomization( + codegenContext, + TsServerModuleDocProvider(ServerModuleDocProvider(codegenContext)), + ), + ) + + // Override `rustCrate` which carries the symbolProvider. + rustCrate = RustCrate( + context.fileManifest, codegenContext.symbolProvider, settings.codegenConfig, + codegenContext.expectModuleDocProvider(), + ) + // Override `protocolGenerator` which carries the symbolProvider. + protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) + } + + /** + * Structure Shape Visitor + * + * For each structure shape, generate: + * - A Rust structure for the shape ([StructureGenerator]). + * - A builder for the shape. + * + * This function _does not_ generate any serializers. + */ + override fun structureShape(shape: StructureShape) { + logger.info("[js-server-codegen] Generating a structure $shape") + rustCrate.useShapeWriter(shape) { + // Use Typescript specific structure generator that adds the #[napi] attribute + // and implementation. + TsServerStructureGenerator(model, codegenContext.symbolProvider, this, shape).render() + + shape.getTrait()?.also { errorTrait -> + ErrorImplGenerator( + model, + codegenContext.symbolProvider, + this, + shape, + errorTrait, + codegenDecorator.errorImplCustomizations(codegenContext, emptyList()), + ).render(CodegenTarget.SERVER) + } + + renderStructureShapeBuilder(shape, this) + } + } + + /** + * String Shape Visitor + * + * Although raw strings require no code generation, enums are actually [EnumTrait] applied to string shapes. + */ + override fun stringShape(shape: StringShape) { + fun tsServerEnumGeneratorFactory(codegenContext: ServerCodegenContext, shape: StringShape) = + TsServerEnumGenerator(codegenContext, shape, validationExceptionConversionGenerator) + stringShape(shape, ::tsServerEnumGeneratorFactory) + } + + /** + * Union Shape Visitor + * + * Generate an `enum` for union shapes. + * + * Note: this does not generate serializers + */ + override fun unionShape(shape: UnionShape) { + throw CodegenException("Union shapes are not supported in Typescript yet") + } + + /** + * Generate service-specific code for the model: + * - Serializers + * - Deserializers + * - Trait implementations + * - Protocol tests + * - Operation structures + * - Typescript operation handlers + */ + override fun serviceShape(shape: ServiceShape) { + super.serviceShape(shape) + + logger.info("[ts-server-codegen] Generating a service $shape") + + val serverProtocol = protocolGeneratorFactory.protocol(codegenContext) as ServerProtocol + rustCrate.withModule(TsServerRustModule.TsServerApplication) { + TsApplicationGenerator(codegenContext, serverProtocol).render(this) + } + } + + override fun operationShape(shape: OperationShape) { + super.operationShape(shape) + rustCrate.withModule(TsServerRustModule.TsOperationAdapter) { + TsServerOperationHandlerGenerator(codegenContext, shape).render(this) + } + + rustCrate.withModule(ServerRustModule.Error) { + TsServerOperationErrorGenerator(codegenContext.model, codegenContext.symbolProvider, shape).render(this) + } + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerModuleDocProvider.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerModuleDocProvider.kt new file mode 100644 index 0000000000..48efe087d6 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerModuleDocProvider.kt @@ -0,0 +1,29 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.docs +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider + +object TsServerRustModule { + val TsOperationAdapter = RustModule.public("ts_operation_adaptor") + val TsServerApplication = RustModule.public("ts_server_application") +} + +class TsServerModuleDocProvider(private val base: ModuleDocProvider) : ModuleDocProvider { + override fun docsWriter(module: RustModule.LeafModule): Writable? { + val strDoc: (String) -> Writable = { str -> writable { docs(str) } } + return when (module) { + TsServerRustModule.TsServerApplication -> strDoc("Ts server and application implementation.") + // TODO(ServerTeam): Document this module (I don't have context) + TsServerRustModule.TsOperationAdapter -> null + else -> base.docsWriter(module) + } + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerRuntimeType.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerRuntimeType.kt new file mode 100644 index 0000000000..281563335f --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerRuntimeType.kt @@ -0,0 +1,28 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType + +/** + * Object used *exclusively* in the runtime of the Node server, for separation concerns. + * Analogous to the companion object in [RuntimeType] and [software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType]; see its documentation for details. + * For a runtime type that is used in the client, or in both the client and the server, use [RuntimeType] directly. + */ +object TsServerRuntimeType { + fun blob(runtimeConfig: RuntimeConfig) = + TsServerCargoDependency.smithyHttpServerTs(runtimeConfig).toType().resolve("types::Blob") + + fun byteStream(runtimeConfig: RuntimeConfig) = + TsServerCargoDependency.smithyHttpServerTs(runtimeConfig).toType().resolve("types::ByteStream") + + fun dateTime(runtimeConfig: RuntimeConfig) = + TsServerCargoDependency.smithyHttpServerTs(runtimeConfig).toType().resolve("types::DateTime") + + fun document(runtimeConfig: RuntimeConfig) = + TsServerCargoDependency.smithyHttpServerTs(runtimeConfig).toType().resolve("types::Document") +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerSymbolProvider.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerSymbolProvider.kt new file mode 100644 index 0000000000..a7ca74d064 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerSymbolProvider.kt @@ -0,0 +1,135 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.BlobShape +import software.amazon.smithy.model.shapes.DocumentShape +import software.amazon.smithy.model.shapes.ListShape +import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.NumberShape +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.TimestampShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig +import software.amazon.smithy.rust.codegen.core.smithy.SymbolMetadataProvider +import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor +import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait +import software.amazon.smithy.rust.codegen.core.util.hasStreamingMember +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.isStreaming +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings +import java.util.logging.Logger + +/** + * Symbol visitor allowing that recursively replace symbols in nested shapes. + * + * Input / output / error structures can refer to complex types like the ones implemented inside + * `aws_smithy_types` (a good example is `aws_smithy_types::Blob`). + * `aws_smithy_http_server_typescript::types` wraps those types that do not implement directly the + * `napi` trait and cannot be shared safely with Typescript, providing an idiomatic Typescript / Rust API. + * + * This symbol provider ensures types not implementing `pyo3::PyClass` are swapped with their wrappers from + * `aws_smithy_http_server_typescript::types`. + */ +class TsServerSymbolVisitor( + settings: ServerRustSettings, + model: Model, + serviceShape: ServiceShape?, + config: RustSymbolProviderConfig, +) : SymbolVisitor(settings, model, serviceShape, config) { + private val runtimeConfig = config.runtimeConfig + private val logger = Logger.getLogger(javaClass.name) + + override fun toSymbol(shape: Shape): Symbol { + val initial = shape.accept(this) + + if (shape !is MemberShape) { + return initial + } + val target = model.expectShape(shape.target) + val container = model.expectShape(shape.container) + + // We are only targeting non-synthetic inputs and outputs. + if (!container.hasTrait() && !container.hasTrait()) { + return initial + } + + // We are only targeting streaming blobs as the rest of the symbols do not change if streaming is enabled. + // For example a TimestampShape doesn't become a different symbol when streaming is involved, but BlobShape + // become a ByteStream. + return if (target is BlobShape && shape.isStreaming(model)) { + TsServerRuntimeType.byteStream(runtimeConfig).toSymbol() + } else { + initial + } + } + + override fun timestampShape(shape: TimestampShape?): Symbol { + return TsServerRuntimeType.dateTime(runtimeConfig).toSymbol() + } + + override fun blobShape(shape: BlobShape?): Symbol { + return TsServerRuntimeType.blob(runtimeConfig).toSymbol() + } + + override fun documentShape(shape: DocumentShape?): Symbol { + return TsServerRuntimeType.document(runtimeConfig).toSymbol() + } +} + +/** + * SymbolProvider to drop the PartialEq bounds in streaming shapes + * + * Streaming shapes equality cannot be checked without reading the body. Because of this, these shapes + * do not implement `PartialEq`. + * + * Note that since streaming members can only be used on the root shape, this can only impact input and output shapes. + */ +class TsStreamingShapeMetadataProvider(private val base: RustSymbolProvider) : SymbolMetadataProvider(base) { + override fun structureMeta(structureShape: StructureShape): RustMetadata { + val baseMetadata = base.toSymbol(structureShape).expectRustMetadata() + return if (structureShape.hasStreamingMember(model)) { + baseMetadata.withoutDerives(RuntimeType.PartialEq) + } else { + baseMetadata + } + } + + override fun unionMeta(unionShape: UnionShape): RustMetadata { + val baseMetadata = base.toSymbol(unionShape).expectRustMetadata() + return if (unionShape.hasStreamingMember(model)) { + baseMetadata.withoutDerives(RuntimeType.PartialEq) + } else { + baseMetadata + } + } + + override fun memberMeta(memberShape: MemberShape) = base.toSymbol(memberShape).expectRustMetadata() + override fun enumMeta(stringShape: StringShape): RustMetadata = + RustMetadata( + setOf(RuntimeType.Eq, RuntimeType.Ord, RuntimeType.PartialEq, RuntimeType.PartialOrd, RuntimeType.Debug), + listOf(), + Visibility.PUBLIC, + ) + + override fun listMeta(listShape: ListShape) = base.toSymbol(listShape).expectRustMetadata() + override fun mapMeta(mapShape: MapShape) = base.toSymbol(mapShape).expectRustMetadata() + override fun stringMeta(stringShape: StringShape) = base.toSymbol(stringShape).expectRustMetadata() + override fun numberMeta(numberShape: NumberShape) = base.toSymbol(numberShape).expectRustMetadata() + override fun blobMeta(blobShape: BlobShape) = base.toSymbol(blobShape).expectRustMetadata() +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/customizations/TsServerCodegenDecorator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/customizations/TsServerCodegenDecorator.kt new file mode 100644 index 0000000000..850af7b51a --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/customizations/TsServerCodegenDecorator.kt @@ -0,0 +1,114 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.customizations + +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.customizations.AddInternalServerErrorToAllOperationsDecorator +import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * Configure the [lib] section of `Cargo.toml`. + * + * [lib] + * name = "$CRATE_NAME" + * crate-type = ["cdylib"] + */ +class CdylibManifestDecorator : ServerCodegenDecorator { + override val name: String = "CdylibDecorator" + override val order: Byte = 0 + + override fun crateManifestCustomizations( + codegenContext: ServerCodegenContext, + ): ManifestCustomizations = + mapOf( + "lib" to mapOf( + "crate-type" to listOf("cdylib"), + ), + ) +} + +class NapiBuildRsDecorator : ServerCodegenDecorator { + override val name: String = "NapiBuildRsDecorator" + override val order: Byte = 0 + private val napi_build = TsServerCargoDependency.NapiBuild.toType() + + override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { + rustCrate.withFile("build.rs") { + rustTemplate( + """ + fn main() { + #{napi_build}::setup(); + } + """, + "napi_build" to napi_build, + ) + } + } +} + +class NapiPackageJsonDecorator : ServerCodegenDecorator { + override val name: String = "NapiPackageJsonDecorator" + override val order: Byte = 0 + + override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { + val name = codegenContext.settings.moduleName.toSnakeCase() + val version = codegenContext.settings.moduleVersion + + // TODO(https://github.com/awslabs/smithy-rs/issues/2317): we should probably use a real JSON writer, but I did not want to add + // other external libraries at this stage. + rustCrate.withFile("package.json") { + val content = """{ + "name": "@amzn/$name", + "version": "$version", + "main": "index.js", + "types": "index.d.ts", + "napi": { + "name": "$name", + "triple": {} + }, + "devDependencies": { + "@napi-rs/cli": ">=2", + "@types/node": ">=18" + }, + "engines": { + "node": ">=18" + }, + "scripts": { + "artifacts": "napi artifacts", + "build": "napi build --platform --release", + "build:debug": "napi build --platform", + "prepublishOnly": "napi prepublish -t npm", + "universal": "napi universal", + "version": "napi version" + }, + "packageManager": "yarn", + "dependencies": { + "yarn": ">=1" + } +}""" + this.write(content) + } + } +} + +val DECORATORS = arrayOf( + /** + * Add the [InternalServerError] error to all operations. + * This is done because the Typescript interpreter can raise eceptions during execution. + */ + AddInternalServerErrorToAllOperationsDecorator(), + // Add the [lib] section to Cargo.toml to configure the generation of the shared library. + CdylibManifestDecorator(), + // Add the build.rs file needed to generate Typescript code. + NapiBuildRsDecorator(), + // Add the napi package.json. + NapiPackageJsonDecorator(), +) diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsApplicationGenerator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsApplicationGenerator.kt new file mode 100644 index 0000000000..92e346fa91 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsApplicationGenerator.kt @@ -0,0 +1,314 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.generators + +import software.amazon.smithy.model.knowledge.TopDownIndex +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.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.util.toPascalCase +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency +import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * Generates a Typescript compatible application and server that can be configured from Typescript. + */ +class TsApplicationGenerator( + codegenContext: CodegenContext, + private val protocol: ServerProtocol, +) { + private val index = TopDownIndex.of(codegenContext.model) + private val operations = index.getContainedOperations(codegenContext.serviceShape).toSortedSet( + compareBy { + it.id + }, + ).toList() + private val symbolProvider = codegenContext.symbolProvider + private val libName = codegenContext.settings.moduleName.toSnakeCase() + private val runtimeConfig = codegenContext.runtimeConfig + private val service = codegenContext.serviceShape + private val serviceName = service.id.name.toPascalCase() + private val model = codegenContext.model + private val codegenScope = + arrayOf( + "SmithyServer" to ServerCargoDependency.smithyHttpServer(runtimeConfig).toType(), + "napi" to TsServerCargoDependency.Napi.toType(), + "napi_derive" to TsServerCargoDependency.NapiDerive.toType(), + "tokio" to TsServerCargoDependency.Tokio.toType(), + "tracing" to TsServerCargoDependency.Tracing.toType(), + "tower" to TsServerCargoDependency.Tower.toType(), + "tower_http" to TsServerCargoDependency.TowerHttp.toType(), + "num_cpus" to TsServerCargoDependency.NumCpus.toType(), + "hyper" to TsServerCargoDependency.Hyper.toType(), + "HashMap" to RuntimeType.HashMap, + "parking_lot" to TsServerCargoDependency.ParkingLot.toType(), + "http" to RuntimeType.Http, + "socket2" to TsServerCargoDependency.Socket2.toType(), + ) + + fun render(writer: RustWriter) { + writer.write("use napi_derive::napi;") + renderHandlers(writer) + renderApp(writer) + + // TODO(https://github.com/napi-rs/napi-rs/issues/1377) Move these to be part of the runtime crate. + renderSocket(writer) + renderServer(writer) + } + + fun renderHandlers(writer: RustWriter) { + Attribute(derive(RuntimeType.Clone)).render(writer) + writer.rustBlock("""pub struct Handlers""") { + operations.map { operation -> + val operationName = symbolProvider.toSymbol(operation).name + val input = "crate::input::${operationName}Input" + val fnName = operationName.toSnakeCase() + rustTemplate( + """ + pub(crate) $fnName: #{napi}::threadsafe_function::ThreadsafeFunction< + $input, + #{napi}::threadsafe_function::ErrorStrategy::Fatal + >, + """, + *codegenScope, + ) + } + } + Attribute("""napi(object)""").render(writer) + writer.rustBlock("pub struct TsHandlers") { + operations.map { operation -> + val operationName = symbolProvider.toSymbol(operation).name + val input = "${operationName}Input" + val output = "${operationName}Output" + val fnName = operationName.toSnakeCase() + rustTemplate( + """ + ##[napi(ts_type = "(input: $input) => Promise<$output>")] + pub $fnName: #{napi}::JsFunction, + """, + *codegenScope, + ) + } + } + } + + private fun renderApp(writer: RustWriter) { + Attribute("napi").render(writer) + writer.rust( + """ + pub struct App { + handlers: Handlers, + } + """, + ) + Attribute("napi").render(writer) + writer.rustBlock("impl App") { + renderAppCreate(writer) + renderAppStart(writer) + } + } + + private fun renderAppCreate(writer: RustWriter) { + Attribute("napi(constructor)").render(writer) + writer.rustBlockTemplate( + """pub fn create(ts_handlers: TsHandlers) -> #{napi}::Result""", + *codegenScope, + ) { + operations.map { operation -> + val operationName = symbolProvider.toSymbol(operation).name + val input = "crate::input::${operationName}Input" + val fnName = operationName.toSnakeCase() + rustTemplate( + """ + let $fnName: #{napi}::threadsafe_function::ThreadsafeFunction< + $input, #{napi}::threadsafe_function::ErrorStrategy::Fatal + > = ts_handlers.$fnName.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value]))?; + """, + *codegenScope, + ) + } + rust("let handlers = Handlers {") + operations.map { operation -> + val operationName = symbolProvider.toSymbol(operation).name.toSnakeCase() + rust(" $operationName: $operationName.clone(),") + } + rust("};") + writer.rust("Ok(Self{ handlers })") + } + } + + private fun renderAppStart(writer: RustWriter) { + Attribute("napi").render(writer) + writer.rustBlockTemplate( + """pub fn start(&self, socket: &TsSocket) -> #{napi}::Result<()>""", + *codegenScope, + ) { + rustTemplate( + """ + let plugins = #{SmithyServer}::plugin::PluginPipeline::new(); + let builder = crate::service::$serviceName::builder_with_plugins(plugins); + """, + *codegenScope, + ) + operations.map { operation -> + val operationName = symbolProvider.toSymbol(operation).name.toSnakeCase() + rust("let builder = builder.$operationName(crate::ts_operation_adaptor::$operationName);") + } + rustTemplate( + """ + let app = builder.build().expect("failed to build instance of $serviceName") + .layer(&#{SmithyServer}::AddExtensionLayer::new(self.handlers.clone())); + let service = #{tower}::util::BoxCloneService::new(app); + start_hyper_worker(socket, service).expect("failed to start the hyper server"); + Ok(()) + """, + *codegenScope, + ) + } + } + + private fun renderSocket(writer: RustWriter) { + Attribute("napi").render(writer) + Attribute(derive(RuntimeType.Debug)).render(writer) + writer.rustTemplate( + """ + pub struct TsSocket(#{socket2}::Socket); + """, + *codegenScope, + ) + + Attribute("napi").render(writer) + writer.rustBlock("impl TsSocket") { + writer.rust( + """ + /// Create a new UNIX `Socket` from an address, port and backlog. + /// If not specified, the backlog defaults to 1024 connections. + """.trimIndent(), + ) + Attribute("napi(constructor)").render(writer) + writer.rustBlockTemplate( + """pub fn new(address: String, port: i32, backlog: Option) -> #{napi}::Result""".trimIndent(), + *codegenScope, + ) { + writer.rustTemplate( + """ + let socket = Self::new_socket(address, port, backlog) + .map_err(|e| #{napi}::Error::from_reason(e.to_string()))?; + Ok(Self(socket)) + """, + *codegenScope, + ) + } + writer.rustBlockTemplate( + """pub fn new_socket(address: String, port: i32, backlog: Option) -> Result<#{socket2}::Socket, Box> """.trimIndent(), + *codegenScope, + ) { + writer.rustTemplate( + """ + let address: std::net::SocketAddr = format!("{}:{}", address, port).parse()?; + let domain = if address.is_ipv6() { + #{socket2}::Domain::IPV6 + } else { + #{socket2}::Domain::IPV4 + }; + let socket = #{socket2}::Socket::new(domain, #{socket2}::Type::STREAM, Some(#{socket2}::Protocol::TCP))?; + // Set value for the `SO_REUSEPORT` and `SO_REUSEADDR` options on this socket. + // This indicates that further calls to `bind` may allow reuse of local + // addresses. For IPv4 sockets this means that a socket may bind even when + // there's a socket already listening on this port. + socket.set_reuse_port(true)?; + socket.set_reuse_address(true)?; + socket.bind(&address.into())?; + socket.listen(backlog.unwrap_or(1024))?; + Ok(socket) + """.trimIndent(), + *codegenScope, + ) + } + + writer.rust( + """ + /// Clone the inner socket allowing it to be shared between multiple + /// Nodejs processes. + """.trimIndent(), + ) + Attribute("napi").render(writer) + writer.rustBlockTemplate( + """pub fn try_clone(&self) -> #{napi}::Result""".trimIndent(), + *codegenScope, + ) { + writer.rustTemplate( + """ + Ok(TsSocket( + self.0 + .try_clone() + .map_err(|e| #{napi}::Error::from_reason(e.to_string()))?, + )) + """.trimIndent(), + *codegenScope, + ) + } + } + + writer.rustBlock("impl TsSocket") { + writer.rustTemplate( + """pub fn to_raw_socket(&self) -> #{napi}::Result<#{socket2}::Socket> { + self.0 + .try_clone() + .map_err(|e| #{napi}::Error::from_reason(e.to_string())) + } + + """.trimIndent(), + *codegenScope, + ) + } + } + + private fun renderServer(writer: RustWriter) { + writer.rustBlockTemplate( + """pub fn start_hyper_worker( + socket: &TsSocket, + app: #{tower}::util::BoxCloneService< + #{http}::Request<#{SmithyServer}::body::Body>, + #{http}::Response<#{SmithyServer}::body::BoxBody>, + std::convert::Infallible, + >, + ) -> #{napi}::Result<()> + """.trimIndent(), + *codegenScope, + ) { + writer.rustTemplate( + """ + let server = #{hyper}::Server::from_tcp( + socket + .to_raw_socket()? + .try_into() + .expect("Unable to convert socket2::Socket into std::net::TcpListener"), + ) + .expect("Unable to create hyper server from shared socket") + .serve(#{SmithyServer}::routing::IntoMakeService::new(app)); + + let handle = #{tokio}::runtime::Handle::current(); + handle.spawn(async move { + // Process each socket concurrently. + server.await + }); + + Ok(()) + """.trimIndent(), + *codegenScope, + ) + } + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerEnumGenerator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerEnumGenerator.kt new file mode 100644 index 0000000000..f12ebb5ef6 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerEnumGenerator.kt @@ -0,0 +1,52 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.generators + +import software.amazon.smithy.model.shapes.StringShape +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGeneratorContext +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedEnum +import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * To share enums defined in Rust with Typescript, `napi-rs` provides the `napi` trait. + * This class generates enums definitions and implements the `napi` trait. + */ +class TsConstrainedEnum( + codegenContext: ServerCodegenContext, + shape: StringShape, + validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, +) : ConstrainedEnum(codegenContext, shape, validationExceptionConversionGenerator) { + private val napiDerive = TsServerCargoDependency.NapiDerive.toType() + + override fun additionalEnumImpls(context: EnumGeneratorContext): Writable = writable { + this.rust("use napi::bindgen_prelude::ToNapiValue;") + } + + override fun additionalEnumAttributes(context: EnumGeneratorContext): List = + listOf(Attribute(napiDerive.resolve("napi"))) +} + +class TsServerEnumGenerator( + codegenContext: ServerCodegenContext, + shape: StringShape, + validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, +) : EnumGenerator( + codegenContext.model, + codegenContext.symbolProvider, + shape, + TsConstrainedEnum( + codegenContext, + shape, + validationExceptionConversionGenerator, + ), +) diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationErrorGenerator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationErrorGenerator.kt new file mode 100644 index 0000000000..6384c5c07c --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationErrorGenerator.kt @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.knowledge.OperationIndex +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +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.RustSymbolProvider +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * Generates a unified error enum for [operation] and adds the Rust implementation for `napi` error. + */ +class TsServerOperationErrorGenerator( + private val model: Model, + private val symbolProvider: RustSymbolProvider, + private val operation: OperationShape, +) { + private val operationIndex = OperationIndex.of(model) + private val errors = operationIndex.getErrors(operation) + + fun render(writer: RustWriter) { + renderFromTsErr(writer) + } + + // TODO(https://github.com/awslabs/smithy-rs/issues/2317): match the Ts error type and return the right one. + private fun renderFromTsErr(writer: RustWriter) { + writer.rustTemplate( + """ + impl #{From}<#{napi}::Error> for #{Error} { + fn from(variant: #{napi}::Error) -> #{Error} { + crate::error::InternalServerError { message: variant.to_string() }.into() + } + } + + """, + "napi" to TsServerCargoDependency.Napi.toType(), + "Error" to symbolProvider.symbolForOperationError(operation), + "From" to RuntimeType.From, + ) + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationHandlerGenerator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationHandlerGenerator.kt new file mode 100644 index 0000000000..787a0b7cc0 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerOperationHandlerGenerator.kt @@ -0,0 +1,58 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.generators + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * The Rust code responsible to run the Typescript business logic is implemented in this class,. + * + * We codegenerate all operations handlers (steps usually left to the developer in a pure + * Rust application), which are built into a `Router` by [TsServerApplicationGenerator]. + */ +class TsServerOperationHandlerGenerator( + codegenContext: CodegenContext, + private val operation: OperationShape, +) { + private val symbolProvider = codegenContext.symbolProvider + private val runtimeConfig = codegenContext.runtimeConfig + private val codegenScope = + arrayOf( + "SmithyTs" to TsServerCargoDependency.smithyHttpServerTs(runtimeConfig).toType(), + "SmithyServer" to TsServerCargoDependency.smithyHttpServer(runtimeConfig).toType(), + "napi" to TsServerCargoDependency.Napi.toType(), + ) + + fun render(writer: RustWriter) { + renderTsOperationHandlerImpl(writer) + } + + private fun renderTsOperationHandlerImpl(writer: RustWriter) { + val operationName = symbolProvider.toSymbol(operation).name + val input = "crate::input::${operationName}Input" + val output = "crate::output::${operationName}Output" + val error = "crate::error::${operationName}Error" + val fnName = operationName.toSnakeCase() + + writer.rustTemplate( + """ + /// Typescript handler for operation `$operationName`. + pub(crate) async fn $fnName( + input: $input, + handlers: #{SmithyServer}::Extension, + ) -> std::result::Result<$output, $error> { + handlers.$fnName.call_async::<#{napi}::bindgen_prelude::Promise<$output>>(input).await?.await.map_err(|e| e.into()) + } + """, + *codegenScope, + ) + } +} diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerStructureGenerator.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerStructureGenerator.kt new file mode 100644 index 0000000000..87d8300ca7 --- /dev/null +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/generators/TsServerStructureGenerator.kt @@ -0,0 +1,50 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.typescript.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.traits.ErrorTrait +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rustInlineTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.server.typescript.smithy.TsServerCargoDependency + +/** + * To share structures defined in Rust with Typescript, `napi-rs` provides the `napi` trait. + * This class generates input / output / error structures definitions and implements the + * `napi` trait. + */ +class TsServerStructureGenerator( + model: Model, + private val symbolProvider: RustSymbolProvider, + private val writer: RustWriter, + private val shape: StructureShape, +) : StructureGenerator(model, symbolProvider, writer, shape, listOf()) { + + private val napiDerive = TsServerCargoDependency.NapiDerive.toType() + + override fun renderStructure() { + val flavour = if (shape.hasTrait()) { + "constructor" + } else { + "object" + } + Attribute( + writable { + rustInlineTemplate( + "#{napi}($flavour)", + "napi" to napiDerive.resolve("napi"), + ) + }, + ).render(writer) + super.renderStructure() + } +} diff --git a/codegen-server/typescript/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin b/codegen-server/typescript/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin new file mode 100644 index 0000000000..ee61dbc10a --- /dev/null +++ b/codegen-server/typescript/src/main/resources/META-INF/services/software.amazon.smithy.build.SmithyBuildPlugin @@ -0,0 +1,5 @@ +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# +software.amazon.smithy.rust.codegen.server.typescript.smithy.RustServerCodegenTsPlugin diff --git a/rust-runtime/aws-smithy-http-server-typescript/Cargo.toml b/rust-runtime/aws-smithy-http-server-typescript/Cargo.toml new file mode 100644 index 0000000000..1147d0da98 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "aws-smithy-http-server-typescript" +version = "0.0.0-smithy-rs-head" +authors = ["Smithy Rust Server "] +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" +keywords = ["smithy", "framework", "web", "api", "aws", "typescript"] +categories = ["asynchronous", "web-programming", "api-bindings"] +description = """ +Typescript server runtime for Smithy Rust Server Framework. +""" +publish = false + +[dependencies] + +[build-dependencies] + +[package.metadata.docs.rs] +all-features = true +targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = ["--cfg", "docsrs"] +# End of docs.rs metadata diff --git a/rust-runtime/aws-smithy-http-server-typescript/LICENSE b/rust-runtime/aws-smithy-http-server-typescript/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/rust-runtime/aws-smithy-http-server-typescript/README.md b/rust-runtime/aws-smithy-http-server-typescript/README.md new file mode 100644 index 0000000000..b5d516d1c2 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/README.md @@ -0,0 +1,7 @@ +# aws-smithy-http-server-typescript + +Server libraries for smithy-rs generated servers, targeting pure Typescript business logic. + + +This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. + diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/.gitignore b/rust-runtime/aws-smithy-http-server-typescript/examples/.gitignore new file mode 100644 index 0000000000..7f71e8f1ca --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/.gitignore @@ -0,0 +1,93 @@ +pokemon-service-client/ +pokemon-service-server-sdk/ +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions +$RECYCLE.BIN/ +**/*.rs.bk +*.cab +*.icloud +*.lcov +*.lnk +*.log +*.msi +*.msix +*.msm +*.msp +*.node +*.pid +*.pid.lock +*.seed +*.stackdump +*.tgz +*.tsbuildinfo +.AppleDB +.AppleDesktop +.AppleDouble +.DS_Store +.DocumentRevisions-V100 +.LSOverride +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +._* +.apdisk +.cache +.cache/ +.com.apple.timemachine.donotpresent +.dynamodb/ +.env +.env.test +.eslintcache +.fseventsd +.fusebox/ +.grunt +.lock-wscript +.next +.node_repl_history +.npm +.nuxt +.nyc_output +.pnp.* +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ +.serverless/ +.tern-port +.vscode-test +.vuepress/dist +.yarn-integrity +.yarn/* +/target +Cargo.lock +Icon +Network Trash Folder +Temporary Items +Thumbs.db +Thumbs.db:encryptable +[Dd]esktop.ini +bower_components +build/Release +coverage +dist +ehthumbs.db +ehthumbs_vista.db +jspm_packages/ +lerna-debug.log* +lib-cov +logs +node_modules/ +npm-debug.log* +pids +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json +typings/ +yarn-debug.log* +yarn-error.log* +package-lock.json +index.d.ts +index.js +pokemon-service-server-sdk.*.node diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/Cargo.toml b/rust-runtime/aws-smithy-http-server-typescript/examples/Cargo.toml new file mode 100644 index 0000000000..95b64ff739 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/Cargo.toml @@ -0,0 +1,9 @@ +# Without this configuration, the workspace will be read from `rust-runtime`, causing the build to fail. +[workspace] +members = [ + "pokemon-service-server-sdk", + "pokemon-service-client" +] + +[profile.release] +lto = true diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/Makefile b/rust-runtime/aws-smithy-http-server-typescript/examples/Makefile new file mode 100644 index 0000000000..f199ac1120 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/Makefile @@ -0,0 +1,44 @@ +SRC_DIR := $(shell git rev-parse --show-toplevel) +CUR_DIR := $(shell pwd) +GRADLE := $(SRC_DIR)/gradlew +SERVER_SDK_DST := $(CUR_DIR)/pokemon-service-server-sdk +CLIENT_SDK_DST := $(CUR_DIR)/pokemon-service-client +SERVER_SDK_SRC := $(SRC_DIR)/codegen-server-test/typescript/build/smithyprojections/codegen-server-test-typescript/pokemon-service-server-sdk/rust-server-codegen-typescript +CLIENT_SDK_SRC := $(SRC_DIR)/codegen-client-test/build/smithyprojections/codegen-client-test/pokemon-service-client/rust-client-codegen + +all: codegen + +codegen: + $(GRADLE) --project-dir $(SRC_DIR) -P modules='pokemon-service-server-sdk,pokemon-service-client' :codegen-client-test:assemble :codegen-server-test:typescript:assemble + mkdir -p $(SERVER_SDK_DST) $(CLIENT_SDK_DST) + cp -av $(SERVER_SDK_SRC)/* $(SERVER_SDK_DST)/ + cp -av $(CLIENT_SDK_SRC)/* $(CLIENT_SDK_DST)/ + +build: + cd pokemon-service-server-sdk && npm run build:debug + ln -sf $(shell find pokemon-service-server-sdk -name '*.node') . + ln -sf pokemon-service-server-sdk/index.d.ts + ln -sf pokemon-service-server-sdk/index.js + +release: + cd pokemon-service-server-sdk && npm run build + ln -sf $(shell find pokemon-service-server-sdk -name '*.node') . + ln -sf pokemon-service-server-sdk/index.d.ts + ln -sf pokemon-service-server-sdk/index.js + +run: build + ts-node pokemon-service.ts + +run-release: release + ts-node pokemon-service.ts + +doc-open: codegen + cargo doc --no-deps --open + +clean: + cargo clean || echo "Unable to run cargo clean" + +distclean: clean + rm -rf $(SERVER_SDK_DST) $(CLIENT_SDK_DST) $(CUR_DIR)/Cargo.lock pokemon-service-server-sdk.*.node index.d.ts + +.PHONY: all diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/README.md b/rust-runtime/aws-smithy-http-server-typescript/examples/README.md new file mode 100644 index 0000000000..8f59208ff8 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/README.md @@ -0,0 +1,30 @@ +# Smithy Rust/Typescript Server SDK example + +This folder contains an example service called Pokémon Service used to showcase +the service framework Typescript bindings capabilities and to run benchmarks. + +The Python implementation of the service can be found inside +[pokemon_service.ts](/rust-runtime/aws-smithy-http-typescript-server/examples/pokemon_service.ts). + +## Depedencies + +TODO: Add NAPI installation instructions + +## Build + +Since this example requires both the server and client SDK to be code-generated +from their [model](/codegen-server-test/model/pokemon.smithy), a Makefile is +provided to build and run the service. Just run `make build` to prepare the first +build. + +Once the example has been built successfully the first time, idiomatic `cargo` +can be used directly. + +`make distclean` can be used for a complete cleanup of all artefacts. + +## Test + +`cargo test` can be used to spawn the Python service and run some simple integration +tests against it. + +More info can be found in the `tests` folder of `pokemon-service-test` package. diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/package.json b/rust-runtime/aws-smithy-http-server-typescript/examples/package.json new file mode 100644 index 0000000000..a311cbd557 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "@types/node": "^18.15.7" + } +} diff --git a/rust-runtime/aws-smithy-http-server-typescript/examples/pokemon-service.ts b/rust-runtime/aws-smithy-http-server-typescript/examples/pokemon-service.ts new file mode 100644 index 0000000000..269753c703 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/examples/pokemon-service.ts @@ -0,0 +1,97 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import cluster from "cluster"; +import { cpus } from "os"; + +import { + App, + TsHandlers, + GetPokemonSpeciesInput, + GetPokemonSpeciesOutput, + Language, + DoNothingInput, + DoNothingOutput, + TsSocket, + CheckHealthOutput, + CheckHealthInput, + GetServerStatisticsInput, + GetServerStatisticsOutput, +} from "."; + +class HandlerImpl implements TsHandlers { + // TODO: implement + async doNothing(input: DoNothingInput): Promise { + return {}; + } + // TODO: implement + async checkHealth(input: CheckHealthInput): Promise { + return {}; + } + // TODO: implement + async getServerStatistics( + input: GetServerStatisticsInput + ): Promise { + return { callsCount: 0 }; + } + async getPokemonSpecies( + input: GetPokemonSpeciesInput, + ): Promise { + return { + name: input.name, + flavorTextEntries: [ + { + language: Language.English, + flavorText: + "When several of these Pokémon gather, their electricity could build and cause lightning storms.", + }, + { + language: Language.Italian, + flavorText: + "Quando vari Pokémon di questo tipo si radunano, la loro energia può causare forti tempeste.", + }, + { + language: Language.Spanish, + flavorText: + "Cuando varios de estos Pokémon se juntan, su energía puede causar fuertes tormentas.", + }, + { + language: Language.Japanese, + flavorText: + "ほっぺたの りょうがわに ちいさい でんきぶくろを もつ。ピンチのときに ほうでんする。", + }, + ], + }; + } +} + +// Pass the handlers to the App. +const app = new App(new HandlerImpl()); +// Start the app 🤘 +const numCPUs = cpus().length / 2; +const address ="127.0.0.1"; +const port = 9090; +const socket = new TsSocket(address, port); +app.start(socket); + +// TODO: This part should be abstracted out and done directly in Rust. +// We could take an optional number of workers and the socket as input +// of the App.start() method. +if (cluster.isPrimary) { + console.log(`Listening on ${address}:${port}`); + // Fork workers. + for (let i = 0; i < numCPUs; i++) { + cluster.fork(); + } +} + +process.on("unhandledRejection", err => { + console.error("Unhandled") + console.error(err) +}) +process.on("uncaughtException", err => { + console.error("Uncaught") + console.error(err) +}) diff --git a/rust-runtime/aws-smithy-http-server-typescript/src/lib.rs b/rust-runtime/aws-smithy-http-server-typescript/src/lib.rs new file mode 100644 index 0000000000..35d0f3613a --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-typescript/src/lib.rs @@ -0,0 +1,4 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ diff --git a/settings.gradle.kts b/settings.gradle.kts index 88f55a4e96..e33a925bb7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,8 +10,10 @@ include(":codegen-client") include(":codegen-client-test") include(":codegen-server") include(":codegen-server:python") +include(":codegen-server:typescript") include(":codegen-server-test") include(":codegen-server-test:python") +include(":codegen-server-test:typescript") include(":rust-runtime") include(":aws:rust-runtime") include(":aws:sdk") diff --git a/tools/ci-build/publisher/src/package.rs b/tools/ci-build/publisher/src/package.rs index 2b0bc90764..7156c9214b 100644 --- a/tools/ci-build/publisher/src/package.rs +++ b/tools/ci-build/publisher/src/package.rs @@ -521,6 +521,7 @@ mod tests { let server_packages = vec![ package("aws-smithy-http-server", &[]), package("aws-smithy-http-server-python", &[]), + package("aws-smithy-http-server-typescript", &[]), ]; for pkg in server_packages { assert_eq!( diff --git a/tools/ci-build/sdk-lints/src/lint_cargo_toml.rs b/tools/ci-build/sdk-lints/src/lint_cargo_toml.rs index e683459cab..2bb7610c5a 100644 --- a/tools/ci-build/sdk-lints/src/lint_cargo_toml.rs +++ b/tools/ci-build/sdk-lints/src/lint_cargo_toml.rs @@ -53,7 +53,11 @@ struct Metadata { const RUST_SDK_TEAM: &str = "AWS Rust SDK Team "; const SERVER_TEAM: &str = "Smithy Rust Server "; -const SERVER_CRATES: &[&str] = &["aws-smithy-http-server", "aws-smithy-http-server-python"]; +const SERVER_CRATES: &[&str] = &[ + "aws-smithy-http-server", + "aws-smithy-http-server-python", + "aws-smithy-http-server-typescript", +]; /// Check crate licensing /// diff --git a/tools/ci-scripts/codegen-diff/diff_lib.py b/tools/ci-scripts/codegen-diff/diff_lib.py index 42068040ca..0da8994026 100644 --- a/tools/ci-scripts/codegen-diff/diff_lib.py +++ b/tools/ci-scripts/codegen-diff/diff_lib.py @@ -33,6 +33,7 @@ def generate_and_commit_generated_code(revision_sha, targets=None): get_cmd_output(f"./gradlew --rerun-tasks {tasks}") if target_codegen_server in targets: get_cmd_output("cd rust-runtime/aws-smithy-http-server-python/examples && make build", shell=True, check=False) + get_cmd_output(f"./gradlew --rerun-tasks codegen-server-test:typescript:assemble") # Move generated code into codegen-diff/ directory get_cmd_output(f"rm -rf {OUTPUT_PATH}") @@ -46,6 +47,9 @@ def generate_and_commit_generated_code(revision_sha, targets=None): get_cmd_output( f"mv rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-server-sdk/ {OUTPUT_PATH}/codegen-server-test-python/", check=False) + get_cmd_output( + f"mv codegen-server-test/typescript/build/smithyprojections/codegen-server-test-typescript {OUTPUT_PATH}/", + check=False) # Clean up the SDK directory get_cmd_output(f"rm -f {OUTPUT_PATH}/aws-sdk/versions.toml") @@ -58,9 +62,16 @@ def generate_and_commit_generated_code(revision_sha, targets=None): # Clean up the server-test folder get_cmd_output(f"rm -rf {OUTPUT_PATH}/codegen-server-test/source") + get_cmd_output(f"rm -rf {OUTPUT_PATH}/codegen-server-test-typescript/source") run(f"find {OUTPUT_PATH}/codegen-server-test | " f"grep -E 'smithy-build-info.json|sources/manifest|model.json' | " f"xargs rm -f", shell=True) + run(f"find {OUTPUT_PATH}/codegen-server-test-python | " + f"grep -E 'smithy-build-info.json|sources/manifest|model.json' | " + f"xargs rm -f", shell=True) + run(f"find {OUTPUT_PATH}/codegen-server-test-typescript | " + f"grep -E 'smithy-build-info.json|sources/manifest|model.json' | " + f"xargs rm -f", shell=True) get_cmd_output(f"git add -f {OUTPUT_PATH}") get_cmd_output(f"git -c 'user.name=GitHub Action (generated code preview)' " @@ -116,6 +127,10 @@ def make_diffs(base_commit_sha, head_commit_sha): head_commit_sha, "server-test-python", whitespace=True) server_nows_python = make_diff("Server Test Python", f"{OUTPUT_PATH}/codegen-server-test-python", base_commit_sha, head_commit_sha, "server-test-python-ignore-whitespace", whitespace=False) + server_ws_typescript = make_diff("Server Test Typescript", f"{OUTPUT_PATH}/codegen-server-test-typescript", base_commit_sha, + head_commit_sha, "server-test-typescript", whitespace=True) + server_nows_typescript = make_diff("Server Test Typescript", f"{OUTPUT_PATH}/codegen-server-test-typescript", base_commit_sha, + head_commit_sha, "server-test-typescript-ignore-whitespace", whitespace=False) sdk_links = diff_link('AWS SDK', 'No codegen difference in the AWS SDK', sdk_ws, 'ignoring whitespace', sdk_nows) @@ -125,12 +140,15 @@ def make_diffs(base_commit_sha, head_commit_sha): server_ws, 'ignoring whitespace', server_nows) server_links_python = diff_link('Server Test Python', 'No codegen difference in the Server Test Python', server_ws_python, 'ignoring whitespace', server_nows_python) + server_links_typescript = diff_link('Server Test Typescript', 'No codegen difference in the Server Test Typescript', + server_ws_typescript, 'ignoring whitespace', server_nows_typescript) # Save escaped newlines so that the GitHub Action script gets the whole message return "A new generated diff is ready to view.\\n" \ f"- {sdk_links}\\n" \ f"- {client_links}\\n" \ f"- {server_links}\\n" \ - f"- {server_links_python}\\n" + f"- {server_links_python}\\n" \ + f"- {server_links_typescript}\\n" def write_to_file(path, text): From 187918a0eb9b0a3f4fc229d3c837aca5f87d09ca Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 6 Apr 2023 12:42:13 -0400 Subject: [PATCH 011/253] Add semver-checks CI job (#2518) * Add semver-checks CI job * Add flag to skip generation * Make it possible to suppress semver checks with label * fix GitHub script * Fix but in semver-checks python script * Cleanup debug info * Move to ci-pr --- .github/workflows/ci-pr.yml | 35 ++++++++++ .github/workflows/ci.yml | 2 + ci.mk | 4 ++ tools/ci-scripts/check-semver | 24 +++++++ .../check-deterministic-codegen.py | 13 ++-- .../codegen-diff/codegen-diff-revisions.py | 4 +- tools/ci-scripts/codegen-diff/diff_lib.py | 23 ++++++- .../ci-scripts/codegen-diff/semver-checks.py | 64 +++++++++++++++++++ 8 files changed, 156 insertions(+), 13 deletions(-) create mode 100755 tools/ci-scripts/check-semver create mode 100755 tools/ci-scripts/codegen-diff/semver-checks.py diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 374d23a9b4..5b53c4dbf1 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -107,3 +107,38 @@ jobs: secrets: SMITHY_RS_PULL_REQUEST_CDN_S3_BUCKET_NAME: ${{ secrets.SMITHY_RS_PULL_REQUEST_CDN_S3_BUCKET_NAME }} SMITHY_RS_PULL_REQUEST_CDN_ROLE_ARN: ${{ secrets.SMITHY_RS_PULL_REQUEST_CDN_ROLE_ARN }} + + semver-checks: + name: check the semver status of this PR + runs-on: smithy_ubuntu-latest_8-core + steps: + - uses: actions/checkout@v3 + with: + path: smithy-rs + ref: ${{ inputs.git_ref }} + - name: Get PR info + id: check-breaking-label + uses: actions/github-script@v6 + with: + script: | + const response = await github.rest.pulls.get({ + pull_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }); + const labels = response.data.labels.map(l => l.name); + const isBreaking = labels.includes("breaking-change"); + const data = { + labels, + isBreaking + }; + console.log("data:", data); + return data; + - name: Run semver check + uses: ./smithy-rs/.github/actions/docker-build + with: + action: check-semver + action-arguments: ${{ github.event.pull_request.base.sha }} ${{ fromJSON(steps.check-breaking-label.outputs.result).isBreaking }} + - name: print help message + if: failure() + run: echo "::error::This pull request contains breaking changes. Please add the `breaking-changes` label and a changelog entry" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 941298298d..c36d5ba08c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,6 +159,8 @@ jobs: with: action: ${{ matrix.test.action }} + + test-rust-windows: name: Rust Tests on Windows runs-on: windows-latest diff --git a/ci.mk b/ci.mk index c30de0d113..ecf6a3363c 100644 --- a/ci.mk +++ b/ci.mk @@ -120,6 +120,10 @@ generate-codegen-diff: check-deterministic-codegen: $(CI_ACTION) $@ $(ARGS) +.PHONY: check-semver +check-semver: + $(CI_ACTION) $@ $(ARGS) + .PHONY: generate-smithy-rs-release generate-smithy-rs-release: $(CI_ACTION) $@ $(ARGS) diff --git a/tools/ci-scripts/check-semver b/tools/ci-scripts/check-semver new file mode 100755 index 0000000000..a23cc3aaa9 --- /dev/null +++ b/tools/ci-scripts/check-semver @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +set -eux +cd smithy-rs + +if [[ $# -gt 2 ]]; then + echo "Usage: check-semver " + exit 1 +fi + +# Override version commit hash to prevent unnecessary diffs +export SMITHY_RS_VERSION_COMMIT_HASH_OVERRIDE=ci +base_revision="$1" +warn_on_failure="$2" +if [[ $warn_on_failure == "true" ]] +then + ./tools/ci-scripts/codegen-diff/semver-checks.py . "${base_revision}" || echo "allowing failure" +else + ./tools/ci-scripts/codegen-diff/semver-checks.py . "${base_revision}" +fi diff --git a/tools/ci-scripts/codegen-diff/check-deterministic-codegen.py b/tools/ci-scripts/codegen-diff/check-deterministic-codegen.py index 71e0b61541..7dc72258b2 100755 --- a/tools/ci-scripts/codegen-diff/check-deterministic-codegen.py +++ b/tools/ci-scripts/codegen-diff/check-deterministic-codegen.py @@ -3,19 +3,18 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 -import sys import os -from diff_lib import get_cmd_output, generate_and_commit_generated_code +import sys + +from diff_lib import get_cmd_output, checkout_commit_and_generate + def main(): repository_root = sys.argv[1] os.chdir(repository_root) (_, head_commit_sha, _) = get_cmd_output("git rev-parse HEAD") - get_cmd_output("git checkout -B once") - generate_and_commit_generated_code(head_commit_sha, targets=['aws:sdk']) - get_cmd_output(f"git checkout {head_commit_sha}") - get_cmd_output("git checkout -B twice") - generate_and_commit_generated_code(head_commit_sha, targets=['aws:sdk']) + checkout_commit_and_generate(head_commit_sha, targets=['aws:sdk'], branch_name='once') + checkout_commit_and_generate(head_commit_sha, targets=['aws:sdk'], branch_name='twice') get_cmd_output('git diff once..twice --exit-code') diff --git a/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py b/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py index 732d223e1e..2e36e6ba5f 100755 --- a/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py +++ b/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py @@ -7,7 +7,7 @@ import sys from diff_lib import eprint, run, get_cmd_status, get_cmd_output, generate_and_commit_generated_code, make_diffs, \ - write_to_file, HEAD_BRANCH_NAME, BASE_BRANCH_NAME, OUTPUT_PATH + write_to_file, HEAD_BRANCH_NAME, BASE_BRANCH_NAME, OUTPUT_PATH, running_in_docker_build # This script can be run and tested locally. To do so, you should check out @@ -32,8 +32,6 @@ # ``` # Make sure the local version matches the version referenced from the GitHub Actions workflow. -def running_in_docker_build(): - return os.environ.get("SMITHY_RS_DOCKER_BUILD_IMAGE") == "1" def main(): diff --git a/tools/ci-scripts/codegen-diff/diff_lib.py b/tools/ci-scripts/codegen-diff/diff_lib.py index 0da8994026..9ccc0ce1bf 100644 --- a/tools/ci-scripts/codegen-diff/diff_lib.py +++ b/tools/ci-scripts/codegen-diff/diff_lib.py @@ -20,6 +20,21 @@ target_aws_sdk = 'aws:sdk' +def running_in_docker_build(): + return os.environ.get("SMITHY_RS_DOCKER_BUILD_IMAGE") == "1" + + +def checkout_commit_and_generate(revision_sha, branch_name, targets=None): + if running_in_docker_build(): + eprint(f"Fetching base revision {revision_sha} from GitHub...") + run(f"git fetch --no-tags --progress --no-recurse-submodules --depth=1 origin {revision_sha}") + + # Generate code for HEAD + eprint(f"Creating temporary branch {branch_name} with generated code for {revision_sha}") + run(f"git checkout {revision_sha} -B {branch_name}") + generate_and_commit_generated_code(revision_sha, targets) + + def generate_and_commit_generated_code(revision_sha, targets=None): targets = targets or [target_codegen_client, target_codegen_server, target_aws_sdk] # Clean the build artifacts before continuing @@ -170,12 +185,14 @@ def run(command, shell=False, check=True): # Returns (status, stdout, stderr) from a shell command -def get_cmd_output(command, cwd=None, check=True, **kwargs): +def get_cmd_output(command, cwd=None, check=True, quiet=False, **kwargs): if isinstance(command, str): - eprint(f"running {command}") + if not quiet: + eprint(f"running {command}") command = shlex.split(command) else: - eprint(f"running {' '.join(command)}") + if not quiet: + eprint(f"running {' '.join(command)}") result = subprocess.run( command, diff --git a/tools/ci-scripts/codegen-diff/semver-checks.py b/tools/ci-scripts/codegen-diff/semver-checks.py new file mode 100755 index 0000000000..cda783ce75 --- /dev/null +++ b/tools/ci-scripts/codegen-diff/semver-checks.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +import sys +import os +from diff_lib import get_cmd_output, get_cmd_status, eprint, running_in_docker_build, checkout_commit_and_generate, \ + OUTPUT_PATH + + +CURRENT_BRANCH = 'current' +BASE_BRANCH = 'base' +# This script runs `cargo semver-checks` against a previous version of codegen +def main(skip_generation=False): + if len(sys.argv) != 3: + eprint("Usage: semver-checks.py ") + sys.exit(1) + + repository_root = sys.argv[1] + base_commit_sha = sys.argv[2] + os.chdir(repository_root) + (_, head_commit_sha, _) = get_cmd_output("git rev-parse HEAD") + + # Make sure the working tree is clean + if get_cmd_status("git diff --quiet") != 0: + eprint("working tree is not clean. aborting") + sys.exit(1) + + if not skip_generation: + checkout_commit_and_generate(head_commit_sha, CURRENT_BRANCH, targets=['aws:sdk']) + checkout_commit_and_generate(base_commit_sha, BASE_BRANCH, targets=['aws:sdk']) + get_cmd_output(f'git checkout {CURRENT_BRANCH}') + sdk_directory = os.path.join(OUTPUT_PATH, 'aws-sdk', 'sdk') + os.chdir(sdk_directory) + + failed = False + for path in os.listdir(): + eprint(f'checking {path}...', end='') + if get_cmd_status(f'git cat-file -e base:{sdk_directory}/{path}/Cargo.toml') == 0: + (status, out, err) = get_cmd_output(f'cargo semver-checks check-release ' + f'--baseline-rev {BASE_BRANCH} ' + # in order to get semver-checks to work with publish-false crates, need to specify + # package and manifest path explicitly + f'--manifest-path {path}/Cargo.toml ' + f'-p {path} ' + f'--release-type patch', check=False, quiet=True) + if status == 0: + eprint('ok!') + else: + failed = True + eprint('failed!') + if out: + eprint(out) + eprint(err) + else: + eprint(f'skipping {path} because it does not exist in base') + if failed: + eprint('One or more crates failed semver checks!') + exit(1) + + +if __name__ == "__main__": + skip_generation = bool(os.environ.get('SKIP_GENERATION') or False) + main(skip_generation=skip_generation) From 742aae9560230f1398ddbcb68ee0810d3af71828 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Thu, 6 Apr 2023 17:38:03 -0500 Subject: [PATCH 012/253] Check the validity of the top-level Cargo.toml (#2553) This commit updates `ci-scripts/check-aws-sdk-service` to implicitly check whether the top-level `Cargo.toml` is valid or not. In this case, "valid" means that examples are properly excluded from the workspace. Co-authored-by: Yuki Saito --- tools/ci-scripts/check-aws-sdk-services | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/ci-scripts/check-aws-sdk-services b/tools/ci-scripts/check-aws-sdk-services index a440c98f83..2c86e975e8 100755 --- a/tools/ci-scripts/check-aws-sdk-services +++ b/tools/ci-scripts/check-aws-sdk-services @@ -7,9 +7,8 @@ set -eux cd aws-sdk -# Remove examples from workspace -sed -i '/"examples\//d' Cargo.toml - +# Invoking `cargo test` at the root directory implicitly checks for the validity +# of the top-level `Cargo.toml` cargo test --all-features for test_dir in tests/*; do From 093b65afc40bd18f8522e8dbea3cc8d462fcd116 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 7 Apr 2023 08:58:34 -0700 Subject: [PATCH 013/253] Implement the `UserAgentInterceptor` for the SDK (#2550) * Implement the `UserAgentInterceptor` for the SDK * Refactor interceptor errors * Centralize config/interceptor registration in codegen --- aws/rust-runtime/aws-http/src/user_agent.rs | 13 +- aws/rust-runtime/aws-runtime/Cargo.toml | 5 +- .../aws-runtime/external-types.toml | 3 + aws/rust-runtime/aws-runtime/src/lib.rs | 3 + .../aws-runtime/src/user_agent.rs | 233 +++++++++++ .../smithy/rustsdk/SigV4AuthDecorator.kt | 11 +- .../smithy/rustsdk/UserAgentDecorator.kt | 35 +- .../aws-sdk-s3/tests/sra_test.rs | 33 +- .../ServiceRuntimePluginGenerator.kt | 26 +- .../aws-smithy-runtime-api/Cargo.toml | 1 + .../src/client/interceptors.rs | 75 ++-- .../src/client/interceptors/error.rs | 366 +++++------------- .../aws-smithy-runtime-api/src/config_bag.rs | 6 +- 13 files changed, 459 insertions(+), 351 deletions(-) create mode 100644 aws/rust-runtime/aws-runtime/src/user_agent.rs diff --git a/aws/rust-runtime/aws-http/src/user_agent.rs b/aws/rust-runtime/aws-http/src/user_agent.rs index a9f39dfbf9..789fa544de 100644 --- a/aws/rust-runtime/aws-http/src/user_agent.rs +++ b/aws/rust-runtime/aws-http/src/user_agent.rs @@ -575,9 +575,8 @@ impl From for UserAgentStageError { } } -lazy_static::lazy_static! { - static ref X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent"); -} +#[allow(clippy::declare_interior_mutable_const)] // we will never mutate this +const X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent"); impl MapRequest for UserAgentStage { type Error = UserAgentStageError; @@ -593,10 +592,8 @@ impl MapRequest for UserAgentStage { .ok_or(UserAgentStageErrorKind::UserAgentMissing)?; req.headers_mut() .append(USER_AGENT, HeaderValue::try_from(ua.ua_header())?); - req.headers_mut().append( - X_AMZ_USER_AGENT.clone(), - HeaderValue::try_from(ua.aws_ua_header())?, - ); + req.headers_mut() + .append(X_AMZ_USER_AGENT, HeaderValue::try_from(ua.aws_ua_header())?); Ok(req) }) @@ -779,7 +776,7 @@ mod test { .get(USER_AGENT) .expect("UA header should be set"); req.headers() - .get(&*X_AMZ_USER_AGENT) + .get(&X_AMZ_USER_AGENT) .expect("UA header should be set"); } } diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index f2e4ab2bf9..defbe83df9 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -8,11 +8,14 @@ license = "Apache-2.0" repository = "https://github.com/awslabs/smithy-rs" [dependencies] -aws-types = { path = "../aws-types" } aws-credential-types = { path = "../aws-credential-types" } +aws-http = { path = "../aws-http" } aws-sigv4 = { path = "../aws-sigv4" } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } +aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } +aws-types = { path = "../aws-types" } +http = "0.2.3" tracing = "0.1" [dev-dependencies] diff --git a/aws/rust-runtime/aws-runtime/external-types.toml b/aws/rust-runtime/aws-runtime/external-types.toml index 3d92860f16..90b77e92e0 100644 --- a/aws/rust-runtime/aws-runtime/external-types.toml +++ b/aws/rust-runtime/aws-runtime/external-types.toml @@ -1,6 +1,9 @@ allowed_external_types = [ "aws_credential_types::*", "aws_sigv4::*", + "aws_smithy_http::body::SdkBody", "aws_smithy_runtime_api::*", "aws_types::*", + "http::request::Request", + "http::response::Response", ] diff --git a/aws/rust-runtime/aws-runtime/src/lib.rs b/aws/rust-runtime/aws-runtime/src/lib.rs index 4070e43e33..73ac01863d 100644 --- a/aws/rust-runtime/aws-runtime/src/lib.rs +++ b/aws/rust-runtime/aws-runtime/src/lib.rs @@ -18,3 +18,6 @@ pub mod auth; /// Supporting code for identity in the AWS SDK. pub mod identity; + +/// Supporting code for user agent headers in the AWS SDK. +pub mod user_agent; diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs new file mode 100644 index 0000000000..efa8e3c0d5 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -0,0 +1,233 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; +use aws_smithy_runtime_api::client::interceptors::error::BoxError; +use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_types::app_name::AppName; +use aws_types::os_shim_internal::Env; +use http::header::{InvalidHeaderValue, USER_AGENT}; +use http::{HeaderName, HeaderValue}; +use std::borrow::Cow; +use std::fmt; + +#[allow(clippy::declare_interior_mutable_const)] // we will never mutate this +const X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent"); + +#[derive(Debug)] +enum UserAgentInterceptorError { + MissingApiMetadata, + InvalidHeaderValue(InvalidHeaderValue), +} + +impl std::error::Error for UserAgentInterceptorError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::InvalidHeaderValue(source) => Some(source), + Self::MissingApiMetadata => None, + } + } +} + +impl fmt::Display for UserAgentInterceptorError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Self::InvalidHeaderValue(_) => "AwsUserAgent generated an invalid HTTP header value. This is a bug. Please file an issue.", + Self::MissingApiMetadata => "The UserAgentInterceptor requires ApiMetadata to be set before the request is made. This is a bug. Please file an issue.", + }) + } +} + +impl From for UserAgentInterceptorError { + fn from(err: InvalidHeaderValue) -> Self { + UserAgentInterceptorError::InvalidHeaderValue(err) + } +} + +/// Generates and attaches the AWS SDK's user agent to a HTTP request +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct UserAgentInterceptor; + +impl UserAgentInterceptor { + /// Creates a new `UserAgentInterceptor` + pub fn new() -> Self { + UserAgentInterceptor + } +} + +fn header_values( + ua: &AwsUserAgent, +) -> Result<(HeaderValue, HeaderValue), UserAgentInterceptorError> { + // Pay attention to the extremely subtle difference between ua_header and aws_ua_header below... + Ok(( + HeaderValue::try_from(ua.ua_header())?, + HeaderValue::try_from(ua.aws_ua_header())?, + )) +} + +impl Interceptor for UserAgentInterceptor { + fn modify_before_signing( + &self, + context: &mut InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let api_metadata = cfg + .get::() + .ok_or(UserAgentInterceptorError::MissingApiMetadata)?; + + // Allow for overriding the user agent by an earlier interceptor (so, for example, + // tests can use `AwsUserAgent::for_tests()`) by attempting to grab one out of the + // config bag before creating one. + let ua: Cow<'_, AwsUserAgent> = cfg + .get::() + .map(Cow::Borrowed) + .unwrap_or_else(|| { + let mut ua = AwsUserAgent::new_from_environment(Env::real(), api_metadata.clone()); + + let maybe_app_name = cfg.get::(); + if let Some(app_name) = maybe_app_name { + ua.set_app_name(app_name.clone()); + } + Cow::Owned(ua) + }); + + let headers = context.request_mut()?.headers_mut(); + let (user_agent, x_amz_user_agent) = header_values(&ua)?; + headers.append(USER_AGENT, user_agent); + headers.append(X_AMZ_USER_AGENT, x_amz_user_agent); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; + use aws_smithy_runtime_api::config_bag::ConfigBag; + use aws_smithy_runtime_api::type_erasure::TypedBox; + use aws_smithy_types::error::display::DisplayErrorContext; + + fn expect_header<'a>( + context: &'a InterceptorContext, + header_name: &str, + ) -> &'a str { + context + .request() + .unwrap() + .headers() + .get(header_name) + .unwrap() + .to_str() + .unwrap() + } + + #[test] + fn test_overridden_ua() { + let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + let mut config = ConfigBag::base(); + config.put(AwsUserAgent::for_tests()); + config.put(ApiMetadata::new("unused", "unused")); + + let interceptor = UserAgentInterceptor::new(); + interceptor + .modify_before_signing(&mut context, &mut config) + .unwrap(); + + let header = expect_header(&context, "user-agent"); + assert_eq!(AwsUserAgent::for_tests().ua_header(), header); + assert!(!header.contains("unused")); + + assert_eq!( + AwsUserAgent::for_tests().aws_ua_header(), + expect_header(&context, "x-amz-user-agent") + ); + } + + #[test] + fn test_default_ua() { + let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + let api_metadata = ApiMetadata::new("some-service", "some-version"); + let mut config = ConfigBag::base(); + config.put(api_metadata.clone()); + + let interceptor = UserAgentInterceptor::new(); + interceptor + .modify_before_signing(&mut context, &mut config) + .unwrap(); + + let expected_ua = AwsUserAgent::new_from_environment(Env::real(), api_metadata); + assert!( + expected_ua.aws_ua_header().contains("some-service"), + "precondition" + ); + assert_eq!( + expected_ua.ua_header(), + expect_header(&context, "user-agent") + ); + assert_eq!( + expected_ua.aws_ua_header(), + expect_header(&context, "x-amz-user-agent") + ); + } + + #[test] + fn test_app_name() { + let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + let api_metadata = ApiMetadata::new("some-service", "some-version"); + let mut config = ConfigBag::base(); + config.put(api_metadata.clone()); + config.put(AppName::new("my_awesome_app").unwrap()); + + let interceptor = UserAgentInterceptor::new(); + interceptor + .modify_before_signing(&mut context, &mut config) + .unwrap(); + + let app_value = "app/my_awesome_app"; + let header = expect_header(&context, "user-agent"); + assert!( + !header.contains(app_value), + "expected `{header}` to not contain `{app_value}`" + ); + + let header = expect_header(&context, "x-amz-user-agent"); + assert!( + header.contains(app_value), + "expected `{header}` to contain `{app_value}`" + ); + } + + #[test] + fn test_api_metadata_missing() { + let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + let mut config = ConfigBag::base(); + + let interceptor = UserAgentInterceptor::new(); + let error = format!( + "{}", + DisplayErrorContext( + &*interceptor + .modify_before_signing(&mut context, &mut config) + .expect_err("it should error") + ) + ); + assert!( + error.contains("This is a bug"), + "`{error}` should contain message `This is a bug`" + ); + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 01e0173372..843d01e22f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -70,14 +70,21 @@ private class AuthServiceRuntimePluginCustomization(codegenContext: ClientCodege } is ServiceRuntimePluginSection.AdditionalConfig -> { + section.putConfigValue(this) { + rustTemplate("#{SigningService}::from_static(self.handle.conf.signing_service())", *codegenScope) + } rustTemplate( """ - cfg.put(#{SigningService}::from_static(self.handle.conf.signing_service())); if let Some(region) = self.handle.conf.region() { - cfg.put(#{SigningRegion}::from(region.clone())); + #{put_signing_region} } """, *codegenScope, + "put_signing_region" to writable { + section.putConfigValue(this) { + rustTemplate("#{SigningRegion}::from(region.clone())", *codegenScope) + } + }, ) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index 38ff9f1324..0e83d2c99d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -10,6 +10,8 @@ import software.amazon.smithy.model.shapes.OperationShape 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.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -25,6 +27,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSectio import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait +import software.amazon.smithy.rust.codegen.core.util.letIf /** * Inserts a UserAgent configuration into the operation @@ -45,9 +48,17 @@ class UserAgentDecorator : ClientCodegenDecorator { operation: OperationShape, baseCustomizations: List, ): List { - return baseCustomizations + UserAgentFeature(codegenContext) + return baseCustomizations + UserAgentMutateOpRequest(codegenContext) } + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + it + listOf(AddApiMetadataIntoConfigBag(codegenContext)) + } + override fun extraSections(codegenContext: ClientCodegenContext): List { return listOf( adhocCustomization { section -> @@ -86,8 +97,26 @@ class UserAgentDecorator : ClientCodegenDecorator { } } - private class UserAgentFeature( - private val codegenContext: ClientCodegenContext, + private class AddApiMetadataIntoConfigBag(codegenContext: ClientCodegenContext) : + ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.AdditionalConfig) { + section.putConfigValue(this) { + rust("#T.clone()", ClientRustModule.Meta.toType().resolve("API_METADATA")) + } + section.registerInterceptor(runtimeConfig, this) { + rust("#T::new()", awsRuntime.resolve("user_agent::UserAgentInterceptor")) + } + } + } + } + + // TODO(enableNewSmithyRuntime): Remove this customization class + private class UserAgentMutateOpRequest( + codegenContext: ClientCodegenContext, ) : OperationCustomization() { private val runtimeConfig = codegenContext.runtimeConfig diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index 1a50e2a8cd..c6c142458a 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -5,8 +5,9 @@ use aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache}; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; +use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; use aws_runtime::auth::sigv4::SigV4OperationSigningConfig; +use aws_runtime::user_agent::UserAgentInterceptor; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::operation::list_objects_v2::{ ListObjectsV2Error, ListObjectsV2Input, ListObjectsV2Output, @@ -16,9 +17,7 @@ use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_runtime::client::connections::adapter::DynConnectorAdapter; use aws_smithy_runtime_api::client::endpoints::StaticUriEndpointResolver; -use aws_smithy_runtime_api::client::interceptors::{ - Interceptor, InterceptorContext, InterceptorError, Interceptors, -}; +use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext, Interceptors}; use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, Connection, HttpRequest, HttpResponse, TraceProbe, }; @@ -27,7 +26,7 @@ use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_runtime_api::type_erasure::TypedBox; use aws_types::region::SigningRegion; use aws_types::SigningService; -use http::{HeaderValue, Uri}; +use http::Uri; use std::sync::Arc; use std::time::{Duration, UNIX_EPOCH}; @@ -134,24 +133,6 @@ async fn sra_manual_test() { cfg.put(SigningService::from_static("s3")); cfg.put(SigningRegion::from(Region::from_static("us-east-1"))); - #[derive(Debug)] - struct UserAgentInterceptor; - impl Interceptor for UserAgentInterceptor { - fn modify_before_signing( - &self, - context: &mut InterceptorContext, - _cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - let ua = AwsUserAgent::for_tests(); - - context.request_mut().unwrap().headers_mut().append( - "x-amz-user-agent", - HeaderValue::from_str(&ua.aws_ua_header()).unwrap(), - ); - Ok(()) - } - } - #[derive(Debug)] struct OverrideSigningTimeInterceptor; impl Interceptor for OverrideSigningTimeInterceptor { @@ -159,7 +140,7 @@ async fn sra_manual_test() { &self, _context: &InterceptorContext, cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { + ) -> Result<(), BoxError> { let mut signing_config = cfg.get::().unwrap().clone(); signing_config.signing_options.request_timestamp = @@ -169,9 +150,11 @@ async fn sra_manual_test() { } } + cfg.put(ApiMetadata::new("unused", "unused")); + cfg.put(AwsUserAgent::for_tests()); // Override the user agent with the test UA cfg.get::>() .expect("interceptors set") - .register_client_interceptor(Arc::new(UserAgentInterceptor) as _) + .register_client_interceptor(Arc::new(UserAgentInterceptor::new()) as _) .register_client_interceptor(Arc::new(OverrideSigningTimeInterceptor) as _); Ok(()) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 52a6519b65..359bb32d1f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -7,8 +7,11 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +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.RuntimeConfig 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.smithy.customize.Section @@ -38,7 +41,28 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { /** * Hook for adding additional things to config inside service runtime plugins. */ - data class AdditionalConfig(val configBagName: String) : ServiceRuntimePluginSection("AdditionalConfig") + data class AdditionalConfig(val configBagName: String) : ServiceRuntimePluginSection("AdditionalConfig") { + /** Adds a value to the config bag */ + fun putConfigValue(writer: RustWriter, value: Writable) { + writer.rust("$configBagName.put(#T);", value) + } + + /** Generates the code to register an interceptor */ + fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { + val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) + writer.rustTemplate( + """ + $configBagName.get::<#{Interceptors}<#{HttpRequest}, #{HttpResponse}>>() + .expect("interceptors set") + .register_client_interceptor(std::sync::Arc::new(#{interceptor}) as _); + """, + "HttpRequest" to smithyRuntimeApi.resolve("client::orchestrator::HttpRequest"), + "HttpResponse" to smithyRuntimeApi.resolve("client::orchestrator::HttpResponse"), + "Interceptors" to smithyRuntimeApi.resolve("client::interceptors::Interceptors"), + "interceptor" to interceptor, + ) + } + } } typealias ServiceRuntimePluginCustomization = NamedCustomization diff --git a/rust-runtime/aws-smithy-runtime-api/Cargo.toml b/rust-runtime/aws-smithy-runtime-api/Cargo.toml index 68e2005e93..a03bac307b 100644 --- a/rust-runtime/aws-smithy-runtime-api/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime-api/Cargo.toml @@ -14,6 +14,7 @@ aws-smithy-http = { path = "../aws-smithy-http" } aws-smithy-types = { path = "../aws-smithy-types" } http = "0.2.3" tokio = { version = "1.25", features = ["sync"] } +tracing = "0.1" [package.metadata.docs.rs] all-features = true diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index c1734d58c2..6312eea80f 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -7,8 +7,9 @@ pub mod context; pub mod error; use crate::config_bag::ConfigBag; +use aws_smithy_types::error::display::DisplayErrorContext; pub use context::InterceptorContext; -pub use error::InterceptorError; +pub use error::{BoxError, InterceptorError}; use std::sync::{Arc, Mutex}; macro_rules! interceptor_trait_fn { @@ -18,7 +19,7 @@ macro_rules! interceptor_trait_fn { &self, context: &InterceptorContext, cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { + ) -> Result<(), BoxError> { let _ctx = context; let _cfg = cfg; Ok(()) @@ -30,7 +31,7 @@ macro_rules! interceptor_trait_fn { &self, context: &mut InterceptorContext, cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { + ) -> Result<(), BoxError> { let _ctx = context; let _cfg = cfg; Ok(()) @@ -544,8 +545,8 @@ pub type SharedInterceptor = Arc + S #[derive(Debug)] struct Inner { - client_interceptors: Vec + Send + Sync>>, - operation_interceptors: Vec + Send + Sync>>, + client_interceptors: Vec>, + operation_interceptors: Vec>, } // The compiler isn't smart enough to realize that TxReq and TxRes don't need to implement `Clone` @@ -591,41 +592,33 @@ macro_rules! interceptor_impl_fn { interceptor_impl_fn!(mut context, $name, $name); }; (context, $outer_name:ident, $inner_name:ident) => { - pub fn $outer_name( - &self, - context: &InterceptorContext, - cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - // Since interceptors can modify the interceptor list (since its in the config bag), copy the list ahead of time. - // This should be cheap since the interceptors inside the list are Arcs. - let client_interceptors = self.inner.lock().unwrap().client_interceptors.clone(); - for interceptor in client_interceptors { - interceptor.$inner_name(context, cfg)?; - } - let operation_interceptors = self.inner.lock().unwrap().operation_interceptors.clone(); - for interceptor in operation_interceptors { - interceptor.$inner_name(context, cfg)?; - } - Ok(()) - } + interceptor_impl_fn!( + $outer_name, + $inner_name(context: &InterceptorContext) + ); }; (mut context, $outer_name:ident, $inner_name:ident) => { + interceptor_impl_fn!( + $outer_name, + $inner_name(context: &mut InterceptorContext) + ); + }; + ($outer_name:ident, $inner_name:ident ($context:ident : $context_ty:ty)) => { pub fn $outer_name( &self, - context: &mut InterceptorContext, + $context: $context_ty, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { - // Since interceptors can modify the interceptor list (since its in the config bag), copy the list ahead of time. - // This should be cheap since the interceptors inside the list are Arcs. - let client_interceptors = self.inner.lock().unwrap().client_interceptors.clone(); - for interceptor in client_interceptors { - interceptor.$inner_name(context, cfg)?; + let mut result: Result<(), BoxError> = Ok(()); + for interceptor in self.interceptors() { + if let Err(new_error) = interceptor.$inner_name($context, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } } - let operation_interceptors = self.inner.lock().unwrap().operation_interceptors.clone(); - for interceptor in operation_interceptors { - interceptor.$inner_name(context, cfg)?; - } - Ok(()) + result.map_err(InterceptorError::$inner_name) } }; } @@ -635,6 +628,22 @@ impl Interceptors { Self::default() } + fn interceptors(&self) -> Vec> { + // Since interceptors can modify the interceptor list (since its in the config bag), copy the list ahead of time. + // This should be cheap since the interceptors inside the list are Arcs. + // TODO(enableNewSmithyRuntime): Remove the ability for interceptors to modify the interceptor list and then simplify this + let mut interceptors = self.inner.lock().unwrap().client_interceptors.clone(); + interceptors.extend( + self.inner + .lock() + .unwrap() + .operation_interceptors + .iter() + .cloned(), + ); + interceptors + } + pub fn register_client_interceptor( &self, interceptor: SharedInterceptor, diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs index fc30027718..e25b797aeb 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs @@ -3,14 +3,37 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Errors related to smithy interceptors +//! Errors related to Smithy interceptors use std::fmt; +macro_rules! interceptor_error_fn { + ($fn_name:ident => $error_kind:ident (with source)) => { + #[doc = concat!("Create a new error indicating a failure with a ", stringify!($fn_name), " interceptor.")] + pub fn $fn_name( + source: impl Into>, + ) -> Self { + Self { + kind: ErrorKind::$error_kind, + source: Some(source.into()), + } + } + }; + ($fn_name:ident => $error_kind:ident (invalid $thing:ident access)) => { + #[doc = concat!("Create a new error indicating that an interceptor tried to access the ", stringify!($thing), " out of turn.")] + pub fn $fn_name() -> Self { + Self { + kind: ErrorKind::$error_kind, + source: None, + } + } + } +} + /// A generic error that behaves itself in async contexts pub type BoxError = Box; -/// An error related to smithy interceptors. +/// An error related to Smithy interceptors. #[derive(Debug)] pub struct InterceptorError { kind: ErrorKind, @@ -18,205 +41,30 @@ pub struct InterceptorError { } impl InterceptorError { - /// Create a new error indicating a failure withing a read_before_execution interceptor - pub fn read_before_execution( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeExecution, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_serialization interceptor - pub fn modify_before_serialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeSerialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_before_serialization interceptor - pub fn read_before_serialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeSerialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_serialization interceptor - pub fn read_after_serialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterSerialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_retry_loop interceptor - pub fn modify_before_retry_loop( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeRetryLoop, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_before_attempt interceptor - pub fn read_before_attempt( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeAttempt, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_signing interceptor - pub fn modify_before_signing( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeSigning, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_before_signing interceptor - pub fn read_before_signing( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeSigning, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_signing interceptor - pub fn read_after_signing( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterSigning, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_transmit interceptor - pub fn modify_before_transmit( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeTransmit, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_before_transmit interceptor - pub fn read_before_transmit( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeTransmit, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_transmit interceptor - pub fn read_after_transmit( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterTransmit, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_deserialization interceptor - pub fn modify_before_deserialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeDeserialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_before_deserialization interceptor - pub fn read_before_deserialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadBeforeDeserialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_deserialization interceptor - pub fn read_after_deserialization( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterDeserialization, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_attempt_completion interceptor - pub fn modify_before_attempt_completion( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeAttemptCompletion, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_attempt interceptor - pub fn read_after_attempt( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterAttempt, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a modify_before_completion interceptor - pub fn modify_before_completion( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ModifyBeforeCompletion, - source: Some(source.into()), - } - } - /// Create a new error indicating a failure withing a read_after_execution interceptor - pub fn read_after_execution( - source: impl Into>, - ) -> Self { - Self { - kind: ErrorKind::ReadAfterExecution, - source: Some(source.into()), - } - } - /// Create a new error indicating that an interceptor tried to access the request out of turn - pub fn invalid_request_access() -> Self { - Self { - kind: ErrorKind::InvalidRequestAccess, - source: None, - } - } - /// Create a new error indicating that an interceptor tried to access the response out of turn - pub fn invalid_response_access() -> Self { - Self { - kind: ErrorKind::InvalidResponseAccess, - source: None, - } - } - /// Create a new error indicating that an interceptor tried to access the input out of turn - pub fn invalid_input_access() -> Self { - Self { - kind: ErrorKind::InvalidInputAccess, - source: None, - } - } - /// Create a new error indicating that an interceptor tried to access the output out of turn - pub fn invalid_output_access() -> Self { - Self { - kind: ErrorKind::InvalidOutputAccess, - source: None, - } - } + interceptor_error_fn!(read_before_execution => ReadBeforeExecution (with source)); + interceptor_error_fn!(modify_before_serialization => ModifyBeforeSerialization (with source)); + interceptor_error_fn!(read_before_serialization => ReadBeforeSerialization (with source)); + interceptor_error_fn!(read_after_serialization => ReadAfterSerialization (with source)); + interceptor_error_fn!(modify_before_retry_loop => ModifyBeforeRetryLoop (with source)); + interceptor_error_fn!(read_before_attempt => ReadBeforeAttempt (with source)); + interceptor_error_fn!(modify_before_signing => ModifyBeforeSigning (with source)); + interceptor_error_fn!(read_before_signing => ReadBeforeSigning (with source)); + interceptor_error_fn!(read_after_signing => ReadAfterSigning (with source)); + interceptor_error_fn!(modify_before_transmit => ModifyBeforeTransmit (with source)); + interceptor_error_fn!(read_before_transmit => ReadBeforeTransmit (with source)); + interceptor_error_fn!(read_after_transmit => ReadAfterTransmit (with source)); + interceptor_error_fn!(modify_before_deserialization => ModifyBeforeDeserialization (with source)); + interceptor_error_fn!(read_before_deserialization => ReadBeforeDeserialization (with source)); + interceptor_error_fn!(read_after_deserialization => ReadAfterDeserialization (with source)); + interceptor_error_fn!(modify_before_attempt_completion => ModifyBeforeAttemptCompletion (with source)); + interceptor_error_fn!(read_after_attempt => ReadAfterAttempt (with source)); + interceptor_error_fn!(modify_before_completion => ModifyBeforeCompletion (with source)); + interceptor_error_fn!(read_after_execution => ReadAfterExecution (with source)); + + interceptor_error_fn!(invalid_request_access => InvalidRequestAccess (invalid request access)); + interceptor_error_fn!(invalid_response_access => InvalidResponseAccess (invalid response access)); + interceptor_error_fn!(invalid_input_access => InvalidInputAccess (invalid input access)); + interceptor_error_fn!(invalid_output_access => InvalidOutputAccess (invalid output access)); } #[derive(Debug)] @@ -259,7 +107,6 @@ enum ErrorKind { ModifyBeforeCompletion, /// An error occurred within the read_after_execution interceptor ReadAfterExecution, - // There is no InvalidModeledRequestAccess because it's always accessible /// An interceptor tried to access the request out of turn InvalidRequestAccess, /// An interceptor tried to access the response out of turn @@ -270,82 +117,51 @@ enum ErrorKind { InvalidOutputAccess, } -impl fmt::Display for InterceptorError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +macro_rules! display_interceptor_err { + ($self:ident, $f:ident, $(($error_kind:ident => $fn_name:ident ($($option:tt)+)),)+) => { + { use ErrorKind::*; - match &self.kind { - ReadBeforeExecution => { - write!(f, "read_before_execution interceptor encountered an error") - } - ModifyBeforeSerialization => write!( - f, - "modify_before_serialization interceptor encountered an error" - ), - ReadBeforeSerialization => write!( - f, - "read_before_serialization interceptor encountered an error" - ), - ReadAfterSerialization => write!( - f, - "read_after_serialization interceptor encountered an error" - ), - ModifyBeforeRetryLoop => write!( - f, - "modify_before_retry_loop interceptor encountered an error" - ), - ReadBeforeAttempt => write!(f, "read_before_attempt interceptor encountered an error"), - ModifyBeforeSigning => { - write!(f, "modify_before_signing interceptor encountered an error") - } - ReadBeforeSigning => write!(f, "read_before_signing interceptor encountered an error"), - ReadAfterSigning => write!(f, "read_after_signing interceptor encountered an error"), - ModifyBeforeTransmit => { - write!(f, "modify_before_transmit interceptor encountered an error") - } - ReadBeforeTransmit => { - write!(f, "read_before_transmit interceptor encountered an error") - } - ReadAfterTransmit => write!(f, "read_after_transmit interceptor encountered an error"), - ModifyBeforeDeserialization => write!( - f, - "modify_before_deserialization interceptor encountered an error" - ), - ReadBeforeDeserialization => write!( - f, - "read_before_deserialization interceptor encountered an error" - ), - ReadAfterDeserialization => write!( - f, - "read_after_deserialization interceptor encountered an error" - ), - ModifyBeforeAttemptCompletion => write!( - f, - "modify_before_attempt_completion interceptor encountered an error" - ), - ReadAfterAttempt => write!(f, "read_after_attempt interceptor encountered an error"), - ModifyBeforeCompletion => write!( - f, - "modify_before_completion interceptor encountered an error" - ), - ReadAfterExecution => { - write!(f, "read_after_execution interceptor encountered an error") - } - InvalidRequestAccess => { - write!(f, "tried to access request before request serialization") - } - InvalidResponseAccess => { - write!(f, "tried to access response before transmitting a request") - } - InvalidInputAccess => write!( - f, - "tried to access the input before response deserialization" - ), - InvalidOutputAccess => write!( - f, - "tried to access the output before response deserialization" - ), + match &$self.kind { + $($error_kind => display_interceptor_err!($f, $fn_name, ($($option)+)),)+ } } + }; + ($f:ident, $fn_name:ident, (interceptor error)) => { + $f.write_str(concat!(stringify!($fn_name), " interceptor encountered an error")) + }; + ($f:ident, $fn_name:ident, (invalid access $name:ident $message:literal)) => { + $f.write_str(concat!("tried to access the ", stringify!($name), " ", $message)) + }; +} + +impl fmt::Display for InterceptorError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + display_interceptor_err!(self, f, + (ReadBeforeExecution => read_before_execution (interceptor error)), + (ModifyBeforeSerialization => modify_before_serialization (interceptor error)), + (ReadBeforeSerialization => read_before_serialization (interceptor error)), + (ReadAfterSerialization => read_after_serialization (interceptor error)), + (ModifyBeforeRetryLoop => modify_before_retry_loop (interceptor error)), + (ReadBeforeAttempt => read_Before_attempt (interceptor error)), + (ModifyBeforeSigning => modify_before_signing (interceptor error)), + (ReadBeforeSigning => read_before_signing (interceptor error)), + (ReadAfterSigning => read_after_signing (interceptor error)), + (ModifyBeforeTransmit => modify_before_transmit (interceptor error)), + (ReadBeforeTransmit => read_before_transmit (interceptor error)), + (ReadAfterTransmit => read_after_transmit (interceptor error)), + (ModifyBeforeDeserialization => modify_before_deserialization (interceptor error)), + (ReadBeforeDeserialization => read_before_deserialization (interceptor error)), + (ReadAfterDeserialization => read_after_deserialization (interceptor error)), + (ModifyBeforeAttemptCompletion => modify_before_attempt_completion (interceptor error)), + (ReadAfterAttempt => read_after_attempt (interceptor error)), + (ModifyBeforeCompletion => modify_before_completion (interceptor error)), + (ReadAfterExecution => read_after_execution (interceptor error)), + (InvalidRequestAccess => invalid_request_access (invalid access request "before request serialization")), + (InvalidResponseAccess => invalid_response_access (invalid access response "before transmitting a request")), + (InvalidInputAccess => invalid_input_access (invalid access input "after request serialization")), + (InvalidOutputAccess => invalid_output_access (invalid access output "before response deserialization")), + ) + } } impl std::error::Error for InterceptorError { diff --git a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs index 78e7b2502e..bf8d42e6cb 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs @@ -10,7 +10,6 @@ //! 1. A new layer of configuration may be applied onto an existing configuration structure without modifying it or taking ownership. //! 2. No lifetime shenanigans to deal with use aws_smithy_http::property_bag::PropertyBag; -use std::any::type_name; use std::fmt::Debug; use std::ops::Deref; use std::sync::Arc; @@ -19,6 +18,7 @@ use std::sync::Arc; /// /// [`ConfigBag`] is the "unlocked" form of the bag. Only the top layer of the bag may be unlocked. #[must_use] +#[derive(Debug)] pub struct ConfigBag { head: Layer, tail: Option, @@ -27,7 +27,7 @@ pub struct ConfigBag { /// Layered Configuration Structure /// /// [`FrozenConfigBag`] is the "locked" form of the bag. -#[derive(Clone)] +#[derive(Clone, Debug)] #[must_use] pub struct FrozenConfigBag(Arc); @@ -55,6 +55,7 @@ enum Value { ExplicitlyUnset, } +#[derive(Debug)] struct Layer { name: &'static str, props: PropertyBag, @@ -122,7 +123,6 @@ impl ConfigBag { pub fn get(&self) -> Option<&T> { let mut source = vec![]; let out = self.sourced_get(&mut source); - println!("searching for {:?} {:#?}", type_name::(), source); out } From cfade88566ffbbddb6ac962fee6e5749b09d802d Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 7 Apr 2023 11:47:10 -0700 Subject: [PATCH 014/253] Implement the `RecursionDetectionInterceptor` for the SDK (#2555) --- .../aws-http/src/recursion_detection.rs | 2 + aws/rust-runtime/aws-http/src/user_agent.rs | 2 + aws/rust-runtime/aws-runtime/Cargo.toml | 5 + aws/rust-runtime/aws-runtime/src/lib.rs | 3 + .../aws-runtime/src/recursion_detection.rs | 169 ++++++++++++++++++ .../test-data/recursion-detection.json | 71 ++++++++ .../smithy/rustsdk/AwsCodegenDecorator.kt | 1 + .../rustsdk/RecursionDetectionDecorator.kt | 44 +++++ .../aws-sdk-s3/tests/sra_test.rs | 2 + 9 files changed, 299 insertions(+) create mode 100644 aws/rust-runtime/aws-runtime/src/recursion_detection.rs create mode 100644 aws/rust-runtime/aws-runtime/test-data/recursion-detection.json create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt diff --git a/aws/rust-runtime/aws-http/src/recursion_detection.rs b/aws/rust-runtime/aws-http/src/recursion_detection.rs index 62faa749cd..a5cc165032 100644 --- a/aws/rust-runtime/aws-http/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-http/src/recursion_detection.rs @@ -11,6 +11,8 @@ use http::HeaderValue; use percent_encoding::{percent_encode, CONTROLS}; use std::borrow::Cow; +// TODO(enableNewSmithyRuntime): Delete this module + /// Recursion Detection Middleware /// /// This middleware inspects the value of the `AWS_LAMBDA_FUNCTION_NAME` and `_X_AMZN_TRACE_ID` environment diff --git a/aws/rust-runtime/aws-http/src/user_agent.rs b/aws/rust-runtime/aws-http/src/user_agent.rs index 789fa544de..8adc03cc3a 100644 --- a/aws/rust-runtime/aws-http/src/user_agent.rs +++ b/aws/rust-runtime/aws-http/src/user_agent.rs @@ -513,6 +513,8 @@ impl fmt::Display for ExecEnvMetadata { } } +// TODO(enableNewSmithyRuntime): Delete the user agent Tower middleware and consider moving all the remaining code into aws-runtime + /// User agent middleware #[non_exhaustive] #[derive(Default, Clone, Debug)] diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index defbe83df9..7fa3274cb4 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -16,9 +16,14 @@ aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } aws-types = { path = "../aws-types" } http = "0.2.3" +percent-encoding = "2.1.0" tracing = "0.1" [dev-dependencies] +aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-test" } +proptest = "1" +serde = { version = "1", features = ["derive"]} +serde_json = "1" tracing-test = "0.2.1" [package.metadata.docs.rs] diff --git a/aws/rust-runtime/aws-runtime/src/lib.rs b/aws/rust-runtime/aws-runtime/src/lib.rs index 73ac01863d..3112279431 100644 --- a/aws/rust-runtime/aws-runtime/src/lib.rs +++ b/aws/rust-runtime/aws-runtime/src/lib.rs @@ -19,5 +19,8 @@ pub mod auth; /// Supporting code for identity in the AWS SDK. pub mod identity; +/// Supporting code for recursion detection in the AWS SDK. +pub mod recursion_detection; + /// Supporting code for user agent headers in the AWS SDK. pub mod user_agent; diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs new file mode 100644 index 0000000000..c14f1d141b --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -0,0 +1,169 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_types::os_shim_internal::Env; +use http::HeaderValue; +use percent_encoding::{percent_encode, CONTROLS}; +use std::borrow::Cow; + +const TRACE_ID_HEADER: &str = "x-amzn-trace-id"; + +mod env { + pub(super) const LAMBDA_FUNCTION_NAME: &str = "AWS_LAMBDA_FUNCTION_NAME"; + pub(super) const TRACE_ID: &str = "_X_AMZN_TRACE_ID"; +} + +/// Recursion Detection Interceptor +/// +/// This interceptor inspects the value of the `AWS_LAMBDA_FUNCTION_NAME` and `_X_AMZN_TRACE_ID` environment +/// variables to detect if the request is being invoked in a Lambda function. If it is, the `X-Amzn-Trace-Id` header +/// will be set. This enables downstream services to prevent accidentally infinitely recursive invocations spawned +/// from Lambda. +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct RecursionDetectionInterceptor { + env: Env, +} + +impl RecursionDetectionInterceptor { + /// Creates a new `RecursionDetectionInterceptor` + pub fn new() -> Self { + Self::default() + } +} + +impl Interceptor for RecursionDetectionInterceptor { + fn modify_before_signing( + &self, + context: &mut InterceptorContext, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let request = context.request_mut()?; + if request.headers().contains_key(TRACE_ID_HEADER) { + return Ok(()); + } + + if let (Ok(_function_name), Ok(trace_id)) = ( + self.env.get(env::LAMBDA_FUNCTION_NAME), + self.env.get(env::TRACE_ID), + ) { + request + .headers_mut() + .insert(TRACE_ID_HEADER, encode_header(trace_id.as_bytes())); + } + Ok(()) + } +} + +/// Encodes a byte slice as a header. +/// +/// ASCII control characters are percent encoded which ensures that all byte sequences are valid headers +fn encode_header(value: &[u8]) -> HeaderValue { + let value: Cow<'_, str> = percent_encode(value, CONTROLS).into(); + HeaderValue::from_bytes(value.as_bytes()).expect("header is encoded, header must be valid") +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use aws_smithy_protocol_test::{assert_ok, validate_headers}; + use aws_smithy_runtime_api::type_erasure::TypedBox; + use aws_types::os_shim_internal::Env; + use http::HeaderValue; + use proptest::{prelude::*, proptest}; + use serde::Deserialize; + use std::collections::HashMap; + + proptest! { + #[test] + fn header_encoding_never_panics(s in any::>()) { + encode_header(&s); + } + } + + #[test] + fn every_char() { + let buff = (0..=255).collect::>(); + assert_eq!( + encode_header(&buff), + HeaderValue::from_static( + r##"%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~%7F%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF"## + ) + ); + } + + #[test] + fn run_tests() { + let test_cases: Vec = + serde_json::from_str(include_str!("../test-data/recursion-detection.json")) + .expect("invalid test case"); + for test_case in test_cases { + check(test_case) + } + } + + #[derive(Deserialize)] + #[serde(rename_all = "camelCase")] + struct TestCase { + env: HashMap, + request_headers_before: Vec, + request_headers_after: Vec, + } + + impl TestCase { + fn env(&self) -> Env { + Env::from(self.env.clone()) + } + + /// Headers on the input request + fn request_headers_before(&self) -> impl Iterator { + Self::split_headers(&self.request_headers_before) + } + + /// Headers on the output request + fn request_headers_after(&self) -> impl Iterator { + Self::split_headers(&self.request_headers_after) + } + + /// Split text headers on `: ` + fn split_headers(headers: &[String]) -> impl Iterator { + headers + .iter() + .map(|header| header.split_once(": ").expect("header must contain :")) + } + } + + fn check(test_case: TestCase) { + let env = test_case.env(); + let mut request = http::Request::builder(); + for (name, value) in test_case.request_headers_before() { + request = request.header(name, value); + } + let request = request.body(SdkBody::empty()).expect("must be valid"); + let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + context.set_request(request); + let mut config = ConfigBag::base(); + + RecursionDetectionInterceptor { env } + .modify_before_signing(&mut context, &mut config) + .expect("interceptor must succeed"); + let mutated_request = context.request().expect("request is still set"); + for name in mutated_request.headers().keys() { + assert_eq!( + mutated_request.headers().get_all(name).iter().count(), + 1, + "No duplicated headers" + ) + } + assert_ok(validate_headers( + mutated_request.headers(), + test_case.request_headers_after(), + )) + } +} diff --git a/aws/rust-runtime/aws-runtime/test-data/recursion-detection.json b/aws/rust-runtime/aws-runtime/test-data/recursion-detection.json new file mode 100644 index 0000000000..59a44307f7 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/test-data/recursion-detection.json @@ -0,0 +1,71 @@ +[ + { + "env": {}, + "requestHeadersBefore": [], + "requestHeadersAfter": [], + "description": [ + "The AWS_LAMBDA_FUNCTION_NAME and _X_AMZN_TRACE_ID environment variables are not set.", + "There should be no X-Amzn-Trace-Id header sent." + ] + }, + { + "env": { + "AWS_LAMBDA_FUNCTION_NAME": "some-function", + "_X_AMZN_TRACE_ID": "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2" + }, + "requestHeadersBefore": [], + "requestHeadersAfter": [ + "X-Amzn-Trace-Id: Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2" + ], + "description": [ + "AWS_LAMBDA_FUNCTION_NAME is set, and", + "_X_AMZN_TRACE_ID is set to \"Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2\".", + "The X-Amzn-Trace-Id header should be sent with that value." + ] + }, + { + "env": { + "_X_AMZN_TRACE_ID": "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2" + }, + "requestHeadersBefore": [], + "requestHeadersAfter": [], + "description": [ + "AWS_LAMBDA_FUNCTION_NAME is NOT set, and", + "_X_AMZN_TRACE_ID is set to \"Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1;lineage=a87bd80c:0,68fd508a:5,c512fbe3:2\".", + "The X-Amzn-Trace-Id header should NOT be sent with that value." + ] + }, + { + "env": { + "AWS_LAMBDA_FUNCTION_NAME": "some-function", + "_X_AMZN_TRACE_ID": "EnvValue" + }, + "requestHeadersBefore": [ + "X-Amzn-Trace-Id: OriginalValue" + ], + "requestHeadersAfter": [ + "X-Amzn-Trace-Id: OriginalValue" + ], + "desciption": [ + "AWS_LAMBDA_FUNCTION_NAME is set, and", + "_X_AMZN_TRACE_ID is set to \"EnvValue\",", + "but the X-Amzn-Trace-Id header is already set on the request.", + "The X-Amzn-Trace-Id header should keep its original value." + ] + }, + { + "env": { + "AWS_LAMBDA_FUNCTION_NAME": "some-function", + "_X_AMZN_TRACE_ID": "first\nsecond¼\t" + }, + "requestHeadersBefore": [], + "requestHeadersAfter": [ + "X-Amzn-Trace-Id: first%0Asecond%C2%BC%09" + ], + "description": [ + "AWS_LAMBDA_FUNCTION_NAME is set, and", + "_X_AMZN_TRACE_ID has ASCII control characters in it.", + "The X-Amzn-Trace-Id header is added with the control characters percent encoded." + ] + } +] 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 a5ce0c3354..21e113a3ca 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 @@ -51,6 +51,7 @@ val DECORATORS: List = listOf( OperationInputTestDecorator(), AwsRequestIdDecorator(), DisabledAuthDecorator(), + RecursionDetectionDecorator(), ), // Service specific decorators diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt new file mode 100644 index 0000000000..09e6a30742 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt @@ -0,0 +1,44 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.util.letIf + +class RecursionDetectionDecorator : ClientCodegenDecorator { + override val name: String get() = "RecursionDetectionDecorator" + override val order: Byte get() = 0 + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + it + listOf(RecursionDetectionRuntimePluginCustomization(codegenContext)) + } +} + +private class RecursionDetectionRuntimePluginCustomization( + private val codegenContext: ClientCodegenContext, +) : ServiceRuntimePluginCustomization() { + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.AdditionalConfig) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rust( + "#T::new()", + AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig) + .resolve("recursion_detection::RecursionDetectionInterceptor"), + ) + } + } + } +} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index c6c142458a..258177c3a4 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -7,6 +7,7 @@ use aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache}; use aws_credential_types::provider::SharedCredentialsProvider; use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; use aws_runtime::auth::sigv4::SigV4OperationSigningConfig; +use aws_runtime::recursion_detection::RecursionDetectionInterceptor; use aws_runtime::user_agent::UserAgentInterceptor; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::operation::list_objects_v2::{ @@ -155,6 +156,7 @@ async fn sra_manual_test() { cfg.get::>() .expect("interceptors set") .register_client_interceptor(Arc::new(UserAgentInterceptor::new()) as _) + .register_client_interceptor(Arc::new(RecursionDetectionInterceptor::new()) as _) .register_client_interceptor(Arc::new(OverrideSigningTimeInterceptor) as _); Ok(()) } From ee324f272409aad626300c0564ee4cff24f30ce2 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 10 Apr 2023 11:24:00 -0400 Subject: [PATCH 015/253] Fix bug in WriteGetObjectResponse endpoint (#2560) --- CHANGELOG.next.toml | 6 ++ .../rustsdk/customize/s3/S3Decorator.kt | 70 +++++++++++++++---- .../integration-tests/s3/tests/endpoints.rs | 19 +++++ 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 7770e39605..260f8ec616 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -80,3 +80,9 @@ message = "Add `into_segments` method to `AggregatedBytes`, for zero-copy conver references = ["smithy-rs#2525"] meta = { "breaking" = false, "tada" = false, "bug" = false } author = "parker-timmerman" + +[[aws-sdk-rust]] +message = "Fix but where an incorrect endpoint was produced for WriteGetObjectResponse" +references = ["smithy-rs#781", "aws-sdk-rust#781"] +meta = { "breaking" = false, "tada" = false, "bug" = true } +author = "rcoh" diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index 7e78f0d47a..664494d021 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -14,6 +14,9 @@ import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rulesengine.traits.EndpointTestCase +import software.amazon.smithy.rulesengine.traits.EndpointTestOperationInput +import software.amazon.smithy.rulesengine.traits.EndpointTestsTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization @@ -31,7 +34,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestXml import software.amazon.smithy.rust.codegen.core.smithy.traits.AllowInvalidXmlRoot import software.amazon.smithy.rust.codegen.core.util.letIf -import software.amazon.smithy.rustsdk.endpoints.stripEndpointTrait import software.amazon.smithy.rustsdk.getBuiltIn import software.amazon.smithy.rustsdk.toWritable import java.util.logging.Logger @@ -63,23 +65,39 @@ class S3Decorator : ClientCodegenDecorator { logger.info("Adding AllowInvalidXmlRoot trait to $it") (it as StructureShape).toBuilder().addTrait(AllowInvalidXmlRoot()).build() } - }.let(StripBucketFromHttpPath()::transform).let(stripEndpointTrait("RequestRoute")) + } + // the model has the bucket in the path + .let(StripBucketFromHttpPath()::transform) + // the tests in EP2 are incorrect and are missing request route + .let( + FilterEndpointTests( + operationInputFilter = { input -> + when (input.operationName) { + // it's impossible to express HostPrefix behavior in the current EP2 rules schema :-/ + // A handwritten test was written to cover this behavior + "WriteGetObjectResponse" -> null + else -> input + } + }, + )::transform, + ) override fun endpointCustomizations(codegenContext: ClientCodegenContext): List { - return listOf(object : EndpointCustomization { - override fun setBuiltInOnServiceConfig(name: String, value: Node, configBuilderRef: String): Writable? { - if (!name.startsWith("AWS::S3")) { - return null - } - val builtIn = codegenContext.getBuiltIn(name) ?: return null - return writable { - rustTemplate( - "let $configBuilderRef = $configBuilderRef.${builtIn.name.rustName()}(#{value});", - "value" to value.toWritable(), - ) + return listOf( + object : EndpointCustomization { + override fun setBuiltInOnServiceConfig(name: String, value: Node, configBuilderRef: String): Writable? { + if (!name.startsWith("AWS::S3")) { + return null + } + val builtIn = codegenContext.getBuiltIn(name) ?: return null + return writable { + rustTemplate( + "let $configBuilderRef = $configBuilderRef.${builtIn.name.rustName()}(#{value});", + "value" to value.toWritable(), + ) + } } - } - }, + }, ) } @@ -88,6 +106,28 @@ class S3Decorator : ClientCodegenDecorator { } } +class FilterEndpointTests( + private val testFilter: (EndpointTestCase) -> EndpointTestCase? = { a -> a }, + private val operationInputFilter: (EndpointTestOperationInput) -> EndpointTestOperationInput? = { a -> a }, +) { + fun updateEndpointTests(endpointTests: List): List { + val filteredTests = endpointTests.mapNotNull { test -> testFilter(test) } + return filteredTests.map { test -> + val operationInputs = test.operationInputs + test.toBuilder().operationInputs(operationInputs.mapNotNull { operationInputFilter(it) }).build() + } + } + + fun transform(model: Model) = ModelTransformer.create().mapTraits(model) { _, trait -> + when (trait) { + is EndpointTestsTrait -> EndpointTestsTrait.builder().testCases(updateEndpointTests(trait.testCases)) + .version(trait.version).build() + + else -> trait + } + } +} + class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContext) { private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( diff --git a/aws/sdk/integration-tests/s3/tests/endpoints.rs b/aws/sdk/integration-tests/s3/tests/endpoints.rs index 357c142900..e74d1a0a83 100644 --- a/aws/sdk/integration-tests/s3/tests/endpoints.rs +++ b/aws/sdk/integration-tests/s3/tests/endpoints.rs @@ -135,3 +135,22 @@ async fn s3_object_lambda_no_cross_region() { err ); } + +#[tokio::test] +async fn write_get_object_response() { + let (req, client) = test_client(|b| b); + let _write = client + .write_get_object_response() + .request_route("req-route") + .request_token("token") + .status_code(200) + .body(vec![1, 2, 3].into()) + .send() + .await; + + let captured_request = req.expect_request(); + assert_eq!( + captured_request.uri().to_string(), + "https://req-route.s3-object-lambda.us-west-4.amazonaws.com/WriteGetObjectResponse?x-id=WriteGetObjectResponse" + ); +} From 33b24bb7f31d269ee9256f867bd66c01b3f2b347 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Mon, 10 Apr 2023 13:46:07 -0500 Subject: [PATCH 016/253] fix: signing params debug impl (#2562) * fix: redact credentials when debug logging signing params * update: CHANGELOG.next.toml * yuki makes a good point --- CHANGELOG.next.toml | 6 ++++++ aws/rust-runtime/aws-sigv4/src/lib.rs | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 260f8ec616..1832de5613 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -86,3 +86,9 @@ message = "Fix but where an incorrect endpoint was produced for WriteGetObjectRe references = ["smithy-rs#781", "aws-sdk-rust#781"] meta = { "breaking" = false, "tada" = false, "bug" = true } author = "rcoh" + +[[aws-sdk-rust]] +message = "Update the `std::fmt::Debug` implementation for `aws-sigv4::SigningParams` so that it will no longer print sensitive information." +references = ["smithy-rs#2562"] +meta = { "breaking" = false, "tada" = true, "bug" = true } +author = "Velfi" diff --git a/aws/rust-runtime/aws-sigv4/src/lib.rs b/aws/rust-runtime/aws-sigv4/src/lib.rs index 14d7a7b5fd..1d3b36ade9 100644 --- a/aws/rust-runtime/aws-sigv4/src/lib.rs +++ b/aws/rust-runtime/aws-sigv4/src/lib.rs @@ -15,6 +15,7 @@ unreachable_pub )] +use std::fmt; use std::time::SystemTime; pub mod sign; @@ -29,7 +30,6 @@ pub mod http_request; /// Parameters to use when signing. #[non_exhaustive] -#[derive(Debug)] pub struct SigningParams<'a, S> { /// Access Key ID to use. pub(crate) access_key: &'a str, @@ -49,6 +49,20 @@ pub struct SigningParams<'a, S> { pub(crate) settings: S, } +impl<'a, S: fmt::Debug> fmt::Debug for SigningParams<'a, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("SigningParams") + .field("access_key", &"** redacted **") + .field("secret_key", &"** redacted **") + .field("security_token", &"** redacted **") + .field("region", &self.region) + .field("service_name", &self.service_name) + .field("time", &self.time) + .field("settings", &self.settings) + .finish() + } +} + impl<'a, S: Default> SigningParams<'a, S> { /// Returns a builder that can create new `SigningParams`. pub fn builder() -> signing_params::Builder<'a, S> { From 1a7a495df59ea71d8cf147595fa59461cc22c5bc Mon Sep 17 00:00:00 2001 From: Burak Date: Tue, 11 Apr 2023 12:21:16 +0100 Subject: [PATCH 017/253] Python: Event Streaming (#2482) * Add `RustType.Application` and `PythonType.Application` variants * Use `RustType.Application` for event streaming symbols * Call symbol provider to get `Receiver` type instead of hardcoding * Add Python specific event streaming symbol provider * Add event streaming wrapper generator * Generate correct type information for event streaming types * Add `CapturePokemon` operation to Python Pokemon service * Add `InternalServerError` variant to all event streaming errors * Use `PythonServerCargoDependency` for PyO3Asyncio imports * Return an attribute error instead of panicking in `IntoPy` impls of wrappers * Add `Sync` bound to `new` methods of wrappers * Revert "Add `InternalServerError` variant to all event streaming errors" This reverts commit b610cb27c7532102e3a3ec15a59da6c56b60c318. * Add `PythonServerEventStreamErrorGenerator` to generate Python specific error types for unions * Try to extract error type or inner type from incoming streaming value and ignore the value otherwise for now * Allow missing type-stubs for `aiohttp` * Propogate modelled errors through stream * Raise modelled exceptions rather than sending through stream * Allow senders from Python side to raise modelled exceptions * Update `EventStreamSymbolProviderTest` to use `Application` type instead of `Opaque` type * Fix `ktlint` issues * Group up common variables in `codegenScope` * Document `RustType.Application` * Format `codegen-server-test/python/model/pokemon.smithy` * Use `tokio-stream` crate instead of `futures` crate * Use a better error message if user tries to reuse a stream * Add some details about event streams to example Pokemon service --------- Co-authored-by: Matteo Bigoi <1781140+crisidev@users.noreply.github.com> --- .../smithy/EventStreamSymbolProviderTest.kt | 14 +- .../rust/codegen/core/rustlang/RustType.kt | 17 +- .../core/smithy/EventStreamSymbolProvider.kt | 11 +- .../rust/codegen/core/smithy/RuntimeType.kt | 4 + .../generators/http/HttpBindingGenerator.kt | 9 +- .../python/model/pokemon.smithy | 93 ++++++- .../smithy/PythonEventStreamSymbolProvider.kt | 104 ++++++++ .../smithy/PythonServerCargoDependency.kt | 1 + .../smithy/PythonServerCodegenVisitor.kt | 16 +- .../python/smithy/PythonServerRustModule.kt | 5 +- .../server/python/smithy/PythonType.kt | 16 ++ .../smithy/RustServerCodegenPythonPlugin.kt | 4 +- .../PythonServerEventStreamErrorGenerator.kt | 120 +++++++++ ...PythonServerEventStreamWrapperGenerator.kt | 241 ++++++++++++++++++ .../PythonServerStructureGenerator.kt | 21 +- .../ServerOperationErrorGenerator.kt | 2 +- .../examples/mypy.ini | 3 + .../examples/pokemon-service-test/Cargo.toml | 2 + .../tests/simple_integration_test.rs | 152 +++++++++++ .../examples/pokemon_service.py | 173 ++++++++++++- 20 files changed, 972 insertions(+), 36 deletions(-) create mode 100644 codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonEventStreamSymbolProvider.kt create mode 100644 codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamErrorGenerator.kt create mode 100644 codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamWrapperGenerator.kt diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/EventStreamSymbolProviderTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/EventStreamSymbolProviderTest.kt index 703ea2a7af..e9e26724d9 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/EventStreamSymbolProviderTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/EventStreamSymbolProviderTest.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSetting import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.EventStreamSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.SymbolVisitor import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer @@ -60,8 +61,17 @@ class EventStreamSymbolProviderTest { val inputType = provider.toSymbol(inputStream).rustType() val outputType = provider.toSymbol(outputStream).rustType() - inputType shouldBe RustType.Opaque("EventStreamSender", "aws_smithy_http::event_stream") - outputType shouldBe RustType.Opaque("Receiver", "aws_smithy_http::event_stream") + val someStream = RustType.Opaque("SomeStream", "crate::types") + val someStreamError = RustType.Opaque("SomeStreamError", "crate::types::error") + + inputType shouldBe RustType.Application( + RuntimeType.eventStreamSender(TestRuntimeConfig).toSymbol().rustType(), + listOf(someStream, someStreamError), + ) + outputType shouldBe RustType.Application( + RuntimeType.eventStreamReceiver(TestRuntimeConfig).toSymbol().rustType(), + listOf(someStream, someStreamError), + ) } @Test diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index a0f684a77b..e4d27fe451 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -172,6 +172,18 @@ sealed class RustType { } data class Opaque(override val name: kotlin.String, override val namespace: kotlin.String? = null) : RustType() + + /** + * Represents application of a Rust type with the given arguments. + * + * For example, we can represent `HashMap` as + * `RustType.Application(RustType.Opaque("HashMap"), listOf(RustType.String, RustType.Integer(64)))`. + * This helps us to separate the type and the arguments which is useful in methods like [qualifiedName]. + */ + data class Application(val type: RustType, val args: List) : RustType() { + override val name = type.name + override val namespace = type.namespace + } } /** @@ -242,7 +254,10 @@ fun RustType.render(fullyQualified: Boolean = true): String { "&${this.lifetime?.let { "'$it" } ?: ""} ${this.member.render(fullyQualified)}" } } - + is RustType.Application -> { + val args = this.args.joinToString(", ") { it.render(fullyQualified) } + "${this.name}<$args>" + } is RustType.Option -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Box -> "${this.name}<${this.member.render(fullyQualified)}>" is RustType.Dyn -> "${this.name} ${this.member.render(fullyQualified)}" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt index 6eeab26d65..fdb6e35a83 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/EventStreamSymbolProvider.kt @@ -11,7 +11,6 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustType -import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait @@ -48,15 +47,15 @@ class EventStreamSymbolProvider( } else { symbolForEventStreamError(unionShape) } - val errorFmt = error.rustType().render(fullyQualified = true) - val innerFmt = initial.rustType().stripOuter().render(fullyQualified = true) + val errorT = error.rustType() + val innerT = initial.rustType().stripOuter() val isSender = (shape.isInputEventStream(model) && target == CodegenTarget.CLIENT) || (shape.isOutputEventStream(model) && target == CodegenTarget.SERVER) val outer = when (isSender) { - true -> "EventStreamSender<$innerFmt, $errorFmt>" - else -> "Receiver<$innerFmt, $errorFmt>" + true -> RuntimeType.eventStreamSender(runtimeConfig).toSymbol().rustType() + else -> RuntimeType.eventStreamReceiver(runtimeConfig).toSymbol().rustType() } - val rustType = RustType.Opaque(outer, "aws_smithy_http::event_stream") + val rustType = RustType.Application(outer, listOf(innerT, errorT)) return initial.toBuilder() .name(rustType.name) .rustType(rustType) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 490aab7c25..b74fbf2b9a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -218,6 +218,9 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) val Bool = std.resolve("primitive::bool") val TryFrom = stdConvert.resolve("TryFrom") val Vec = std.resolve("vec::Vec") + val Arc = std.resolve("sync::Arc") + val Send = std.resolve("marker::Send") + val Sync = std.resolve("marker::Sync") // external cargo dependency types val Bytes = CargoDependency.Bytes.toType().resolve("Bytes") @@ -277,6 +280,7 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun document(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("Document") fun retryErrorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ErrorKind") fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::Receiver") + fun eventStreamSender(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::EventStreamSender") fun errorMetadata(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::ErrorMetadata") fun errorMetadataBuilder(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::metadata::Builder") fun provideErrorMetadataTrait(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::metadata::ProvideErrorMetadata") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index d53e09df68..ab920bc53f 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -30,6 +30,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asOptional +import software.amazon.smithy.rust.codegen.core.rustlang.qualifiedName import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock @@ -222,7 +223,7 @@ class HttpBindingGenerator( // Streaming unions are Event Streams and should be handled separately val target = model.expectShape(binding.member.target) if (target is UnionShape) { - bindEventStreamOutput(operationShape, target) + bindEventStreamOutput(operationShape, outputT, target) } else { deserializeStreamingBody(binding) } @@ -243,22 +244,22 @@ class HttpBindingGenerator( } } - private fun RustWriter.bindEventStreamOutput(operationShape: OperationShape, targetShape: UnionShape) { + private fun RustWriter.bindEventStreamOutput(operationShape: OperationShape, outputT: Symbol, targetShape: UnionShape) { val unmarshallerConstructorFn = EventStreamUnmarshallerGenerator( protocol, codegenContext, operationShape, targetShape, ).render() + val receiver = outputT.rustType().qualifiedName() rustTemplate( """ let unmarshaller = #{unmarshallerConstructorFn}(); let body = std::mem::replace(body, #{SdkBody}::taken()); - Ok(#{Receiver}::new(unmarshaller, body)) + Ok($receiver::new(unmarshaller, body)) """, "SdkBody" to RuntimeType.sdkBody(runtimeConfig), "unmarshallerConstructorFn" to unmarshallerConstructorFn, - "Receiver" to RuntimeType.eventStreamReceiver(runtimeConfig), ) } diff --git a/codegen-server-test/python/model/pokemon.smithy b/codegen-server-test/python/model/pokemon.smithy index 4ad42a0b69..b019d183a2 100644 --- a/codegen-server-test/python/model/pokemon.smithy +++ b/codegen-server-test/python/model/pokemon.smithy @@ -1,19 +1,18 @@ /// TODO(https://github.com/awslabs/smithy-rs/issues/1508) -/// $econcile this model with the main one living inside codegen-server-test/model/pokemon.smithy +/// Reconcile this model with the main one living inside codegen-server-test/model/pokemon.smithy /// once the Python implementation supports Streaming and Union shapes. $version: "1.0" namespace com.aws.example.python use aws.protocols#restJson1 +use com.aws.example#CheckHealth +use com.aws.example#DoNothing +use com.aws.example#GetServerStatistics use com.aws.example#PokemonSpecies use com.aws.example#Storage -use com.aws.example#GetServerStatistics -use com.aws.example#DoNothing -use com.aws.example#CheckHealth use smithy.framework#ValidationException - /// The Pokémon Service allows you to retrieve information about Pokémon species. @title("Pokémon Service") @restJson1 @@ -23,11 +22,93 @@ service PokemonService { operations: [ GetServerStatistics DoNothing + CapturePokemon CheckHealth StreamPokemonRadio - ], + ] +} + +/// Capture Pokémons via event streams. +@http(uri: "/capture-pokemon-event/{region}", method: "POST") +operation CapturePokemon { + input: CapturePokemonEventsInput + output: CapturePokemonEventsOutput + errors: [ + UnsupportedRegionError + ThrottlingError + ValidationException + ] } +@input +structure CapturePokemonEventsInput { + @httpPayload + events: AttemptCapturingPokemonEvent + @httpLabel + @required + region: String +} + +@output +structure CapturePokemonEventsOutput { + @httpPayload + events: CapturePokemonEvents +} + +@streaming +union AttemptCapturingPokemonEvent { + event: CapturingEvent + masterball_unsuccessful: MasterBallUnsuccessful +} + +structure CapturingEvent { + @eventPayload + payload: CapturingPayload +} + +structure CapturingPayload { + name: String + pokeball: String +} + +@streaming +union CapturePokemonEvents { + event: CaptureEvent + invalid_pokeball: InvalidPokeballError + throttlingError: ThrottlingError +} + +structure CaptureEvent { + @eventHeader + name: String + @eventHeader + captured: Boolean + @eventHeader + shiny: Boolean + @eventPayload + pokedex_update: Blob +} + +@error("server") +structure UnsupportedRegionError { + @required + region: String +} + +@error("client") +structure InvalidPokeballError { + @required + pokeball: String +} + +@error("server") +structure MasterBallUnsuccessful { + message: String +} + +@error("client") +structure ThrottlingError {} + /// Fetch the radio song from the database and stream it back as a playable audio. @readonly @http(uri: "/radio", method: "GET") diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonEventStreamSymbolProvider.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonEventStreamSymbolProvider.kt new file mode 100644 index 0000000000..38d3830bac --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonEventStreamSymbolProvider.kt @@ -0,0 +1,104 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.WrappingSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait +import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors +import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.core.util.isEventStream +import software.amazon.smithy.rust.codegen.core.util.isOutputEventStream +import software.amazon.smithy.rust.codegen.core.util.toPascalCase + +/** + * Symbol provider for Python that maps event streaming member shapes to their respective Python wrapper types. + * + * For example given a model: + * ```smithy + * @input + * structure CapturePokemonInput { + * @httpPayload + * events: AttemptCapturingPokemonEvent, + * } + * + * @streaming + * union AttemptCapturingPokemonEvent { + * ... + * } + * ``` + * for the member shape `CapturePokemonInput$events` it will return a symbol that points to + * `crate::python_event_stream::CapturePokemonInputEventsReceiver`. + */ +class PythonEventStreamSymbolProvider( + private val runtimeConfig: RuntimeConfig, + base: RustSymbolProvider, +) : WrappingSymbolProvider(base) { + override fun toSymbol(shape: Shape): Symbol { + val initial = super.toSymbol(shape) + + // We only want to wrap with Event Stream types when dealing with member shapes + if (shape !is MemberShape || !shape.isEventStream(model)) { + return initial + } + + // We can only wrap the type if it's either an input or an output that used in an operation + model.expectShape(shape.container).let { maybeInputOutput -> + val operationId = maybeInputOutput.getTrait()?.operation + ?: maybeInputOutput.getTrait()?.operation + operationId?.let { model.expectShape(it, OperationShape::class.java) } + } ?: return initial + + val unionShape = model.expectShape(shape.target).asUnionShape().get() + val error = if (unionShape.eventStreamErrors().isEmpty()) { + RuntimeType.smithyHttp(runtimeConfig).resolve("event_stream::MessageStreamError").toSymbol() + } else { + symbolForEventStreamError(unionShape) + } + val inner = initial.rustType().stripOuter() + val innerSymbol = Symbol.builder().name(inner.name).rustType(inner).build() + val containerName = shape.container.name + val memberName = shape.memberName.toPascalCase() + val outer = when (shape.isOutputEventStream(model)) { + true -> "${containerName}${memberName}EventStreamSender" + else -> "${containerName}${memberName}Receiver" + } + val rustType = RustType.Opaque(outer, PythonServerRustModule.PythonEventStream.fullyQualifiedPath()) + return Symbol.builder() + .name(rustType.name) + .rustType(rustType) + .addReference(innerSymbol) + .addReference(error) + .addDependency(CargoDependency.smithyHttp(runtimeConfig).withFeature("event-stream")) + .addDependency(PythonServerCargoDependency.TokioStream) + .addDependency(PythonServerCargoDependency.PyO3Asyncio.withFeature("unstable-streams")) + .build() + } + + companion object { + data class EventStreamSymbol(val innerT: RustType, val errorT: RustType) + + fun parseSymbol(symbol: Symbol): EventStreamSymbol { + check(symbol.references.size >= 2) { + "`PythonEventStreamSymbolProvider` adds inner type and error type as references to resulting symbol" + } + val innerT = symbol.references[0].symbol.rustType() + val errorT = symbol.references[1].symbol.rustType() + return EventStreamSymbol(innerT, errorT) + } + } +} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt index d1ad482f2a..911edb893e 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt @@ -18,6 +18,7 @@ object PythonServerCargoDependency { val PyO3: CargoDependency = CargoDependency("pyo3", CratesIo("0.17")) val PyO3Asyncio: CargoDependency = CargoDependency("pyo3-asyncio", CratesIo("0.17"), features = setOf("attributes", "tokio-runtime")) val Tokio: CargoDependency = CargoDependency("tokio", CratesIo("1.20.1"), features = setOf("full")) + val TokioStream: CargoDependency = CargoDependency("tokio-stream", CratesIo("0.1.12")) val Tracing: CargoDependency = CargoDependency("tracing", CratesIo("0.1")) val Tower: CargoDependency = CargoDependency("tower", CratesIo("0.4")) val TowerHttp: CargoDependency = CargoDependency("tower-http", CratesIo("0.3"), features = setOf("trace")) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index 216f21a4f5..788b6e695f 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.python.smithy import software.amazon.smithy.build.PluginContext import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.NullableIndex +import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.StringShape @@ -23,6 +24,8 @@ import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.isEventStream import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonApplicationGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEnumGenerator +import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEventStreamErrorGenerator +import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEventStreamWrapperGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerOperationErrorGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerOperationHandlerGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerStructureGenerator @@ -37,7 +40,6 @@ import software.amazon.smithy.rust.codegen.server.smithy.ServerSymbolProviders import software.amazon.smithy.rust.codegen.server.smithy.canReachConstrainedShape import software.amazon.smithy.rust.codegen.server.smithy.createInlineModuleCreator import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator -import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerOperationErrorGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedUnionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader @@ -205,7 +207,7 @@ class PythonServerCodegenVisitor( if (shape.isEventStream()) { rustCrate.withModule(ServerRustModule.Error) { - ServerOperationErrorGenerator(model, codegenContext.symbolProvider, shape).render(this) + PythonServerEventStreamErrorGenerator(model, codegenContext.symbolProvider, shape).render(this) } } } @@ -246,4 +248,14 @@ class PythonServerCodegenVisitor( PythonServerOperationErrorGenerator(codegenContext.model, codegenContext.symbolProvider, shape).render(this) } } + + override fun memberShape(shape: MemberShape) { + super.memberShape(shape) + + if (shape.isEventStream(model)) { + rustCrate.withModule(PythonServerRustModule.PythonEventStream) { + PythonServerEventStreamWrapperGenerator(codegenContext, shape).render(this) + } + } + } } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRustModule.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRustModule.kt index c5438b4a9f..9df4937579 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRustModule.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerRustModule.kt @@ -15,6 +15,7 @@ object PythonServerRustModule { val PythonModuleExport = RustModule.public("python_module_export") val PythonOperationAdapter = RustModule.public("python_operation_adaptor") val PythonServerApplication = RustModule.public("python_server_application") + val PythonEventStream = RustModule.public("python_event_stream") } class PythonServerModuleDocProvider(private val base: ModuleDocProvider) : ModuleDocProvider { @@ -23,8 +24,8 @@ class PythonServerModuleDocProvider(private val base: ModuleDocProvider) : Modul return when (module) { PythonServerRustModule.PythonModuleExport -> strDoc("Export PyO3 symbols in the shared library") PythonServerRustModule.PythonServerApplication -> strDoc("Python server and application implementation.") - // TODO(ServerTeam): Document this module (I don't have context) - PythonServerRustModule.PythonOperationAdapter -> null + PythonServerRustModule.PythonOperationAdapter -> strDoc("Operation adapters that delegate to Python handlers.") + PythonServerRustModule.PythonEventStream -> strDoc("Python wrapper types for event streams.") else -> base.docsWriter(module) } } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt index c5606b5d38..95af2cd14e 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt @@ -90,6 +90,16 @@ sealed class PythonType { override val namespace: String = "typing" } + data class AsyncIterator(override val member: PythonType) : PythonType(), Container { + override val name: String = "AsyncIterator" + override val namespace: String = "typing" + } + + data class Application(val type: PythonType, val args: kotlin.collections.List) : PythonType() { + override val name = type.name + override val namespace = type.namespace + } + data class Opaque(override val name: String, val rustNamespace: String? = null) : PythonType() { // Since Python doesn't have a something like Rust's `crate::` we are using a custom placeholder here // and in our stub generation script we will replace placeholder with the real root module name. @@ -125,6 +135,7 @@ fun RustType.pythonType(): PythonType = is RustType.Option -> PythonType.Optional(this.member.pythonType()) is RustType.Box -> this.member.pythonType() is RustType.Dyn -> this.member.pythonType() + is RustType.Application -> PythonType.Application(this.type.pythonType(), this.args.map { it.pythonType() }) is RustType.Opaque -> PythonType.Opaque(this.name, this.namespace) // TODO(Constraints): How to handle this? // Revisit as part of https://github.com/awslabs/smithy-rs/issues/2114 @@ -154,6 +165,11 @@ fun PythonType.render(fullyQualified: Boolean = true): String { is PythonType.Set -> "${this.name}[${this.member.render(fullyQualified)}]" is PythonType.Awaitable -> "${this.name}[${this.member.render(fullyQualified)}]" is PythonType.Optional -> "${this.name}[${this.member.render(fullyQualified)}]" + is PythonType.AsyncIterator -> "${this.name}[${this.member.render(fullyQualified)}]" + is PythonType.Application -> { + val args = this.args.joinToString(", ") { it.render(fullyQualified) } + "${this.name}[$args]" + } is PythonType.Callable -> { val args = this.args.joinToString(", ") { it.render(fullyQualified) } val rtype = this.rtype.render(fullyQualified) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustServerCodegenPythonPlugin.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustServerCodegenPythonPlugin.kt index faf01b1fcf..a2c9ab45de 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustServerCodegenPythonPlugin.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/RustServerCodegenPythonPlugin.kt @@ -12,8 +12,6 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWordSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.BaseSymbolMetadataProvider -import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget -import software.amazon.smithy.rust.codegen.core.smithy.EventStreamSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig import software.amazon.smithy.rust.codegen.server.python.smithy.customizations.DECORATORS import software.amazon.smithy.rust.codegen.server.smithy.ConstrainedShapeSymbolMetadataProvider @@ -88,7 +86,7 @@ class RustServerCodegenPythonPlugin : SmithyBuildPlugin { if (includeConstrainedShapeProvider) PythonConstrainedShapeSymbolProvider(it, serviceShape, constrainedTypes) else it } // Generate different types for EventStream shapes (e.g. transcribe streaming) - .let { EventStreamSymbolProvider(rustSymbolProviderConfig.runtimeConfig, it, CodegenTarget.SERVER) } + .let { PythonEventStreamSymbolProvider(rustSymbolProviderConfig.runtimeConfig, it) } // Add Rust attributes (like `#[derive(PartialEq)]`) to generated shapes .let { BaseSymbolMetadataProvider(it, additionalAttributes = listOf()) } // Constrained shapes generate newtypes that need the same derives we place on types generated from aggregate shapes. diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamErrorGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamErrorGenerator.kt new file mode 100644 index 0000000000..02a545b7eb --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamErrorGenerator.kt @@ -0,0 +1,120 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy.generators + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate +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.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.transformers.eventStreamErrors +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency +import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerOperationErrorGenerator + +/** + * Generates Python compatible error types for event streaming union types. + * It just uses [ServerOperationErrorGenerator] under the hood to generate pure Rust error types and then adds + * some implementation to errors to make it possible to moving errors from Rust to Python and vice-versa. + * It adds following implementations to errors: + * - `pyo3::FromPyObject`: To allow extracting Rust errors from Python errors + * - `pyo3::IntoPy`: To allow converting Rust errors to Python errors + * - `impl From<#{Error}> for pyo3::PyErr`: To allow converting Rust errors to Python exceptions, + * it uses previous impl to convert errors + */ +class PythonServerEventStreamErrorGenerator( + private val model: Model, + private val symbolProvider: RustSymbolProvider, + val shape: UnionShape, +) : ServerOperationErrorGenerator( + model, + symbolProvider, + shape, +) { + private val errorSymbol = symbolProvider.symbolForEventStreamError(shape) + private val errors = shape.eventStreamErrors().map { + model.expectShape(it.asMemberShape().get().target, StructureShape::class.java) + } + + private val pyO3 = PythonServerCargoDependency.PyO3.toType() + + override fun render(writer: RustWriter) { + super.render(writer) + renderFromPyObjectImpl(writer) + renderIntoPyImpl(writer) + renderIntoPyErrImpl(writer) + } + + private fun renderFromPyObjectImpl(writer: RustWriter) { + writer.rustBlockTemplate("impl<'source> #{PyO3}::FromPyObject<'source> for ${errorSymbol.name}", "PyO3" to pyO3) { + writer.rustBlockTemplate("fn extract(obj: &'source #{PyO3}::PyAny) -> #{PyO3}::PyResult", "PyO3" to pyO3) { + errors.forEach { + val symbol = symbolProvider.toSymbol(it) + writer.rust( + """ + if let Ok(it) = obj.extract::<#T>() { + return Ok(Self::${symbol.name}(it)); + } + """, + symbol, + ) + } + writer.rustTemplate( + """ + Err(#{PyO3}::exceptions::PyTypeError::new_err( + format!( + "failed to extract '${errorSymbol.name}' from '{}'", + obj + ) + )) + """, + "PyO3" to pyO3, + ) + } + } + } + + private fun renderIntoPyImpl(writer: RustWriter) { + writer.rustBlockTemplate("impl #{PyO3}::IntoPy<#{PyO3}::PyObject> for ${errorSymbol.name}", "PyO3" to pyO3) { + writer.rustBlockTemplate("fn into_py(self, py: #{PyO3}::Python<'_>) -> #{PyO3}::PyObject", "PyO3" to pyO3) { + writer.rustBlock("match self") { + errors.forEach { + val symbol = symbolProvider.toSymbol(it) + writer.rustTemplate( + """ + Self::${symbol.name}(it) => match #{PyO3}::Py::new(py, it) { + Ok(it) => it.into_py(py), + Err(err) => err.into_py(py), + } + """, + "PyO3" to pyO3, + ) + } + } + } + } + } + + private fun renderIntoPyErrImpl(writer: RustWriter) { + writer.rustBlockTemplate("impl #{From}<${errorSymbol.name}> for #{PyO3}::PyErr", "PyO3" to pyO3, "From" to RuntimeType.From) { + writer.rustBlockTemplate("fn from(err: ${errorSymbol.name}) -> #{PyO3}::PyErr", "PyO3" to pyO3) { + writer.rustTemplate( + """ + #{PyO3}::Python::with_gil(|py| { + let py_err = #{PyO3}::IntoPy::into_py(err, py); + #{PyO3}::PyErr::from_value(py_err.as_ref(py)) + }) + """, + "PyO3" to pyO3, + ) + } + } + } +} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamWrapperGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamWrapperGenerator.kt new file mode 100644 index 0000000000..676b56a708 --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamWrapperGenerator.kt @@ -0,0 +1,241 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy.generators + +import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata +import software.amazon.smithy.rust.codegen.core.util.isOutputEventStream +import software.amazon.smithy.rust.codegen.core.util.toPascalCase +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonEventStreamSymbolProvider +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency + +/** + * Generates Python wrapper types for event streaming members. + * In pure Rust we use `aws_smithy_http::event_stream::{Receiver,EventStreamSender}` for event streaming members, + * this is not viable for Python because PyO3 expects following for a type to be exposed to Python, but we fail to satisfy: + * - It should be `Clone` and `Send` + * - It shouldn't have any generic parameters + * + * So we generate wrapper types for every streaming member, that looks like: + * ```rust + * #[pyo3::pyclass] + * #[derive(std::clone::Clone, std::fmt::Debug)] + * pub struct CapturePokemonInputEventsReceiver { + * // Arc makes it cloneable + * inner: std::sync::Arc< + * // Mutex makes it sendable + * tokio::sync::Mutex< + * // Filling generic args with specific impls make outer type non-generic + * aws_smithy_http::event_stream::Receiver< + * crate::model::AttemptCapturingPokemonEvent, + * crate::error::AttemptCapturingPokemonEventError, + * >, + * >, + * >, + * } + * ``` + * + * For Receiver and Sender types we also implement: + * Receiver: `__anext__` so it can be used in async for loops in Python + * Sender: `FromPyObject` that converts an async Python generator to a Rust stream + */ +class PythonServerEventStreamWrapperGenerator( + codegenContext: CodegenContext, + private val shape: MemberShape, +) { + private val runtimeConfig = codegenContext.runtimeConfig + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + + private val symbol = symbolProvider.toSymbol(shape) + private val eventStreamSymbol = PythonEventStreamSymbolProvider.parseSymbol(symbol) + private val innerT = eventStreamSymbol.innerT + private val errorT = eventStreamSymbol.errorT + private val containerName = shape.container.name + private val memberName = shape.memberName.toPascalCase() + + private val pyO3 = PythonServerCargoDependency.PyO3.toType() + private val codegenScope = + arrayOf( + "Inner" to innerT, + "Error" to errorT, + "SmithyPython" to PythonServerCargoDependency.smithyHttpServerPython(runtimeConfig).toType(), + "SmithyHttp" to RuntimeType.smithyHttp(runtimeConfig), + "Tracing" to PythonServerCargoDependency.Tracing.toType(), + "PyO3" to pyO3, + "PyO3Asyncio" to PythonServerCargoDependency.PyO3Asyncio.toType(), + "TokioStream" to PythonServerCargoDependency.TokioStream.toType(), + "Mutex" to PythonServerCargoDependency.ParkingLot.toType().resolve("Mutex"), + "AsyncMutex" to PythonServerCargoDependency.Tokio.toType().resolve("sync::Mutex"), + "Send" to RuntimeType.Send, + "Sync" to RuntimeType.Sync, + "Option" to RuntimeType.Option, + "Arc" to RuntimeType.Arc, + "Body" to RuntimeType.sdkBody(runtimeConfig), + "UnmarshallMessage" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::UnmarshallMessage"), + "MarshallMessage" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::MarshallMessage"), + "SignMessage" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::SignMessage"), + "MessageStreamAdapter" to RuntimeType.smithyHttp(runtimeConfig).resolve("event_stream::MessageStreamAdapter"), + ) + + fun render(writer: RustWriter) { + if (shape.isOutputEventStream(model)) { + renderSender(writer) + } else { + renderReceiver(writer) + } + } + + private fun renderSender(writer: RustWriter) { + val name = "${containerName}${memberName}EventStreamSender" + val wrappedT = RuntimeType.eventStreamSender(runtimeConfig) + val containerMeta = symbol.expectRustMetadata().withDerives(RuntimeType.Clone, RuntimeType.Debug) + containerMeta.render(writer) + writer.rustBlock("struct $name") { + writer.rustTemplate( + "inner: #{Arc}<#{Mutex}<#{Option}<#{Wrapped}<#{Inner}, #{Error}>>>>", + *codegenScope, + "Wrapped" to wrappedT, + ) + } + + writer.rustBlock("impl $name") { + writer.rustTemplate( + """ + pub fn into_body_stream( + self, + marshaller: impl #{MarshallMessage} + #{Send} + #{Sync} + 'static, + error_marshaller: impl #{MarshallMessage} + #{Send} + #{Sync} + 'static, + signer: impl #{SignMessage} + #{Send} + #{Sync} + 'static, + ) -> #{MessageStreamAdapter}<#{Inner}, #{Error}> { + let mut inner = self.inner.lock(); + let inner = inner.take().expect( + "attempted to reuse an event stream. \ + that means you kept a reference to an event stream and tried to reuse it in another request, \ + event streams are request scoped and shouldn't be used outside of their bounded request scope" + ); + inner.into_body_stream(marshaller, error_marshaller, signer) + } + """, + *codegenScope, + "Wrapped" to wrappedT, + ) + } + + writer.rustTemplate( + """ + impl<'source> #{PyO3}::FromPyObject<'source> for $name { + fn extract(obj: &'source #{PyO3}::PyAny) -> #{PyO3}::PyResult { + use #{TokioStream}::StreamExt; + let stream = #{PyO3Asyncio}::tokio::into_stream_v1(obj)?; + let stream = stream.filter_map(|res| { + #{PyO3}::Python::with_gil(|py| { + // TODO(EventStreamImprovements): Add `InternalServerError` variant to all event streaming + // errors and return that variant in case of errors here? + match res { + Ok(obj) => { + match obj.extract::<#{Inner}>(py) { + Ok(it) => Some(Ok(it)), + Err(err) => { + let rich_py_err = #{SmithyPython}::rich_py_err(err); + #{Tracing}::error!(error = ?rich_py_err, "could not extract the output type '#{Inner}' from streamed value"); + None + }, + } + }, + Err(err) => { + match #{PyO3}::IntoPy::into_py(err, py).extract::<#{Error}>(py) { + Ok(modelled_error) => Some(Err(modelled_error)), + Err(err) => { + let rich_py_err = #{SmithyPython}::rich_py_err(err); + #{Tracing}::error!(error = ?rich_py_err, "could not extract the error type '#{Error}' from raised exception"); + None + } + } + } + } + }) + }); + + Ok($name { inner: #{Arc}::new(#{Mutex}::new(Some(stream.into()))) }) + } + } + + impl #{PyO3}::IntoPy<#{PyO3}::PyObject> for $name { + fn into_py(self, py: #{PyO3}::Python<'_>) -> #{PyO3}::PyObject { + #{PyO3}::exceptions::PyAttributeError::new_err("this is a write-only object").into_py(py) + } + } + """, + *codegenScope, + ) + } + + private fun renderReceiver(writer: RustWriter) { + val name = "${containerName}${memberName}Receiver" + val wrappedT = RuntimeType.eventStreamReceiver(runtimeConfig) + val containerMeta = symbol.expectRustMetadata().withDerives(RuntimeType.Clone, RuntimeType.Debug) + Attribute(pyO3.resolve("pyclass")).render(writer) + containerMeta.render(writer) + writer.rustBlock("struct $name") { + writer.rustTemplate( + "inner: #{Arc}<#{AsyncMutex}<#{Wrapped}<#{Inner}, #{Error}>>>", + *codegenScope, + "Wrapped" to wrappedT, + ) + } + + writer.rustBlock("impl $name") { + writer.rustTemplate( + """ + pub fn new( + unmarshaller: impl #{UnmarshallMessage} + #{Send} + #{Sync} + 'static, + body: #{Body} + ) -> $name { + let inner = #{Wrapped}::new(unmarshaller, body); + let inner = #{Arc}::new(#{AsyncMutex}::new(inner)); + $name { inner } + } + """, + *codegenScope, + "Wrapped" to wrappedT, + ) + } + + Attribute(pyO3.resolve("pymethods")).render(writer) + writer.rustBlock("impl $name") { + writer.rustTemplate( + """ + pub fn __aiter__(slf: #{PyO3}::PyRef) -> #{PyO3}::PyRef { + slf + } + + pub fn __anext__(slf: #{PyO3}::PyRefMut) -> #{PyO3}::PyResult> { + let body = slf.inner.clone(); + let fut = #{PyO3Asyncio}::tokio::future_into_py(slf.py(), async move { + let mut inner = body.lock().await; + let next = inner.recv().await; + match next { + Ok(Some(data)) => Ok(#{PyO3}::Python::with_gil(|py| #{PyO3}::IntoPy::into_py(data, py))), + Ok(None) => Err(#{PyO3}::exceptions::PyStopAsyncIteration::new_err("stream exhausted")), + Err(#{SmithyHttp}::result::SdkError::ServiceError(service_err)) => Err(service_err.into_err().into()), + Err(err) => Err(#{PyO3}::exceptions::PyRuntimeError::new_err(err.to_string())), + } + })?; + Ok(Some(fut.into())) + } + """, + *codegenScope, + ) + } + } +} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt index 5f226267f6..28b48d31e3 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt @@ -22,6 +22,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.isEventStream +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonEventStreamSymbolProvider import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency import software.amazon.smithy.rust.codegen.server.python.smithy.PythonType import software.amazon.smithy.rust.codegen.server.python.smithy.pythonType @@ -69,7 +71,7 @@ class PythonServerStructureGenerator( writer.addDependency(PythonServerCargoDependency.PyO3) // Above, we manually add dependency since we can't use a `RuntimeType` below Attribute("pyo3(get, set)").render(writer) - writer.rustTemplate("#{Signature:W}", "Signature" to renderSymbolSignature(memberSymbol)) + writer.rustTemplate("#{Signature:W}", "Signature" to renderMemberSignature(member, memberSymbol)) super.renderStructureMember(writer, member, memberName, memberSymbol) } @@ -116,17 +118,26 @@ class PythonServerStructureGenerator( private fun renderConstructorSignature(): Writable = writable { - forEachMember(members) { _, memberName, memberSymbol -> - val memberType = memberSymbol.rustType().pythonType() + forEachMember(members) { member, memberName, memberSymbol -> + val memberType = memberPythonType(member, memberSymbol) rust("/// :param $memberName ${memberType.renderAsDocstring()}:") } rust("/// :rtype ${PythonType.None.renderAsDocstring()}:") } - private fun renderSymbolSignature(symbol: Symbol): Writable = + private fun renderMemberSignature(shape: MemberShape, symbol: Symbol): Writable = writable { - val pythonType = symbol.rustType().pythonType() + val pythonType = memberPythonType(shape, symbol) rust("/// :type ${pythonType.renderAsDocstring()}:") } + + private fun memberPythonType(shape: MemberShape, symbol: Symbol): PythonType = + if (shape.isEventStream(model)) { + val eventStreamSymbol = PythonEventStreamSymbolProvider.parseSymbol(symbol) + val innerT = eventStreamSymbol.innerT.pythonType() + PythonType.AsyncIterator(innerT) + } else { + symbol.rustType().pythonType() + } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt index ee09c47047..0330b05905 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGenerator.kt @@ -43,7 +43,7 @@ open class ServerOperationErrorGenerator( (operationOrEventStream as UnionShape).eventStreamErrors() .map { model.expectShape(it.asMemberShape().get().target, StructureShape::class.java) } - fun render(writer: RustWriter) { + open fun render(writer: RustWriter) { val (errorSymbol, errors) = when (operationOrEventStream) { is OperationShape -> symbolProvider.symbolForOperationError(operationOrEventStream) to operationErrors() is UnionShape -> symbolProvider.symbolForEventStreamError(operationOrEventStream) to eventStreamErrors() diff --git a/rust-runtime/aws-smithy-http-server-python/examples/mypy.ini b/rust-runtime/aws-smithy-http-server-python/examples/mypy.ini index c6a867948d..7b6f4e1690 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/mypy.ini +++ b/rust-runtime/aws-smithy-http-server-python/examples/mypy.ini @@ -3,3 +3,6 @@ [mypy] strict = True + +[mypy-aiohttp.*] +ignore_missing_imports = True diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/Cargo.toml b/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/Cargo.toml index 0efedc3d63..6edcebf160 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/Cargo.toml @@ -7,6 +7,8 @@ authors = ["Smithy-rs Server Team "] description = "Run tests against the Python server implementation" [dev-dependencies] +rand = "0.8" +async-stream = "0.3" command-group = "1.0" tokio = { version = "1.20.1", features = ["full"] } serial_test = "0.9.0" diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/simple_integration_test.rs b/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/simple_integration_test.rs index 34efeba18a..39f979e9a3 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/simple_integration_test.rs +++ b/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/simple_integration_test.rs @@ -7,10 +7,16 @@ // These tests only have access to your crate's public API. // See: https://doc.rust-lang.org/book/ch11-03-test-organization.html#integration-tests +use async_stream::stream; use aws_smithy_types::error::display::DisplayErrorContext; +use rand::Rng; use serial_test::serial; use crate::helpers::{client, http2_client, PokemonClient, PokemonService}; +use pokemon_service_client::{ + types::error::{AttemptCapturingPokemonEventError, MasterBallUnsuccessful}, + types::{AttemptCapturingPokemonEvent, CapturingEvent, CapturingPayload}, +}; mod helpers; @@ -62,3 +68,149 @@ async fn simple_integration_test_with_client(client: PokemonClient) { let _health_check = client.check_health().send().await.unwrap(); } + +#[tokio::test] +#[serial] +async fn event_stream_test() { + let _program = PokemonService::run().await; + + let mut team = vec![]; + let input_stream = stream! { + // Always Pikachu + yield Ok(AttemptCapturingPokemonEvent::Event( + CapturingEvent::builder() + .payload(CapturingPayload::builder() + .name("Pikachu") + .pokeball("Master Ball") + .build()) + .build() + )); + yield Ok(AttemptCapturingPokemonEvent::Event( + CapturingEvent::builder() + .payload(CapturingPayload::builder() + .name("Regieleki") + .pokeball("Fast Ball") + .build()) + .build() + )); + yield Err(AttemptCapturingPokemonEventError::MasterBallUnsuccessful(MasterBallUnsuccessful::builder().build())); + // The next event should not happen + yield Ok(AttemptCapturingPokemonEvent::Event( + CapturingEvent::builder() + .payload(CapturingPayload::builder() + .name("Charizard") + .pokeball("Great Ball") + .build()) + .build() + )); + }; + + // Throw many! + let mut output = client() + .capture_pokemon() + .region("Kanto") + .events(input_stream.into()) + .send() + .await + .unwrap(); + loop { + match output.events.recv().await { + Ok(Some(capture)) => { + let pokemon = capture.as_event().unwrap().name.as_ref().unwrap().clone(); + let pokedex = capture + .as_event() + .unwrap() + .pokedex_update + .as_ref() + .unwrap() + .clone(); + let shiny = if *capture.as_event().unwrap().shiny.as_ref().unwrap() { + "" + } else { + "not " + }; + let expected_pokedex: Vec = (0..255).collect(); + println!("captured {} ({}shiny)", pokemon, shiny); + if expected_pokedex == pokedex.into_inner() { + println!("pokedex updated") + } + team.push(pokemon); + } + Err(e) => { + println!("error from the server: {:?}", e); + break; + } + Ok(None) => break, + } + } + + while team.len() < 6 { + let pokeball = get_pokeball(); + let pokemon = get_pokemon_to_capture(); + let input_stream = stream! { + yield Ok(AttemptCapturingPokemonEvent::Event( + CapturingEvent::builder() + .payload(CapturingPayload::builder() + .name(pokemon) + .pokeball(pokeball) + .build()) + .build() + )) + }; + let mut output = client() + .capture_pokemon() + .region("Kanto") + .events(input_stream.into()) + .send() + .await + .unwrap(); + match output.events.recv().await { + Ok(Some(capture)) => { + let pokemon = capture.as_event().unwrap().name.as_ref().unwrap().clone(); + let pokedex = capture + .as_event() + .unwrap() + .pokedex_update + .as_ref() + .unwrap() + .clone(); + let shiny = if *capture.as_event().unwrap().shiny.as_ref().unwrap() { + "" + } else { + "not " + }; + let expected_pokedex: Vec = (0..255).collect(); + println!("captured {} ({}shiny)", pokemon, shiny); + if expected_pokedex == pokedex.into_inner() { + println!("pokedex updated") + } + team.push(pokemon); + } + Err(e) => { + println!("error from the server: {:?}", e); + break; + } + Ok(None) => {} + } + } + println!("Team: {:?}", team); +} + +fn get_pokeball() -> String { + let random = rand::thread_rng().gen_range(0..100); + let pokeball = if random < 5 { + "Master Ball" + } else if random < 30 { + "Great Ball" + } else if random < 80 { + "Fast Ball" + } else { + "Smithy Ball" + }; + pokeball.to_string() +} + +fn get_pokemon_to_capture() -> String { + let pokemons = vec!["Charizard", "Pikachu", "Regieleki"]; + pokemons[rand::thread_rng().gen_range(0..pokemons.len())].to_string() +} diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py b/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py index a3c7bf1c93..210d232687 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py +++ b/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py @@ -8,18 +8,24 @@ import random from threading import Lock from dataclasses import dataclass -from typing import Dict, Any, List, Optional, Callable, Awaitable +from typing import Dict, Any, List, Optional, Callable, Awaitable, AsyncIterator from pokemon_service_server_sdk import App from pokemon_service_server_sdk.tls import TlsConfig from pokemon_service_server_sdk.aws_lambda import LambdaContext -from pokemon_service_server_sdk.error import ResourceNotFoundException +from pokemon_service_server_sdk.error import ( + ResourceNotFoundException, + UnsupportedRegionError, + MasterBallUnsuccessful, + InvalidPokeballError, +) from pokemon_service_server_sdk.input import ( DoNothingInput, GetPokemonSpeciesInput, GetServerStatisticsInput, CheckHealthInput, StreamPokemonRadioInput, + CapturePokemonInput, ) from pokemon_service_server_sdk.logging import TracingHandler from pokemon_service_server_sdk.middleware import ( @@ -27,13 +33,19 @@ Response, Request, ) -from pokemon_service_server_sdk.model import FlavorText, Language +from pokemon_service_server_sdk.model import ( + CapturePokemonEvents, + CaptureEvent, + FlavorText, + Language, +) from pokemon_service_server_sdk.output import ( DoNothingOutput, GetPokemonSpeciesOutput, GetServerStatisticsOutput, CheckHealthOutput, StreamPokemonRadioOutput, + CapturePokemonOutput, ) from pokemon_service_server_sdk.types import ByteStream @@ -244,12 +256,165 @@ def check_health(_: CheckHealthInput) -> CheckHealthOutput: return CheckHealthOutput() +########################################################### +# Event streams +############################################################ +# An event stream is an abstraction that allows multiple messages to be sent asynchronously +# between a client and server. Event streams support both duplex and simplex streaming. +# You can find more details about event streaming in Smithy Spec: +# https://smithy.io/2.0/spec/streaming.html#event-streams +# +# Event streams modeled as asynchronous Python generators, that means: +# For receiving, you can use `async for` to iterate incoming events from the client and you can +# `break` from the loop if you want to stop receiving events. +# For sending, you can use `yield` to sent an event to the client and you can `return` from your +# generator function to stop `yield`ing events. +# +# Event streams also has a concept of "Modeled Errors" and those events are considered as +# terminal errors and they stop the event stream. They are modeled as exceptions in Python. +# Incoming event streams can raise modeled exceptions to terminate the event stream and you can +# catch those errors by wrapping incoming streams in a `try-except` block, and you can also raise +# a modeled error to terminate event stream. +# See Smithy Spec for more information about modeled errors: +# https://smithy.io/2.0/spec/streaming.html#modeled-errors-in-event-streams +# +# Depending on your use case, your functions usually should look like: +# +# Receiving only: +# ```python +# def receiving(input: Input) -> Output: +# # Initial message handling... +# +# async def events(input): +# try: +# async for event in input.events: +# # Handle incoming `event`... +# # `input.events` will gracefully stop if client stops sending events, +# # you can also `break` if you want to stop receiving +# except YourModeledError as err: +# # Handle modeled error... +# # The stream is terminated +# +# # Return immediately and let your generator run in background, +# # you can also have initial messages and send them here +# return Output(events=events(input), initial="value") +# ``` +# +# Sending only: +# ```python +# def sending(input: Input) -> Output: +# # Initial message handling... +# +# async def events(input): +# while True: +# # You can send values by `yield`ing them... +# yield some_value +# +# # You can just `break` the loop to stop sending events gracefully +# if some_cond: +# break +# +# # You can also stop sending events by raising modeled errors +# if some_other_cond: +# raise YourModeledError(...) +# +# # Return immediately and let your generator run in background, +# # you can also have initial messages and send them here +# return Output(events=events(input), initial="value") +# ``` +# +# Both receiving and sending: +# ```python +# def bidirectional(input: Input) -> Output: +# # Initial message handling... +# +# async def events(input): +# try: +# async for event in input.events: +# # Handle incoming `event`... +# # `input.events` will gracefully stop if client stops sending events, +# # you can also `break` if you want to stop receiving +# +# # Send some event to the client by `yield`ing them, or stop sending events +# # by `break`ing the loop or raising modeled errors +# yield outgoing +# except YourModeledError as err: +# # Handle modeled error... +# # The stream is terminated +# +# # Return immediately and let your generator run in background, +# # you can also have initial messages and send them here +# return Output(events=events(input), initial="value") +# ``` +# +# You can see an example implementation of a duplex streaming for capturing a Pokemon, +# you can find Smithy model in `codegen-server-test/python/model/pokemon.smithy`. +@app.capture_pokemon +def capture_pokemon(input: CapturePokemonInput) -> CapturePokemonOutput: + # You can have initial messages that provides an opportunity for a client or server to + # provide metadata about an event stream before transmitting events. + # See Smithy spec for more details: https://smithy.io/2.0/spec/streaming.html#initial-messages + if input.region != "Kanto": + raise UnsupportedRegionError(input.region) + + # Here is your event stream, which is an `async` Python function that can `await` on incoming + # events and `yield` to sent an event + async def events(input: CapturePokemonInput) -> AsyncIterator[CapturePokemonEvents]: + # We're wrapping incoming event stream with a `try-catch` block to catch modeled errors + try: + # Asynchronously iterate through incoming events + async for incoming in input.events: + # `incoming` is an union of all possible non-error types from your Smithy model. + # You can use `is_{variant}` and `as_{variant}` methods to inspect incoming message + if incoming.is_event(): + event = incoming.as_event() + payload = event.payload + # You can ignore incoming messages just by `continue`ing the loop + if not payload: + logging.debug("no payload provided, ignoring the event") + continue + + name = payload.name or "" + pokeball = payload.pokeball or "" + # You can terminate the stream by raising modeled errors + if pokeball not in ["Master Ball", "Fast Ball"]: + raise InvalidPokeballError(pokeball) + + # Outgoing type is also an union of all possible non-error types from + # your Smithy model. You can construct the variant you want by calling + # constructor functions for your variant: + outgoing_event = CapturePokemonEvents.event( + CaptureEvent( + name=name, + captured=random.choice([True, False]), + shiny=random.choice([True, False]), + ) + ) + # You can `yield` your outgoing type to sent an event to the client + yield outgoing_event + else: + # You can stop receiving incoming events by just `break`ing the loop + logging.error("unknown event") + break + # You can catch modeled errors and act accordingly, they will terminate the event stream + except MasterBallUnsuccessful as err: + logging.error(f"masterball unsuccessful: {err}") + + # Here event stream is going to be completed because we stopped receiving events and we'll + # no longer `yield` new values and this asynchronous Python generator will end here + logging.debug("done") + + # You should immediately return your output here, your asynchronous Python generator will + # run in background without blocking your threads. + return CapturePokemonOutput(events=events(input)) + + # Stream a random Pokémon song. @app.stream_pokemon_radio async def stream_pokemon_radio( _: StreamPokemonRadioInput, context: Context ) -> StreamPokemonRadioOutput: - import aiohttp # type: ignore + import aiohttp radio_url = context.get_random_radio_stream() logging.info("Random radio URL for this stream is %s", radio_url) From eff74374a1c1b424b1c6ee7fdc3bc53535f40b44 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 11 Apr 2023 08:04:20 -0700 Subject: [PATCH 018/253] Fix the `HttpSigner` trait args (#2558) This trait should have been taking a `PropertyBag` that originates from `HttpAuthOption` rather than the `ConfigBag`. --- aws/rust-runtime/aws-runtime/src/auth.rs | 6 +++--- .../smithy/rustsdk/SigV4AuthDecorator.kt | 18 +++++++----------- .../src/client/orchestrator.rs | 2 +- .../src/client/orchestrator/auth.rs | 2 +- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/aws/rust-runtime/aws-runtime/src/auth.rs b/aws/rust-runtime/aws-runtime/src/auth.rs index 4546d58aee..503a4bf2af 100644 --- a/aws/rust-runtime/aws-runtime/src/auth.rs +++ b/aws/rust-runtime/aws-runtime/src/auth.rs @@ -11,12 +11,12 @@ pub mod sigv4 { SignableRequest, SignatureLocation, SigningParams, SigningSettings, UriPathNormalizationMode, }; + use aws_smithy_http::property_bag::PropertyBag; use aws_smithy_runtime_api::client::identity::Identity; use aws_smithy_runtime_api::client::orchestrator::{ BoxError, HttpAuthScheme, HttpRequest, HttpRequestSigner, IdentityResolver, IdentityResolvers, }; - use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_types::region::SigningRegion; use aws_types::SigningService; use std::time::{Duration, SystemTime}; @@ -197,9 +197,9 @@ pub mod sigv4 { &self, request: &mut HttpRequest, identity: &Identity, - cfg: &ConfigBag, + signing_properties: &PropertyBag, ) -> Result<(), BoxError> { - let operation_config = cfg + let operation_config = signing_properties .get::() .ok_or("missing operation signing config for SigV4")?; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 843d01e22f..a0cba8142b 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -119,16 +119,6 @@ private class AuthOperationRuntimePluginCustomization(private val codegenContext is OperationRuntimePluginSection.AdditionalConfig -> { val authSchemes = serviceIndex.getEffectiveAuthSchemes(codegenContext.serviceShape, section.operationShape) if (authSchemes.containsKey(SigV4Trait.ID)) { - rustTemplate( - """ - let auth_option_resolver = #{AuthOptionListResolver}::new( - vec![#{HttpAuthOption}::new(#{SIGV4_SCHEME_ID}, std::sync::Arc::new(#{PropertyBag}::new()))] - ); - ${section.configBagName}.set_auth_option_resolver(auth_option_resolver); - """, - *codegenScope, - ) - val unsignedPayload = section.operationShape.hasTrait() val doubleUriEncode = unsignedPayload || !disableDoubleEncode(codegenContext.serviceShape) val contentSha256Header = needsAmzSha256(codegenContext.serviceShape) @@ -144,11 +134,17 @@ private class AuthOperationRuntimePluginCustomization(private val codegenContext signing_options.normalize_uri_path = $normalizeUrlPath; signing_options.signing_optional = $signingOptional; signing_options.payload_override = #{payload_override}; - ${section.configBagName}.put(#{SigV4OperationSigningConfig} { + + let mut sigv4_properties = #{PropertyBag}::new(); + sigv4_properties.insert(#{SigV4OperationSigningConfig} { region: signing_region, service: signing_service, signing_options, }); + let auth_option_resolver = #{AuthOptionListResolver}::new( + vec![#{HttpAuthOption}::new(#{SIGV4_SCHEME_ID}, std::sync::Arc::new(sigv4_properties))] + ); + ${section.configBagName}.set_auth_option_resolver(auth_option_resolver); """, *codegenScope, "payload_override" to writable { diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 1cb5f34006..fa16d0154c 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -174,7 +174,7 @@ pub trait HttpRequestSigner: Send + Sync + Debug { &self, request: &mut HttpRequest, identity: &Identity, - cfg: &ConfigBag, + signing_properties: &PropertyBag, ) -> Result<(), BoxError>; } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 705ca9f44e..38a5cd58a1 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -44,7 +44,7 @@ pub(super) async fn orchestrate_auth( .map_err(construction_failure)?; return dispatch_phase.include_mut(|ctx| { let request = ctx.request_mut()?; - request_signer.sign_request(request, &identity, cfg)?; + request_signer.sign_request(request, &identity, scheme_properties)?; Result::<_, BoxError>::Ok(()) }); } From 1e27efe05fe7b991c9f9bbf3d63a297b2dded334 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 11 Apr 2023 08:05:03 -0700 Subject: [PATCH 019/253] Make a minor optimization to `AuthOptionResolver` (#2557) --- .../src/client/auth/option_resolver.rs | 9 +++++---- .../src/client/orchestrator.rs | 13 +++++++------ .../src/client/orchestrator/auth.rs | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs index 66ef4fd10d..ca3acef5f6 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs @@ -6,6 +6,7 @@ use crate::client::orchestrator::{ AuthOptionResolver, AuthOptionResolverParams, BoxError, HttpAuthOption, }; +use std::borrow::Cow; /// New-type around a `Vec` that implements `AuthOptionResolver`. /// @@ -23,11 +24,11 @@ impl AuthOptionListResolver { } impl AuthOptionResolver for AuthOptionListResolver { - fn resolve_auth_options( - &self, + fn resolve_auth_options<'a>( + &'a self, _params: &AuthOptionResolverParams, - ) -> Result, BoxError> { - Ok(self.auth_options.clone()) + ) -> Result, BoxError> { + Ok(Cow::Borrowed(&self.auth_options)) } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index fa16d0154c..29baf6bf00 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -11,6 +11,7 @@ use crate::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::property_bag::PropertyBag; use std::any::Any; +use std::borrow::Cow; use std::fmt::Debug; use std::future::Future; use std::pin::Pin; @@ -72,17 +73,17 @@ impl AuthOptionResolverParams { } pub trait AuthOptionResolver: Send + Sync + Debug { - fn resolve_auth_options( - &self, + fn resolve_auth_options<'a>( + &'a self, params: &AuthOptionResolverParams, - ) -> Result, BoxError>; + ) -> Result, BoxError>; } impl AuthOptionResolver for Box { - fn resolve_auth_options( - &self, + fn resolve_auth_options<'a>( + &'a self, params: &AuthOptionResolverParams, - ) -> Result, BoxError> { + ) -> Result, BoxError> { (**self).resolve_auth_options(params) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 38a5cd58a1..4a99001920 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -24,7 +24,7 @@ pub(super) async fn orchestrate_auth( .map_err(construction_failure)?; let identity_resolvers = cfg.identity_resolvers(); - for option in auth_options { + for option in auth_options.as_ref() { let scheme_id = option.scheme_id(); let scheme_properties = option.properties(); if let Some(auth_scheme) = cfg.http_auth_schemes().scheme(scheme_id) { From 3aa4cc24a5a90a6145beea67596bd09a205f660a Mon Sep 17 00:00:00 2001 From: Burak Date: Tue, 11 Apr 2023 18:40:27 +0100 Subject: [PATCH 020/253] Execute Python's diff commands inside correct directory (#2568) --- tools/ci-scripts/codegen-diff/codegen-diff-revisions.py | 2 +- tools/ci-scripts/codegen-diff/diff_lib.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py b/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py index 2e36e6ba5f..75b3b1d40a 100755 --- a/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py +++ b/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py @@ -20,7 +20,7 @@ # # ``` # $ cd test/smithy-rs -# $ ../../smithy-rs/tools/ci-scripts/codegen-diff-revisions.py . +# $ ../../smithy-rs/tools/ci-scripts/codegen-diff/codegen-diff-revisions.py . # ``` # # It will diff the generated code from HEAD against any commit hash you feed it. If you want to test diff --git a/tools/ci-scripts/codegen-diff/diff_lib.py b/tools/ci-scripts/codegen-diff/diff_lib.py index 9ccc0ce1bf..60fa3d6edf 100644 --- a/tools/ci-scripts/codegen-diff/diff_lib.py +++ b/tools/ci-scripts/codegen-diff/diff_lib.py @@ -15,6 +15,8 @@ CDN_URL = "https://d2luzm2xt3nokh.cloudfront.net" +PYTHON_EXAMPLES_PATH = "rust-runtime/aws-smithy-http-server-python/examples" + target_codegen_client = 'codegen-client-test' target_codegen_server = 'codegen-server-test' target_aws_sdk = 'aws:sdk' @@ -40,14 +42,14 @@ def generate_and_commit_generated_code(revision_sha, targets=None): # Clean the build artifacts before continuing get_cmd_output("rm -rf aws/sdk/build") if target_codegen_server in targets: - get_cmd_output("cd rust-runtime/aws-smithy-http-server-python/examples && make distclean", shell=True) + get_cmd_output("make distclean", shell=True, cwd=PYTHON_EXAMPLES_PATH) get_cmd_output("./gradlew codegen-core:clean codegen-client:clean codegen-server:clean aws:sdk-codegen:clean") # Generate code tasks = ' '.join([f'{t}:assemble' for t in targets]) get_cmd_output(f"./gradlew --rerun-tasks {tasks}") if target_codegen_server in targets: - get_cmd_output("cd rust-runtime/aws-smithy-http-server-python/examples && make build", shell=True, check=False) + get_cmd_output("make build", shell=True, check=False, cwd=PYTHON_EXAMPLES_PATH) get_cmd_output(f"./gradlew --rerun-tasks codegen-server-test:typescript:assemble") # Move generated code into codegen-diff/ directory @@ -60,7 +62,7 @@ def generate_and_commit_generated_code(revision_sha, targets=None): get_cmd_output(f"mv {target}/build/smithyprojections/{target} {OUTPUT_PATH}/") if target == target_codegen_server: get_cmd_output( - f"mv rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-server-sdk/ {OUTPUT_PATH}/codegen-server-test-python/", + f"mv {PYTHON_EXAMPLES_PATH}/pokemon-service-server-sdk/ {OUTPUT_PATH}/codegen-server-test-python/", check=False) get_cmd_output( f"mv codegen-server-test/typescript/build/smithyprojections/codegen-server-test-typescript {OUTPUT_PATH}/", From d97defbd14906fff39ef77d1ffea82f0202cbd35 Mon Sep 17 00:00:00 2001 From: Matteo Bigoi <1781140+crisidev@users.noreply.github.com> Date: Thu, 13 Apr 2023 14:55:33 +0100 Subject: [PATCH 021/253] [Python] Support more testing model (#2541) * Remove parameter from `Protocol`s `structuredDataParser`, `structuredDataSerializer` No implementation of the `Protocol` interface makes use of the `OperationShape` parameter in the `structuredDataParser` and `structuredDataSerializer` methods. * Remove the TypeConversionGenerator class in favor of using customizations for JsonParserGenerator and ServerHttpBoundProtocolGenerator. Signed-off-by: Bigo <1781140+crisidev@users.noreply.github.com> * Make the additionaParserCustomizations default to empty list * Fix merge conflict * Fix missing ; * Use better defaults when checking for customizations * Use better defaults when checking for customizations * Add HttpBindingCustomization and relax the datetime symbol check * Support recursive shapes and add a lot more models to the tests Signed-off-by: Bigo <1781140+crisidev@users.noreply.github.com> * Support naming obstacle course * Add support for constrained blobs conversions * Support constraint traits * Try to generate the full diff Signed-off-by: Bigo <1781140+crisidev@users.noreply.github.com> * A better way of checking if we need to go into the Timestamp branch * Remove wheels folder --------- Signed-off-by: Bigo <1781140+crisidev@users.noreply.github.com> Co-authored-by: david-perez --- .gitignore | 3 + .../generators/TypeConversionGenerator.kt | 54 -------- .../generators/http/HttpBindingGenerator.kt | 11 +- .../protocols/parse/JsonParserGenerator.kt | 31 ++++- codegen-server-test/python/build.gradle.kts | 45 ++++++- .../smithy/PythonServerCodegenVisitor.kt | 27 +++- .../ConstrainedPythonBlobGenerator.kt | 100 ++++++++++++++ .../generators/PythonApplicationGenerator.kt | 20 +-- .../generators/PythonServerModuleGenerator.kt | 5 +- .../PythonServerOperationHandlerGenerator.kt | 9 +- .../PythonServerStructureGenerator.kt | 22 +++ .../generators/PythonServerUnionGenerator.kt | 4 +- .../protocols/PythonServerProtocolLoader.kt | 125 ++++++++++++++++++ .../http/ServerRequestBindingGenerator.kt | 4 +- .../http/ServerResponseBindingGenerator.kt | 2 + .../generators/protocol/ServerProtocol.kt | 10 +- .../server/smithy/protocols/ServerAwsJson.kt | 19 ++- .../ServerHttpBoundProtocolGenerator.kt | 51 +++++-- .../server/smithy/protocols/ServerRestJson.kt | 20 ++- tools/ci-scripts/codegen-diff/diff_lib.py | 39 +++--- 20 files changed, 467 insertions(+), 134 deletions(-) delete mode 100644 codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/TypeConversionGenerator.kt create mode 100644 codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/ConstrainedPythonBlobGenerator.kt create mode 100644 codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/protocols/PythonServerProtocolLoader.kt diff --git a/.gitignore b/.gitignore index 710a23e16b..268344ecb1 100644 --- a/.gitignore +++ b/.gitignore @@ -56,3 +56,6 @@ target/ # tools .tool-versions + +# python +__pycache__ diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/TypeConversionGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/TypeConversionGenerator.kt deleted file mode 100644 index b79200f952..0000000000 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/TypeConversionGenerator.kt +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.core.smithy.generators - -import software.amazon.smithy.codegen.core.Symbol -import software.amazon.smithy.model.Model -import software.amazon.smithy.model.shapes.BlobShape -import software.amazon.smithy.model.shapes.Shape -import software.amazon.smithy.model.shapes.TimestampShape -import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.rustType - -/* - * Utility class used to force casting a non primitive type into one overriden by a new symbol provider, - * by explicitly calling `from()` or into(). - * - * For example we use this in the server Python implementation, where we override types like [Blob] and [DateTime] - * with wrappers compatible with Python, without touching the original implementation coming from `aws-smithy-types`. - */ -class TypeConversionGenerator(private val model: Model, private val symbolProvider: RustSymbolProvider, private val runtimeConfig: RuntimeConfig) { - private fun findOldSymbol(shape: Shape): Symbol { - return when (shape) { - is BlobShape -> RuntimeType.blob(runtimeConfig).toSymbol() - is TimestampShape -> RuntimeType.dateTime(runtimeConfig).toSymbol() - else -> symbolProvider.toSymbol(shape) - } - } - - fun convertViaFrom(shape: Shape): Writable = - writable { - val oldSymbol = findOldSymbol(shape) - val newSymbol = symbolProvider.toSymbol(shape) - if (oldSymbol.rustType() != newSymbol.rustType()) { - rust(".map($newSymbol::from)") - } - } - - fun convertViaInto(shape: Shape): Writable = - writable { - val oldSymbol = findOldSymbol(shape) - val newSymbol = symbolProvider.toSymbol(shape) - if (oldSymbol.rustType() != newSymbol.rustType()) { - rust(".into()") - } - } -} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index ab920bc53f..881d0f77af 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -88,6 +88,9 @@ sealed class HttpBindingSection(name: String) : Section(name) { data class AfterDeserializingIntoAHashMapOfHttpPrefixHeaders(val memberShape: MemberShape) : HttpBindingSection("AfterDeserializingIntoAHashMapOfHttpPrefixHeaders") + + data class AfterDeserializingIntoADateTimeOfHttpHeaders(val memberShape: MemberShape) : + HttpBindingSection("AfterDeserializingIntoADateTimeOfHttpHeaders") } typealias HttpBindingCustomization = NamedCustomization @@ -353,7 +356,7 @@ class HttpBindingGenerator( rustType to targetShape } val parsedValue = safeName() - if (coreType == dateTime) { + if (coreShape.isTimestampShape()) { val timestampFormat = index.determineTimestampFormat( memberShape, @@ -362,10 +365,14 @@ class HttpBindingGenerator( ) val timestampFormatType = RuntimeType.parseTimestampFormat(codegenTarget, runtimeConfig, timestampFormat) rust( - "let $parsedValue: Vec<${coreType.render()}> = #T::many_dates(headers, #T)?;", + "let $parsedValue: Vec<${coreType.render()}> = #T::many_dates(headers, #T)?", headerUtil, timestampFormatType, ) + for (customization in customizations) { + customization.section(HttpBindingSection.AfterDeserializingIntoADateTimeOfHttpHeaders(memberShape))(this) + } + rust(";") } else if (coreShape.isPrimitive()) { rust( "let $parsedValue = #T::read_many_primitive::<${coreType.render()}>(headers)?;", diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index dd7255e47e..1566d84bf3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -39,7 +39,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.canUseDefault import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section -import software.amazon.smithy.rust.codegen.core.smithy.generators.TypeConversionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.UnionGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.renderUnknownVariant import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName @@ -61,6 +60,12 @@ import software.amazon.smithy.utils.StringUtils */ sealed class JsonParserSection(name: String) : Section(name) { data class BeforeBoxingDeserializedMember(val shape: MemberShape) : JsonParserSection("BeforeBoxingDeserializedMember") + + data class AfterTimestampDeserializedMember(val shape: MemberShape) : JsonParserSection("AfterTimestampDeserializedMember") + + data class AfterBlobDeserializedMember(val shape: MemberShape) : JsonParserSection("AfterBlobDeserializedMember") + + data class AfterDocumentDeserializedMember(val shape: MemberShape) : JsonParserSection("AfterDocumentDeserializedMember") } /** @@ -94,7 +99,6 @@ class JsonParserGenerator( private val runtimeConfig = codegenContext.runtimeConfig private val codegenTarget = codegenContext.target private val smithyJson = CargoDependency.smithyJson(runtimeConfig).toType() - private val typeConversionGenerator = TypeConversionGenerator(model, symbolProvider, runtimeConfig) private val protocolFunctions = ProtocolFunctions(codegenContext) private val codegenScope = arrayOf( "Error" to smithyJson.resolve("deserialize::error::DeserializeError"), @@ -276,13 +280,13 @@ class JsonParserGenerator( is StringShape -> deserializeString(target) is BooleanShape -> rustTemplate("#{expect_bool_or_null}(tokens.next())?", *codegenScope) is NumberShape -> deserializeNumber(target) - is BlobShape -> deserializeBlob() + is BlobShape -> deserializeBlob(memberShape) is TimestampShape -> deserializeTimestamp(target, memberShape) is CollectionShape -> deserializeCollection(target) is MapShape -> deserializeMap(target) is StructureShape -> deserializeStruct(target) is UnionShape -> deserializeUnion(target) - is DocumentShape -> rustTemplate("Some(#{expect_document}(tokens)?)", *codegenScope) + is DocumentShape -> deserializeDocument(memberShape) else -> PANIC("unexpected shape: $target") } val symbol = symbolProvider.toSymbol(memberShape) @@ -294,11 +298,21 @@ class JsonParserGenerator( } } - private fun RustWriter.deserializeBlob() { + private fun RustWriter.deserializeDocument(member: MemberShape) { + rustTemplate("Some(#{expect_document}(tokens)?)", *codegenScope) + for (customization in customizations) { + customization.section(JsonParserSection.AfterDocumentDeserializedMember(member))(this) + } + } + + private fun RustWriter.deserializeBlob(member: MemberShape) { rustTemplate( "#{expect_blob_or_null}(tokens.next())?", *codegenScope, ) + for (customization in customizations) { + customization.section(JsonParserSection.AfterBlobDeserializedMember(member))(this) + } } private fun RustWriter.deserializeStringInner(target: StringShape, escapedStrName: String) { @@ -349,9 +363,12 @@ class JsonParserGenerator( ) val timestampFormatType = RuntimeType.parseTimestampFormat(codegenTarget, runtimeConfig, timestampFormat) rustTemplate( - "#{expect_timestamp_or_null}(tokens.next(), #{T})?#{ConvertFrom:W}", - "T" to timestampFormatType, "ConvertFrom" to typeConversionGenerator.convertViaFrom(shape), *codegenScope, + "#{expect_timestamp_or_null}(tokens.next(), #{T})?", + "T" to timestampFormatType, *codegenScope, ) + for (customization in customizations) { + customization.section(JsonParserSection.AfterTimestampDeserializedMember(member))(this) + } } private fun RustWriter.deserializeCollection(shape: CollectionShape) { diff --git a/codegen-server-test/python/build.gradle.kts b/codegen-server-test/python/build.gradle.kts index 35144f42f0..87ff7cc5bd 100644 --- a/codegen-server-test/python/build.gradle.kts +++ b/codegen-server-test/python/build.gradle.kts @@ -54,14 +54,49 @@ val allCodegenTests = "../../codegen-core/common-test-models".let { commonModels // TODO(https://github.com/awslabs/smithy-rs/issues/1401) `@uniqueItems` is used. extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, ), - // TODO(https://github.com/awslabs/smithy-rs/issues/2476) + CodegenTest( + "aws.protocoltests.json#JsonProtocol", + "json_rpc11", + extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, + ), + CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), + CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), + CodegenTest( + "aws.protocoltests.restjson#RestJsonExtras", + "rest_json_extras", + imports = listOf("$commonModels/rest-json-extras.smithy"), + ), + // TODO(https://github.com/awslabs/smithy-rs/issues/2551) // CodegenTest( - // "aws.protocoltests.json#JsonProtocol", - // "json_rpc11", + // "aws.protocoltests.restjson.validation#RestJsonValidation", + // "rest_json_validation", + // // `@range` trait is used on floating point shapes, which we deliberately don't want to support. + // // See https://github.com/awslabs/smithy-rs/issues/1401. // extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, // ), - // TODO(https://github.com/awslabs/smithy-rs/issues/2479) - // CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), + CodegenTest( + "com.amazonaws.constraints#ConstraintsService", + "constraints", + imports = listOf("$commonModels/constraints.smithy"), + ), + CodegenTest( + "com.amazonaws.constraints#ConstraintsService", + "constraints_without_public_constrained_types", + imports = listOf("$commonModels/constraints.smithy"), + extraConfig = """, "codegen": { "publicConstrainedTypes": false } """, + ), + CodegenTest( + "com.amazonaws.constraints#UniqueItemsService", + "unique_items", + imports = listOf("$commonModels/unique-items.smithy"), + ), + CodegenTest( + "naming_obs_structs#NamingObstacleCourseStructs", + "naming_test_structs", + imports = listOf("$commonModels/naming-obstacle-course-structs.smithy"), + ), + CodegenTest("casing#ACRONYMInside_Service", "naming_test_casing", imports = listOf("$commonModels/naming-obstacle-course-casing.smithy")), + CodegenTest("crate#Config", "naming_test_ops", imports = listOf("$commonModels/naming-obstacle-course-ops.smithy")), ) } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index 788b6e695f..fa1f8eeb65 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.python.smithy import software.amazon.smithy.build.PluginContext import software.amazon.smithy.model.Model import software.amazon.smithy.model.knowledge.NullableIndex +import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape @@ -22,6 +23,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig import software.amazon.smithy.rust.codegen.core.smithy.generators.error.ErrorImplGenerator import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.isEventStream +import software.amazon.smithy.rust.codegen.server.python.smithy.generators.ConstrainedPythonBlobGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonApplicationGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEnumGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerEventStreamErrorGenerator @@ -30,6 +32,7 @@ import software.amazon.smithy.rust.codegen.server.python.smithy.generators.Pytho import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerOperationHandlerGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerStructureGenerator import software.amazon.smithy.rust.codegen.server.python.smithy.generators.PythonServerUnionGenerator +import software.amazon.smithy.rust.codegen.server.python.smithy.protocols.PythonServerProtocolLoader import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenVisitor import software.amazon.smithy.rust.codegen.server.smithy.ServerModuleDocProvider @@ -42,8 +45,9 @@ import software.amazon.smithy.rust.codegen.server.smithy.createInlineModuleCreat import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator import software.amazon.smithy.rust.codegen.server.smithy.generators.UnconstrainedUnionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocol -import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerProtocolLoader +import software.amazon.smithy.rust.codegen.server.smithy.isDirectlyConstrained import software.amazon.smithy.rust.codegen.server.smithy.traits.isReachableFromOperationInput +import software.amazon.smithy.rust.codegen.server.smithy.withModuleOrWithStructureBuilderModule /** * Entrypoint for Python server-side code generation. This class will walk the in-memory model and @@ -68,10 +72,10 @@ class PythonServerCodegenVisitor( val baseModel = baselineTransform(context.model) val service = settings.getService(baseModel) val (protocol, generator) = - ServerProtocolLoader( + PythonServerProtocolLoader( codegenDecorator.protocols( service.id, - ServerProtocolLoader.DefaultProtocols, + PythonServerProtocolLoader.defaultProtocols(settings.runtimeConfig), ), ) .protocolFor(context.model, service) @@ -258,4 +262,21 @@ class PythonServerCodegenVisitor( } } } + + override fun blobShape(shape: BlobShape) { + logger.info("[python-server-codegen] Generating a service $shape") + super.blobShape(shape) + + if (shape.isDirectlyConstrained(codegenContext.symbolProvider)) { + rustCrate.withModuleOrWithStructureBuilderModule(ServerRustModule.Model, shape, codegenContext) { + ConstrainedPythonBlobGenerator( + codegenContext, + rustCrate.createInlineModuleCreator(), + this, + shape, + validationExceptionConversionGenerator, + ).render() + } + } + } } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/ConstrainedPythonBlobGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/ConstrainedPythonBlobGenerator.kt new file mode 100644 index 0000000000..a9c2021520 --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/ConstrainedPythonBlobGenerator.kt @@ -0,0 +1,100 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy.generators + +import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.shapes.BlobShape +import software.amazon.smithy.model.traits.LengthTrait +import software.amazon.smithy.rust.codegen.core.rustlang.RustType +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.join +import software.amazon.smithy.rust.codegen.core.rustlang.render +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.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained +import software.amazon.smithy.rust.codegen.core.smithy.rustType +import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.core.util.orNull +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.InlineModuleCreator +import software.amazon.smithy.rust.codegen.server.smithy.PubCrateConstraintViolationSymbolProvider +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.generators.BlobLength +import software.amazon.smithy.rust.codegen.server.smithy.generators.TraitInfo +import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator + +class ConstrainedPythonBlobGenerator( + val codegenContext: ServerCodegenContext, + private val inlineModuleCreator: InlineModuleCreator, + val writer: RustWriter, + val shape: BlobShape, + private val validationExceptionConversionGenerator: ValidationExceptionConversionGenerator, +) { + val constrainedShapeSymbolProvider = codegenContext.constrainedShapeSymbolProvider + val publicConstrainedTypes = codegenContext.settings.codegenConfig.publicConstrainedTypes + private val constraintViolationSymbolProvider = + with(codegenContext.constraintViolationSymbolProvider) { + if (publicConstrainedTypes) { + this + } else { + PubCrateConstraintViolationSymbolProvider(this) + } + } + val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) + private val blobConstraintsInfo: List = listOf(LengthTrait::class.java) + .mapNotNull { shape.getTrait(it).orNull() } + .map { BlobLength(it) } + private val constraintsInfo: List = blobConstraintsInfo.map { it.toTraitInfo() } + + fun render() { + val symbol = constrainedShapeSymbolProvider.toSymbol(shape) + val blobType = PythonServerRuntimeType.blob(codegenContext.runtimeConfig).toSymbol().rustType() + renderFrom(symbol, blobType) + renderTryFrom(symbol, blobType) + } + + fun renderFrom(symbol: Symbol, blobType: RustType) { + val name = symbol.name + val inner = blobType.render() + writer.rustTemplate( + """ + impl #{From}<$inner> for #{MaybeConstrained} { + fn from(value: $inner) -> Self { + Self::Unconstrained(value.into()) + } + } + + impl #{From}<$name> for $inner { + fn from(value: $name) -> Self { + value.into_inner().into() + } + } + """, + "MaybeConstrained" to symbol.makeMaybeConstrained(), + "From" to RuntimeType.From, + ) + } + + fun renderTryFrom(symbol: Symbol, blobType: RustType) { + val name = symbol.name + val inner = blobType.render() + writer.rustTemplate( + """ + impl #{TryFrom}<$inner> for $name { + type Error = #{ConstraintViolation}; + + fn try_from(value: $inner) -> Result { + value.try_into() + } + } + """, + "TryFrom" to RuntimeType.TryFrom, + "ConstraintViolation" to constraintViolation, + "TryFromChecks" to constraintsInfo.map { it.tryFromCheck }.join("\n"), + ) + } +} diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt index cc4804bb21..deec6f6b29 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.server.python.smithy.generators import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.DocumentationTrait +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate @@ -203,14 +204,13 @@ class PythonApplicationGenerator( *codegenScope, ) for (operation in operations) { - val operationName = symbolProvider.toSymbol(operation).name - val name = operationName.toSnakeCase() + val fnName = RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(operation).name.toSnakeCase()) rustTemplate( """ - let ${name}_locals = #{pyo3_asyncio}::TaskLocals::new(event_loop); - let handler = self.handlers.get("$name").expect("Python handler for operation `$name` not found").clone(); - let builder = builder.$name(move |input, state| { - #{pyo3_asyncio}::tokio::scope(${name}_locals.clone(), crate::python_operation_adaptor::$name(input, state, handler.clone())) + let ${fnName}_locals = #{pyo3_asyncio}::TaskLocals::new(event_loop); + let handler = self.handlers.get("$fnName").expect("Python handler for operation `$fnName` not found").clone(); + let builder = builder.$fnName(move |input, state| { + #{pyo3_asyncio}::tokio::scope(${fnName}_locals.clone(), crate::python_operation_adaptor::$fnName(input, state, handler.clone())) }); """, *codegenScope, @@ -342,7 +342,7 @@ class PythonApplicationGenerator( ) operations.map { operation -> val operationName = symbolProvider.toSymbol(operation).name - val name = operationName.toSnakeCase() + val fnName = RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(operation).name.toSnakeCase()) val input = PythonType.Opaque("${operationName}Input", "crate::input") val output = PythonType.Opaque("${operationName}Output", "crate::output") @@ -363,15 +363,15 @@ class PythonApplicationGenerator( rustTemplate( """ - /// Method to register `$name` Python implementation inside the handlers map. + /// Method to register `$fnName` Python implementation inside the handlers map. /// It can be used as a function decorator in Python. /// /// :param func ${handler.renderAsDocstring()}: /// :rtype ${PythonType.None.renderAsDocstring()}: ##[pyo3(text_signature = "(${'$'}self, func)")] - pub fn $name(&mut self, py: #{pyo3}::Python, func: #{pyo3}::PyObject) -> #{pyo3}::PyResult<()> { + pub fn $fnName(&mut self, py: #{pyo3}::Python, func: #{pyo3}::PyObject) -> #{pyo3}::PyResult<()> { use #{SmithyPython}::PyApp; - self.register_operation(py, "$name", func) + self.register_operation(py, "$fnName", func) } """, *codegenScope, diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt index 8a622c51ef..5baf6e83ea 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerRustModule @@ -71,13 +72,13 @@ class PythonServerModuleGenerator( when (shape) { is UnionShape -> rustTemplate( """ - $moduleType.add_class::()?; + $moduleType.add_class::()?; """, *codegenScope, ) else -> rustTemplate( """ - $moduleType.add_class::()?; + $moduleType.add_class::()?; """, *codegenScope, ) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt index f107806098..e8a704ecdd 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt @@ -6,11 +6,13 @@ package software.amazon.smithy.rust.codegen.server.python.smithy.generators import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.CodegenContext +import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency @@ -50,10 +52,11 @@ class PythonServerOperationHandlerGenerator( private fun renderPythonOperationHandlerImpl(writer: RustWriter) { val operationName = symbolProvider.toSymbol(operation).name - val input = "crate::input::${operationName}Input" - val output = "crate::output::${operationName}Output" + val input = "crate::input::${operationName.toPascalCase()}Input" + val output = "crate::output::${operationName.toPascalCase()}Output" + // TODO(https://github.com/awslabs/smithy-rs/issues/2552) - Use to pascalCase for error shapes. val error = "crate::error::${operationName}Error" - val fnName = operationName.toSnakeCase() + val fnName = RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(operation).name.toSnakeCase()) writer.rustTemplate( """ diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt index 28b48d31e3..1502f5422a 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt @@ -60,6 +60,9 @@ class PythonServerStructureGenerator( writer.rustTemplate("#{ConstructorSignature:W}", "ConstructorSignature" to renderConstructorSignature()) super.renderStructure() renderPyO3Methods() + if (!shape.hasTrait()) { + renderPyBoxTraits() + } } override fun renderStructureMember( @@ -101,6 +104,25 @@ class PythonServerStructureGenerator( ) } + private fun renderPyBoxTraits() { + writer.rustTemplate( + """ + impl<'source> #{pyo3}::FromPyObject<'source> for std::boxed::Box<$name> { + fn extract(ob: &'source #{pyo3}::PyAny) -> #{pyo3}::PyResult { + ob.extract::<$name>().map(Box::new) + } + } + + impl #{pyo3}::IntoPy<#{pyo3}::PyObject> for std::boxed::Box<$name> { + fn into_py(self, py: #{pyo3}::Python<'_>) -> #{pyo3}::PyObject { + (*self).into_py(py) + } + } + """, + "pyo3" to pyO3, + ) + } + private fun renderStructSignatureMembers(): Writable = writable { forEachMember(members) { _, memberName, memberSymbol -> diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt index df083751ae..6b7a7bee8a 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt @@ -121,7 +121,7 @@ class PythonServerUnionGenerator( ) writer.rust("/// :rtype ${unionSymbol.name}:") writer.rustBlock("pub fn $funcNamePart() -> Self") { - rust("Self(${unionSymbol.name}::$variantName") + rust("Self(${unionSymbol.name}::$variantName)") } } else { val memberSymbol = symbolProvider.toSymbol(member) @@ -157,7 +157,7 @@ class PythonServerUnionGenerator( writer.rustBlockTemplate("pub fn as_$funcNamePart(&self) -> #{pyo3}::PyResult<()>", "pyo3" to pyo3) { rustTemplate( """ - self.0.as_$funcNamePart().map_err(#{pyo3}::exceptions::PyValueError::new_err( + self.0.as_$funcNamePart().map_err(|_| #{pyo3}::exceptions::PyValueError::new_err( "${unionSymbol.name} variant is not None" )) """, diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/protocols/PythonServerProtocolLoader.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/protocols/PythonServerProtocolLoader.kt new file mode 100644 index 0000000000..f84d35e7cb --- /dev/null +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/protocols/PythonServerProtocolLoader.kt @@ -0,0 +1,125 @@ + +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.python.smithy.protocols + +import software.amazon.smithy.aws.traits.protocols.AwsJson1_0Trait +import software.amazon.smithy.aws.traits.protocols.AwsJson1_1Trait +import software.amazon.smithy.aws.traits.protocols.RestJson1Trait +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingSection +import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolLoader +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserCustomization +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserSection +import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerRuntimeType +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator +import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerAwsJsonFactory +import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerHttpBoundProtocolCustomization +import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerHttpBoundProtocolSection +import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerRestJsonFactory + +/** + * Customization class used to force casting a non primitive type into one overriden by a new symbol provider, + * by explicitly calling `from()` on it. + * + * For example we use this in the server Python implementation, where we override types like [Blob], [DateTime] and [Document] + * with wrappers compatible with Python, without touching the original implementation coming from `aws-smithy-types`. + */ +class PythonServerAfterDeserializedMemberJsonParserCustomization(private val runtimeConfig: RuntimeConfig) : + JsonParserCustomization() { + override fun section(section: JsonParserSection): Writable = when (section) { + is JsonParserSection.AfterTimestampDeserializedMember -> writable { + rust(".map(#T::from)", PythonServerRuntimeType.dateTime(runtimeConfig).toSymbol()) + } + is JsonParserSection.AfterBlobDeserializedMember -> writable { + rust(".map(#T::from)", PythonServerRuntimeType.blob(runtimeConfig).toSymbol()) + } + is JsonParserSection.AfterDocumentDeserializedMember -> writable { + rust(".map(#T::from)", PythonServerRuntimeType.document(runtimeConfig).toSymbol()) + } + else -> emptySection + } +} + +/** + * Customization class used to force casting a non primitive type into one overriden by a new symbol provider, + * by explicitly calling `into()` on it. + */ +class PythonServerAfterDeserializedMemberServerHttpBoundCustomization() : + ServerHttpBoundProtocolCustomization() { + override fun section(section: ServerHttpBoundProtocolSection): Writable = when (section) { + is ServerHttpBoundProtocolSection.AfterTimestampDeserializedMember -> writable { + rust(".into()") + } + else -> emptySection + } +} + +/** + * Customization class used to force casting a `Vec` into one a Python `Vec` + */ +class PythonServerAfterDeserializedMemberHttpBindingCustomization(private val runtimeConfig: RuntimeConfig) : + HttpBindingCustomization() { + override fun section(section: HttpBindingSection): Writable = when (section) { + is HttpBindingSection.AfterDeserializingIntoADateTimeOfHttpHeaders -> writable { + rust(".into_iter().map(#T::from).collect()", PythonServerRuntimeType.dateTime(runtimeConfig).toSymbol()) + } + else -> emptySection + } +} + +class PythonServerProtocolLoader( + private val supportedProtocols: ProtocolMap, +) : ProtocolLoader(supportedProtocols) { + + companion object { + fun defaultProtocols(runtimeConfig: RuntimeConfig) = + mapOf( + RestJson1Trait.ID to ServerRestJsonFactory( + additionalParserCustomizations = listOf( + PythonServerAfterDeserializedMemberJsonParserCustomization(runtimeConfig), + ), + additionalServerHttpBoundProtocolCustomizations = listOf( + PythonServerAfterDeserializedMemberServerHttpBoundCustomization(), + ), + additionalHttpBindingCustomizations = listOf( + PythonServerAfterDeserializedMemberHttpBindingCustomization(runtimeConfig), + ), + ), + AwsJson1_0Trait.ID to ServerAwsJsonFactory( + AwsJsonVersion.Json10, + additionalParserCustomizations = listOf( + PythonServerAfterDeserializedMemberJsonParserCustomization(runtimeConfig), + ), + additionalServerHttpBoundProtocolCustomizations = listOf( + PythonServerAfterDeserializedMemberServerHttpBoundCustomization(), + ), + additionalHttpBindingCustomizations = listOf( + PythonServerAfterDeserializedMemberHttpBindingCustomization(runtimeConfig), + ), + ), + AwsJson1_1Trait.ID to ServerAwsJsonFactory( + AwsJsonVersion.Json11, + additionalParserCustomizations = listOf( + PythonServerAfterDeserializedMemberJsonParserCustomization(runtimeConfig), + ), + additionalServerHttpBoundProtocolCustomizations = listOf( + PythonServerAfterDeserializedMemberServerHttpBoundCustomization(), + ), + additionalHttpBindingCustomizations = listOf( + PythonServerAfterDeserializedMemberHttpBindingCustomization(runtimeConfig), + ), + ), + ) + } +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt index 41201b8695..e1e6c747f7 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerRequestBindingGenerator.kt @@ -28,6 +28,7 @@ class ServerRequestBindingGenerator( protocol: Protocol, codegenContext: ServerCodegenContext, operationShape: OperationShape, + additionalHttpBindingCustomizations: List = listOf(), ) { private val httpBindingGenerator = HttpBindingGenerator( @@ -39,7 +40,7 @@ class ServerRequestBindingGenerator( ServerRequestAfterDeserializingIntoAHashMapOfHttpPrefixHeadersWrapInUnconstrainedMapHttpBindingCustomization( codegenContext, ), - ), + ) + additionalHttpBindingCustomizations, ) fun generateDeserializeHeaderFn(binding: HttpBindingDescriptor): RuntimeType = @@ -81,5 +82,6 @@ class ServerRequestAfterDeserializingIntoAHashMapOfHttpPrefixHeadersWrapInUncons ) } } + else -> emptySection } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt index cc47830054..01448d27a4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/http/ServerResponseBindingGenerator.kt @@ -71,6 +71,7 @@ class ServerResponseBeforeIteratingOverMapBoundWithHttpPrefixHeadersUnwrapConstr is HttpBindingSection.BeforeRenderingHeaderValue, is HttpBindingSection.AfterDeserializingIntoAHashMapOfHttpPrefixHeaders, + is HttpBindingSection.AfterDeserializingIntoADateTimeOfHttpHeaders, -> emptySection } } @@ -100,6 +101,7 @@ class ServerResponseBeforeRenderingHeadersHttpBindingCustomization(val codegenCo is HttpBindingSection.BeforeIteratingOverMapShapeBoundWithHttpPrefixHeaders, is HttpBindingSection.AfterDeserializingIntoAHashMapOfHttpPrefixHeaders, + is HttpBindingSection.AfterDeserializingIntoADateTimeOfHttpHeaders, -> emptySection } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index 0b466f95c2..3ee16c233f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -106,6 +106,7 @@ fun jsonParserGenerator( codegenContext: ServerCodegenContext, httpBindingResolver: HttpBindingResolver, jsonName: (MemberShape) -> String, + additionalParserCustomizations: List = listOf(), ): JsonParserGenerator = JsonParserGenerator( codegenContext, @@ -114,12 +115,13 @@ fun jsonParserGenerator( returnSymbolToParseFn(codegenContext), listOf( ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonParserCustomization(codegenContext), - ), + ) + additionalParserCustomizations, ) class ServerAwsJsonProtocol( private val serverCodegenContext: ServerCodegenContext, awsJsonVersion: AwsJsonVersion, + private val additionalParserCustomizations: List = listOf(), ) : AwsJson(serverCodegenContext, awsJsonVersion), ServerProtocol { private val runtimeConfig = codegenContext.runtimeConfig @@ -130,7 +132,7 @@ class ServerAwsJsonProtocol( } override fun structuredDataParser(): StructuredDataParserGenerator = - jsonParserGenerator(serverCodegenContext, httpBindingResolver, ::awsJsonFieldName) + jsonParserGenerator(serverCodegenContext, httpBindingResolver, ::awsJsonFieldName, additionalParserCustomizations) override fun structuredDataSerializer(): StructuredDataSerializerGenerator = ServerAwsJsonSerializerGenerator(serverCodegenContext, httpBindingResolver, awsJsonVersion) @@ -183,13 +185,14 @@ private fun restRouterType(runtimeConfig: RuntimeConfig) = class ServerRestJsonProtocol( private val serverCodegenContext: ServerCodegenContext, + private val additionalParserCustomizations: List = listOf(), ) : RestJson(serverCodegenContext), ServerProtocol { val runtimeConfig = codegenContext.runtimeConfig override val protocolModulePath: String = "rest_json_1" override fun structuredDataParser(): StructuredDataParserGenerator = - jsonParserGenerator(serverCodegenContext, httpBindingResolver, ::restJsonFieldName) + jsonParserGenerator(serverCodegenContext, httpBindingResolver, ::restJsonFieldName, additionalParserCustomizations) override fun structuredDataSerializer(): StructuredDataSerializerGenerator = ServerRestJsonSerializerGenerator(serverCodegenContext, httpBindingResolver) @@ -254,5 +257,6 @@ class ServerRequestBeforeBoxingDeserializedMemberConvertToMaybeConstrainedJsonPa rust(".map(|x| x.into())") } } + else -> emptySection } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index 2a3467cd6b..920813e34d 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -10,11 +10,13 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.escape import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJsonVersion import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory import software.amazon.smithy.rust.codegen.core.smithy.protocols.awsJsonFieldName +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerSection @@ -30,13 +32,22 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.Ser * AwsJson 1.0 and 1.1 server-side protocol factory. This factory creates the [ServerHttpBoundProtocolGenerator] * with AwsJson specific configurations. */ -class ServerAwsJsonFactory(private val version: AwsJsonVersion) : - ProtocolGeneratorFactory { +class ServerAwsJsonFactory( + private val version: AwsJsonVersion, + private val additionalParserCustomizations: List = listOf(), + private val additionalServerHttpBoundProtocolCustomizations: List = listOf(), + private val additionalHttpBindingCustomizations: List = listOf(), +) : ProtocolGeneratorFactory { override fun protocol(codegenContext: ServerCodegenContext): ServerProtocol = - ServerAwsJsonProtocol(codegenContext, version) + ServerAwsJsonProtocol(codegenContext, version, additionalParserCustomizations) override fun buildProtocolGenerator(codegenContext: ServerCodegenContext): ServerHttpBoundProtocolGenerator = - ServerHttpBoundProtocolGenerator(codegenContext, protocol(codegenContext)) + ServerHttpBoundProtocolGenerator( + codegenContext, + protocol(codegenContext), + additionalServerHttpBoundProtocolCustomizations, + additionalHttpBindingCustomizations, + ) override fun support(): ProtocolSupport { return ProtocolSupport( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 9a7e45f112..889967412e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.model.pattern.UriPattern import software.amazon.smithy.model.shapes.BooleanShape import software.amazon.smithy.model.shapes.CollectionShape import software.amazon.smithy.model.shapes.MapShape +import software.amazon.smithy.model.shapes.MemberShape import software.amazon.smithy.model.shapes.NumberShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.Shape @@ -42,8 +43,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget 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.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.generators.TypeConversionGenerator +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.isOptional @@ -77,6 +80,18 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.Ser import software.amazon.smithy.rust.codegen.server.smithy.generators.serverBuilderSymbol import java.util.logging.Logger +/** + * Class describing a ServerHttpBoundProtocol section that can be used in a customization. + */ +sealed class ServerHttpBoundProtocolSection(name: String) : Section(name) { + data class AfterTimestampDeserializedMember(val shape: MemberShape) : ServerHttpBoundProtocolSection("AfterTimestampDeserializedMember") +} + +/** + * Customization for the ServerHttpBoundProtocol generator. + */ +typealias ServerHttpBoundProtocolCustomization = NamedCustomization + /** * Implement operations' input parsing and output serialization. Protocols can plug their own implementations * and overrides by creating a protocol factory inheriting from this class and feeding it to the [ServerProtocolLoader]. @@ -85,10 +100,12 @@ import java.util.logging.Logger class ServerHttpBoundProtocolGenerator( codegenContext: ServerCodegenContext, protocol: ServerProtocol, + customizations: List = listOf(), + additionalHttpBindingCustomizations: List = listOf(), ) : ServerProtocolGenerator( codegenContext, protocol, - ServerHttpBoundProtocolTraitImplGenerator(codegenContext, protocol), + ServerHttpBoundProtocolTraitImplGenerator(codegenContext, protocol, customizations, additionalHttpBindingCustomizations), ) { // Define suffixes for operation input / output / error wrappers companion object { @@ -104,6 +121,8 @@ class ServerHttpBoundProtocolGenerator( class ServerHttpBoundProtocolTraitImplGenerator( private val codegenContext: ServerCodegenContext, private val protocol: ServerProtocol, + private val customizations: List, + private val additionalHttpBindingCustomizations: List, ) { private val logger = Logger.getLogger(javaClass.name) private val symbolProvider = codegenContext.symbolProvider @@ -111,7 +130,6 @@ class ServerHttpBoundProtocolTraitImplGenerator( private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val httpBindingResolver = protocol.httpBindingResolver - private val typeConversionGenerator = TypeConversionGenerator(model, symbolProvider, runtimeConfig) private val protocolFunctions = ProtocolFunctions(codegenContext) private val codegenScope = arrayOf( @@ -568,9 +586,9 @@ class ServerHttpBoundProtocolTraitImplGenerator( private fun serverRenderHttpResponseCode(defaultCode: Int) = writable { check(defaultCode in 100..999) { """ - Smithy library lied to us. According to https://smithy.io/2.0/spec/http-bindings.html#http-trait, - "The provided value SHOULD be between 100 and 599, and it MUST be between 100 and 999". - """.replace("\n", "").trimIndent() + Smithy library lied to us. According to https://smithy.io/2.0/spec/http-bindings.html#http-trait, + "The provided value SHOULD be between 100 and 599, and it MUST be between 100 and 999". + """.replace("\n", "").trimIndent() } rustTemplate( """ @@ -611,7 +629,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( inputShape: StructureShape, bindings: List, ) { - val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape) + val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape, additionalHttpBindingCustomizations) val structuredDataParser = protocol.structuredDataParser() Attribute.AllowUnusedMut.render(this) rust( @@ -952,12 +970,15 @@ class ServerHttpBoundProtocolTraitImplGenerator( val timestampFormatType = RuntimeType.parseTimestampFormat(CodegenTarget.SERVER, runtimeConfig, timestampFormat) rustTemplate( """ - let v = #{DateTime}::from_str(&v, #{format})?#{ConvertInto:W}; + let v = #{DateTime}::from_str(&v, #{format})? """.trimIndent(), *codegenScope, "format" to timestampFormatType, - "ConvertInto" to typeConversionGenerator.convertViaInto(memberShape), ) + for (customization in customizations) { + customization.section(ServerHttpBoundProtocolSection.AfterTimestampDeserializedMember(it.member))(this) + } + rust(";") } else -> { // Number or boolean. rust( @@ -1047,7 +1068,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( } private fun serverRenderHeaderParser(writer: RustWriter, binding: HttpBindingDescriptor, operationShape: OperationShape) { - val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape) + val httpBindingGenerator = ServerRequestBindingGenerator(protocol, codegenContext, operationShape, additionalHttpBindingCustomizations) val deserializer = httpBindingGenerator.generateDeserializeHeaderFn(binding) writer.rustTemplate( """ @@ -1109,22 +1130,24 @@ class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ let value = #{PercentEncoding}::percent_decode_str(value).decode_utf8()?; - let value = #{DateTime}::from_str(value.as_ref(), #{format})?#{ConvertInto:W}; + let value = #{DateTime}::from_str(value.as_ref(), #{format})? """, *codegenScope, "format" to timestampFormatType, - "ConvertInto" to typeConversionGenerator.convertViaInto(target), ) } else { rustTemplate( """ - let value = #{DateTime}::from_str(value, #{format})?#{ConvertInto:W}; + let value = #{DateTime}::from_str(value, #{format})? """, *codegenScope, "format" to timestampFormatType, - "ConvertInto" to typeConversionGenerator.convertViaInto(target), ) } + for (customization in customizations) { + customization.section(ServerHttpBoundProtocolSection.AfterTimestampDeserializedMember(binding.member))(this) + } + rust(";") } else -> { check(target is NumberShape || target is BooleanShape) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt index 744ef93bc5..ddf1ca08c3 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerRestJson.kt @@ -5,10 +5,12 @@ package software.amazon.smithy.rust.codegen.server.smithy.protocols +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingResolver import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory +import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.JsonParserCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.restJsonFieldName import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.JsonSerializerGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.StructuredDataSerializerGenerator @@ -21,11 +23,23 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.Ser * RestJson1 server-side protocol factory. This factory creates the [ServerHttpProtocolGenerator] * with RestJson1 specific configurations. */ -class ServerRestJsonFactory : ProtocolGeneratorFactory { - override fun protocol(codegenContext: ServerCodegenContext): Protocol = ServerRestJsonProtocol(codegenContext) +class ServerRestJsonFactory( + private val additionalParserCustomizations: List = listOf(), + private val additionalServerHttpBoundProtocolCustomizations: List = listOf(), + private val additionalHttpBindingCustomizations: List = listOf(), +) : ProtocolGeneratorFactory { + override fun protocol(codegenContext: ServerCodegenContext): Protocol = ServerRestJsonProtocol(codegenContext, additionalParserCustomizations) override fun buildProtocolGenerator(codegenContext: ServerCodegenContext): ServerHttpBoundProtocolGenerator = - ServerHttpBoundProtocolGenerator(codegenContext, ServerRestJsonProtocol(codegenContext)) + ServerHttpBoundProtocolGenerator( + codegenContext, + ServerRestJsonProtocol( + codegenContext, + additionalParserCustomizations, + ), + additionalServerHttpBoundProtocolCustomizations, + additionalHttpBindingCustomizations, + ) override fun support(): ProtocolSupport { return ProtocolSupport( diff --git a/tools/ci-scripts/codegen-diff/diff_lib.py b/tools/ci-scripts/codegen-diff/diff_lib.py index 60fa3d6edf..09f7edbf86 100644 --- a/tools/ci-scripts/codegen-diff/diff_lib.py +++ b/tools/ci-scripts/codegen-diff/diff_lib.py @@ -2,9 +2,9 @@ # SPDX-License-Identifier: Apache-2.0 import os -import sys -import subprocess import shlex +import subprocess +import sys HEAD_BRANCH_NAME = "__tmp-localonly-head" BASE_BRANCH_NAME = "__tmp-localonly-base" @@ -15,10 +15,10 @@ CDN_URL = "https://d2luzm2xt3nokh.cloudfront.net" -PYTHON_EXAMPLES_PATH = "rust-runtime/aws-smithy-http-server-python/examples" - target_codegen_client = 'codegen-client-test' target_codegen_server = 'codegen-server-test' +target_codegen_server_python = 'codegen-server-test:python' +target_codegen_server_typescript = 'codegen-server-test:typescript' target_aws_sdk = 'aws:sdk' @@ -38,19 +38,19 @@ def checkout_commit_and_generate(revision_sha, branch_name, targets=None): def generate_and_commit_generated_code(revision_sha, targets=None): - targets = targets or [target_codegen_client, target_codegen_server, target_aws_sdk] + targets = targets or [ + target_codegen_client, + target_codegen_server, + target_aws_sdk, + target_codegen_server_python, + target_codegen_server_typescript + ] # Clean the build artifacts before continuing + assemble_tasks = ' '.join([f'{t}:assemble' for t in targets]) + clean_tasks = ' '.join([f'{t}:clean' for t in targets]) get_cmd_output("rm -rf aws/sdk/build") - if target_codegen_server in targets: - get_cmd_output("make distclean", shell=True, cwd=PYTHON_EXAMPLES_PATH) - get_cmd_output("./gradlew codegen-core:clean codegen-client:clean codegen-server:clean aws:sdk-codegen:clean") - - # Generate code - tasks = ' '.join([f'{t}:assemble' for t in targets]) - get_cmd_output(f"./gradlew --rerun-tasks {tasks}") - if target_codegen_server in targets: - get_cmd_output("make build", shell=True, check=False, cwd=PYTHON_EXAMPLES_PATH) - get_cmd_output(f"./gradlew --rerun-tasks codegen-server-test:typescript:assemble") + get_cmd_output(f"./gradlew --rerun-tasks {clean_tasks}") + get_cmd_output(f"./gradlew --rerun-tasks {assemble_tasks}") # Move generated code into codegen-diff/ directory get_cmd_output(f"rm -rf {OUTPUT_PATH}") @@ -61,12 +61,8 @@ def generate_and_commit_generated_code(revision_sha, targets=None): if target in targets: get_cmd_output(f"mv {target}/build/smithyprojections/{target} {OUTPUT_PATH}/") if target == target_codegen_server: - get_cmd_output( - f"mv {PYTHON_EXAMPLES_PATH}/pokemon-service-server-sdk/ {OUTPUT_PATH}/codegen-server-test-python/", - check=False) - get_cmd_output( - f"mv codegen-server-test/typescript/build/smithyprojections/codegen-server-test-typescript {OUTPUT_PATH}/", - check=False) + get_cmd_output(f"mv {target}/python/build/smithyprojections/{target}-python {OUTPUT_PATH}/") + get_cmd_output(f"mv {target}/typescript/build/smithyprojections/{target}-typescript {OUTPUT_PATH}/") # Clean up the SDK directory get_cmd_output(f"rm -f {OUTPUT_PATH}/aws-sdk/versions.toml") @@ -79,6 +75,7 @@ def generate_and_commit_generated_code(revision_sha, targets=None): # Clean up the server-test folder get_cmd_output(f"rm -rf {OUTPUT_PATH}/codegen-server-test/source") + get_cmd_output(f"rm -rf {OUTPUT_PATH}/codegen-server-test-python/source") get_cmd_output(f"rm -rf {OUTPUT_PATH}/codegen-server-test-typescript/source") run(f"find {OUTPUT_PATH}/codegen-server-test | " f"grep -E 'smithy-build-info.json|sources/manifest|model.json' | " From 35f2f27a8380a1310c264a386e162cd9f2180137 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Thu, 13 Apr 2023 17:58:43 -0500 Subject: [PATCH 022/253] Re-export `aws_smithy_types::date_time::Format` in service client crates (#2534) * Add more client crate re-exports This commit adds two more client re-exports that were not included in smithy-rs#2437: - `aws_smithy_http::byte_stream::Length` - `aws_smithy_types::date_time::Format` * Remove re-export of `aws_smithy_http::byte_stream::Length` This commit removes re-export of `aws_smithy_http::byte_stream::Length` for now as it is a feature-gated type, which requires further consideration. * CHANGELOG.next.toml --------- Co-authored-by: Yuki Saito --- CHANGELOG.next.toml | 12 ++++++++++++ .../smithy/rust/codegen/core/smithy/RuntimeType.kt | 1 + .../smithy/customizations/SmithyTypesPubUseExtra.kt | 1 + 3 files changed, 14 insertions(+) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 1832de5613..e07d004d2a 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -92,3 +92,15 @@ message = "Update the `std::fmt::Debug` implementation for `aws-sigv4::SigningPa references = ["smithy-rs#2562"] meta = { "breaking" = false, "tada" = true, "bug" = true } author = "Velfi" + +[[aws-sdk-rust]] +message = "`aws_smithy_types::date_time::Format` has been re-exported in service client crates." +references = ["smithy-rs#2534"] +meta = { "breaking" = false, "tada" = false, "bug" = false } +author = "ysaito1001" + +[[smithy-rs]] +message = "`aws_smithy_types::date_time::Format` has been re-exported in service client crates." +references = ["smithy-rs#2534"] +meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } +author = "ysaito1001" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index b74fbf2b9a..7e53055195 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -278,6 +278,7 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun classifyRetry(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("retry::ClassifyRetry") fun dateTime(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("DateTime") fun document(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("Document") + fun format(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("date_time::Format") fun retryErrorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ErrorKind") fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::Receiver") fun eventStreamSender(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::EventStreamSender") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt index ec49a97613..301ee11da8 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtra.kt @@ -58,6 +58,7 @@ private fun pubUseTypesThatShouldBeExported(codegenContext: CodegenContext, mode listOf( PubUseType(RuntimeType.blob(runtimeConfig), ::hasBlobs), PubUseType(RuntimeType.dateTime(runtimeConfig), ::hasDateTimes), + PubUseType(RuntimeType.format(runtimeConfig), ::hasDateTimes, "DateTimeFormat"), ) + RuntimeType.smithyHttp(runtimeConfig).let { http -> listOf( PubUseType(http.resolve("byte_stream::ByteStream"), ::hasStreamingOperations), From fc63800f6af21a9e6e44608e2ac705d8134da2e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20B=C3=BCsch?= Date: Mon, 17 Apr 2023 23:51:01 +1000 Subject: [PATCH 023/253] Implement `StdError::source()` for Error enum (#2564) ## Motivation and Context This is an attempt at fixing https://github.com/awslabs/aws-sdk-rust/issues/784. The service-level `Error` enum implements `std::error::Error` but does not implement its `source()` method. This means that an error library like `anyhow` or `eyre` won't be able to display the root cause of an error, which is especially problematic for the `Unhandled` variant. ## Description I modified `ServiceErrorGenerator` in the `codegen-client` crate and replaced the line that output `impl std::error::Error for Error {}` with an impl block that implements the `source()` method by delegating to the inner error structure. ## Testing I've added a simple unit test to `ServiceErrorGeneratorTest`. ## Checklist - [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._ --- CHANGELOG.next.toml | 12 +++ .../generators/error/ServiceErrorGenerator.kt | 11 ++- .../error/ServiceErrorGeneratorTest.kt | 74 ++++++++++++------- 3 files changed, 71 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index e07d004d2a..eb431fbb7a 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -11,6 +11,18 @@ # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} # author = "rcoh" +[[aws-sdk-rust]] +message = "Implement std::error::Error#source() properly for the service meta Error enum" +references = ["aws-sdk-rust#784"] +meta = { "breaking" = false, "tada" = false, "bug" = false } +author = "abusch" + +[[smithy-rs]] +message = "Implement std::error::Error#source() properly for the service meta Error enum" +references = ["aws-sdk-rust#784"] +meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client"} +author = "abusch" + [[aws-sdk-rust]] message = "The outputs for event stream operations (for example, S3's SelectObjectContent) now implement the `Sync` auto-trait." references = ["smithy-rs#2496"] diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt index f461d59e05..c11fbfecf8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGenerator.kt @@ -80,7 +80,16 @@ class ServiceErrorGenerator( errors.map { it.id }, ) } - rust("impl #T for Error {}", RuntimeType.StdError) + rustBlock("impl #T for Error", RuntimeType.StdError) { + rustBlock("fn source(&self) -> std::option::Option<&(dyn #T + 'static)>", RuntimeType.StdError) { + rustBlock("match self") { + allErrors.forEach { + rust("Error::${symbolProvider.toSymbol(it).name}(inner) => inner.source(),") + } + rust("Error::Unhandled(inner) => inner.source()") + } + } + } writeCustomizations(customizations, ErrorSection.ServiceErrorAdditionalTraitImpls(allErrors)) } crate.lib { rust("pub use error_meta::Error;") } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt index 1cbb274cfb..0f26fc7b42 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/ServiceErrorGeneratorTest.kt @@ -6,45 +6,48 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.error import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest +import software.amazon.smithy.rust.codegen.core.util.lookup internal class ServiceErrorGeneratorTest { - @Test - fun `top level errors are send + sync`() { - val model = """ - namespace com.example + private val model = """ + namespace com.example - use aws.protocols#restJson1 + use aws.protocols#restJson1 - @restJson1 - service HelloService { - operations: [SayHello], - version: "1" - } + @restJson1 + service HelloService { + operations: [SayHello], + version: "1" + } - @http(uri: "/", method: "POST") - operation SayHello { - input: EmptyStruct, - output: EmptyStruct, - errors: [SorryBusy, CanYouRepeatThat, MeDeprecated] - } + @http(uri: "/", method: "POST") + operation SayHello { + input: EmptyStruct, + output: EmptyStruct, + errors: [SorryBusy, CanYouRepeatThat, MeDeprecated] + } - structure EmptyStruct { } + structure EmptyStruct { } - @error("server") - structure SorryBusy { } + @error("server") + structure SorryBusy { } - @error("client") - structure CanYouRepeatThat { } + @error("client") + structure CanYouRepeatThat { } - @error("client") - @deprecated - structure MeDeprecated { } - """.asSmithyModel() + @error("client") + @deprecated + structure MeDeprecated { } + """.asSmithyModel() + @Test + fun `top level errors are send + sync`() { clientIntegrationTest(model) { codegenContext, rustCrate -> rustCrate.integrationTest("validate_errors") { rust( @@ -60,4 +63,25 @@ internal class ServiceErrorGeneratorTest { } } } + + @Test + fun `generates combined error enums`() { + clientIntegrationTest(model) { _, rustCrate -> + rustCrate.moduleFor(model.lookup("com.example#CanYouRepeatThat")) { + unitTest( + name = "generates_combined_error_enums", + test = """ + use std::error::Error as StdError; + use crate::Error; + use crate::operation::say_hello::SayHelloError; + + // Unhandled variants properly delegate source. + let error = Error::from(SayHelloError::unhandled("some other error")); + let source = error.source().expect("source should not be None"); + assert_eq!(format!("{}", source), "some other error"); + """, + ) + } + } + } } From a2d37ad261ba6c72d50fe67ee9c7e11c9adbe948 Mon Sep 17 00:00:00 2001 From: Matteo Bigoi <1781140+crisidev@users.noreply.github.com> Date: Mon, 17 Apr 2023 17:19:12 +0100 Subject: [PATCH 024/253] [Python] Automatically generate stubs (#2576) ## Motivation and Context We want to automatically generate stubs in the codegen diff to ensure they can be reviewed and have a simple way to generate and include the stubs inside the Maturin wheel. ## Description The Python example has been moved to the `examples` folder and refactored. The refactoring ensures the script `stubgen.py` is included in the codegeneration of the SDK crate. The script is later used to generate stubs automatically during testing and can be used by customers to add their own stubs before the Maturin build ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Signed-off-by: Bigo <1781140+crisidev@users.noreply.github.com> Co-authored-by: Burak --- .../rust/codegen/core/rustlang/RustWriter.kt | 1 + .../rust/codegen/core/smithy/RuntimeType.kt | 2 + codegen-server-test/python/build.gradle.kts | 17 +++- .../smithy/PythonServerCargoDependency.kt | 4 +- .../PythonServerCodegenDecorator.kt | 28 ++++++ .../generators/PythonServerModuleGenerator.kt | 40 ++++++-- .../RustCrateInlineModuleComposingWriter.kt | 4 +- .../examples => examples/python}/.gitignore | 0 .../examples => examples/python}/Cargo.toml | 0 .../examples => examples/python}/Makefile | 14 ++- examples/python/README.md | 94 +++++++++++++++++++ .../examples => examples/python}/mypy.ini | 0 .../python}/pokemon-service-test/Cargo.toml | 14 +-- .../pokemon-service-test/tests/helpers.rs | 0 .../tests/simple_integration_test.rs | 0 .../tests/testdata/localhost.crt | 0 .../tests/testdata/localhost.key | 0 .../python}/pokemon_service.py | 0 .../aws-smithy-http-server-python/Cargo.toml | 14 +-- .../examples/README.md | 52 ---------- .../aws-smithy-http-server-python/src/tls.rs | 6 +- .../src/tls/listener.rs | 2 +- .../{examples => }/stubgen.py | 75 +++++++-------- .../aws-smithy-http-server-python/stubgen.sh | 48 ++++++++++ .../{examples => }/stubgen_test.py | 0 tools/ci-scripts/check-server-python-e2e-test | 2 +- tools/ci-scripts/codegen-diff/diff_lib.py | 13 +-- 27 files changed, 291 insertions(+), 139 deletions(-) rename {rust-runtime/aws-smithy-http-server-python/examples => examples/python}/.gitignore (100%) rename {rust-runtime/aws-smithy-http-server-python/examples => examples/python}/Cargo.toml (100%) rename {rust-runtime/aws-smithy-http-server-python/examples => examples/python}/Makefile (90%) create mode 100644 examples/python/README.md rename {rust-runtime/aws-smithy-http-server-python/examples => examples/python}/mypy.ini (100%) rename {rust-runtime/aws-smithy-http-server-python/examples => examples/python}/pokemon-service-test/Cargo.toml (53%) rename {rust-runtime/aws-smithy-http-server-python/examples => examples/python}/pokemon-service-test/tests/helpers.rs (100%) rename {rust-runtime/aws-smithy-http-server-python/examples => examples/python}/pokemon-service-test/tests/simple_integration_test.rs (100%) rename {rust-runtime/aws-smithy-http-server-python/examples => examples/python}/pokemon-service-test/tests/testdata/localhost.crt (100%) rename {rust-runtime/aws-smithy-http-server-python/examples => examples/python}/pokemon-service-test/tests/testdata/localhost.key (100%) rename {rust-runtime/aws-smithy-http-server-python/examples => examples/python}/pokemon_service.py (100%) delete mode 100644 rust-runtime/aws-smithy-http-server-python/examples/README.md rename rust-runtime/aws-smithy-http-server-python/{examples => }/stubgen.py (88%) create mode 100755 rust-runtime/aws-smithy-http-server-python/stubgen.sh rename rust-runtime/aws-smithy-http-server-python/{examples => }/stubgen_test.py (100%) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt index cf2ff4b5d5..8de5cdd59c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt @@ -469,6 +469,7 @@ class RustWriter private constructor( devDependenciesOnly = true, ) fileName == "package.json" -> rawWriter(fileName, debugMode = debugMode) + fileName == "stubgen.sh" -> rawWriter(fileName, debugMode = debugMode) else -> RustWriter(fileName, namespace, debugMode = debugMode) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 7e53055195..6be653bd59 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -96,6 +96,8 @@ data class RuntimeConfig( val crateSrcPrefix: String = cratePrefix.replace("-", "_") + fun runtimeCratesPath(): String? = runtimeCrateLocation.path + fun smithyRuntimeCrate( runtimeCrateName: String, optional: Boolean = false, diff --git a/codegen-server-test/python/build.gradle.kts b/codegen-server-test/python/build.gradle.kts index 87ff7cc5bd..e70e2b0e5d 100644 --- a/codegen-server-test/python/build.gradle.kts +++ b/codegen-server-test/python/build.gradle.kts @@ -66,7 +66,7 @@ val allCodegenTests = "../../codegen-core/common-test-models".let { commonModels "rest_json_extras", imports = listOf("$commonModels/rest-json-extras.smithy"), ), - // TODO(https://github.com/awslabs/smithy-rs/issues/2551) + // TODO(https://github.com/awslabs/smithy-rs/issues/2477) // CodegenTest( // "aws.protocoltests.restjson.validation#RestJsonValidation", // "rest_json_validation", @@ -104,6 +104,21 @@ project.registerGenerateSmithyBuildTask(rootProject, pluginName, allCodegenTests project.registerGenerateCargoWorkspaceTask(rootProject, pluginName, allCodegenTests, workingDirUnderBuildDir) project.registerGenerateCargoConfigTomlTask(buildDir.resolve(workingDirUnderBuildDir)) +tasks.register("stubs") { + description = "Generate Python stubs for all models" + dependsOn("assemble") + + doLast { + allCodegenTests.forEach { test -> + val crateDir = "$buildDir/$workingDirUnderBuildDir/${test.module}/$pluginName" + val moduleName = test.module.replace("-", "_") + exec { + commandLine("bash", "$crateDir/stubgen.sh", moduleName, "$crateDir/Cargo.toml", "$crateDir/python/$moduleName") + } + } + } +} + tasks["smithyBuildJar"].dependsOn("generateSmithyBuild") tasks["assemble"].finalizedBy("generateCargoWorkspace") diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt index 911edb893e..2782e44b94 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCargoDependency.kt @@ -15,8 +15,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig * For a dependency that is used in the client, or in both the client and the server, use [CargoDependency] directly. */ object PythonServerCargoDependency { - val PyO3: CargoDependency = CargoDependency("pyo3", CratesIo("0.17")) - val PyO3Asyncio: CargoDependency = CargoDependency("pyo3-asyncio", CratesIo("0.17"), features = setOf("attributes", "tokio-runtime")) + val PyO3: CargoDependency = CargoDependency("pyo3", CratesIo("0.18")) + val PyO3Asyncio: CargoDependency = CargoDependency("pyo3-asyncio", CratesIo("0.18"), features = setOf("attributes", "tokio-runtime")) val Tokio: CargoDependency = CargoDependency("tokio", CratesIo("1.20.1"), features = setOf("full")) val TokioStream: CargoDependency = CargoDependency("tokio-stream", CratesIo("0.1.12")) val Tracing: CargoDependency = CargoDependency("tracing", CratesIo("0.1")) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt index 9e353db2cd..ff4b6be482 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt @@ -23,6 +23,7 @@ import software.amazon.smithy.rust.codegen.server.python.smithy.generators.Pytho import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.customizations.AddInternalServerErrorToAllOperationsDecorator import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator +import java.io.File /** * Configure the [lib] section of `Cargo.toml`. @@ -194,6 +195,31 @@ class PyTypedMarkerDecorator : ServerCodegenDecorator { } } +/** + * Copies the stubgen scripts to the generated crate root. + * + * The shell script `stubgen.sh` runs a quick build and uses `stubgen.py` to generate mypy compatibile + * types stubs for the project. + */ +class AddStubgenScriptDecorator : ServerCodegenDecorator { + override val name: String = "AddStubgenScriptDecorator" + override val order: Byte = 0 + + override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { + val runtimeCratesPath = codegenContext.runtimeConfig.runtimeCratesPath() + val stubgenPythonLocation = "$runtimeCratesPath/aws-smithy-http-server-python/stubgen.py" + val stubgenPythonContent = File(stubgenPythonLocation).readText(Charsets.UTF_8) + rustCrate.withFile("stubgen.py") { + writeWithNoFormatting("$stubgenPythonContent") + } + val stubgenShellLocation = "$runtimeCratesPath/aws-smithy-http-server-python/stubgen.sh" + val stubgenShellContent = File(stubgenShellLocation).readText(Charsets.UTF_8) + rustCrate.withFile("stubgen.sh") { + writeWithNoFormatting("$stubgenShellContent") + } + } +} + val DECORATORS = arrayOf( /** * Add the [InternalServerError] error to all operations. @@ -214,4 +240,6 @@ val DECORATORS = arrayOf( InitPyDecorator(), // Generate `py.typed` for the Python source. PyTypedMarkerDecorator(), + // Generate scripts for stub generation. + AddStubgenScriptDecorator(), ) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt index 5baf6e83ea..577999a724 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerModuleGenerator.kt @@ -11,7 +11,9 @@ import software.amazon.smithy.model.shapes.ResourceShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.UnionShape +import software.amazon.smithy.rust.codegen.core.Version import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RustCrate @@ -51,6 +53,8 @@ class PythonServerModuleGenerator( renderPyTlsTypes() renderPyLambdaTypes() renderPyApplicationType() + renderCodegenVersion() + rust("Ok(())") } } } @@ -62,13 +66,23 @@ class PythonServerModuleGenerator( let input = #{pyo3}::types::PyModule::new(py, "input")?; let output = #{pyo3}::types::PyModule::new(py, "output")?; let error = #{pyo3}::types::PyModule::new(py, "error")?; - let model = #{pyo3}::types::PyModule::new(py, "model")?; """, *codegenScope, ) + // The `model` type section can be unused in models like `simple`, so we accommodate for it. + var visitedModelType = false serviceShapes.forEach { shape -> val moduleType = moduleType(shape) if (moduleType != null) { + if (moduleType == "model" && !visitedModelType) { + rustTemplate( + """ + let model = #{pyo3}::types::PyModule::new(py, "model")?; + """, + *codegenScope, + ) + visitedModelType = true + } when (shape) { is UnionShape -> rustTemplate( """ @@ -93,11 +107,18 @@ class PythonServerModuleGenerator( m.add_submodule(output)?; #{pyo3}::py_run!(py, error, "import sys; sys.modules['$libName.error'] = error"); m.add_submodule(error)?; - #{pyo3}::py_run!(py, model, "import sys; sys.modules['$libName.model'] = model"); - m.add_submodule(model)?; """, *codegenScope, ) + if (visitedModelType) { + rustTemplate( + """ + #{pyo3}::py_run!(py, model, "import sys; sys.modules['$libName.model'] = model"); + m.add_submodule(model)?; + """, + *codegenScope, + ) + } } // Render wrapper types that are substituted to the ones coming from `aws_smithy_types`. @@ -211,13 +232,12 @@ class PythonServerModuleGenerator( // Render Python application type. private fun RustWriter.renderPyApplicationType() { - rustTemplate( - """ - m.add_class::()?; - Ok(()) - """, - *codegenScope, - ) + rust("""m.add_class::()?;""") + } + + // Render the codegeneration version as module attribute. + private fun RustWriter.renderCodegenVersion() { + rust("""m.add("CODEGEN_VERSION", "${Version.crateVersion()}")?;""") } // Convert to symbol and check the namespace to figure out where they should be imported from. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriter.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriter.kt index 32abe41d69..96b3b5739c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriter.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/RustCrateInlineModuleComposingWriter.kt @@ -335,7 +335,9 @@ class InnerModule(private val moduleDocProvider: ModuleDocProvider, debugMode: B inlineWriter } else { check(inlineModuleAndWriter.inlineModule == lookForModule) { - "The two inline modules have the same name but different attributes on them." + """The two inline modules have the same name but different attributes on them: + 1) ${inlineModuleAndWriter.inlineModule} + 2) $lookForModule""" } inlineModuleAndWriter.writer diff --git a/rust-runtime/aws-smithy-http-server-python/examples/.gitignore b/examples/python/.gitignore similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/.gitignore rename to examples/python/.gitignore diff --git a/rust-runtime/aws-smithy-http-server-python/examples/Cargo.toml b/examples/python/Cargo.toml similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/Cargo.toml rename to examples/python/Cargo.toml diff --git a/rust-runtime/aws-smithy-http-server-python/examples/Makefile b/examples/python/Makefile similarity index 90% rename from rust-runtime/aws-smithy-http-server-python/examples/Makefile rename to examples/python/Makefile index 3b73f08ca6..56213fe289 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/Makefile +++ b/examples/python/Makefile @@ -35,25 +35,23 @@ install-wheel: find $(WHEELS) -type f -name '*.whl' | xargs python3 -m pip install --user --force-reinstall generate-stubs: - python3 $(CUR_DIR)/stubgen.py pokemon_service_server_sdk $(SERVER_SDK_DST)/python/pokemon_service_server_sdk + bash $(SERVER_SDK_DST)/stubgen.sh pokemon_service_server_sdk $(SERVER_SDK_DST)/Cargo.toml $(SERVER_SDK_DST)/python/pokemon_service_server_sdk build: codegen + $(MAKE) generate-stubs $(MAKE) build-wheel - $(MAKE) install-wheel + +release: codegen $(MAKE) generate-stubs $(MAKE) build-wheel-release - $(MAKE) install-wheel run: build python3 $(CUR_DIR)/pokemon_service.py -py-check: build +py-check: install-wheel python3 -m mypy pokemon_service.py -py-test: - python3 stubgen_test.py - -test: build py-check py-test +test: build py-check cargo test clippy: codegen diff --git a/examples/python/README.md b/examples/python/README.md new file mode 100644 index 0000000000..ebe6cfb39e --- /dev/null +++ b/examples/python/README.md @@ -0,0 +1,94 @@ +# Smithy Rust/Python Server SDK example + +This folder contains an example service called Pokémon Service used to showcase +the service framework Python bindings capabilities and to run benchmarks. + +The Python implementation of the service can be found inside +[pokemon_service.py](./pokemon_service.py). + +* [Build](#build) + * [Build dependencies](#build-dependencies) + * [Makefile](#makefile) + * [Python stub generation](#python-stub-generation) +* [Run](#run) +* [Test](#test) +* [Uvloop](#uvloop) +* [MacOs](#macos) + +## Build + +Since this example requires both the server and client SDK to be code-generated +from their [model](/codegen-server-test/model/pokemon.smithy), a Makefile is +provided to build and run the service. Just run `make build` to prepare the first +build. + +### Build dependencies + +Ensure these dependencies are installed. + +``` +pip install maturin uvloop aiohttp mypy +``` + +### Makefile + +The build logic is drive by the Makefile: + +* `make codegen`: run the codegenerator. +* `make build`: build the Maturin package in debug mode (includes type stubs + generation). +* `make release`: build the Maturin package in release mode (includes type stub + generation). +* `make install`: install the latest release or debug build. +* `make run`: run the example server. +* `make test`: run the end-to-end integration test. + +### Python stub generation + +We support the generation of mypy python stubs and every SDK crate ships with +a script called `stubgen.sh`. **Note that the script is not called +automatically as part of the build**. We suggest users to call it after code generation. +It will do first compilation of the crate, generate the types and exit. + +The script takes some command line arguments: + +``` +./stubgen.sh module_name manifest_path output_directory +``` + +* module_name: name of the Python module to generate stubs for, IE `pokemon_service_server_sdk`. +* manifest_path: path for the crate manifest used to build the types. +* output_directory: directory where to generate the stub hierarchy. **This + directory should be a folder `python/$module_name` in the root of the Maturin package.** + +## Run + +`make run` can be used to start the Pokémon service on `http://localhost:13734`. + +## Test + +`make test` can be used to spawn the Python service and run some simple integration +tests against it. + +More info can be found in the `tests` folder of `pokemon-service-test` package. + +## Uvloop + +The server can depend on [uvloop](https://pypi.org/project/uvloop/) for a +faster event loop implementation. Uvloop can be installed with your favourite +package manager or by using pip: + +```sh +pip instal uvloop +``` + +and it will be automatically used instead of the standard library event loop if +it is found in the dependencies' closure. + +## MacOs + +To compile and test on MacOs, please follow the official PyO3 guidelines on how +to [configure your linker](https://pyo3.rs/latest/building_and_distribution.html?highlight=rustflags#macos). + +Please note that the `.cargo/config.toml` with linkers override can be local to +your project. diff --git a/rust-runtime/aws-smithy-http-server-python/examples/mypy.ini b/examples/python/mypy.ini similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/mypy.ini rename to examples/python/mypy.ini diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/Cargo.toml b/examples/python/pokemon-service-test/Cargo.toml similarity index 53% rename from rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/Cargo.toml rename to examples/python/pokemon-service-test/Cargo.toml index 6edcebf160..8dbe8c6974 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/Cargo.toml +++ b/examples/python/pokemon-service-test/Cargo.toml @@ -9,15 +9,15 @@ description = "Run tests against the Python server implementation" [dev-dependencies] rand = "0.8" async-stream = "0.3" -command-group = "1.0" +command-group = "2.1.0" tokio = { version = "1.20.1", features = ["full"] } -serial_test = "0.9.0" +serial_test = "2.0.0" rustls-pemfile = "1.0.1" -tokio-rustls = "0.23.4" -hyper-rustls = { version = "0.23.0", features = ["http2"] } +tokio-rustls = "0.24.0" +hyper-rustls = { version = "0.24.0", features = ["http2"] } # Local paths -aws-smithy-client = { path = "../../../aws-smithy-client/", features = ["rustls"] } -aws-smithy-http = { path = "../../../aws-smithy-http/" } -aws-smithy-types = { path = "../../../aws-smithy-types/" } +aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client/", features = ["rustls"] } +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/" } diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/helpers.rs b/examples/python/pokemon-service-test/tests/helpers.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/helpers.rs rename to examples/python/pokemon-service-test/tests/helpers.rs diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/simple_integration_test.rs b/examples/python/pokemon-service-test/tests/simple_integration_test.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/simple_integration_test.rs rename to examples/python/pokemon-service-test/tests/simple_integration_test.rs diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/testdata/localhost.crt b/examples/python/pokemon-service-test/tests/testdata/localhost.crt similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/testdata/localhost.crt rename to examples/python/pokemon-service-test/tests/testdata/localhost.crt diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/testdata/localhost.key b/examples/python/pokemon-service-test/tests/testdata/localhost.key similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/pokemon-service-test/tests/testdata/localhost.key rename to examples/python/pokemon-service-test/tests/testdata/localhost.key diff --git a/rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py b/examples/python/pokemon_service.py similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/pokemon_service.py rename to examples/python/pokemon_service.py diff --git a/rust-runtime/aws-smithy-http-server-python/Cargo.toml b/rust-runtime/aws-smithy-http-server-python/Cargo.toml index 69e9577160..738b86418b 100644 --- a/rust-runtime/aws-smithy-http-server-python/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server-python/Cargo.toml @@ -22,9 +22,9 @@ bytes = "1.2" futures = "0.3" http = "0.2" hyper = { version = "0.14.20", features = ["server", "http1", "http2", "tcp", "stream"] } -tls-listener = { version = "0.5.1", features = ["rustls", "hyper-h2"] } +tls-listener = { version = "0.7.0", features = ["rustls", "hyper-h2"] } rustls-pemfile = "1.0.1" -tokio-rustls = "0.23.4" +tokio-rustls = "0.24.0" lambda_http = { version = "0.7.1" } # There is a breaking change in `lambda_runtime` between `0.7.0` and `0.7.1`, # and `lambda_http` depends on `0.7` which by default resolves to `0.7.1` but in our CI @@ -34,10 +34,10 @@ lambda_runtime = { version = "0.7.1" } num_cpus = "1.13.1" parking_lot = "0.12.1" pin-project-lite = "0.2" -pyo3 = "0.17.0" -pyo3-asyncio = { version = "0.17.0", features = ["tokio-runtime"] } +pyo3 = "0.18.2" +pyo3-asyncio = { version = "0.18.0", features = ["tokio-runtime"] } signal-hook = { version = "0.3.14", features = ["extended-siginfo"] } -socket2 = { version = "0.4.4", features = ["all"] } +socket2 = { version = "0.5.2", features = ["all"] } thiserror = "1.0.32" tokio = { version = "1.20.1", features = ["full"] } tokio-stream = "0.1" @@ -51,9 +51,9 @@ pretty_assertions = "1" futures-util = { version = "0.3.16", default-features = false } tower-test = "0.4" tokio-test = "0.4" -pyo3-asyncio = { version = "0.17.0", features = ["testing", "attributes", "tokio-runtime", "unstable-streams"] } +pyo3-asyncio = { version = "0.18.0", features = ["testing", "attributes", "tokio-runtime", "unstable-streams"] } rcgen = "0.10.0" -hyper-rustls = { version = "0.23.1", features = ["http2"] } +hyper-rustls = { version = "0.24.0", features = ["http2"] } # PyO3 Asyncio tests cannot use Cargo's default testing harness because `asyncio` # wants to control the main thread. So we need to use testing harness provided by `pyo3_asyncio` diff --git a/rust-runtime/aws-smithy-http-server-python/examples/README.md b/rust-runtime/aws-smithy-http-server-python/examples/README.md deleted file mode 100644 index f765f4b57b..0000000000 --- a/rust-runtime/aws-smithy-http-server-python/examples/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# Smithy Rust/Python Server SDK example - -This folder contains an example service called Pokémon Service used to showcase -the service framework Python bindings capabilities and to run benchmarks. - -The Python implementation of the service can be found inside -[pokemon_service.py](/rust-runtime/aws-smithy-http-python-server/examples/pokemon_service.py). - -## Build - -Since this example requires both the server and client SDK to be code-generated -from their [model](/codegen-server-test/model/pokemon.smithy), a Makefile is -provided to build and run the service. Just run `make build` to prepare the first -build. - -Once the example has been built successfully the first time, idiomatic `cargo` -can be used directly. - -`make distclean` can be used for a complete cleanup of all artefacts. - -### Uvloop - -The server can depend on [uvloop](https://pypi.org/project/uvloop/) for a -faster event loop implementation. Uvloop can be installed with your favourite -package manager or by using pip: - -```sh -pip instal uvloop -``` - -and it will be automatically used instead of the standard library event loop if -it is found in the dependencies' closure. - -### MacOs - -To compile and test on MacOs, please follow the official PyO3 guidelines on how -to [configure your linker](https://pyo3.rs/latest/building_and_distribution.html?highlight=rustflags#macos). - -Please note that the `.cargo/config.toml` with linkers override can be local to -your project. - -## Run - -`cargo run` can be used to start the Pokémon service on -`http://localhost:13734`. - -## Test - -`cargo test` can be used to spawn the Python service and run some simple integration -tests against it. - -More info can be found in the `tests` folder of `pokemon-service-test` package. diff --git a/rust-runtime/aws-smithy-http-server-python/src/tls.rs b/rust-runtime/aws-smithy-http-server-python/src/tls.rs index 538508fcec..c817cabaca 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/tls.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/tls.rs @@ -105,7 +105,7 @@ impl PyTlsConfig { #[pymethods] impl PyTlsConfig { #[new] - #[args(reload_secs = "86400")] // <- 1 Day by default + #[pyo3(signature = (key_path, cert_path, reload_secs=86400))] fn py_new(key_path: PathBuf, cert_path: PathBuf, reload_secs: u64) -> Self { // TODO(BugOnUpstream): `reload: &PyDelta` segfaults, create an issue on PyO3 Self { @@ -146,11 +146,11 @@ mod tests { const TEST_KEY: &str = concat!( env!("CARGO_MANIFEST_DIR"), - "/examples/pokemon-service-test/tests/testdata/localhost.key" + "/../../examples/python/pokemon-service-test/tests/testdata/localhost.key" ); const TEST_CERT: &str = concat!( env!("CARGO_MANIFEST_DIR"), - "/examples/pokemon-service-test/tests/testdata/localhost.crt" + "/../../examples/python/pokemon-service-test/tests/testdata/localhost.crt" ); #[test] diff --git a/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs b/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs index fb4f759f89..fb3aa7ac91 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs @@ -191,7 +191,7 @@ mod tests { assert!(response .unwrap_err() .to_string() - .contains("invalid peer certificate: InvalidCertValidity")); + .contains("invalid peer certificate: Expired")); } // Make a new acceptor with a valid cert and replace diff --git a/rust-runtime/aws-smithy-http-server-python/examples/stubgen.py b/rust-runtime/aws-smithy-http-server-python/stubgen.py similarity index 88% rename from rust-runtime/aws-smithy-http-server-python/examples/stubgen.py rename to rust-runtime/aws-smithy-http-server-python/stubgen.py index 30348838d2..458ee2b9c5 100644 --- a/rust-runtime/aws-smithy-http-server-python/examples/stubgen.py +++ b/rust-runtime/aws-smithy-http-server-python/stubgen.py @@ -2,11 +2,12 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -import re + import inspect +import re import textwrap from pathlib import Path -from typing import Any, Set, Dict, List, Tuple, Optional +from typing import Any, Dict, List, Optional, Set, Tuple ROOT_MODULE_NAME_PLACEHOLDER = "__root_module_name__" @@ -36,11 +37,7 @@ def fix_path(self, path: str) -> str: Returns fixed version of given type path. It unescapes `\\[` and `\\]` and also populates placeholder for root module name. """ - return ( - path.replace(ROOT_MODULE_NAME_PLACEHOLDER, self.root_module_name) - .replace("\\[", "[") - .replace("\\]", "]") - ) + return path.replace(ROOT_MODULE_NAME_PLACEHOLDER, self.root_module_name).replace("\\[", "[").replace("\\]", "]") def submodule(self, path: Path) -> Writer: w = Writer(path, self.root_module_name) @@ -98,27 +95,21 @@ def __init__(self) -> None: def parse_type_directive(line: str, res: DocstringParserResult): parts = line.split(" ", maxsplit=1) if len(parts) != 2: - raise ValueError( - f"Invalid `:type` directive: `{line}` must be in `:type T:` format" - ) + raise ValueError(f"Invalid `:type` directive: `{line}` must be in `:type T:` format") res.types.append(parts[1].rstrip(":")) def parse_rtype_directive(line: str, res: DocstringParserResult): parts = line.split(" ", maxsplit=1) if len(parts) != 2: - raise ValueError( - f"Invalid `:rtype` directive: `{line}` must be in `:rtype T:` format" - ) + raise ValueError(f"Invalid `:rtype` directive: `{line}` must be in `:rtype T:` format") res.rtypes.append(parts[1].rstrip(":")) def parse_param_directive(line: str, res: DocstringParserResult): parts = line.split(" ", maxsplit=2) if len(parts) != 3: - raise ValueError( - f"Invalid `:param` directive: `{line}` must be in `:param name T:` format" - ) + raise ValueError(f"Invalid `:param` directive: `{line}` must be in `:param name T:` format") name = parts[1] ty = parts[2].rstrip(":") res.params.append((name, ty)) @@ -127,18 +118,14 @@ def parse_param_directive(line: str, res: DocstringParserResult): def parse_generic_directive(line: str, res: DocstringParserResult): parts = line.split(" ", maxsplit=1) if len(parts) != 2: - raise ValueError( - f"Invalid `:generic` directive: `{line}` must be in `:generic T:` format" - ) + raise ValueError(f"Invalid `:generic` directive: `{line}` must be in `:generic T:` format") res.generics.append(parts[1].rstrip(":")) def parse_extends_directive(line: str, res: DocstringParserResult): parts = line.split(" ", maxsplit=1) if len(parts) != 2: - raise ValueError( - f"Invalid `:extends` directive: `{line}` must be in `:extends Base[...]:` format" - ) + raise ValueError(f"Invalid `:extends` directive: `{line}` must be in `:extends Base[...]:` format") res.extends.append(parts[1].rstrip(":")) @@ -201,13 +188,13 @@ def clean_doc(obj: Any) -> str: if not doc: return "" - def predicate(l: str) -> bool: + def predicate(line: str) -> bool: for k in DocstringParserDirectives.keys(): - if l.startswith(f":{k} ") and l.endswith(":"): + if line.startswith(f":{k} ") and line.endswith(":"): return False return True - return "\n".join([l for l in doc.splitlines() if predicate(l)]).strip() + return "\n".join([line for line in doc.splitlines() if predicate(line)]).strip() def indent(code: str, level: int = 4) -> str: @@ -225,6 +212,10 @@ def is_fn_like(obj: Any) -> bool: ) +def is_scalar(obj: Any) -> bool: + return isinstance(obj, (str, float, int, bool)) + + def join(args: List[str], delim: str = "\n") -> str: return delim.join(filter(lambda x: x, args)) @@ -266,7 +257,7 @@ def make_function( sig: Optional[inspect.Signature] = None try: sig = inspect.signature(obj) - except: + except Exception: pass def has_default(param: str, ty: str) -> bool: @@ -312,9 +303,7 @@ def {name}({params}) -> {rtype}: def make_class(writer: Writer, name: str, klass: Any) -> str: - bases = list( - filter(lambda n: n != "object", map(lambda b: b.__name__, klass.__bases__)) - ) + bases = list(filter(lambda n: n != "object", map(lambda b: b.__name__, klass.__bases__))) class_sig = DocstringParser.parse_class(klass) if class_sig: (generics, extends) = class_sig @@ -386,7 +375,7 @@ class {name}{bases_str}: def walk_module(writer: Writer, mod: Any): exported = mod.__all__ - for (name, member) in inspect.getmembers(mod): + for name, member in inspect.getmembers(mod): if name not in exported: continue @@ -397,10 +386,25 @@ def walk_module(writer: Writer, mod: Any): writer.define(make_class(writer, name, member)) elif is_fn_like(member): writer.define(make_function(writer, name, member)) + elif is_scalar(member): + writer.define(f"{name}: {type(member).__name__} = ...") else: print(f"Unknown type: {member}") +def generate(module: str, outdir: str): + path = Path(outdir) / f"{module}.pyi" + writer = Writer( + path, + module, + ) + walk_module( + writer, + importlib.import_module(module), + ) + writer.dump() + + if __name__ == "__main__": import argparse import importlib @@ -410,13 +414,4 @@ def walk_module(writer: Writer, mod: Any): parser.add_argument("outdir") args = parser.parse_args() - path = Path(args.outdir) / f"{args.module}.pyi" - writer = Writer( - path, - args.module, - ) - walk_module( - writer, - importlib.import_module(args.module), - ) - writer.dump() + generate(args.module, args.outdir) diff --git a/rust-runtime/aws-smithy-http-server-python/stubgen.sh b/rust-runtime/aws-smithy-http-server-python/stubgen.sh new file mode 100755 index 0000000000..f7708845b3 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server-python/stubgen.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -x + +if [ $# -lt 3 ]; then + echo "usage: $0 package manifest_path output_directory" + exit 1 +fi + +# input arguments +package=$1 +manifest=$2 +output=$3 + +# the directory of the script +source_dir="$(git rev-parse --show-toplevel)" +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +if [ -n "$source_dir" ]; then + CARGO_TARGET_DIR="$source_dir/target" +else + CARGO_TARGET_DIR=$(mktemp -d) + mkdir -p "$CARGO_TARGET_DIR" + # cleanup temporary directory + function cleanup { + # shellcheck disable=2317 + rm -rf "$CARGO_TARGET_DIR" + } + # register the cleanup function to be called on the EXIT signal + trap cleanup EXIT +fi +export CARGO_TARGET_DIR + +shared_object_extension="so" +# generate the Python stubs, +if [ "$(uname)" == "Darwin" ]; then + shared_object_extension="dylib" + export CARGO_TARGET_X86_64_APPLE_DARWIN_RUSTFLAGS="-C link-arg=-undefined -C link-arg=dynamic_lookup" + export CARGO_TARGET_AARCH64_APPLE_DARWIN_RUSTFLAGS="-C link-arg=-undefined -C link-arg=dynamic_lookup" +fi + +cargo build --manifest-path "$manifest" +# The link target have to end with .so to be sure it is importable by the stubgen.py script. +ln -sf "$CARGO_TARGET_DIR/debug/lib$package.$shared_object_extension" "$CARGO_TARGET_DIR/debug/$package.so" +PYTHONPATH=$CARGO_TARGET_DIR/debug:$PYTHONPATH python3 "$script_dir/stubgen.py" "$package" "$output" + +exit 0 diff --git a/rust-runtime/aws-smithy-http-server-python/examples/stubgen_test.py b/rust-runtime/aws-smithy-http-server-python/stubgen_test.py similarity index 100% rename from rust-runtime/aws-smithy-http-server-python/examples/stubgen_test.py rename to rust-runtime/aws-smithy-http-server-python/stubgen_test.py diff --git a/tools/ci-scripts/check-server-python-e2e-test b/tools/ci-scripts/check-server-python-e2e-test index fbaf9b0d67..26b9e15914 100755 --- a/tools/ci-scripts/check-server-python-e2e-test +++ b/tools/ci-scripts/check-server-python-e2e-test @@ -4,6 +4,6 @@ # SPDX-License-Identifier: Apache-2.0 set -eux -cd smithy-rs/rust-runtime/aws-smithy-http-server-python/examples +cd smithy-rs/examples/python make test clippy diff --git a/tools/ci-scripts/codegen-diff/diff_lib.py b/tools/ci-scripts/codegen-diff/diff_lib.py index 09f7edbf86..d39bbaba17 100644 --- a/tools/ci-scripts/codegen-diff/diff_lib.py +++ b/tools/ci-scripts/codegen-diff/diff_lib.py @@ -39,12 +39,12 @@ def checkout_commit_and_generate(revision_sha, branch_name, targets=None): def generate_and_commit_generated_code(revision_sha, targets=None): targets = targets or [ - target_codegen_client, - target_codegen_server, - target_aws_sdk, - target_codegen_server_python, - target_codegen_server_typescript - ] + target_codegen_client, + target_codegen_server, + target_aws_sdk, + target_codegen_server_python, + target_codegen_server_typescript + ] # Clean the build artifacts before continuing assemble_tasks = ' '.join([f'{t}:assemble' for t in targets]) clean_tasks = ' '.join([f'{t}:clean' for t in targets]) @@ -61,6 +61,7 @@ def generate_and_commit_generated_code(revision_sha, targets=None): if target in targets: get_cmd_output(f"mv {target}/build/smithyprojections/{target} {OUTPUT_PATH}/") if target == target_codegen_server: + get_cmd_output(f"./gradlew --rerun-tasks {target_codegen_server_python}:stubs") get_cmd_output(f"mv {target}/python/build/smithyprojections/{target}-python {OUTPUT_PATH}/") get_cmd_output(f"mv {target}/typescript/build/smithyprojections/{target}-typescript {OUTPUT_PATH}/") From 7cb9d6af3ea4c0f55c65c662f1e7b62880bc7728 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Mon, 17 Apr 2023 15:59:49 -0500 Subject: [PATCH 025/253] Add DefaultEndpointResolver to the orchestrator (#2577) ## Motivation and Context This PR adds `DefaultEndpointResolver`, a default implementer of the [EndpointResolver](https://github.com/awslabs/smithy-rs/blob/1e27efe05fe7b991c9f9bbf3d63a297b2dded334/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs#L182-L184) trait, to the orchestrator. ## Description Roughly speaking, the endpoint-related work is currently done by `make_operation` and `SmithyEndpointStage`, where `make_operation` constructs endpoint params and applies an endpoint resolver and the `SmithyEndpointStage` later updates the request header based on the resolved endpoint. In the orchestrator world, that work is handled by `DefaultEndpointResolver::resolve_and_apply_endpoint`. The way endpoint parameters and an endpoint prefix are made available to `DefaultEndpointResolver::resolve_and_apply_endpoint` is done by interceptors because they _may_ require pieces of information only available in an operation input struct. The place that has access to both an operation input struct and a config bag happens to be an interceptor. There are two interceptors `EndpointParamsInterceptor` and `EndpointParamsFinalizerInterceptor` per operation. We pass around endpoint params _builder_ through interceptors to allow it to be configured with more information at a later point; An end point parameters builder is first initialized within the `ServiceRuntimePlugin` with the field values obtained from the service config, and is stored in a config bag. The finalizer interceptor "seals" the builder and creates the actual endpoint params before it is passed to `DefaultEndpointResolver::resolve_and_apply_endpoint`. These interceptors implement `read_before_execution` because they need to access an operation input before it gets serialized (otherwise, `context.input()` would return a `None`). ## Testing Replaced `StaticUriEndpointResolver` with `DefaultEndpointResolver` in `sra_test` and verified the test continued passing. UPDATE: The test currently fails in the `main` branch (it returns a `None` when extracting `SigV4OperationSigningConfig` from the config bag in `OverrideSigningTimeInterceptor`, hence `unwrap` fails), and by merging the `main` branch this PR no longer passes the test, but it does not add new failures either. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- .../smithy/rustsdk/SigV4AuthDecorator.kt | 2 + .../integration-tests/aws-sdk-s3/Cargo.toml | 24 +-- .../aws-sdk-s3/tests/sra_test.rs | 94 ++++++++++- .../client/smithy/RustClientCodegenPlugin.kt | 2 + .../customizations/EndpointPrefixGenerator.kt | 11 +- .../customize/ClientCodegenDecorator.kt | 4 +- .../endpoint/EndpointConfigCustomization.kt | 2 + .../endpoint/EndpointParamsDecorator.kt | 56 +++++++ .../smithy/endpoint/EndpointTypesGenerator.kt | 1 + .../generators/EndpointParamsGenerator.kt | 4 +- .../EndpointParamsInterceptorGenerator.kt | 147 ++++++++++++++++++ .../generators/EndpointResolverGenerator.kt | 2 +- .../EndpointTraitBindingGenerator.kt | 17 +- .../OperationRuntimePluginGenerator.kt | 19 ++- .../ServiceRuntimePluginGenerator.kt | 16 +- .../protocol/ClientProtocolGenerator.kt | 6 +- .../generators/EndpointTraitBindingsTest.kt | 9 +- .../rust/codegen/core/rustlang/RustWriter.kt | 4 +- rust-runtime/aws-smithy-http/src/endpoint.rs | 33 +++- .../src/endpoint/middleware.rs | 2 + .../src/client/endpoints.rs | 83 +++++++++- .../src/client/orchestrator.rs | 33 +++- .../src/client/orchestrator.rs | 9 +- .../src/client/orchestrator/endpoints.rs | 25 +++ .../inlineable/src/endpoint_lib/partition.rs | 6 +- 25 files changed, 559 insertions(+), 52 deletions(-) create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt create mode 100644 rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index a0cba8142b..751788df29 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.aws.traits.auth.SigV4Trait import software.amazon.smithy.aws.traits.auth.UnsignedPayloadTrait import software.amazon.smithy.model.knowledge.ServiceIndex +import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator @@ -38,6 +39,7 @@ class SigV4AuthDecorator : ClientCodegenDecorator { override fun operationRuntimePluginCustomizations( codegenContext: ClientCodegenContext, + operation: OperationShape, baseCustomizations: List, ): List = baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml index 65cfa7528d..51611304b2 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml +++ b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml @@ -6,18 +6,18 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -aws-credential-types = { path = "../../../rust-runtime/aws-credential-types", features = ["test-util"] } -aws-http = { path = "../../../rust-runtime/aws-http" } -aws-runtime = { path = "../../../rust-runtime/aws-runtime" } -aws-sdk-s3 = { path = "../../build/sdk/aws-sdk-s3", features = ["test-util"] } -aws-sigv4 = { path = "../../../rust-runtime/aws-sigv4" } -aws-types = { path = "../../../rust-runtime/aws-types" } -aws-smithy-async = { path = "../../../../rust-runtime/aws-smithy-async", features = ["rt-tokio"] } -aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client", features = ["test-util"] } -aws-smithy-types = { path = "../../../../rust-runtime/aws-smithy-types" } -aws-smithy-http = { path = "../../../../rust-runtime/aws-smithy-http" } -aws-smithy-runtime = { path = "../../../../rust-runtime/aws-smithy-runtime", features = ["test-util"] } -aws-smithy-runtime-api = { path = "../../../../rust-runtime/aws-smithy-runtime-api" } +aws-credential-types = { path = "../../../sdk/build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } +aws-http = { path = "../../../sdk/build/aws-sdk/sdk/aws-http" } +aws-runtime = { path = "../../../sdk/build/aws-sdk/sdk/aws-runtime" } +aws-sdk-s3 = { path = "../../../sdk/build/aws-sdk/sdk/s3/", features = ["test-util"] } +aws-sigv4 = { path = "../../../sdk/build/aws-sdk/sdk/aws-sigv4" } +aws-types = { path = "../../../sdk/build/aws-sdk/sdk/aws-types" } +aws-smithy-async = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-async", features = ["rt-tokio"] } +aws-smithy-client = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-client", features = ["test-util"] } +aws-smithy-types = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-types" } +aws-smithy-http = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-http" } +aws-smithy-runtime = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-runtime", features = ["test-util"] } +aws-smithy-runtime-api = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-runtime-api" } tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3.15", features = ["env-filter", "json"] } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index 258177c3a4..da778c3737 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -17,8 +17,10 @@ use aws_sdk_s3::primitives::SdkBody; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_runtime::client::connections::adapter::DynConnectorAdapter; -use aws_smithy_runtime_api::client::endpoints::StaticUriEndpointResolver; -use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext, Interceptors}; +use aws_smithy_runtime_api::client::endpoints::DefaultEndpointResolver; +use aws_smithy_runtime_api::client::interceptors::{ + Interceptor, InterceptorContext, InterceptorError, Interceptors, +}; use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, Connection, HttpRequest, HttpResponse, TraceProbe, }; @@ -27,7 +29,6 @@ use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_runtime_api::type_erasure::TypedBox; use aws_types::region::SigningRegion; use aws_types::SigningService; -use http::Uri; use std::sync::Arc; use std::time::{Duration, UNIX_EPOCH}; @@ -108,9 +109,16 @@ async fn sra_manual_test() { ), ); - cfg.set_endpoint_resolver(StaticUriEndpointResolver::uri(Uri::from_static( - "https://test-bucket.s3.us-east-1.amazonaws.com/", - ))); + cfg.set_endpoint_resolver(DefaultEndpointResolver::new( + aws_smithy_http::endpoint::SharedEndpointResolver::new( + aws_sdk_s3::endpoint::DefaultResolver::new(), + ), + )); + + let params_builder = aws_sdk_s3::endpoint::Params::builder() + .set_region(Some("us-east-1".to_owned())) + .set_endpoint(Some("https://s3.us-east-1.amazonaws.com/".to_owned())); + cfg.put(params_builder); cfg.set_retry_strategy( aws_smithy_runtime_api::client::retries::NeverRetryStrategy::new(), @@ -162,6 +170,77 @@ async fn sra_manual_test() { } } + // This is a temporary operation runtime plugin until EndpointParamsInterceptor and + // EndpointParamsFinalizerInterceptor have been fully implemented, in which case + // `.with_operation_plugin(ManualOperationRuntimePlugin)` can be removed. + struct ManualOperationRuntimePlugin; + + impl RuntimePlugin for ManualOperationRuntimePlugin { + fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { + #[derive(Debug)] + struct ListObjectsV2EndpointParamsInterceptor; + impl Interceptor for ListObjectsV2EndpointParamsInterceptor { + fn read_before_execution( + &self, + context: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let input = context.input()?; + let input = input + .downcast_ref::() + .ok_or_else(|| InterceptorError::invalid_input_access())?; + let mut params_builder = cfg + .get::() + .ok_or(InterceptorError::read_before_execution( + "missing endpoint params builder", + ))? + .clone(); + params_builder = params_builder.set_bucket(input.bucket.clone()); + cfg.put(params_builder); + + Ok(()) + } + } + + #[derive(Debug)] + struct ListObjectsV2EndpointParamsFinalizerInterceptor; + impl Interceptor for ListObjectsV2EndpointParamsFinalizerInterceptor { + fn read_before_execution( + &self, + _context: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let params_builder = cfg + .get::() + .ok_or(InterceptorError::read_before_execution( + "missing endpoint params builder", + ))? + .clone(); + let params = params_builder + .build() + .map_err(InterceptorError::read_before_execution)?; + cfg.put( + aws_smithy_runtime_api::client::orchestrator::EndpointResolverParams::new( + params, + ), + ); + + Ok(()) + } + } + + cfg.get::>() + .expect("interceptors set") + .register_operation_interceptor( + Arc::new(ListObjectsV2EndpointParamsInterceptor) as _ + ) + .register_operation_interceptor(Arc::new( + ListObjectsV2EndpointParamsFinalizerInterceptor, + ) as _); + Ok(()) + } + } + let conn = TestConnection::new(vec![( http::Request::builder() .header("authorization", "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=ae78f74d26b6b0c3a403d9e8cc7ec3829d6264a2b33db672bf2b151bbb901786") @@ -187,7 +266,8 @@ async fn sra_manual_test() { let runtime_plugins = aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins::new() .with_client_plugin(ManualServiceRuntimePlugin(conn.clone())) - .with_operation_plugin(aws_sdk_s3::operation::list_objects_v2::ListObjectsV2::new()); + .with_operation_plugin(aws_sdk_s3::operation::list_objects_v2::ListObjectsV2::new()) + .with_operation_plugin(ManualOperationRuntimePlugin); let input = ListObjectsV2Input::builder() .bucket("test-bucket") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt index f8852a700d..2e72185f9d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.NoOpEventStreamSigningDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.RequiredCustomizations +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointParamsDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointsDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientDecorator import software.amazon.smithy.rust.codegen.client.testutil.ClientDecoratableBuildPlugin @@ -58,6 +59,7 @@ class RustClientCodegenPlugin : ClientDecoratableBuildPlugin() { RequiredCustomizations(), FluentClientDecorator(), EndpointsDecorator(), + EndpointParamsDecorator(), NoOpEventStreamSigningDecorator(), ApiKeyAuthDecorator(), *decorator, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt index 110ca580c6..2fe90239de 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt @@ -7,16 +7,16 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.EndpointTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.generators.EndpointTraitBindings 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.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection -class EndpointPrefixGenerator(private val codegenContext: CodegenContext, private val shape: OperationShape) : +class EndpointPrefixGenerator(private val codegenContext: ClientCodegenContext, private val shape: OperationShape) : OperationCustomization() { override fun section(section: OperationSection): Writable = when (section) { is OperationSection.MutateRequest -> writable { @@ -29,11 +29,16 @@ class EndpointPrefixGenerator(private val codegenContext: CodegenContext, privat epTrait, ) withBlock("let endpoint_prefix = ", "?;") { - endpointTraitBindings.render(this, "self") + endpointTraitBindings.render( + this, + "self", + codegenContext.settings.codegenConfig.enableNewSmithyRuntime, + ) } rust("request.properties_mut().insert(endpoint_prefix);") } } + else -> emptySection } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt index d7066256a7..262baad0c4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt @@ -74,6 +74,7 @@ interface ClientCodegenDecorator : CoreCodegenDecorator { */ fun operationRuntimePluginCustomizations( codegenContext: ClientCodegenContext, + operation: OperationShape, baseCustomizations: List, ): List = baseCustomizations } @@ -135,10 +136,11 @@ open class CombinedClientCodegenDecorator(decorators: List, ): List = combineCustomizations(baseCustomizations) { decorator, customizations -> - decorator.operationRuntimePluginCustomizations(codegenContext, customizations) + decorator.operationRuntimePluginCustomizations(codegenContext, operation, customizations) } companion object { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index d6bb2cccc7..2d742bbbec 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -69,6 +69,7 @@ internal class EndpointConfigCustomization( /// use aws_smithy_http::endpoint; /// use $moduleUseName::endpoint::{Params as EndpointParams, DefaultResolver}; /// /// Endpoint resolver which adds a prefix to the generated endpoint + /// ##[derive(Debug)] /// struct PrefixResolver { /// base_resolver: DefaultResolver, /// prefix: String @@ -132,6 +133,7 @@ internal class EndpointConfigCustomization( RuntimeType.forInlineFun("MissingResolver", ClientRustModule.Endpoint) { rustTemplate( """ + ##[derive(Debug)] pub(crate) struct MissingResolver; impl #{ResolveEndpoint} for MissingResolver { fn resolve_endpoint(&self, _params: &T) -> #{Result} { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt new file mode 100644 index 0000000000..ba728bcdf8 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt @@ -0,0 +1,56 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.endpoint + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.util.letIf + +/** + * Decorator that injects operation-level interceptors that configure an endpoint parameters builder + * with operation specific information, e.g. a bucket name. + * + * Whenever a setter needs to be called on the endpoint parameters builder with operation specific information, + * this decorator must be used. + */ +class EndpointParamsDecorator : ClientCodegenDecorator { + override val name: String get() = "EndpointParamsDecorator" + override val order: Byte get() = 0 + + override fun operationRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + it + listOf(EndpointParametersRuntimePluginCustomization(codegenContext, operation)) + } +} + +private class EndpointParametersRuntimePluginCustomization( + private val codegenContext: ClientCodegenContext, + private val operation: OperationShape, +) : OperationRuntimePluginCustomization() { + override fun section(section: OperationRuntimePluginSection): Writable = writable { + val symbolProvider = codegenContext.symbolProvider + val operationName = symbolProvider.toSymbol(operation).name + if (section is OperationRuntimePluginSection.AdditionalConfig) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rust("${operationName}EndpointParamsInterceptor") + } + // The finalizer interceptor should be registered last + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rust("${operationName}EndpointParamsFinalizerInterceptor") + } + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt index 2f6e496e69..0906a4f6db 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt @@ -41,6 +41,7 @@ class EndpointTypesGenerator( } fun paramsStruct(): RuntimeType = EndpointParamsGenerator(params).paramsStruct() + fun paramsBuilder(): RuntimeType = EndpointParamsGenerator(params).paramsBuilder() fun defaultResolver(): RuntimeType? = rules?.let { EndpointResolverGenerator(stdlib, runtimeConfig).defaultEndpointResolver(it) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt index 286f34443d..ff19163afc 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt @@ -118,7 +118,7 @@ internal class EndpointParamsGenerator(private val parameters: Parameters) { generateEndpointsStruct(this) } - private fun endpointsBuilder(): RuntimeType = RuntimeType.forInlineFun("ParamsBuilder", ClientRustModule.Endpoint) { + internal fun paramsBuilder(): RuntimeType = RuntimeType.forInlineFun("ParamsBuilder", ClientRustModule.Endpoint) { generateEndpointParamsBuilder(this) } @@ -182,7 +182,7 @@ internal class EndpointParamsGenerator(private val parameters: Parameters) { #{Builder}::default() } """, - "Builder" to endpointsBuilder(), + "Builder" to paramsBuilder(), ) parameters.toList().forEach { parameter -> val name = parameter.memberName() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt new file mode 100644 index 0000000000..8343bbc9bd --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -0,0 +1,147 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.traits.EndpointTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointTypesGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.EndpointTraitBindings +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +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.withBlockTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.util.inputShape + +class EndpointParamsInterceptorGenerator( + private val codegenContext: ClientCodegenContext, +) { + private val model = codegenContext.model + private val symbolProvider = codegenContext.symbolProvider + private val codegenScope = codegenContext.runtimeConfig.let { rc -> + val endpointTypesGenerator = EndpointTypesGenerator.fromContext(codegenContext) + val runtimeApi = CargoDependency.smithyRuntimeApi(rc).toType() + val interceptors = runtimeApi.resolve("client::interceptors") + val orchestrator = runtimeApi.resolve("client::orchestrator") + arrayOf( + "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), + "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), + "EndpointResolverParams" to orchestrator.resolve("EndpointResolverParams"), + "HttpResponse" to orchestrator.resolve("HttpResponse"), + "HttpRequest" to orchestrator.resolve("HttpRequest"), + "Interceptor" to interceptors.resolve("Interceptor"), + "InterceptorContext" to interceptors.resolve("InterceptorContext"), + "InterceptorError" to interceptors.resolve("error::InterceptorError"), + "ParamsBuilder" to endpointTypesGenerator.paramsBuilder(), + ) + } + + fun render(writer: RustWriter, operationShape: OperationShape) { + val operationName = symbolProvider.toSymbol(operationShape).name + renderInterceptor( + writer, + "${operationName}EndpointParamsInterceptor", + implInterceptorBodyForEndpointParams(operationShape), + ) + renderInterceptor( + writer, "${operationName}EndpointParamsFinalizerInterceptor", + implInterceptorBodyForEndpointParamsFinalizer, + ) + } + + private fun renderInterceptor(writer: RustWriter, interceptorName: String, implBody: Writable) { + writer.rustTemplate( + """ + ##[derive(Debug)] + struct $interceptorName; + + impl #{Interceptor}<#{HttpRequest}, #{HttpResponse}> for $interceptorName { + fn read_before_execution( + &self, + context: &#{InterceptorContext}<#{HttpRequest}, #{HttpResponse}>, + cfg: &mut #{ConfigBag}, + ) -> Result<(), #{BoxError}> { + #{body:W} + } + } + """, + *codegenScope, + "body" to implBody, + ) + } + + private fun implInterceptorBodyForEndpointParams(operationShape: OperationShape): Writable = writable { + val operationInput = symbolProvider.toSymbol(operationShape.inputShape(model)) + rustTemplate( + """ + let input = context.input()?; + let _input = input + .downcast_ref::<${operationInput.name}>() + .ok_or_else(|| #{InterceptorError}::invalid_input_access())?; + let params_builder = cfg + .get::<#{ParamsBuilder}>() + .ok_or(#{InterceptorError}::read_before_execution("missing endpoint params builder"))? + .clone(); + ${"" /* TODO(EndpointResolver): Call setters on `params_builder` to update its fields by using values from `_input` */} + cfg.put(params_builder); + + #{endpoint_prefix:W} + + Ok(()) + """, + *codegenScope, + "endpoint_prefix" to endpointPrefix(operationShape), + ) + } + + private fun endpointPrefix(operationShape: OperationShape): Writable = writable { + operationShape.getTrait(EndpointTrait::class.java).map { epTrait -> + val endpointTraitBindings = EndpointTraitBindings( + codegenContext.model, + symbolProvider, + codegenContext.runtimeConfig, + operationShape, + epTrait, + ) + withBlockTemplate( + "let endpoint_prefix = ", + ".map_err(#{InterceptorError}::read_before_execution)?;", + *codegenScope, + ) { + endpointTraitBindings.render( + this, + "_input", + codegenContext.settings.codegenConfig.enableNewSmithyRuntime, + ) + } + rust("cfg.put(endpoint_prefix);") + } + } + + private val implInterceptorBodyForEndpointParamsFinalizer: Writable = writable { + rustTemplate( + """ + let _ = context; + let params_builder = cfg + .get::<#{ParamsBuilder}>() + .ok_or(#{InterceptorError}::read_before_execution("missing endpoint params builder"))? + .clone(); + let params = params_builder + .build() + .map_err(#{InterceptorError}::read_before_execution)?; + cfg.put( + #{EndpointResolverParams}::new(params) + ); + + Ok(()) + """, + *codegenScope, + ) + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt index d85282f16e..7dbede9596 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt @@ -168,7 +168,7 @@ internal class EndpointResolverGenerator(stdlib: List, ru rustTemplate( """ /// The default endpoint resolver - ##[derive(Default)] + ##[derive(Debug, Default)] pub struct DefaultResolver { #{custom_fields:W} } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt index c0f17d46ad..4cc4b93347 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt @@ -44,7 +44,7 @@ class EndpointTraitBindings( * * The returned expression is a `Result` */ - fun render(writer: RustWriter, input: String) { + fun render(writer: RustWriter, input: String, enableNewSmithyRuntime: Boolean) { // the Rust format pattern to make the endpoint prefix e.g. "{}.foo" val formatLiteral = endpointTrait.prefixFormatString() if (endpointTrait.hostPrefix.labels.isEmpty()) { @@ -67,12 +67,23 @@ class EndpointTraitBindings( // NOTE: this is dead code until we start respecting @required rust("let $field = &$input.$field;") } - rustTemplate( + val contents = if (enableNewSmithyRuntime) { + // TODO(enableNewSmithyRuntime): Remove the allow attribute once all places need .into method + """ + if $field.is_empty() { + ##[allow(clippy::useless_conversion)] + return Err(#{invalidFieldError:W}.into()) + } + """ + } else { """ if $field.is_empty() { return Err(#{invalidFieldError:W}) } - """, + """ + } + rustTemplate( + contents, "invalidFieldError" to OperationBuildError(runtimeConfig).invalidField( field, "$field was unset or empty but must be set as part of the endpoint prefix", diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index fb3e9f84b4..2ad5d454a2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -8,8 +8,10 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +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.RuntimeConfig 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.smithy.customize.Section @@ -22,7 +24,22 @@ sealed class OperationRuntimePluginSection(name: String) : Section(name) { data class AdditionalConfig( val configBagName: String, val operationShape: OperationShape, - ) : OperationRuntimePluginSection("AdditionalConfig") + ) : OperationRuntimePluginSection("AdditionalConfig") { + fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { + val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) + writer.rustTemplate( + """ + $configBagName.get::<#{Interceptors}<#{HttpRequest}, #{HttpResponse}>>() + .expect("interceptors set") + .register_operation_interceptor(std::sync::Arc::new(#{interceptor}) as _); + """, + "HttpRequest" to smithyRuntimeApi.resolve("client::orchestrator::HttpRequest"), + "HttpResponse" to smithyRuntimeApi.resolve("client::orchestrator::HttpResponse"), + "Interceptors" to smithyRuntimeApi.resolve("client::interceptors::Interceptors"), + "interceptor" to interceptor, + ) + } + } } typealias OperationRuntimePluginCustomization = NamedCustomization diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 359bb32d1f..acb93bbfa7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointTypesGenerator import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -72,7 +73,9 @@ typealias ServiceRuntimePluginCustomization = NamedCustomization + val http = RuntimeType.smithyHttp(rc) val runtimeApi = RuntimeType.smithyRuntimeApi(rc) val runtime = RuntimeType.smithyRuntime(rc) arrayOf( @@ -83,12 +86,15 @@ class ServiceRuntimePluginGenerator( "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), "Connection" to runtimeApi.resolve("client::orchestrator::Connection"), "ConnectorSettings" to RuntimeType.smithyClient(rc).resolve("http_connector::ConnectorSettings"), + "DefaultEndpointResolver" to runtimeApi.resolve("client::endpoints::DefaultEndpointResolver"), "DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"), "HttpAuthSchemes" to runtimeApi.resolve("client::orchestrator::HttpAuthSchemes"), "IdentityResolvers" to runtimeApi.resolve("client::orchestrator::IdentityResolvers"), "NeverRetryStrategy" to runtimeApi.resolve("client::retries::NeverRetryStrategy"), + "Params" to endpointTypesGenerator.paramsStruct(), + "ResolveEndpoint" to http.resolve("endpoint::ResolveEndpoint"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), - "StaticUriEndpointResolver" to runtimeApi.resolve("client::endpoints::StaticUriEndpointResolver"), + "SharedEndpointResolver" to http.resolve("endpoint::SharedEndpointResolver"), "TestConnection" to runtime.resolve("client::connections::test_connection::TestConnection"), "TraceProbe" to runtimeApi.resolve("client::orchestrator::TraceProbe"), ) @@ -125,8 +131,12 @@ class ServiceRuntimePluginGenerator( // Set an empty auth option resolver to be overridden by operations that need auth. cfg.set_auth_option_resolver(#{AuthOptionListResolver}::new(Vec::new())); - // TODO(RuntimePlugins): Resolve the correct endpoint - cfg.set_endpoint_resolver(#{StaticUriEndpointResolver}::http_localhost(1234)); + let endpoint_resolver = #{DefaultEndpointResolver}::<#{Params}>::new( + #{SharedEndpointResolver}::from(self.handle.conf.endpoint_resolver())); + cfg.set_endpoint_resolver(endpoint_resolver); + + ${"" /* TODO(EndpointResolver): Create endpoint params builder from service config */} + cfg.put(#{Params}::builder()); // TODO(RuntimePlugins): Wire up standard retry cfg.set_retry_strategy(#{NeverRetryStrategy}::new()); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt index ebe387d4d6..3c8fc7c974 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsInterceptorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBoundProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute @@ -101,13 +102,16 @@ open class ClientProtocolGenerator( operationWriter, operationShape, operationName, - codegenDecorator.operationRuntimePluginCustomizations(codegenContext, emptyList()), + codegenDecorator.operationRuntimePluginCustomizations(codegenContext, operationShape, emptyList()), ) ResponseDeserializerGenerator(codegenContext, protocol) .render(operationWriter, operationShape, operationCustomizations) RequestSerializerGenerator(codegenContext, protocol, bodyGenerator) .render(operationWriter, operationShape, operationCustomizations) + + EndpointParamsInterceptorGenerator(codegenContext) + .render(operationWriter, operationShape) } } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt index 841986cb91..6fe1328bb8 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt @@ -7,6 +7,8 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.EndpointTrait import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest @@ -34,8 +36,9 @@ internal class EndpointTraitBindingsTest { epTrait.prefixFormatString() shouldBe ("\"{foo}.data\"") } - @Test - fun `generate endpoint prefixes`() { + @ParameterizedTest + @ValueSource(booleans = [true, false]) + fun `generate endpoint prefixes`(enableNewSmithyRuntime: Boolean) { val model = """ namespace test @readonly @@ -73,7 +76,7 @@ internal class EndpointTraitBindingsTest { RuntimeType.smithyHttp(TestRuntimeConfig), TestRuntimeConfig.operationBuildError(), ) { - endpointBindingGenerator.render(this, "self") + endpointBindingGenerator.render(this, "self", enableNewSmithyRuntime) } } unitTest( diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt index 8de5cdd59c..61f219488a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt @@ -87,7 +87,9 @@ fun > T.withBlockTemplate( block: T.() -> Unit, ): T { return withTemplate(textBeforeNewLine, ctx) { header -> - conditionalBlock(header, textAfterNewLine, conditional = true, block = block) + withTemplate(textAfterNewLine, ctx) { tail -> + conditionalBlock(header, tail, conditional = true, block = block) + } } } diff --git a/rust-runtime/aws-smithy-http/src/endpoint.rs b/rust-runtime/aws-smithy-http/src/endpoint.rs index e73cd05131..d9d7844746 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint.rs @@ -9,8 +9,10 @@ use crate::endpoint::error::InvalidEndpointError; use crate::operation::error::BuildError; use http::uri::{Authority, Uri}; use std::borrow::Cow; +use std::fmt::Debug; use std::result::Result as StdResult; use std::str::FromStr; +use std::sync::Arc; pub mod error; pub mod middleware; @@ -21,7 +23,7 @@ pub use error::ResolveEndpointError; pub type Result = std::result::Result; /// Implementors of this trait can resolve an endpoint that will be applied to a request. -pub trait ResolveEndpoint: Send + Sync { +pub trait ResolveEndpoint: Debug + Send + Sync { /// Given some endpoint parameters, resolve an endpoint or return an error when resolution is /// impossible. fn resolve_endpoint(&self, params: &Params) -> Result; @@ -35,6 +37,35 @@ impl ResolveEndpoint for &'static str { } } +/// Endpoint Resolver wrapper that may be shared +#[derive(Clone, Debug)] +pub struct SharedEndpointResolver(Arc>); + +impl SharedEndpointResolver { + /// Create a new `SharedEndpointResolver` from `ResolveEndpoint` + pub fn new(resolve_endpoint: impl ResolveEndpoint + 'static) -> Self { + Self(Arc::new(resolve_endpoint)) + } +} + +impl AsRef> for SharedEndpointResolver { + fn as_ref(&self) -> &(dyn ResolveEndpoint + 'static) { + self.0.as_ref() + } +} + +impl From>> for SharedEndpointResolver { + fn from(resolve_endpoint: Arc>) -> Self { + SharedEndpointResolver(resolve_endpoint) + } +} + +impl ResolveEndpoint for SharedEndpointResolver { + fn resolve_endpoint(&self, params: &T) -> Result { + self.0.resolve_endpoint(params) + } +} + /// API Endpoint /// /// This implements an API endpoint as specified in the diff --git a/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs b/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs index 1665df9bce..fd934058d2 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs @@ -13,6 +13,8 @@ use http::header::HeaderName; use http::{HeaderValue, Uri}; use std::str::FromStr; +// TODO(enableNewSmithyRuntime): Delete this module + /// Middleware to apply an HTTP endpoint to the request /// /// This middleware reads [`aws_smithy_types::endpoint::Endpoint`] out of the request properties and applies diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs b/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs index 1e823e713a..b9b4f4a512 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs @@ -3,9 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::client::orchestrator::{BoxError, EndpointResolver, HttpRequest}; -use aws_smithy_http::endpoint::apply_endpoint; -use http::Uri; +use crate::client::orchestrator::{ + BoxError, EndpointResolver, EndpointResolverParams, HttpRequest, +}; +use aws_smithy_http::endpoint::error::ResolveEndpointError; +use aws_smithy_http::endpoint::{ + apply_endpoint, EndpointPrefix, ResolveEndpoint, SharedEndpointResolver, +}; +use http::header::HeaderName; +use http::{HeaderValue, Uri}; use std::fmt::Debug; use std::str::FromStr; @@ -28,8 +34,77 @@ impl StaticUriEndpointResolver { } impl EndpointResolver for StaticUriEndpointResolver { - fn resolve_and_apply_endpoint(&self, request: &mut HttpRequest) -> Result<(), BoxError> { + fn resolve_and_apply_endpoint( + &self, + _params: &EndpointResolverParams, + _endpoint_prefix: Option<&EndpointPrefix>, + request: &mut HttpRequest, + ) -> Result<(), BoxError> { apply_endpoint(request.uri_mut(), &self.endpoint, None)?; Ok(()) } } + +#[derive(Debug, Clone)] +pub struct DefaultEndpointResolver { + inner: SharedEndpointResolver, +} + +impl DefaultEndpointResolver { + pub fn new(resolve_endpoint: SharedEndpointResolver) -> Self { + Self { + inner: resolve_endpoint, + } + } +} + +impl EndpointResolver for DefaultEndpointResolver +where + Params: Debug + Send + Sync + 'static, +{ + fn resolve_and_apply_endpoint( + &self, + params: &EndpointResolverParams, + endpoint_prefix: Option<&EndpointPrefix>, + request: &mut HttpRequest, + ) -> Result<(), BoxError> { + let endpoint = match params.get::() { + Some(params) => self.inner.resolve_endpoint(params)?, + None => { + return Err(Box::new(ResolveEndpointError::message( + "params of expected type was not present", + ))); + } + }; + + let uri: Uri = endpoint.url().parse().map_err(|err| { + ResolveEndpointError::from_source("endpoint did not have a valid uri", err) + })?; + + apply_endpoint(request.uri_mut(), &uri, endpoint_prefix).map_err(|err| { + ResolveEndpointError::message(format!( + "failed to apply endpoint `{:?}` to request `{:?}`", + uri, request, + )) + .with_source(Some(err.into())) + })?; + + for (header_name, header_values) in endpoint.headers() { + request.headers_mut().remove(header_name); + for value in header_values { + request.headers_mut().insert( + HeaderName::from_str(header_name).map_err(|err| { + ResolveEndpointError::message("invalid header name") + .with_source(Some(err.into())) + })?, + HeaderValue::from_str(value).map_err(|err| { + ResolveEndpointError::message("invalid header value") + .with_source(Some(err.into())) + })?, + ); + } + } + + Ok(()) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 29baf6bf00..35b85e3bcb 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -9,6 +9,7 @@ use crate::client::interceptors::InterceptorContext; use crate::config_bag::ConfigBag; use crate::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_http::body::SdkBody; +use aws_smithy_http::endpoint::EndpointPrefix; use aws_smithy_http::property_bag::PropertyBag; use std::any::Any; use std::borrow::Cow; @@ -179,8 +180,26 @@ pub trait HttpRequestSigner: Send + Sync + Debug { ) -> Result<(), BoxError>; } +#[derive(Debug)] +pub struct EndpointResolverParams(TypeErasedBox); + +impl EndpointResolverParams { + pub fn new(params: T) -> Self { + Self(TypedBox::new(params).erase()) + } + + pub fn get(&self) -> Option<&T> { + self.0.downcast_ref() + } +} + pub trait EndpointResolver: Send + Sync + Debug { - fn resolve_and_apply_endpoint(&self, request: &mut HttpRequest) -> Result<(), BoxError>; + fn resolve_and_apply_endpoint( + &self, + params: &EndpointResolverParams, + endpoint_prefix: Option<&EndpointPrefix>, + request: &mut HttpRequest, + ) -> Result<(), BoxError>; } pub trait ConfigBagAccessors { @@ -193,6 +212,9 @@ pub trait ConfigBagAccessors { fn auth_option_resolver(&self) -> &dyn AuthOptionResolver; fn set_auth_option_resolver(&mut self, auth_option_resolver: impl AuthOptionResolver + 'static); + fn endpoint_resolver_params(&self) -> &EndpointResolverParams; + fn set_endpoint_resolver_params(&mut self, endpoint_resolver_params: EndpointResolverParams); + fn endpoint_resolver(&self) -> &dyn EndpointResolver; fn set_endpoint_resolver(&mut self, endpoint_resolver: impl EndpointResolver + 'static); @@ -266,6 +288,15 @@ impl ConfigBagAccessors for ConfigBag { self.put::>(Box::new(retry_strategy)); } + fn endpoint_resolver_params(&self) -> &EndpointResolverParams { + self.get::() + .expect("endpoint resolver params must be set") + } + + fn set_endpoint_resolver_params(&mut self, endpoint_resolver_params: EndpointResolverParams) { + self.put::(endpoint_resolver_params); + } + fn endpoint_resolver(&self) -> &dyn EndpointResolver { &**self .get::>() diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index fbc83cadfb..217a0eaae7 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -4,6 +4,7 @@ */ use self::auth::orchestrate_auth; +use crate::client::orchestrator::endpoints::orchestrate_endpoint; use crate::client::orchestrator::http::read_body; use crate::client::orchestrator::phase::Phase; use aws_smithy_http::result::SdkError; @@ -17,6 +18,7 @@ use aws_smithy_runtime_api::config_bag::ConfigBag; use tracing::{debug_span, Instrument}; mod auth; +mod endpoints; mod http; pub(self) mod phase; @@ -105,12 +107,7 @@ async fn make_an_attempt( ) -> Result> { let dispatch_phase = dispatch_phase .include(|ctx| interceptors.read_before_attempt(ctx, cfg))? - .include_mut(|ctx| { - let request = ctx.request_mut().expect("request has been set"); - - let endpoint_resolver = cfg.endpoint_resolver(); - endpoint_resolver.resolve_and_apply_endpoint(request) - })? + .include_mut(|ctx| orchestrate_endpoint(ctx, cfg))? .include_mut(|ctx| interceptors.modify_before_signing(ctx, cfg))? .include(|ctx| interceptors.read_before_signing(ctx, cfg))?; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs new file mode 100644 index 0000000000..5da7bfe517 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -0,0 +1,25 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::endpoint::EndpointPrefix; +use aws_smithy_runtime_api::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::{ + BoxError, ConfigBagAccessors, HttpRequest, HttpResponse, +}; +use aws_smithy_runtime_api::config_bag::ConfigBag; + +pub(super) fn orchestrate_endpoint( + ctx: &mut InterceptorContext, + cfg: &ConfigBag, +) -> Result<(), BoxError> { + let params = cfg.endpoint_resolver_params(); + let endpoint_prefix = cfg.get::(); + let request = ctx.request_mut()?; + + let endpoint_resolver = cfg.endpoint_resolver(); + endpoint_resolver.resolve_and_apply_endpoint(params, endpoint_prefix, request)?; + + Ok(()) +} diff --git a/rust-runtime/inlineable/src/endpoint_lib/partition.rs b/rust-runtime/inlineable/src/endpoint_lib/partition.rs index 55b97a21d2..b9b4002ed1 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/partition.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/partition.rs @@ -17,7 +17,7 @@ use std::borrow::Cow; use std::collections::HashMap; /// Determine the AWS partition metadata for a given region -#[derive(Default)] +#[derive(Debug, Default)] pub(crate) struct PartitionResolver { partitions: Vec, } @@ -151,6 +151,7 @@ impl PartitionResolver { type Str = Cow<'static, str>; +#[derive(Debug)] pub(crate) struct PartitionMetadata { id: Str, region_regex: Regex, @@ -203,6 +204,7 @@ impl PartitionMetadata { } } +#[derive(Debug)] pub(crate) struct PartitionOutput { name: Str, dns_suffix: Str, @@ -211,7 +213,7 @@ pub(crate) struct PartitionOutput { supports_dual_stack: bool, } -#[derive(Default)] +#[derive(Debug, Default)] pub(crate) struct PartitionOutputOverride { name: Option, dns_suffix: Option, From 04cf5e6c2c8c06f90a2e52e7412a7e0ce652dbb2 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 18 Apr 2023 02:41:00 -0700 Subject: [PATCH 026/253] Fix typo in changelog entry (#2587) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- CHANGELOG.next.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index eb431fbb7a..abe0d7fdb4 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -94,7 +94,7 @@ meta = { "breaking" = false, "tada" = false, "bug" = false } author = "parker-timmerman" [[aws-sdk-rust]] -message = "Fix but where an incorrect endpoint was produced for WriteGetObjectResponse" +message = "Fix bug where an incorrect endpoint was produced for `WriteGetObjectResponse`" references = ["smithy-rs#781", "aws-sdk-rust#781"] meta = { "breaking" = false, "tada" = false, "bug" = true } author = "rcoh" @@ -106,7 +106,7 @@ meta = { "breaking" = false, "tada" = true, "bug" = true } author = "Velfi" [[aws-sdk-rust]] -message = "`aws_smithy_types::date_time::Format` has been re-exported in service client crates." +message = "`aws_smithy_types::date_time::Format` has been re-exported in SDK crates." references = ["smithy-rs#2534"] meta = { "breaking" = false, "tada" = false, "bug" = false } author = "ysaito1001" From 80f25d42dca7ea93b4143444219e06242230bf71 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 18 Apr 2023 12:35:22 +0200 Subject: [PATCH 027/253] Fix generation of constrained shapes reaching `@sensitive` shapes (#2585) Constrained shapes should always be able to `#[derive(Debug)]`. Fixes #2582. ## Testing The modified integration test fails without this patch applied. ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ Co-authored-by: Matteo Bigoi <1781140+crisidev@users.noreply.github.com> --- CHANGELOG.next.toml | 6 ++++++ codegen-core/common-test-models/constraints.smithy | 9 +++++++++ .../smithy/ConstrainedShapeSymbolMetadataProvider.kt | 6 ++++++ 3 files changed, 21 insertions(+) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index abe0d7fdb4..a428a3186e 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -116,3 +116,9 @@ message = "`aws_smithy_types::date_time::Format` has been re-exported in service references = ["smithy-rs#2534"] meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } author = "ysaito1001" + +[[smithy-rs]] +message = "Fix generation of constrained shapes reaching `@sensitive` shapes" +references = ["smithy-rs#2582", "smithy-rs#2585"] +meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" } +author = "david-perez" diff --git a/codegen-core/common-test-models/constraints.smithy b/codegen-core/common-test-models/constraints.smithy index 2651b77335..bac10a0c15 100644 --- a/codegen-core/common-test-models/constraints.smithy +++ b/codegen-core/common-test-models/constraints.smithy @@ -454,6 +454,7 @@ structure ConA { conBList: ConBList, lengthList: LengthList, + sensitiveLengthList: SensitiveLengthList, conBSet: ConBSet, @@ -866,6 +867,14 @@ list LengthList { member: String } +@length(max: 69) +list SensitiveLengthList { + member: SensitiveStructure +} + +@sensitive +structure SensitiveStructure { } + set ConBSet { member: ConBSetInner } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolMetadataProvider.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolMetadataProvider.kt index 0c83795f2f..2c7b4f1fc5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolMetadataProvider.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ConstrainedShapeSymbolMetadataProvider.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.SymbolMetadataProvider import software.amazon.smithy.rust.codegen.core.smithy.containerDefaultMetadata @@ -47,6 +48,11 @@ class ConstrainedShapeSymbolMetadataProvider( derives += containerDefaultMetadata(shape, model).derives } + // We should _always_ be able to `#[derive(Debug)]`: all constrained shapes' types are simply tuple newtypes + // wrapping a single type which always implements `Debug`. + // The wrapped type may not _derive_ `Debug` though, hence why this line is required; see https://github.com/awslabs/smithy-rs/issues/2582. + derives += RuntimeType.Debug + val visibility = Visibility.publicIf(constrainedTypes, Visibility.PUBCRATE) return RustMetadata(derives, additionalAttributes, visibility) } From a66f23417bad2b022fe19e9379bb0ed26309d8dd Mon Sep 17 00:00:00 2001 From: Julian Antonielli Date: Tue, 18 Apr 2023 18:00:09 +0100 Subject: [PATCH 028/253] Fix docs for generic clients (`call()` -> `send()`) (#2590) ## Description Docs were wrong, the function is called `send()`, not `call()`. ----- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../client/smithy/generators/client/FluentClientDocs.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDocs.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDocs.kt index 3e8c66f64b..b83cb7a860 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDocs.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDocs.kt @@ -202,13 +202,13 @@ object FluentClientDocs { A client has a function for every operation that can be performed by the service. For example, the [`${operationSymbol.name}`](${operationSymbol.namespace}) operation has a [`Client::$operationFnName`], function which returns a builder for that operation. - The fluent builder ultimately has a `call()` function that returns an async future that + The fluent builder ultimately has a `send()` function that returns an async future that returns a result, as illustrated below: ```rust,ignore let result = client.$operationFnName() .${memberSymbol.name}("example") - .call() + .send() .await; ``` From d8cda15364f4a3f933713846e93d8f8e81300731 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Tue, 18 Apr 2023 18:17:15 +0100 Subject: [PATCH 029/253] Add examples folder to CODEOWNERS (#2591) ## Motivation and Context This was missing from the https://github.com/awslabs/smithy-rs/pull/2481 change. --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 44bcc8d692..79b21893c0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -4,6 +4,7 @@ /codegen-server-test/ @awslabs/smithy-rs-server /codegen-server/ @awslabs/smithy-rs-server /rust-runtime/aws-smithy-http-server/ @awslabs/smithy-rs-server +/examples/ @awslabs/smithy-rs-server # Python Server /codegen-server-test/python/ @awslabs/smithy-rs-python-server @awslabs/smithy-rs-server From 63a1e78f5817451eef405862e58ec190375ccdb7 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 19 Apr 2023 11:48:34 +0200 Subject: [PATCH 030/253] Fix server code generation of `@httpPayload`-bound constrained shapes (#2584) The issue is we're not changing the return type of the payload deserializing function to be the unconstrained type (e.g. the builder in case of an `@httpPayload`-bound structure shape) when the shape is constrained. Yet another example of why code-generating `constraints.smithy` (see #2101) is important. Closes #2583. ## Testing The added integration test operation fails to compile without this patch. ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ --- CHANGELOG.next.toml | 6 ++++++ .../common-test-models/constraints.smithy | 15 +++++++++++++++ .../generators/http/HttpBindingGenerator.kt | 2 +- .../HttpBoundProtocolPayloadGenerator.kt | 6 +++++- .../smithy/protocols/parse/JsonParserGenerator.kt | 7 ++++--- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index a428a3186e..7992dd6e5c 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -122,3 +122,9 @@ message = "Fix generation of constrained shapes reaching `@sensitive` shapes" references = ["smithy-rs#2582", "smithy-rs#2585"] meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" } author = "david-perez" + +[[smithy-rs]] +message = "Fix server code generation bug affecting constrained shapes bound with `@httpPayload`" +references = ["smithy-rs#2583", "smithy-rs#2584"] +meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" } +author = "david-perez" diff --git a/codegen-core/common-test-models/constraints.smithy b/codegen-core/common-test-models/constraints.smithy index bac10a0c15..a2add6c86a 100644 --- a/codegen-core/common-test-models/constraints.smithy +++ b/codegen-core/common-test-models/constraints.smithy @@ -12,7 +12,9 @@ service ConstraintsService { operations: [ ConstrainedShapesOperation, ConstrainedHttpBoundShapesOperation, + ConstrainedHttpPayloadBoundShapeOperation, ConstrainedRecursiveShapesOperation, + // `httpQueryParams` and `httpPrefixHeaders` are structurually // exclusive, so we need one operation per target shape type // combination. @@ -59,6 +61,13 @@ operation ConstrainedHttpBoundShapesOperation { errors: [ValidationException] } +@http(uri: "/constrained-http-payload-bound-shape-operation", method: "POST") +operation ConstrainedHttpPayloadBoundShapeOperation { + input: ConstrainedHttpPayloadBoundShapeOperationInputOutput, + output: ConstrainedHttpPayloadBoundShapeOperationInputOutput, + errors: [ValidationException] +} + @http(uri: "/constrained-recursive-shapes-operation", method: "POST") operation ConstrainedRecursiveShapesOperation { input: ConstrainedRecursiveShapesOperationInputOutput, @@ -311,6 +320,12 @@ structure ConstrainedHttpBoundShapesOperationInputOutput { enumStringListQuery: ListOfEnumString, } +structure ConstrainedHttpPayloadBoundShapeOperationInputOutput { + @required + @httpPayload + httpPayloadBoundConstrainedShape: ConA +} + structure QueryParamsTargetingMapOfPatternStringOperationInputOutput { @httpQueryParams mapOfPatternString: MapOfPatternString diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt index 881d0f77af..7268dfd12a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/http/HttpBindingGenerator.kt @@ -235,7 +235,7 @@ class HttpBindingGenerator( // The output needs to be Optional when deserializing the payload body or the caller signature // will not match. val outputT = symbolProvider.toSymbol(binding.member).makeOptional() - rustBlock("pub fn $fnName(body: &[u8]) -> std::result::Result<#T, #T>", outputT, errorSymbol) { + rustBlock("pub(crate) fn $fnName(body: &[u8]) -> std::result::Result<#T, #T>", outputT, errorSymbol) { deserializePayloadBody( binding, errorSymbol, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt index 10d4b83cec..6d4e7bf850 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt @@ -239,7 +239,11 @@ class HttpBoundProtocolPayloadGenerator( ) { val ref = if (payloadMetadata.takesOwnership) "" else "&" val serializer = protocolFunctions.serializeFn(member, fnNameSuffix = "http_payload") { fnName -> - val outputT = if (member.isStreaming(model)) symbolProvider.toSymbol(member) else RuntimeType.ByteSlab.toSymbol() + val outputT = if (member.isStreaming(model)) { + symbolProvider.toSymbol(member) + } else { + RuntimeType.ByteSlab.toSymbol() + } rustBlockTemplate( "pub fn $fnName(payload: $ref#{Member}) -> Result<#{outputT}, #{BuildError}>", "Member" to symbolProvider.toSymbol(member), diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt index 1566d84bf3..01c2b64e6a 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/JsonParserGenerator.kt @@ -154,14 +154,15 @@ class JsonParserGenerator( override fun payloadParser(member: MemberShape): RuntimeType { val shape = model.expectShape(member.target) + val returnSymbolToParse = returnSymbolToParse(shape) check(shape is UnionShape || shape is StructureShape || shape is DocumentShape) { - "payload parser should only be used on structures & unions" + "Payload parser should only be used on structure shapes, union shapes, and document shapes." } return protocolFunctions.deserializeFn(shape, fnNameSuffix = "payload") { fnName -> rustBlockTemplate( - "pub fn $fnName(input: &[u8]) -> Result<#{Shape}, #{Error}>", + "pub(crate) fn $fnName(input: &[u8]) -> Result<#{ReturnType}, #{Error}>", *codegenScope, - "Shape" to symbolProvider.toSymbol(shape), + "ReturnType" to returnSymbolToParse.symbol, ) { val input = if (shape is DocumentShape) { "input" From 2f70ac8ec08aacc5896bf51e02e6a992c0190370 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 19 Apr 2023 13:00:17 +0200 Subject: [PATCH 031/253] `DEBUG`-log server request rejections (#2597) This commit logs server request rejections at the `DEBUG` level in an operation's `FromRequest` implementation. This commit is analogous to the one in PR #2524 for response rejections. However, request rejections are _not_ errors, so they shouldn't be logged at the `ERROR` level. Indeed, they happen every time the server rejects a malformed request. Prior to this commit, the `RuntimeError::NotAcceptable` variant was the only `RuntimeError` variant that was manually constructed. This commit makes it so that it now results from a conversion from a new `RequestRejection::NotAcceptable` variant. We now leverage `futures_util::future::TryFutureExt::map` to map a future that uses `RequestRejection` as its error into a future that uses `RuntimeError`, and centrally log the rejection there. `futures_util` is already a transitive dependency of server SDKs (via e.g. `hyper` and `tower`), so adding it is a direct dependency is not worse. This helps with #2521. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../server/smithy/ServerCargoDependency.kt | 1 + .../ServerHttpBoundProtocolGenerator.kt | 17 ++++++++++------- .../src/proto/aws_json/rejection.rs | 2 ++ .../src/proto/rest_json_1/rejection.rs | 5 +++++ .../src/proto/rest_json_1/runtime_error.rs | 1 + .../src/proto/rest_xml/rejection.rs | 3 +++ 6 files changed, 22 insertions(+), 7 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCargoDependency.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCargoDependency.kt index 1726057a9f..956a88ecf2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCargoDependency.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCargoDependency.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig object ServerCargoDependency { val AsyncTrait: CargoDependency = CargoDependency("async-trait", CratesIo("0.1")) val FormUrlEncoded: CargoDependency = CargoDependency("form_urlencoded", CratesIo("1")) + val FuturesUtil: CargoDependency = CargoDependency("futures-util", CratesIo("0.3")) val Mime: CargoDependency = CargoDependency("mime", CratesIo("0.3")) val Nom: CargoDependency = CargoDependency("nom", CratesIo("7")) val OnceCell: CargoDependency = CargoDependency("once_cell", CratesIo("1.13")) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 889967412e..85b624b567 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -137,6 +137,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( "Cow" to RuntimeType.Cow, "DateTime" to RuntimeType.dateTime(runtimeConfig), "FormUrlEncoded" to ServerCargoDependency.FormUrlEncoded.toType(), + "FuturesUtil" to ServerCargoDependency.FuturesUtil.toType(), "HttpBody" to RuntimeType.HttpBody, "header_util" to RuntimeType.smithyHttp(runtimeConfig).resolve("header"), "Hyper" to RuntimeType.Hyper, @@ -182,7 +183,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( rustTemplate( """ if !#{SmithyHttpServer}::protocols::accept_header_classifier(request.headers(), ${contentType.dq()}) { - return Err(#{RuntimeError}::NotAcceptable) + return Err(#{RequestRejection}::NotAcceptable); } """, *codegenScope, @@ -201,9 +202,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( ?.let { "Some(${it.dq()})" } ?: "None" rustTemplate( """ - if #{SmithyHttpServer}::protocols::content_type_header_classifier(request.headers(), $expectedRequestContentType).is_err() { - return Err(#{RuntimeError}::UnsupportedMediaType) - } + #{SmithyHttpServer}::protocols::content_type_header_classifier(request.headers(), $expectedRequestContentType)?; """, *codegenScope, ) @@ -213,9 +212,9 @@ class ServerHttpBoundProtocolTraitImplGenerator( // Implement `from_request` trait for input types. val inputFuture = "${inputSymbol.name}Future" + // TODO(https://github.com/awslabs/smithy-rs/issues/2238): Remove the `Pin>` and replace with thin wrapper around `Collect`. rustTemplate( """ - // TODO(https://github.com/awslabs/smithy-rs/issues/2238): Remove the `Pin>` and replace with thin wrapper around `Collect`. #{PinProjectLite}::pin_project! { /// A [`Future`](std::future::Future) aggregating the body bytes of a [`Request`] and constructing the /// [`${inputSymbol.name}`](#{I}) using modelled bindings. @@ -252,13 +251,17 @@ class ServerHttpBoundProtocolTraitImplGenerator( .await .map_err(Into::into) }; + use #{FuturesUtil}::future::TryFutureExt; + let fut = fut.map_err(|e: #{RequestRejection}| { + #{Tracing}::debug!(error = %e, "failed to deserialize request"); + #{RuntimeError}::from(e) + }); $inputFuture { inner: Box::pin(fut) } } } - - """.trimIndent(), + """, *codegenScope, "I" to inputSymbol, "Marker" to protocol.markerStruct(), diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs index 10b94964df..491e865dd6 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs @@ -18,6 +18,8 @@ pub enum ResponseRejection { pub enum RequestRejection { #[error("error converting non-streaming body to bytes: {0}")] BufferHttpBodyBytes(crate::Error), + #[error("request contains invalid value for `Accept` header")] + NotAcceptable, #[error("expected `Content-Type` header not found: {0}")] MissingContentType(#[from] MissingContentTypeReason), #[error("error deserializing request HTTP body as JSON: {0}")] diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs index 87925e2f03..5ccf0225f7 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs @@ -109,6 +109,11 @@ pub enum RequestRejection { #[error("error converting non-streaming body to bytes: {0}")] BufferHttpBodyBytes(crate::Error), + /// Used when the request contained an `Accept` header with a MIME type, and the server cannot + /// return a response body adhering to that MIME type. + #[error("request contains invalid value for `Accept` header")] + NotAcceptable, + /// Used when checking the `Content-Type` header. #[error("expected `Content-Type` header not found: {0}")] MissingContentType(#[from] MissingContentTypeReason), diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/runtime_error.rs index fdf55e623a..0525d9b576 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/runtime_error.rs @@ -115,6 +115,7 @@ impl From for RuntimeError { match err { RequestRejection::MissingContentType(_reason) => Self::UnsupportedMediaType, RequestRejection::ConstraintViolation(reason) => Self::Validation(reason), + RequestRejection::NotAcceptable => Self::NotAcceptable, _ => Self::Serialization(crate::Error::new(err)), } } diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs index 829d330dfe..51b0ac0ad6 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs @@ -28,6 +28,9 @@ pub enum RequestRejection { #[error("error converting non-streaming body to bytes: {0}")] BufferHttpBodyBytes(crate::Error), + #[error("request contains invalid value for `Accept` header")] + NotAcceptable, + #[error("expected `Content-Type` header not found: {0}")] MissingContentType(#[from] MissingContentTypeReason), From 8498ce847ea27a3aac46362c6f7818ccd003e0f1 Mon Sep 17 00:00:00 2001 From: Julian Antonielli Date: Wed, 19 Apr 2023 13:52:48 +0100 Subject: [PATCH 032/253] Fix `rewrite_base_url`: don't discard query params (#2599) ## Description The previous version of this function discarded query params, which meant that if our model had a `@httpQuery` bound input, those would not be sent to the server and the requests would fail. ## Testing Ran existing tests ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- examples/pokemon-service-common/src/lib.rs | 15 ++++++++++++--- .../tests/plugins_execution_order.rs | 5 +---- examples/pokemon-service-tls/tests/common/mod.rs | 8 +++++--- examples/pokemon-service/tests/common/mod.rs | 8 +++++--- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/examples/pokemon-service-common/src/lib.rs b/examples/pokemon-service-common/src/lib.rs index f431270da6..0658151a05 100644 --- a/examples/pokemon-service-common/src/lib.rs +++ b/examples/pokemon-service-common/src/lib.rs @@ -17,6 +17,10 @@ use std::{ use async_stream::stream; use aws_smithy_http::operation::Request; use aws_smithy_http_server::Extension; +use http::{ + uri::{Authority, Scheme}, + Uri, +}; use pokemon_service_server_sdk::{ error, input, model, model::CapturingPayload, output, types::Blob, }; @@ -33,11 +37,16 @@ const PIKACHU_JAPANESE_FLAVOR_TEXT: &str = "ほっぺたの りょうがわに ちいさい でんきぶくろを もつ。ピンチのときに ほうでんする。"; /// Rewrites the base URL of a request -pub fn rewrite_base_url(base_url: String) -> impl Fn(Request) -> Request + Clone { +pub fn rewrite_base_url( + scheme: Scheme, + authority: Authority, +) -> impl Fn(Request) -> Request + Clone { move |mut req| { let http_req = req.http_mut(); - let uri = format!("{base_url}{}", http_req.uri().path()); - *http_req.uri_mut() = uri.parse().unwrap(); + let mut uri_parts = http_req.uri().clone().into_parts(); + uri_parts.authority = Some(authority.clone()); + uri_parts.scheme = Some(scheme.clone()); + *http_req.uri_mut() = Uri::from_parts(uri_parts).expect("failed to create uri from parts"); req } } diff --git a/examples/pokemon-service-common/tests/plugins_execution_order.rs b/examples/pokemon-service-common/tests/plugins_execution_order.rs index 78dccecde1..4965bbf9d8 100644 --- a/examples/pokemon-service-common/tests/plugins_execution_order.rs +++ b/examples/pokemon-service-common/tests/plugins_execution_order.rs @@ -64,10 +64,7 @@ struct SentinelPlugin { impl SentinelPlugin { pub fn new(name: &'static str, output: Arc>>) -> Self { - Self { - name, - output: output, - } + Self { name, output } } } diff --git a/examples/pokemon-service-tls/tests/common/mod.rs b/examples/pokemon-service-tls/tests/common/mod.rs index 746b7823e2..17c96adb52 100644 --- a/examples/pokemon-service-tls/tests/common/mod.rs +++ b/examples/pokemon-service-tls/tests/common/mod.rs @@ -3,13 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::{fs::File, io::BufReader, process::Command, time::Duration}; +use std::{fs::File, io::BufReader, process::Command, str::FromStr, time::Duration}; use assert_cmd::prelude::*; use aws_smithy_client::{ erase::{DynConnector, DynMiddleware}, hyper_ext::Adapter, }; +use hyper::http::uri::{Authority, Scheme}; use tokio::time::sleep; use pokemon_service_client::{Builder, Client, Config}; @@ -48,10 +49,11 @@ pub fn client_http2_only() -> Client> .enable_http2() .build(); - let base_url = format!("https://{DEFAULT_DOMAIN}:{DEFAULT_PORT}"); + let authority = Authority::from_str(&format!("{DEFAULT_DOMAIN}:{DEFAULT_PORT}")) + .expect("could not parse authority"); let raw_client = Builder::new() .connector(DynConnector::new(Adapter::builder().build(connector))) - .middleware_fn(rewrite_base_url(base_url)) + .middleware_fn(rewrite_base_url(Scheme::HTTPS, authority)) .build_dyn(); let config = Config::builder().build(); Client::with_config(raw_client, config) diff --git a/examples/pokemon-service/tests/common/mod.rs b/examples/pokemon-service/tests/common/mod.rs index b21eb2a78b..e73901feb5 100644 --- a/examples/pokemon-service/tests/common/mod.rs +++ b/examples/pokemon-service/tests/common/mod.rs @@ -3,10 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::{process::Command, time::Duration}; +use std::{process::Command, str::FromStr, time::Duration}; use assert_cmd::prelude::*; use aws_smithy_client::erase::{DynConnector, DynMiddleware}; +use hyper::http::uri::{Authority, Scheme}; use tokio::time::sleep; use pokemon_service::{DEFAULT_ADDRESS, DEFAULT_PORT}; @@ -25,10 +26,11 @@ pub async fn run_server() -> ChildDrop { } pub fn client() -> Client> { - let base_url = format!("http://{DEFAULT_ADDRESS}:{DEFAULT_PORT}"); + let authority = Authority::from_str(&format!("{DEFAULT_ADDRESS}:{DEFAULT_PORT}")) + .expect("could not parse authority"); let raw_client = Builder::new() .rustls_connector(Default::default()) - .middleware_fn(rewrite_base_url(base_url)) + .middleware_fn(rewrite_base_url(Scheme::HTTP, authority)) .build_dyn(); let config = Config::builder().build(); Client::with_config(raw_client, config) From d626fd281f409d125f29300b45381ebe3a397853 Mon Sep 17 00:00:00 2001 From: "Nate McMaster (AWS)" <88004173+mcmasn-amzn@users.noreply.github.com> Date: Wed, 19 Apr 2023 08:18:13 -0700 Subject: [PATCH 033/253] Fix for Rust compiler warning about derive helpers (#2581) ## Motivation and Context Follow-up to https://github.com/awslabs/smithy-rs/pull/2434 ## Description This provides a way to fix a compiler warning. Attributes created using RustMetadata.additionalAttributes may trigger compiler warnings (https://github.com/rust-lang/rust/issues/79202) such as ``` warning: derive helper attribute is used before it is introduced --> src/model.rs:7674:3 | 7674 | #[serde(tag = "_type", content = "_content")] | ^^^^^ 7675 | #[derive( 7676 | serde::Deserialize, | ------------------ the attribute is introduced here | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #79202 ``` ## Testing Added a unit test to validate the sort order is applied correctly to Attributes with isDeriveHelper = true. --------- Co-authored-by: david-perez --- .../rust/codegen/core/rustlang/RustType.kt | 17 ++++++++++++++--- .../codegen/core/rustlang/RustWriterTest.kt | 13 +++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index e4d27fe451..56f0b63c52 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -385,11 +385,17 @@ data class RustMetadata( this.copy(derives = derives - withoutDerives.toSet()) fun renderAttributes(writer: RustWriter): RustMetadata { - additionalAttributes.forEach { + val (deriveHelperAttrs, otherAttrs) = additionalAttributes.partition { it.isDeriveHelper } + otherAttrs.forEach { it.render(writer) } + Attribute(derive(derives)).render(writer) + // Derive helper attributes must come after derive, see https://github.com/rust-lang/rust/issues/79202 + deriveHelperAttrs.forEach { + it.render(writer) + } return this } @@ -450,17 +456,22 @@ enum class AttributeKind { * [Attributes](https://doc.rust-lang.org/reference/attributes.html) are general free form metadata * that are interpreted by the compiler. * + * If the attribute is a "derive helper", such as `#[serde]`, set `isDeriveHelper` to `true` so it is sorted correctly after + * the derive attribute is rendered. (See https://github.com/rust-lang/rust/issues/79202 for why sorting matters.) + * * For example: * ```rust + * #[allow(missing_docs)] // <-- this is an attribute, and it is not a derive helper * #[derive(Clone, PartialEq, Serialize)] // <-- this is an attribute - * #[serde(serialize_with = "abc")] // <-- this is an attribute + * #[serde(serialize_with = "abc")] // <-- this attribute is a derive helper because the `Serialize` derive uses it * struct Abc { * a: i64 * } * ``` */ -class Attribute(val inner: Writable) { +class Attribute(val inner: Writable, val isDeriveHelper: Boolean = false) { constructor(str: String) : this(writable(str)) + constructor(str: String, isDeriveHelper: Boolean) : this(writable(str), isDeriveHelper) constructor(runtimeType: RuntimeType) : this(runtimeType.writable) fun render(writer: RustWriter, attributeKind: AttributeKind = AttributeKind.Outer) { diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt index 3b0ef7c557..ac14bcd20f 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt @@ -133,6 +133,19 @@ class RustWriterTest { sut.toString().shouldContain("#[foo]\n/// here's an attribute") } + @Test + fun `attributes with derive helpers must come after derives`() { + val attr = Attribute("foo", isDeriveHelper = true) + val metadata = RustMetadata( + derives = setOf(RuntimeType.Debug), + additionalAttributes = listOf(Attribute.AllowDeprecated, attr), + visibility = Visibility.PUBLIC, + ) + val sut = RustWriter.root() + metadata.render(sut) + sut.toString().shouldContain("#[allow(deprecated)]\n#[derive(std::fmt::Debug)]\n#[foo]") + } + @Test fun `deprecated attribute without any field`() { val sut = RustWriter.root() From b9a3ded4d21fb24e19df2ec3ab1cc1af74958b00 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Wed, 19 Apr 2023 16:54:21 +0100 Subject: [PATCH 034/253] Remove hardcoded crate names from `run_server` functions (#2529) ## Motivation and Context In the case of a crate rename we don't want these functions to break. ## Description Replace string literals representing the example crate name with `std::env::var("CARGO_PKG_NAME").unwrap()` --- examples/pokemon-service-tls/tests/common/mod.rs | 6 ++---- examples/pokemon-service/src/main.rs | 1 + examples/pokemon-service/tests/common/mod.rs | 6 ++---- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/examples/pokemon-service-tls/tests/common/mod.rs b/examples/pokemon-service-tls/tests/common/mod.rs index 17c96adb52..5eb9d804ba 100644 --- a/examples/pokemon-service-tls/tests/common/mod.rs +++ b/examples/pokemon-service-tls/tests/common/mod.rs @@ -18,10 +18,8 @@ use pokemon_service_common::{rewrite_base_url, ChildDrop}; use pokemon_service_tls::{DEFAULT_DOMAIN, DEFAULT_PORT, DEFAULT_TEST_CERT}; pub async fn run_server() -> ChildDrop { - let child = Command::cargo_bin("pokemon-service-tls") - .unwrap() - .spawn() - .unwrap(); + let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); + let child = Command::cargo_bin(crate_name).unwrap().spawn().unwrap(); sleep(Duration::from_millis(500)).await; diff --git a/examples/pokemon-service/src/main.rs b/examples/pokemon-service/src/main.rs index f26e6d14ba..b62118c816 100644 --- a/examples/pokemon-service/src/main.rs +++ b/examples/pokemon-service/src/main.rs @@ -14,6 +14,7 @@ use aws_smithy_http_server::{ use clap::Parser; use plugin::PrintExt; + use pokemon_service::{ do_nothing_but_log_request_ids, get_storage_with_local_approved, DEFAULT_ADDRESS, DEFAULT_PORT, }; diff --git a/examples/pokemon-service/tests/common/mod.rs b/examples/pokemon-service/tests/common/mod.rs index e73901feb5..0cc34064df 100644 --- a/examples/pokemon-service/tests/common/mod.rs +++ b/examples/pokemon-service/tests/common/mod.rs @@ -15,10 +15,8 @@ use pokemon_service_client::{Builder, Client, Config}; use pokemon_service_common::{rewrite_base_url, ChildDrop}; pub async fn run_server() -> ChildDrop { - let child = Command::cargo_bin("pokemon-service") - .unwrap() - .spawn() - .unwrap(); + let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); + let child = Command::cargo_bin(crate_name).unwrap().spawn().unwrap(); sleep(Duration::from_millis(500)).await; From ab6f607f0bc44b668145a1257ddab8f6363fbdbe Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Wed, 19 Apr 2023 11:48:50 -0500 Subject: [PATCH 035/253] add: orchestrator design doc (#2543) This document is intended to capture the philosophy and ideas underpinning the current state of the orchestrator and should grow alongside the orchestrator. Abhinav requested something like this and I think it'll be helpful for future people. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti --- design/src/SUMMARY.md | 3 + design/src/client/orchestrator.md | 211 ++++++++++++++++++++++++++++++ design/src/client/overview.md | 6 + 3 files changed, 220 insertions(+) create mode 100644 design/src/client/orchestrator.md create mode 100644 design/src/client/overview.md diff --git a/design/src/SUMMARY.md b/design/src/SUMMARY.md index ae41ea5246..35979a3237 100644 --- a/design/src/SUMMARY.md +++ b/design/src/SUMMARY.md @@ -14,6 +14,9 @@ - [Endpoint Resolution](smithy/endpoint.md) - [Backwards Compatibility](smithy/backwards-compat.md) +- [Client](./client/overview.md) + - [What is the 'orchestrator' and why does it exist?](./client/orchestrator.md) + - [Server](./server/overview.md) - [Middleware](./server/middleware.md) - [Instrumentation](./server/instrumentation.md) diff --git a/design/src/client/orchestrator.md b/design/src/client/orchestrator.md new file mode 100644 index 0000000000..b773d5b13b --- /dev/null +++ b/design/src/client/orchestrator.md @@ -0,0 +1,211 @@ +## What is the orchestrator? + +At a very high level, an orchestrator is a process for transforming requests into responses. Please enjoy this fancy chart: + +```mermaid +flowchart TB + A(Orchestrate)-->|Input|B(Request serialization) + B-->|Transmit Request|C(Connection) + C-->|Transmit Response|D(Response deserialization) + D-->|Success|E("Ok(Output)") + D-->|Unretryable Failure|F("Err(SdkError)") + D-->|Retryable Failure|C +``` + +This process is also referred to as the "request/response lifecycle." In this example, the types of "transmit request" and "transmit response" are protocol-dependent. Typical operations use [HTTP], but we plan to support other protocols like [MQTT] in the future. + +In addition to the above steps, the orchestrator must also handle: + +- **Endpoint resolution:** figuring out which URL to send a request to. +- **Authentication, identity resolution, and request signing:** Figuring out who is sending the request, their credentials, and how we should insert the credentials into a request. +- **Interceptors**: Running lifecycle hooks at each point in the request/response lifecycle. +- **Runtime Plugins:** Resolving configuration from config builders. +- **Retries:** Categorizing responses from services and deciding whether to retry and how long to wait before doing so. +- **Trace Probes:** A [sink] for events that occur during the request/response lifecycle. + +## How is an orchestrator configured? + +While the structure of an orchestrator is fixed, the actions it takes during its lifecycle are highly configurable. Users have two ways to configure this process: + +- **Runtime Plugins**: + - **When can these be set?** Any time before calling `orchestrate`. + - **When are they called by the orchestrator?** In two batches, at the very beginning of `orchestrate`. + - **What can they do?** + - They can set configuration to be used by the orchestrator or in interceptors. + - They can set interceptors. + - **Are they user-definable?** No. At present, only smithy-rs maintainers may define these. +- **Interceptors**: + - **When can these be set?** Any time before calling `orchestrate`. + - **When are they called by the orchestrator?** At each step in the request-response lifecycle. + - **What can they do?** + - They can set configuration to be used by the orchestrator or in interceptors. + - They can log information. + - Depending on when they're run, they can modify the input, transmit request, transmit response, and the output/error. + - **Are they user-definable?** Yes. + +Configuration for a request is constructed by runtime plugins just after calling `orchestrate`. Configuration is stored in a `ConfigBag`: a hash map that's keyed on type's [`TypeId`][TypeId] (an opaque object, managed by the Rust compiler, which references some type.) + +## What does the orchestrator do? + +The orchestrator's work is divided into four phases: + +*NOTE: If an interceptor fails, then the other interceptors for that lifecycle event are still run. All resulting errors are collected and emitted together.* + +0. **Building the `ConfigBag` and mounting interceptors**. + - *This phase is infallible.* + - An interceptor context is created. This will hold request and response objects, making them available to interceptors. + - All runtime plugins set at the client-level are run. These plugins can set config and mount interceptors. Any _"read before execution"_ interceptors that have been set get run. + - All runtime plugins set at the operation-level are run. These plugins can also set config and mount interceptors. Any new _"read before execution"_ interceptors that have been set get run. +1. **Request Construction** + - *This phase is fallible.* + - The _"read before serialization"_ and _"modify before serialization"_ interceptors are called. + - The input is serialized into a transmit request. + - The _"read after serialization"_ and _"modify before retry_ loop" interceptors are called. + - Before making an attempt, the retry handler is called to check if an attempt should be made. The retry handler makes this decision for an initial attempt as well as for the retry attempts. If an initial attempt should be made, then the orchestrator enters the Dispatch phase. Otherwise, a throttling error is returned. +2. **Request Dispatch** + - *This phase is fallible. This phase's tasks are performed in a loop. Retryable request failures will be retried, and unretryable failures will end the loop.* + - The _"read before attempt"_ interceptors are run. + - An endpoint is resolved according to an endpoint resolver. The resolved endpoint is then applied to the transmit request. + - The _"read before signing"_ and _"modify before signing"_ interceptors are run. + - An identity and a signer are resolved according to an authentication resolver. The signer then signs the transmit request with the identity. + - The _"read after signing"_, _"read before transmit"_, and _"modify before transmit"_ interceptors are run. + - The transmit request is passed into the connection, and a transmit response is received. + - The _"read after transmit"_, _"read before deserialization"_, and _"modify before deserialization"_ interceptors are run. + - The transmit response is deserialized. + - The _"read after attempt"_ and _"modify before attempt_ completion" interceptors are run. + - The retry strategy is called to check if a retry is necessary. If a retry is required, the Dispatch phase restarts. Otherwise, the orchestrator enters the Response Handling phase. +3. **Response Handling** + - *This phase is fallible.* + - The _"read after deserialization"_ and _"modify before completion"_ interceptors are run. + - Events are dispatched to any trace probes that the user has set. + - The _"read after execution"_ interceptors are run. + +At the end of all this, the response is returned. If an error occurred at any point, then the response will contain one or more errors, depending on what failed. Otherwise, the output will be returned. + +## How is the orchestrator implemented in Rust? + +### Avoiding generics at all costs + +In designing the orchestrator, we sought to solve the problems we had with the original smithy client. The client made heavy use of generics, allowing for increased performance, but at the cost of increased maintenance burden and [increased compile times][monomorphization]. The Rust compiler, usually very helpful, isn't well-equipped to explain trait errors when bounds are this complex, and so the resulting client was difficult to extend. [Trait aliases] would have helped, but they're not (at the time of writing) available. + +*The type signatures for the old client and its `call` method:* + +```rust +impl Client +where + C: bounds::SmithyConnector, + M: bounds::SmithyMiddleware, + R: retry::NewRequestPolicy, +{ + pub async fn call(&self, op: Operation) -> Result> + where + O: Send + Sync, + E: std::error::Error + Send + Sync + 'static, + Retry: Send + Sync, + R::Policy: bounds::SmithyRetryPolicy, + Retry: ClassifyRetry, SdkError>, + bounds::Parsed<>::Service, O, Retry>: + Service, Response=SdkSuccess, Error=SdkError> + Clone, + { + self.call_raw(op).await.map(|res| res.parsed) + } + + pub async fn call_raw( + &self, + op: Operation, + ) -> Result, SdkError> + where + O: Send + Sync, + E: std::error::Error + Send + Sync + 'static, + Retry: Send + Sync, + R::Policy: bounds::SmithyRetryPolicy, + Retry: ClassifyRetry, SdkError>, + // This bound is not _technically_ inferred by all the previous bounds, but in practice it + // is because _we_ know that there is only implementation of Service for Parsed + // (ParsedResponseService), and it will apply as long as the bounds on C, M, and R hold, + // and will produce (as expected) Response = SdkSuccess, Error = SdkError. But Rust + // doesn't know that -- there _could_ theoretically be other implementations of Service for + // Parsed that don't return those same types. So, we must give the bound. + bounds::Parsed<>::Service, O, Retry>: + Service, Response=SdkSuccess, Error=SdkError> + Clone, + { + // The request/response lifecycle + } +} +``` + +*The type signature for the new `orchestrate` method:* + +```rust +pub async fn orchestrate( + input: Input, + runtime_plugins: &RuntimePlugins, + // Currently, SdkError is HTTP-only. We currently use it for backwards-compatibility purposes. + // The `HttpResponse` generic will likely be removed in the future. +) -> Result> { + // The request/response lifecycle +} +``` + +Wait a second, I hear you ask. "I see an `Input` and `Output` there, but you're not declaring any generic type arguments. What gives?" + +I'm glad you asked. Generally, when you need traits, but you aren't willing to use generic type arguments, then you must [`Box`][std::boxed]. Polymorphism is achieved through [dynamic dispatch] instead of [static dispatch], and this comes with a small runtime cost. + +So, what are `Input` and `Output`? They're our own special flavor of a boxed trait object. + +```rust +pub type Input = TypeErasedBox; +pub type Output = TypeErasedBox; +pub type Error = TypeErasedBox; + +/// A new-type around `Box` +#[derive(Debug)] +pub struct TypeErasedBox { + inner: Box, +} +``` + +The orchestrator itself doesn't know about any concrete types. Instead, it passes boxed data between the various components of the request/response lifecycle. Individual components access data in two ways: + +1. **From the `ConfigBag`:** + - *(with an accessor)* `let retry_strategy = cfg.retry_strategy();` + - *(with the get method)* `let retry_strategy = cfg.get::>()` +2. **From the `InterceptorContext`:** + - *(owned)* `let put_object_input: PutObjectInput = ctx.take_input().unwrap().downcast().unwrap()?;` + - *(by reference)* `let put_object_input = ctx.input().unwrap().downcast_ref::().unwrap();` + +Users can only call `ConfigBag::get` or [downcast] a `TypeErasedBox` to types they have access to, which allows maintainers to ensure encapsulation. For example: a plugin writer may declare a private type, place it in the config bag, and then later retrieve it. Because the type is private, only code in the same crate/module can ever insert or retrieve it. Therefore, there's less worry that someone will depend on a hidden, internal detail and no worry they'll accidentally overwrite a type in the bag. + +> *NOTE: When inserting values into a config bag, using one of the `set_` methods is always preferred, as this prevents mistakes related to inserting similar, but incorrect types.* + +### The actual code + +The current implementation of `orchestrate` is defined [here][orchestrate-impl], in the [`aws-smithy-runtime` crate][aws-smithy-runtime]. Related code can be found in the [`aws-smithy-runtime-api` crate][aws-smithy-runtime]. + +## Frequently asked questions + +### Why can't users create and use their own runtime plugins? + +We chose to hide the runtime plugin API from users because we are concerned that exposing it will cause more problems than it solves. Instead, we encourage users to use interceptors. This is because, when setting a runtime plugin, any existing runtime plugin with the same type will be replaced. For example, there can only be one retry strategy or response deserializer. Errors resulting from unintentionally overriding a plugin would be difficult for users to diagnose, and would consume valuable development time. + +### Why does the orchestrator exist? + +The orchestrator exists because there is an AWS-internal initiative to bring the architecture of all AWS SDKs closer to one another. + +### Why does this document exist when there's already an orchestrator RFC? + +Because RFCs become outdated as designs evolve. It is our intention to keep this document up to date with our current implementation. + +[HTTP]: https://en.wikipedia.org/wiki/HTTP +[MQTT]: https://en.wikipedia.org/wiki/MQTT +[sink]: https://en.wikipedia.org/wiki/Sink_(computing) +[trait aliases]: https://github.com/rust-lang/rust/issues/41517 +[std::boxed]: https://doc.rust-lang.org/std/boxed/index.html +[monomorphization]: https://rustc-dev-guide.rust-lang.org/backend/monomorph.html#polymorphization +[static dispatch]: https://doc.rust-lang.org/book/ch10-01-syntax.html#performance-of-code-using-generics +[dynamic dispatch]: https://doc.rust-lang.org/book/ch17-02-trait-objects.html#trait-objects-perform-dynamic-dispatch +[TypeId]: https://doc.rust-lang.org/stable/std/any/struct.TypeId.html +[downcast]: https://en.wikipedia.org/wiki/Downcasting +[orchestrate-impl]: https://github.com/awslabs/smithy-rs/blob/8bc93fc04dd8c8d7447bfe3f5196a75cba0b10ba/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs#L23 +[aws-smithy-runtime]: https://github.com/awslabs/smithy-rs/tree/main/rust-runtime/aws-smithy-runtime +[aws-smithy-runtime-api]: https://github.com/awslabs/smithy-rs/tree/main/rust-runtime/aws-smithy-runtime-api diff --git a/design/src/client/overview.md b/design/src/client/overview.md new file mode 100644 index 0000000000..398d20d957 --- /dev/null +++ b/design/src/client/overview.md @@ -0,0 +1,6 @@ +# Smithy Client + +`smithy-rs` provides the ability to generate a client whose operations defined by a smithy model. The documents referenced +here explain aspects of the client in greater detail. + +- [What is the 'orchestrator' and why does it exist?](./orchestrator.md) From 2e6b6345ee10858733a8c6b3523401734b9d84fe Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 19 Apr 2023 10:47:49 -0700 Subject: [PATCH 036/253] Add an identity and auth design doc (#2559) ## Motivation and Context This PR adds a design doc for client identity and auth for the new client orchestrator. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Zelda Hessler --- design/src/SUMMARY.md | 1 + design/src/client/identity_and_auth.md | 189 +++++++++++++++++++++++++ design/src/client/overview.md | 1 + 3 files changed, 191 insertions(+) create mode 100644 design/src/client/identity_and_auth.md diff --git a/design/src/SUMMARY.md b/design/src/SUMMARY.md index 35979a3237..6b3510a643 100644 --- a/design/src/SUMMARY.md +++ b/design/src/SUMMARY.md @@ -16,6 +16,7 @@ - [Client](./client/overview.md) - [What is the 'orchestrator' and why does it exist?](./client/orchestrator.md) + - [Identity and Auth](./client/identity_and_auth.md) - [Server](./server/overview.md) - [Middleware](./server/middleware.md) diff --git a/design/src/client/identity_and_auth.md b/design/src/client/identity_and_auth.md new file mode 100644 index 0000000000..e6cfb2cdac --- /dev/null +++ b/design/src/client/identity_and_auth.md @@ -0,0 +1,189 @@ +Identity and Auth in Clients +============================ + +The [Smithy specification establishes several auth related modeling traits] that can be applied to +operation and service shapes. To briefly summarize: + +- The auth schemes that are supported by a service are declared on the service shape +- Operation shapes MAY specify the subset of service-defined auth schemes they support. If none are specified, then all service-defined auth schemes are supported. + +A smithy code generator MUST support at least one auth scheme for every modeled operation, but it need not support ALL modeled auth schemes. + +This design document establishes how smithy-rs implements this specification. + +Terminology +----------- + +- **Auth:** Either a shorthand that represents both of the authentication and authorization terms below, +or an ambiguous representation of one of them. In this doc, this term will always refer to both. +- **Authentication:** The process of proving an entity is who they claim they are, sometimes referred to as AuthN. +- **Authorization:** The process of granting an authenticated entity the permission to +do something, sometimes referred to as AuthZ. +- **Identity:** The information required for authentication. +- **Signing:** The process of attaching metadata to a request that allows a server to authenticate that request. + +Overview of Smithy Client Auth +------------------------------ + +There are two stages to identity and auth: +1. Configuration +2. Execution + +### The configuration stage + +First, let's establish the aspects of auth that can be configured from the model at codegen time. + +- **Data** + - **AuthOptionResolverParams:** parameters required to resolve auth options. These parameters are allowed + to come from both the client config and the operation input structs. + - **HttpAuthSchemes:** a list of auth schemes that can be used to sign HTTP requests. This information + comes directly from the service model. + - **AuthSchemeProperties:** configuration from the auth scheme for the signer. + - **IdentityResolvers:** list of available identity resolvers. +- **Implementations** + - **IdentityResolver:** resolves an identity for use in authentication. + There can be multiple identity resolvers that need to be selected from. + - **HttpRequestSigner:** a signing implementation that signs a HTTP request. + - **AuthOptionResolver:** resolves a list of auth options for a given operation and its inputs. + +As it is undocumented (at time of writing), this document assumes that the code generator +creates one service-level runtime plugin, and an operation-level runtime plugin per operation, hence +referred to as the service runtime plugin and operation runtime plugin. + +The code generator emits code to add identity resolvers and HTTP auth schemes to the config bag +in the service runtime plugin. It then emits code to register an interceptor in the operation runtime +plugin that reads the operation input to generate the auth option resolver params (which also get added +to the config bag). + +### The execution stage + +At a high-level, the process of resolving an identity and signing a request looks as follows: + +1. Retrieve the `AuthOptionResolverParams` from the config bag. The `AuthOptionResolverParams` allow client +config and operation inputs to play a role in which auth option is selected. +2. Retrieve the `AuthOptionResolver` from the config bag, and use it to resolve the auth options available +with the `AuthOptionResolverParams`. The returned auth options are in priority order. +3. Retrieve the `IdentityResolvers` list from the config bag. +4. For each auth option: + 1. Attempt to find an HTTP auth scheme for that auth option in the config bag (from the `HttpAuthSchemes` list). + 2. If an auth scheme is found: + 1. Use the auth scheme to extract the correct identity resolver from the `IdentityResolvers` list. + 2. Retrieve the `HttpRequestSigner` implementation from the auth scheme. + 3. Use the `IdentityResolver` to resolve the identity needed for signing. + 4. Sign the request with the identity, and break out of the loop from step #4. + +In general, it is assumed that if an HTTP auth scheme exists for an auth option, then an identity resolver +also exists for that auth option. Otherwise, the auth option was configured incorrectly during codegen. + +How this looks in Rust +---------------------- + +The client will use trait objects and dynamic dispatch for the `IdentityResolver`, +`HttpRequestSigner`, and `AuthOptionResolver` implementations. Generics could potentially be used, +but the number of generic arguments and trait bounds in the orchestrator would balloon to +unmaintainable levels if each configurable implementation in it was made generic. + +These traits look like this: + +```rust +#[derive(Clone, Debug)] +pub struct HttpAuthOption { + scheme_id: &'static str, + properties: Arc, +} + +pub trait AuthOptionResolver: Send + Sync + Debug { + fn resolve_auth_options<'a>( + &'a self, + params: &AuthOptionResolverParams, + ) -> Result, BoxError>; +} + +pub trait IdentityResolver: Send + Sync + Debug { + // `identity_properties` come from `HttpAuthOption::properties` + fn resolve_identity(&self, identity_properties: &PropertyBag) -> BoxFallibleFut; +} + +pub trait HttpRequestSigner: Send + Sync + Debug { + /// Return a signed version of the given request using the given identity. + /// + /// If the provided identity is incompatible with this signer, an error must be returned. + fn sign_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + // `signing_properties` come from `HttpAuthOption::properties` + signing_properties: &PropertyBag, + ) -> Result<(), BoxError>; +} +``` + +`IdentityResolver` and `HttpRequestSigner` implementations are both given an `Identity`, but +will need to understand what the concrete data type underlying that identity is. The `Identity` struct +uses a `Arc` to represent the actual identity data so that generics are not needed in +the traits: + +```rust +#[derive(Clone, Debug)] +pub struct Identity { + data: Arc, + expiration: Option, +} +``` + +Identities can often be cached and reused across several requests, which is why the `Identity` uses `Arc` +rather than `Box`. This also reduces the allocations required. The signer implementations +will use downcasting to access the identity data types they understand. For example, with AWS SigV4, +it might look like the following: + +```rust +fn sign_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + signing_properties: &PropertyBag +) -> Result<(), BoxError> { + let aws_credentials = identity.data::() + .ok_or_else(|| "The SigV4 signer requires AWS credentials")?; + let access_key = &aws_credentials.secret_access_key; + // -- snip -- +} +``` + +Also note that identity data structs are expected to censor their own sensitive fields, as +`Identity` implements the automatically derived `Debug` trait. + +### Challenges with this `Identity` design + +A keen observer would note that there is an `expiration` field on `Identity`, and may ask, "what about +non-expiring identities?" This is the result of a limitation on `Box`, where it can only be +downcasted to concrete types. There is no way to downcast to a `dyn Trait` since the information required +to verify that that type is that trait is lost at compile time (a `std::any::TypeId` only encodes information +about the concrete type). + +In an ideal world, it would be possible to extract the expiration like this: +```rust +pub trait ExpiringIdentity { + fn expiration(&self) -> SystemTime; +} + +let identity: Identity = some_identity(); +if let Some(expiration) = identity.data::<&dyn ExpiringIdentity>().map(ExpiringIdentity::expiration) { + // make a decision based on that expiration +} +``` + +Theoretically, you should be able to save off additional type information alongside the `Box` and use +unsafe code to transmute to known traits, but it is difficult to implement in practice, and adds unsafe code +in a security critical piece of code that could otherwise be avoided. + +The `expiration` field is a special case that is allowed onto the `Identity` struct directly since identity +cache implementations will always need to be aware of this piece of information, and having it as an `Option` +still allows for non-expiring identities. + +Ultimately, this design constrains `HttpRequestSigner` implementations to concrete types. There is no world +where an `HttpRequestSigner` can operate across multiple unknown identity data types via trait, and that +should be OK since the signer implementation can always be wrapped with an implementation that is aware +of the concrete type provided by the identity resolver, and can do any necessary conversions. + +[Smithy specification establishes several auth related modeling traits]: https://smithy.io/2.0/spec/authentication-traits.html diff --git a/design/src/client/overview.md b/design/src/client/overview.md index 398d20d957..4d4f31d7e0 100644 --- a/design/src/client/overview.md +++ b/design/src/client/overview.md @@ -4,3 +4,4 @@ here explain aspects of the client in greater detail. - [What is the 'orchestrator' and why does it exist?](./orchestrator.md) +- [Identity and Auth](./identity_and_auth.md) From 165253cdbc3dfbf252016cf840778df36eb6d64a Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Wed, 19 Apr 2023 16:44:03 -0500 Subject: [PATCH 037/253] Add skeleton for supporting operation level config (#2589) ## Motivation and Context This PR adds skeleton code for supporting operation level config behind `enableNewSmithyRuntime`. ## Description The approach is described in https://github.com/awslabs/smithy-rs/pull/2527, where fluent builders now have a new method, `config_override`, that takes a service config builder. The builder implements the `RuntimePlugin` trait and once `TODO(RuntimePlugins)` is implemented, allows itself to put field values into a config bag within `send_v2`. ## Testing Added a line of code to an ignored test in `sra_test`, which, however, does check compilation. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito Co-authored-by: John DiSanti --- .../aws-sdk-s3/tests/sra_test.rs | 1 + .../smithy/generators/ServiceGenerator.kt | 7 +- .../client/FluentClientGenerator.kt | 126 +++++++++++++----- .../config/ServiceConfigGenerator.kt | 24 +++- .../inlineable/src/idempotency_token.rs | 2 + 5 files changed, 127 insertions(+), 33 deletions(-) diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index da778c3737..2250e7927e 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -59,6 +59,7 @@ async fn sra_test() { let _ = dbg!( client .list_objects_v2() + .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) .bucket("test-bucket") .prefix("prefix~") .send_v2() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index 3be412dfee..ac5804aed9 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -40,14 +40,17 @@ class ServiceGenerator( ).render(rustCrate) rustCrate.withModule(ClientRustModule.Config) { - ServiceConfigGenerator.withBaseBehavior( + val serviceConfigGenerator = ServiceConfigGenerator.withBaseBehavior( codegenContext, extraCustomizations = decorator.configCustomizations(codegenContext, listOf()), - ).render(this) + ) + serviceConfigGenerator.render(this) if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { ServiceRuntimePluginGenerator(codegenContext) .render(this, decorator.serviceRuntimePluginCustomizations(codegenContext, emptyList())) + + serviceConfigGenerator.renderRuntimePluginImplForBuilder(this, codegenContext) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 6669ee04f1..062e9d3d0a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -33,10 +33,12 @@ import software.amazon.smithy.rust.codegen.core.rustlang.escape import software.amazon.smithy.rust.codegen.core.rustlang.normalizeHtml import software.amazon.smithy.rust.codegen.core.rustlang.qualifiedName import software.amazon.smithy.rust.codegen.core.rustlang.render +import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTypeParameters import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter +import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate 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.RustCrate @@ -60,11 +62,13 @@ class FluentClientGenerator( client = RuntimeType.smithyClient(codegenContext.runtimeConfig), ), private val customizations: List = emptyList(), - private val retryClassifier: RuntimeType = RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("retry::DefaultResponseRetryClassifier"), + private val retryClassifier: RuntimeType = RuntimeType.smithyHttp(codegenContext.runtimeConfig) + .resolve("retry::DefaultResponseRetryClassifier"), ) { companion object { fun clientOperationFnName(operationShape: OperationShape, symbolProvider: RustSymbolProvider): String = RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(operationShape).name.toSnakeCase()) + fun clientOperationModuleName(operationShape: OperationShape, symbolProvider: RustSymbolProvider): String = RustReservedWords.escapeIfNeeded( symbolProvider.toSymbol(operationShape).name.toSnakeCase(), @@ -79,6 +83,7 @@ class FluentClientGenerator( private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val core = FluentClientCore(model) + private val enableNewSmithyRuntime = codegenContext.settings.codegenConfig.enableNewSmithyRuntime fun render(crate: RustCrate) { renderFluentClient(crate) @@ -242,18 +247,23 @@ class FluentClientGenerator( documentShape(operation, model, autoSuppressMissingDocs = false) deprecatedShape(operation) Attribute(derive(derives.toSet())).render(this) - rustTemplate( - """ - pub struct $builderName#{generics:W} { + withBlockTemplate( + "pub struct $builderName#{generics:W} {", + "}", + "generics" to generics.decl, + ) { + rustTemplate( + """ handle: std::sync::Arc, - inner: #{Inner} + inner: #{Inner}, + """, + "Inner" to symbolProvider.symbolForBuilder(input), + "generics" to generics.decl, + ) + if (enableNewSmithyRuntime) { + rust("config_override: std::option::Option,") } - """, - "Inner" to symbolProvider.symbolForBuilder(input), - "client" to RuntimeType.smithyClient(runtimeConfig), - "generics" to generics.decl, - "operation" to operationSymbol, - ) + } rustBlockTemplate( "impl${generics.inst} $builderName${generics.inst} #{bounds:W}", @@ -263,14 +273,24 @@ class FluentClientGenerator( val outputType = symbolProvider.toSymbol(operation.outputShape(model)) val errorType = symbolProvider.symbolForOperationError(operation) - // Have to use fully-qualified result here or else it could conflict with an op named Result + rust("/// Creates a new `${operationSymbol.name}`.") + withBlockTemplate( + "pub(crate) fn new(handle: std::sync::Arc) -> Self {", + "}", + "generics" to generics.decl, + ) { + withBlockTemplate( + "Self {", + "}", + ) { + rust("handle, inner: Default::default(),") + if (enableNewSmithyRuntime) { + rust("config_override: None,") + } + } + } rustTemplate( """ - /// Creates a new `${operationSymbol.name}`. - pub(crate) fn new(handle: std::sync::Arc) -> Self { - Self { handle, inner: Default::default() } - } - /// Consume this builder, creating a customizable operation that can be modified before being /// sent. The operation's inner [http::Request] can be modified as well. pub async fn customize(self) -> std::result::Result< @@ -316,7 +336,7 @@ class FluentClientGenerator( generics.toRustGenerics(), ), ) - if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + if (enableNewSmithyRuntime) { rustTemplate( """ // TODO(enableNewSmithyRuntime): Replace `send` with `send_v2` @@ -329,9 +349,12 @@ class FluentClientGenerator( /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be /// set when configuring the client. pub async fn send_v2(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { - let runtime_plugins = #{RuntimePlugins}::new() - .with_client_plugin(crate::config::ServiceRuntimePlugin::new(self.handle.clone())) - .with_operation_plugin(#{Operation}::new()); + let mut runtime_plugins = #{RuntimePlugins}::new() + .with_client_plugin(crate::config::ServiceRuntimePlugin::new(self.handle.clone())); + if let Some(config_override) = self.config_override { + runtime_plugins = runtime_plugins.with_operation_plugin(config_override); + } + runtime_plugins = runtime_plugins.with_operation_plugin(#{Operation}::new()); let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; let input = #{TypedBox}::new(input).erase(); let output = #{invoke}(input, &runtime_plugins) @@ -346,29 +369,72 @@ class FluentClientGenerator( Ok(#{TypedBox}::<#{OperationOutput}>::assume_from(output).expect("correct output type").unwrap()) } """, - "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse"), + "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::HttpResponse"), "OperationError" to errorType, "Operation" to symbolProvider.toSymbol(operation), "OperationOutput" to outputType, - "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugins"), + "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::RuntimePlugins"), "SdkError" to RuntimeType.sdkError(runtimeConfig), "TypedBox" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypedBox"), "invoke" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke"), ) - } - PaginatorGenerator.paginatorType(codegenContext, generics, operation, retryClassifier)?.also { paginatorType -> + rustTemplate( """ - /// Create a paginator for this request + /// Sets the `config_override` for the builder. + /// + /// `config_override` is applied to the operation configuration level. + /// The fields in the builder that are `Some` override those applied to the service + /// configuration level. For instance, + /// + /// Config A overridden by Config B == Config C + /// field_1: None, field_1: Some(v2), field_1: Some(v2), + /// field_2: Some(v1), field_2: Some(v2), field_2: Some(v2), + /// field_3: Some(v1), field_3: None, field_3: Some(v1), + pub fn config_override( + mut self, + config_override: impl Into, + ) -> Self { + self.set_config_override(Some(config_override.into())); + self + } + + /// Sets the `config_override` for the builder. + /// + /// `config_override` is applied to the operation configuration level. + /// The fields in the builder that are `Some` override those applied to the service + /// configuration level. For instance, /// - /// Paginators are used by calling [`send().await`](#{Paginator}::send) which returns a `Stream`. - pub fn into_paginator(self) -> #{Paginator}${generics.inst} { - #{Paginator}::new(self.handle, self.inner) + /// Config A overridden by Config B == Config C + /// field_1: None, field_1: Some(v2), field_1: Some(v2), + /// field_2: Some(v1), field_2: Some(v2), field_2: Some(v2), + /// field_3: Some(v1), field_3: None, field_3: Some(v1), + pub fn set_config_override( + &mut self, + config_override: Option, + ) -> &mut Self { + self.config_override = config_override; + self } """, - "Paginator" to paginatorType, ) } + PaginatorGenerator.paginatorType(codegenContext, generics, operation, retryClassifier) + ?.also { paginatorType -> + rustTemplate( + """ + /// Create a paginator for this request + /// + /// Paginators are used by calling [`send().await`](#{Paginator}::send) which returns a `Stream`. + pub fn into_paginator(self) -> #{Paginator}${generics.inst} { + #{Paginator}::new(self.handle, self.inner) + } + """, + "Paginator" to paginatorType, + ) + } writeCustomizations( customizations, FluentClientSection.FluentBuilderImpl( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 00bc592ff4..f6972f5e6a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -20,9 +20,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.docsOrFallback import software.amazon.smithy.rust.codegen.core.rustlang.raw import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.CodegenContext +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.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.makeOptional @@ -226,7 +228,7 @@ class ServiceConfigGenerator(private val customizations: List Result<(), #{BoxError}> { + // TODO(RuntimePlugins): Put into `cfg` the fields in `self.config_override` that are not `None`. + + Ok(()) + } + """, + "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), + "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), + ) + } + } } diff --git a/rust-runtime/inlineable/src/idempotency_token.rs b/rust-runtime/inlineable/src/idempotency_token.rs index 9d55f46db5..ac71874bc1 100644 --- a/rust-runtime/inlineable/src/idempotency_token.rs +++ b/rust-runtime/inlineable/src/idempotency_token.rs @@ -37,10 +37,12 @@ pub(crate) fn uuid_v4(input: u128) -> String { /// for testing, two options are available: /// 1. Utilize the From<&'static str>` implementation to hard code an idempotency token /// 2. Seed the token provider with [`IdempotencyTokenProvider::with_seed`](IdempotencyTokenProvider::with_seed) +#[derive(Debug)] pub struct IdempotencyTokenProvider { inner: Inner, } +#[derive(Debug)] enum Inner { Static(&'static str), Random(Mutex), From c805f89be6949bb9f75c142d594a49366b53c95a Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 20 Apr 2023 13:45:08 +0200 Subject: [PATCH 038/253] Undeprecate `{content_type,accept}_header_classifier` from `aws-smithy-http-server` (#2604) These functions were mistakenly deprecated when deprecating the old service builder API in #1886. However, they're fine, and they're internally used by the generated server SDKs. The module they reside in is `#[doc(hidden)]`. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- rust-runtime/aws-smithy-http-server/src/protocols.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rust-runtime/aws-smithy-http-server/src/protocols.rs b/rust-runtime/aws-smithy-http-server/src/protocols.rs index a2428a768b..2db5e7163d 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocols.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocols.rs @@ -34,7 +34,7 @@ fn parse_content_type(headers: &HeaderMap) -> Result, @@ -66,7 +66,6 @@ pub fn content_type_header_classifier( Ok(()) } -#[allow(deprecated)] pub fn accept_header_classifier(headers: &HeaderMap, content_type: &'static str) -> bool { if !headers.contains_key(http::header::ACCEPT) { return true; @@ -105,7 +104,6 @@ pub fn accept_header_classifier(headers: &HeaderMap, content_type: &'static str) }) } -#[allow(deprecated)] #[cfg(test)] mod tests { use super::*; From 9c585a223b5c7b3416c274d7b854151996ae5445 Mon Sep 17 00:00:00 2001 From: Matteo Bigoi <1781140+crisidev@users.noreply.github.com> Date: Thu, 20 Apr 2023 15:18:15 +0100 Subject: [PATCH 039/253] [Python] Improve Python stubs generation (#2606) ## Motivation and Context This PR improves the Python stubs generation. ## Description The main change is about avoiding to setup a placeholder for the Python module and use the real module name, which allows to generate correct docstrings during codegeneration. We also change the stubs layout on disk, with the main stub entrypoint called `__init__.pyi` instead of `$module_name.pyi`. The README from the Rust runtime crate has been moved completely to the example folder and I run autoformatting and style checks on the Python example code. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Signed-off-by: Bigo <1781140+crisidev@users.noreply.github.com> Co-authored-by: Burak --- .../smithy/PythonServerCodegenVisitor.kt | 4 +- .../server/python/smithy/PythonType.kt | 42 ++++---- .../generators/PythonApplicationGenerator.kt | 12 +-- .../PythonServerStructureGenerator.kt | 13 ++- .../generators/PythonServerUnionGenerator.kt | 14 ++- .../PythonTypeInformationGenerationTest.kt | 3 +- examples/python/README.md | 99 +++++++++++++++---- examples/python/pokemon_service.py | 74 +++++--------- .../aws-smithy-http-server-python/README.md | 70 ------------- .../aws-smithy-http-server-python/stubgen.py | 2 +- 10 files changed, 156 insertions(+), 177 deletions(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index fa1f8eeb65..9dd3d0d658 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -152,7 +152,7 @@ class PythonServerCodegenVisitor( rustCrate.useShapeWriter(shape) { // Use Python specific structure generator that adds the #[pyclass] attribute // and #[pymethods] implementation. - PythonServerStructureGenerator(model, codegenContext.symbolProvider, this, shape).render() + PythonServerStructureGenerator(model, codegenContext, this, shape).render() shape.getTrait()?.also { errorTrait -> ErrorImplGenerator( @@ -190,7 +190,7 @@ class PythonServerCodegenVisitor( override fun unionShape(shape: UnionShape) { logger.info("[python-server-codegen] Generating an union shape $shape") rustCrate.useShapeWriter(shape) { - PythonServerUnionGenerator(model, codegenContext.symbolProvider, this, shape, renderUnknownVariant = false).render() + PythonServerUnionGenerator(model, codegenContext, this, shape, renderUnknownVariant = false).render() } if (shape.isReachableFromOperationInput() && shape.canReachConstrainedShape( diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt index 95af2cd14e..42f878d6fc 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt @@ -100,17 +100,14 @@ sealed class PythonType { override val namespace = type.namespace } - data class Opaque(override val name: String, val rustNamespace: String? = null) : PythonType() { - // Since Python doesn't have a something like Rust's `crate::` we are using a custom placeholder here - // and in our stub generation script we will replace placeholder with the real root module name. - private val pythonRootModulePlaceholder = "__root_module_name__" + data class Opaque(override val name: String, val pythonRootModuleName: String, val rustNamespace: String? = null) : PythonType() { override val namespace: String? = rustNamespace?.split("::")?.joinToString(".") { when (it) { - "crate" -> pythonRootModulePlaceholder + "crate" -> pythonRootModuleName // In Python, we expose submodules from `aws_smithy_http_server_python` - // like `types`, `middleware`, `tls` etc. from `__root_module__name` - "aws_smithy_http_server_python" -> pythonRootModulePlaceholder + // like `types`, `middleware`, `tls` etc. from Python root module + "aws_smithy_http_server_python" -> pythonRootModuleName else -> it } } @@ -120,26 +117,29 @@ sealed class PythonType { /** * Return corresponding [PythonType] for a [RustType]. */ -fun RustType.pythonType(): PythonType = +fun RustType.pythonType(pythonRootModuleName: String): PythonType = when (this) { is RustType.Unit -> PythonType.None is RustType.Bool -> PythonType.Bool is RustType.Float -> PythonType.Float is RustType.Integer -> PythonType.Int is RustType.String -> PythonType.Str - is RustType.Vec -> PythonType.List(this.member.pythonType()) - is RustType.Slice -> PythonType.List(this.member.pythonType()) - is RustType.HashMap -> PythonType.Dict(this.key.pythonType(), this.member.pythonType()) - is RustType.HashSet -> PythonType.Set(this.member.pythonType()) - is RustType.Reference -> this.member.pythonType() - is RustType.Option -> PythonType.Optional(this.member.pythonType()) - is RustType.Box -> this.member.pythonType() - is RustType.Dyn -> this.member.pythonType() - is RustType.Application -> PythonType.Application(this.type.pythonType(), this.args.map { it.pythonType() }) - is RustType.Opaque -> PythonType.Opaque(this.name, this.namespace) - // TODO(Constraints): How to handle this? - // Revisit as part of https://github.com/awslabs/smithy-rs/issues/2114 - is RustType.MaybeConstrained -> this.member.pythonType() + is RustType.Vec -> PythonType.List(this.member.pythonType(pythonRootModuleName)) + is RustType.Slice -> PythonType.List(this.member.pythonType(pythonRootModuleName)) + is RustType.HashMap -> PythonType.Dict(this.key.pythonType(pythonRootModuleName), this.member.pythonType(pythonRootModuleName)) + is RustType.HashSet -> PythonType.Set(this.member.pythonType(pythonRootModuleName)) + is RustType.Reference -> this.member.pythonType(pythonRootModuleName) + is RustType.Option -> PythonType.Optional(this.member.pythonType(pythonRootModuleName)) + is RustType.Box -> this.member.pythonType(pythonRootModuleName) + is RustType.Dyn -> this.member.pythonType(pythonRootModuleName) + is RustType.Application -> PythonType.Application( + this.type.pythonType(pythonRootModuleName), + this.args.map { + it.pythonType(pythonRootModuleName) + }, + ) + is RustType.Opaque -> PythonType.Opaque(this.name, pythonRootModuleName, rustNamespace = this.namespace) + is RustType.MaybeConstrained -> this.member.pythonType(pythonRootModuleName) } /** diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt index deec6f6b29..96ecbfe75c 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonApplicationGenerator.kt @@ -250,11 +250,11 @@ class PythonApplicationGenerator( """, *codegenScope, ) { - val middlewareRequest = PythonType.Opaque("Request", "crate::middleware") - val middlewareResponse = PythonType.Opaque("Response", "crate::middleware") + val middlewareRequest = PythonType.Opaque("Request", libName, rustNamespace = "crate::middleware") + val middlewareResponse = PythonType.Opaque("Response", libName, rustNamespace = "crate::middleware") val middlewareNext = PythonType.Callable(listOf(middlewareRequest), PythonType.Awaitable(middlewareResponse)) val middlewareFunc = PythonType.Callable(listOf(middlewareRequest, middlewareNext), PythonType.Awaitable(middlewareResponse)) - val tlsConfig = PythonType.Opaque("TlsConfig", "crate::tls") + val tlsConfig = PythonType.Opaque("TlsConfig", libName, rustNamespace = "crate::tls") rustTemplate( """ @@ -344,9 +344,9 @@ class PythonApplicationGenerator( val operationName = symbolProvider.toSymbol(operation).name val fnName = RustReservedWords.escapeIfNeeded(symbolProvider.toSymbol(operation).name.toSnakeCase()) - val input = PythonType.Opaque("${operationName}Input", "crate::input") - val output = PythonType.Opaque("${operationName}Output", "crate::output") - val context = PythonType.Opaque("Ctx") + val input = PythonType.Opaque("${operationName}Input", libName, rustNamespace = "crate::input") + val output = PythonType.Opaque("${operationName}Output", libName, rustNamespace = "crate::output") + val context = PythonType.Opaque("Ctx", libName) val returnType = PythonType.Union(listOf(output, PythonType.Awaitable(output))) val handler = PythonType.Union( listOf( diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt index 1502f5422a..d9d2df8cc2 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerStructureGenerator.kt @@ -18,16 +18,17 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustInlineTemplate 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.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureGenerator import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.isEventStream +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.python.smithy.PythonEventStreamSymbolProvider import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency import software.amazon.smithy.rust.codegen.server.python.smithy.PythonType import software.amazon.smithy.rust.codegen.server.python.smithy.pythonType import software.amazon.smithy.rust.codegen.server.python.smithy.renderAsDocstring +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /** * To share structures defined in Rust with Python, `pyo3` provides the `PyClass` trait. @@ -36,11 +37,13 @@ import software.amazon.smithy.rust.codegen.server.python.smithy.renderAsDocstrin */ class PythonServerStructureGenerator( model: Model, - private val symbolProvider: RustSymbolProvider, + private val codegenContext: ServerCodegenContext, private val writer: RustWriter, private val shape: StructureShape, -) : StructureGenerator(model, symbolProvider, writer, shape, emptyList()) { +) : StructureGenerator(model, codegenContext.symbolProvider, writer, shape, emptyList()) { + private val symbolProvider = codegenContext.symbolProvider + private val libName = codegenContext.settings.moduleName.toSnakeCase() private val pyO3 = PythonServerCargoDependency.PyO3.toType() override fun renderStructure() { @@ -157,9 +160,9 @@ class PythonServerStructureGenerator( private fun memberPythonType(shape: MemberShape, symbol: Symbol): PythonType = if (shape.isEventStream(model)) { val eventStreamSymbol = PythonEventStreamSymbolProvider.parseSymbol(symbol) - val innerT = eventStreamSymbol.innerT.pythonType() + val innerT = eventStreamSymbol.innerT.pythonType(libName) PythonType.AsyncIterator(innerT) } else { - symbol.rustType().pythonType() + symbol.rustType().pythonType(libName) } } diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt index 6b7a7bee8a..01a2a833af 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerUnionGenerator.kt @@ -26,6 +26,7 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency import software.amazon.smithy.rust.codegen.server.python.smithy.pythonType import software.amazon.smithy.rust.codegen.server.python.smithy.renderAsDocstring +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext /* * Generate unions that are compatible with Python by wrapping the Rust implementation into @@ -34,11 +35,13 @@ import software.amazon.smithy.rust.codegen.server.python.smithy.renderAsDocstrin */ class PythonServerUnionGenerator( model: Model, - private val symbolProvider: SymbolProvider, + private val codegenContext: ServerCodegenContext, private val writer: RustWriter, shape: UnionShape, private val renderUnknownVariant: Boolean = true, -) : UnionGenerator(model, symbolProvider, writer, shape, renderUnknownVariant) { +) : UnionGenerator(model, codegenContext.symbolProvider, writer, shape, renderUnknownVariant) { + private val symbolProvider = codegenContext.symbolProvider + private val libName = codegenContext.settings.moduleName.toSnakeCase() private val sortedMembers: List = shape.allMembers.values.sortedBy { symbolProvider.toMemberName(it) } private val unionSymbol = symbolProvider.toSymbol(shape) @@ -125,7 +128,7 @@ class PythonServerUnionGenerator( } } else { val memberSymbol = symbolProvider.toSymbol(member) - val pythonType = memberSymbol.rustType().pythonType() + val pythonType = memberSymbol.rustType().pythonType(libName) val targetType = memberSymbol.rustType() Attribute("staticmethod").render(writer) writer.rust( @@ -166,7 +169,7 @@ class PythonServerUnionGenerator( } } else { val memberSymbol = symbolProvider.toSymbol(member) - val pythonType = memberSymbol.rustType().pythonType() + val pythonType = memberSymbol.rustType().pythonType(libName) val targetSymbol = symbolProvider.toSymbol(model.expectShape(member.target)) val rustType = memberSymbol.rustType() writer.rust( @@ -181,12 +184,13 @@ class PythonServerUnionGenerator( } else { "variant.clone()" } + val errorVariant = memberSymbol.rustType().pythonType(libName).renderAsDocstring() rustTemplate( """ match self.0.as_$funcNamePart() { Ok(variant) => Ok($variantType), Err(_) => Err(#{pyo3}::exceptions::PyValueError::new_err( - r"${unionSymbol.name} variant is not of type ${memberSymbol.rustType().pythonType().renderAsDocstring()}" + r"${unionSymbol.name} variant is not of type $errorVariant" )), } """, diff --git a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonTypeInformationGenerationTest.kt b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonTypeInformationGenerationTest.kt index 1473edcffe..c552d32edd 100644 --- a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonTypeInformationGenerationTest.kt +++ b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonTypeInformationGenerationTest.kt @@ -28,9 +28,8 @@ internal class PythonTypeInformationGenerationTest { val foo = model.lookup("test#Foo") val codegenContext = serverTestCodegenContext(model) - val symbolProvider = codegenContext.symbolProvider val writer = RustWriter.forModule("model") - PythonServerStructureGenerator(model, symbolProvider, writer, foo).render() + PythonServerStructureGenerator(model, codegenContext, writer, foo).render() val result = writer.toString() diff --git a/examples/python/README.md b/examples/python/README.md index ebe6cfb39e..e3fd43929a 100644 --- a/examples/python/README.md +++ b/examples/python/README.md @@ -12,8 +12,8 @@ The Python implementation of the service can be found inside * [Python stub generation](#python-stub-generation) * [Run](#run) * [Test](#test) -* [Uvloop](#uvloop) * [MacOs](#macos) +* [Running servers on AWS Lambda](#running-servers-on-aws-lambda) ## Build @@ -26,10 +26,14 @@ build. Ensure these dependencies are installed. -``` +```bash pip install maturin uvloop aiohttp mypy ``` +The server can depend on [uvloop](https://pypi.org/project/uvloop/) for a faster +event loop implementation and it will be automatically used instead of the standard +library event loop if it is found in the dependencies' closure. + ### Makefile The build logic is drive by the Makefile: @@ -37,11 +41,13 @@ The build logic is drive by the Makefile: * `make codegen`: run the codegenerator. * `make build`: build the Maturin package in debug mode (includes type stubs generation). -* `make release`: build the Maturin package in release mode (includes type stub +* `make release`: build the Maturin package in release mode (includes type stubs generation). -* `make install`: install the latest release or debug build. +* `make install-wheel`: install the latest release or debug wheel using `pip`. * `make run`: run the example server. * `make test`: run the end-to-end integration test. +* `make clean`: clean the Cargo artefacts. +* `make dist-clean`: clean the Cargo artefacts and generated folders. ### Python stub generation @@ -52,7 +58,7 @@ It will do first compilation of the crate, generate the types and exit. The script takes some command line arguments: -``` +```bash ./stubgen.sh module_name manifest_path output_directory ``` @@ -72,19 +78,6 @@ tests against it. More info can be found in the `tests` folder of `pokemon-service-test` package. -## Uvloop - -The server can depend on [uvloop](https://pypi.org/project/uvloop/) for a -faster event loop implementation. Uvloop can be installed with your favourite -package manager or by using pip: - -```sh -pip instal uvloop -``` - -and it will be automatically used instead of the standard library event loop if -it is found in the dependencies' closure. - ## MacOs To compile and test on MacOs, please follow the official PyO3 guidelines on how @@ -92,3 +85,73 @@ to [configure your linker](https://pyo3.rs/latest/building_and_distribution.html Please note that the `.cargo/config.toml` with linkers override can be local to your project. + +## Running servers on AWS Lambda + +`aws-smithy-http-server-python` supports running your services on [AWS Lambda](https://aws.amazon.com/lambda/). + +You need to use `run_lambda` method instead of `run` method to start +the [custom runtime](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html) +instead of the [Hyper](https://hyper.rs/) HTTP server. + +In your `app.py`: + +```diff +from pokemon_service_server_sdk import App +from pokemon_service_server_sdk.error import ResourceNotFoundException + +# ... + +# Get the number of requests served by this server. +@app.get_server_statistics +def get_server_statistics( + _: GetServerStatisticsInput, context: Context +) -> GetServerStatisticsOutput: + calls_count = context.get_calls_count() + logging.debug("The service handled %d requests", calls_count) + return GetServerStatisticsOutput(calls_count=calls_count) + +# ... + +-app.run() ++app.run_lambda() +``` + +`aws-smithy-http-server-python` comes with a +[custom runtime](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html) +so you should run your service without any provided runtimes. +You can achieve that with a `Dockerfile` similar to this: + +```dockerfile +# You can use any image that has your desired Python version +FROM public.ecr.aws/lambda/python:3.8-x86_64 + +# Copy your application code to `LAMBDA_TASK_ROOT` +COPY app.py ${LAMBDA_TASK_ROOT} + +# When you build your Server SDK for your service, you will get a Python wheel. +# You just need to copy that wheel and install it via `pip` inside your image. +# Note that you need to build your library for Linux, and Python version used to +# build your SDK should match with your image's Python version. +# For cross compiling, you can consult to: +# https://pyo3.rs/latest/building_and_distribution.html#cross-compiling +COPY wheels/ ${LAMBDA_TASK_ROOT}/wheels +RUN pip3 install ${LAMBDA_TASK_ROOT}/wheels/*.whl + +# You can install your application's other dependencies listed in `requirements.txt`. +COPY requirements.txt . +RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" + +# Create a symlink for your application's entrypoint, +# so we can use `/app.py` to refer it +RUN ln -s ${LAMBDA_TASK_ROOT}/app.py /app.py + +# By default `public.ecr.aws/lambda/python` images comes with Python runtime, +# we need to override `ENTRYPOINT` and `CMD` to not call that runtime and +# instead run directly your service and it will start our custom runtime. +ENTRYPOINT [ "/var/lang/bin/python3.8" ] +CMD [ "/app.py" ] +``` + +See [https://docs.aws.amazon.com/lambda/latest/dg/images-create.html#images-create-from-base](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html#images-create-from-base) +for more details on building your custom image. diff --git a/examples/python/pokemon_service.py b/examples/python/pokemon_service.py index 210d232687..7ee4a7284f 100644 --- a/examples/python/pokemon_service.py +++ b/examples/python/pokemon_service.py @@ -6,47 +6,38 @@ import argparse import logging import random -from threading import Lock from dataclasses import dataclass -from typing import Dict, Any, List, Optional, Callable, Awaitable, AsyncIterator +from threading import Lock +from typing import Any, AsyncIterator, Awaitable, Callable, Dict, List, Optional from pokemon_service_server_sdk import App -from pokemon_service_server_sdk.tls import TlsConfig from pokemon_service_server_sdk.aws_lambda import LambdaContext from pokemon_service_server_sdk.error import ( + InvalidPokeballError, + MasterBallUnsuccessful, ResourceNotFoundException, UnsupportedRegionError, - MasterBallUnsuccessful, - InvalidPokeballError, ) from pokemon_service_server_sdk.input import ( + CapturePokemonInput, + CheckHealthInput, DoNothingInput, GetPokemonSpeciesInput, GetServerStatisticsInput, - CheckHealthInput, StreamPokemonRadioInput, - CapturePokemonInput, ) from pokemon_service_server_sdk.logging import TracingHandler -from pokemon_service_server_sdk.middleware import ( - MiddlewareException, - Response, - Request, -) -from pokemon_service_server_sdk.model import ( - CapturePokemonEvents, - CaptureEvent, - FlavorText, - Language, -) +from pokemon_service_server_sdk.middleware import MiddlewareException, Request, Response +from pokemon_service_server_sdk.model import CaptureEvent, CapturePokemonEvents, FlavorText, Language from pokemon_service_server_sdk.output import ( + CapturePokemonOutput, + CheckHealthOutput, DoNothingOutput, GetPokemonSpeciesOutput, GetServerStatisticsOutput, - CheckHealthOutput, StreamPokemonRadioOutput, - CapturePokemonOutput, ) +from pokemon_service_server_sdk.tls import TlsConfig from pokemon_service_server_sdk.types import ByteStream # Logging can bee setup using standard Python tooling. We provide @@ -164,17 +155,16 @@ def get_random_radio_stream(self) -> str: # Next is either the next middleware in the stack or the handler. Next = Callable[[Request], Awaitable[Response]] + # This middleware checks the `Content-Type` from the request header, # logs some information depending on that and then calls `next`. @app.middleware async def check_content_type_header(request: Request, next: Next) -> Response: content_type = request.headers.get("content-type") if content_type == "application/json": - logging.debug("Found valid `application/json` content type") + logging.debug("found valid `application/json` content type") else: - logging.warning( - f"Invalid content type {content_type}, dumping headers: {request.headers.items()}" - ) + logging.warning("invalid content type %s, dumping headers: %s", content_type, request.headers.items()) return await next(request) @@ -184,7 +174,7 @@ async def check_content_type_header(request: Request, next: Next) -> Response: @app.middleware async def add_x_amzn_answer_header(request: Request, next: Next) -> Response: request.headers["x-amzn-answer"] = "42" - logging.debug("Setting `x-amzn-answer` header to 42") + logging.debug("setting `x-amzn-answer` header to 42") return await next(request) @@ -205,18 +195,15 @@ async def check_x_amzn_answer_header(request: Request, next: Next) -> Response: # DoNothing operation used for raw benchmarking. @app.do_nothing def do_nothing(_: DoNothingInput) -> DoNothingOutput: - # logging.debug("Running the DoNothing operation") return DoNothingOutput() # Get the translation of a Pokémon specie or an error. @app.get_pokemon_species -def get_pokemon_species( - input: GetPokemonSpeciesInput, context: Context -) -> GetPokemonSpeciesOutput: +def get_pokemon_species(input: GetPokemonSpeciesInput, context: Context) -> GetPokemonSpeciesOutput: if context.lambda_ctx is not None: logging.debug( - "Lambda Context: %s", + "lambda Context: %s", dict( request_id=context.lambda_ctx.request_id, deadline=context.lambda_ctx.deadline, @@ -229,24 +216,19 @@ def get_pokemon_species( context.increment_calls_count() flavor_text_entries = context.get_pokemon_description(input.name) if flavor_text_entries: - logging.debug("Total requests executed: %s", context.get_calls_count()) - logging.info("Found description for Pokémon %s", input.name) - logging.error("Found some stuff") - return GetPokemonSpeciesOutput( - name=input.name, flavor_text_entries=flavor_text_entries - ) + logging.debug("total requests executed: %s", context.get_calls_count()) + logging.info("found description for Pokémon %s", input.name) + return GetPokemonSpeciesOutput(name=input.name, flavor_text_entries=flavor_text_entries) else: - logging.warning("Description for Pokémon %s not in the database", input.name) + logging.warning("description for Pokémon %s not in the database", input.name) raise ResourceNotFoundException("Requested Pokémon not available") # Get the number of requests served by this server. @app.get_server_statistics -def get_server_statistics( - _: GetServerStatisticsInput, context: Context -) -> GetServerStatisticsOutput: +def get_server_statistics(_: GetServerStatisticsInput, context: Context) -> GetServerStatisticsOutput: calls_count = context.get_calls_count() - logging.debug("The service handled %d requests", calls_count) + logging.debug("the service handled %d requests", calls_count) return GetServerStatisticsOutput(calls_count=calls_count) @@ -398,7 +380,7 @@ async def events(input: CapturePokemonInput) -> AsyncIterator[CapturePokemonEven break # You can catch modeled errors and act accordingly, they will terminate the event stream except MasterBallUnsuccessful as err: - logging.error(f"masterball unsuccessful: {err}") + logging.error("masterball unsuccessful: %s", err) # Here event stream is going to be completed because we stopped receiving events and we'll # no longer `yield` new values and this asynchronous Python generator will end here @@ -411,17 +393,15 @@ async def events(input: CapturePokemonInput) -> AsyncIterator[CapturePokemonEven # Stream a random Pokémon song. @app.stream_pokemon_radio -async def stream_pokemon_radio( - _: StreamPokemonRadioInput, context: Context -) -> StreamPokemonRadioOutput: +async def stream_pokemon_radio(_: StreamPokemonRadioInput, context: Context) -> StreamPokemonRadioOutput: import aiohttp radio_url = context.get_random_radio_stream() - logging.info("Random radio URL for this stream is %s", radio_url) + logging.info("random radio URL for this stream is %s", radio_url) async with aiohttp.ClientSession() as session: async with session.get(radio_url) as response: data = ByteStream(await response.read()) - logging.debug("Successfully fetched radio url %s", radio_url) + logging.debug("successfully fetched radio url %s", radio_url) return StreamPokemonRadioOutput(data=data) diff --git a/rust-runtime/aws-smithy-http-server-python/README.md b/rust-runtime/aws-smithy-http-server-python/README.md index 7b8d218f3c..da3dc2c131 100644 --- a/rust-runtime/aws-smithy-http-server-python/README.md +++ b/rust-runtime/aws-smithy-http-server-python/README.md @@ -2,76 +2,6 @@ Server libraries for smithy-rs generated servers, targeting pure Python business logic. -## Running servers on AWS Lambda - -`aws-smithy-http-server-python` supports running your services on [AWS Lambda](https://aws.amazon.com/lambda/). - -You need to use `run_lambda` method instead of `run` method to start -the [custom runtime](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html) -instead of the [Hyper](https://hyper.rs/) HTTP server. - -In your `app.py`: - -```diff -from pokemon_service_server_sdk import App -from pokemon_service_server_sdk.error import ResourceNotFoundException - -# ... - -# Get the number of requests served by this server. -@app.get_server_statistics -def get_server_statistics( - _: GetServerStatisticsInput, context: Context -) -> GetServerStatisticsOutput: - calls_count = context.get_calls_count() - logging.debug("The service handled %d requests", calls_count) - return GetServerStatisticsOutput(calls_count=calls_count) - -# ... - --app.run() -+app.run_lambda() -``` - -`aws-smithy-http-server-python` comes with a -[custom runtime](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html) -so you should run your service without any provided runtimes. -You can achieve that with a `Dockerfile` similar to this: - -```dockerfile -# You can use any image that has your desired Python version -FROM public.ecr.aws/lambda/python:3.8-x86_64 - -# Copy your application code to `LAMBDA_TASK_ROOT` -COPY app.py ${LAMBDA_TASK_ROOT} - -# When you build your Server SDK for your service, you will get a Python wheel. -# You just need to copy that wheel and install it via `pip` inside your image. -# Note that you need to build your library for Linux, and Python version used to -# build your SDK should match with your image's Python version. -# For cross compiling, you can consult to: -# https://pyo3.rs/latest/building_and_distribution.html#cross-compiling -COPY wheels/ ${LAMBDA_TASK_ROOT}/wheels -RUN pip3 install ${LAMBDA_TASK_ROOT}/wheels/*.whl - -# You can install your application's other dependencies listed in `requirements.txt`. -COPY requirements.txt . -RUN pip3 install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" - -# Create a symlink for your application's entrypoint, -# so we can use `/app.py` to refer it -RUN ln -s ${LAMBDA_TASK_ROOT}/app.py /app.py - -# By default `public.ecr.aws/lambda/python` images comes with Python runtime, -# we need to override `ENTRYPOINT` and `CMD` to not call that runtime and -# instead run directly your service and it will start our custom runtime. -ENTRYPOINT [ "/var/lang/bin/python3.8" ] -CMD [ "/app.py" ] -``` - -See [https://docs.aws.amazon.com/lambda/latest/dg/images-create.html#images-create-from-base](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html#images-create-from-base) -for more details on building your custom image. - This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. diff --git a/rust-runtime/aws-smithy-http-server-python/stubgen.py b/rust-runtime/aws-smithy-http-server-python/stubgen.py index 458ee2b9c5..0edf1d3ef2 100644 --- a/rust-runtime/aws-smithy-http-server-python/stubgen.py +++ b/rust-runtime/aws-smithy-http-server-python/stubgen.py @@ -393,7 +393,7 @@ def walk_module(writer: Writer, mod: Any): def generate(module: str, outdir: str): - path = Path(outdir) / f"{module}.pyi" + path = Path(outdir) / "__init__.pyi" writer = Writer( path, module, From 9a41e359ddc97721cb1b84ec40d68c915dfe9941 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 20 Apr 2023 13:26:01 -0400 Subject: [PATCH 040/253] Add credentials exposure test & fix STS + SSO (#2603) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation and Context - credentials providers may leak credentials in the HTTP body at the debug level ## Description This adds a test to aws-config that looks for leaked credentials in all of our provider integration tests—since these test use AWS APIs under the hood, this also serves to test AWS services in general. To support this, `sensitive` was added to the ParseHttpResponse trait and code was generated to take action based on this change. - [x] Add environment variable to force logging of the body - [x] consider if we want to suppress request body logging as well ## Testing ## Checklist - [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._ --------- Co-authored-by: John DiSanti --- CHANGELOG.next.toml | 16 +++ aws/rust-runtime/aws-config/Cargo.toml | 1 + .../src/default_provider/credentials.rs | 4 - .../src/http_credential_provider.rs | 4 + .../aws-config/src/imds/client.rs | 4 + .../aws-config/src/imds/client/token.rs | 4 + aws/rust-runtime/aws-config/src/lib.rs | 4 +- .../aws-config/src/profile/credentials.rs | 2 - .../aws-config/src/profile/profile_file.rs | 2 +- aws/rust-runtime/aws-config/src/test_case.rs | 103 +++++++++++++++++- .../credential_process/fs/home/.aws/config | 3 +- .../aws-sigv4/src/http_request/sign.rs | 16 ++- .../smithy/rustsdk/AwsCodegenDecorator.kt | 2 + .../rustsdk/customize/sso/SSODecorator.kt | 32 ++++++ .../rustsdk/customize/sts/STSDecorator.kt | 6 + .../smithy/generators/SensitiveIndex.kt | 29 +++++ .../protocols/HttpBoundProtocolGenerator.kt | 21 +++- .../smithy/generators/SensitiveIndexTest.kt | 81 ++++++++++++++ .../aws-smithy-http/src/middleware.rs | 12 +- rust-runtime/aws-smithy-http/src/response.rs | 18 +++ 20 files changed, 346 insertions(+), 18 deletions(-) create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndex.kt create mode 100644 codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndexTest.kt diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 7992dd6e5c..b9ca3ff1be 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -128,3 +128,19 @@ message = "Fix server code generation bug affecting constrained shapes bound wit references = ["smithy-rs#2583", "smithy-rs#2584"] meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" } author = "david-perez" + +[[aws-sdk-rust]] +message = """Reduce several instances of credential exposure in the SDK logs: +- IMDS now suppresses the body of the response from logs +- `aws-sigv4` marks the `x-amz-session-token` header as sensitive +- STS & SSO credentials have been manually marked as sensitive which suppresses logging of response bodies for relevant operations +""" +author = "rcoh" +references = ["smithy-rs#2603"] +meta = { "breaking" = false, "tada" = false, "bug" = false } + +[[smithy-rs]] +message = "Add a sensitive method to `ParseHttpResponse`. When this returns true, logging of the HTTP response body will be suppressed." +author = "rcoh" +references = ["smithy-rs#2603"] +meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index 92cacc1c52..9fb7ff39d4 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -49,6 +49,7 @@ zeroize = { version = "1", optional = true } [dev-dependencies] futures-util = { version = "0.3.16", default-features = false } tracing-test = "0.2.1" +tracing-subscriber = { version = "0.3.16", features = ["fmt", "json"] } tokio = { version = "1.23.1", features = ["full", "test-util"] } diff --git a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs index 7f04b334b5..e1cb203ebc 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs @@ -198,8 +198,6 @@ impl Builder { #[cfg(test)] mod test { - use tracing_test::traced_test; - use aws_credential_types::provider::ProvideCredentials; use crate::default_provider::credentials::DefaultCredentialsChain; @@ -242,7 +240,6 @@ mod test { make_test!($name, execute, $provider_config_builder); }; ($name: ident, $func: ident, $provider_config_builder: expr) => { - #[traced_test] #[tokio::test] async fn $name() { crate::test_case::TestEnvironment::from_dir(concat!( @@ -324,7 +321,6 @@ mod test { } #[tokio::test] - #[traced_test] #[cfg(feature = "client-hyper")] async fn no_providers_configured_err() { use crate::provider_config::ProviderConfig; diff --git a/aws/rust-runtime/aws-config/src/http_credential_provider.rs b/aws/rust-runtime/aws-config/src/http_credential_provider.rs index df894f59ef..e868ffdac4 100644 --- a/aws/rust-runtime/aws-config/src/http_credential_provider.rs +++ b/aws/rust-runtime/aws-config/src/http_credential_provider.rs @@ -149,6 +149,10 @@ impl ParseStrictResponse for CredentialsResponseParser { )), } } + + fn sensitive(&self) -> bool { + true + } } #[derive(Clone, Debug)] diff --git a/aws/rust-runtime/aws-config/src/imds/client.rs b/aws/rust-runtime/aws-config/src/imds/client.rs index 6bc8a290bf..05e7236cbd 100644 --- a/aws/rust-runtime/aws-config/src/imds/client.rs +++ b/aws/rust-runtime/aws-config/src/imds/client.rs @@ -280,6 +280,10 @@ impl ParseStrictResponse for ImdsGetResponseHandler { Err(InnerImdsError::BadStatus) } } + + fn sensitive(&self) -> bool { + true + } } /// IMDSv2 Endpoint Mode diff --git a/aws/rust-runtime/aws-config/src/imds/client/token.rs b/aws/rust-runtime/aws-config/src/imds/client/token.rs index a4de2c0deb..4cd3f80075 100644 --- a/aws/rust-runtime/aws-config/src/imds/client/token.rs +++ b/aws/rust-runtime/aws-config/src/imds/client/token.rs @@ -197,4 +197,8 @@ impl ParseStrictResponse for GetTokenResponseHandler { expiry: self.time.now() + Duration::from_secs(ttl), }) } + + fn sensitive(&self) -> bool { + true + } } diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 88f55c4c3f..2d237a5b8f 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -374,7 +374,7 @@ mod loader { /// /// # Example: Using a custom profile file path /// - /// ``` + /// ```no_run /// use aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider}; /// use aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind}; /// @@ -417,7 +417,7 @@ mod loader { /// /// # Example: Using a custom profile name /// - /// ``` + /// ```no_run /// use aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider}; /// use aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind}; /// diff --git a/aws/rust-runtime/aws-config/src/profile/credentials.rs b/aws/rust-runtime/aws-config/src/profile/credentials.rs index 9ce085503b..1f043c0959 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials.rs @@ -455,14 +455,12 @@ async fn build_provider_chain( #[cfg(test)] mod test { - use tracing_test::traced_test; use crate::profile::credentials::Builder; use crate::test_case::TestEnvironment; macro_rules! make_test { ($name: ident) => { - #[traced_test] #[tokio::test] async fn $name() { TestEnvironment::from_dir(concat!( diff --git a/aws/rust-runtime/aws-config/src/profile/profile_file.rs b/aws/rust-runtime/aws-config/src/profile/profile_file.rs index a90472c8e6..dfe9eb33ca 100644 --- a/aws/rust-runtime/aws-config/src/profile/profile_file.rs +++ b/aws/rust-runtime/aws-config/src/profile/profile_file.rs @@ -20,7 +20,7 @@ use std::path::PathBuf; /// /// # Example: Using a custom profile file path /// -/// ``` +/// ```no_run /// use aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider}; /// use aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind}; /// use std::sync::Arc; diff --git a/aws/rust-runtime/aws-config/src/test_case.rs b/aws/rust-runtime/aws-config/src/test_case.rs index 38593fcb46..28c10afd7d 100644 --- a/aws/rust-runtime/aws-config/src/test_case.rs +++ b/aws/rust-runtime/aws-config/src/test_case.rs @@ -16,11 +16,17 @@ use serde::Deserialize; use crate::connector::default_connector; use aws_smithy_types::error::display::DisplayErrorContext; use std::collections::HashMap; +use std::env; use std::error::Error; use std::fmt::Debug; use std::future::Future; +use std::io::Write; use std::path::{Path, PathBuf}; +use std::sync::{Arc, Mutex}; use std::time::{Duration, UNIX_EPOCH}; +use tracing::dispatcher::DefaultGuard; +use tracing::Level; +use tracing_subscriber::fmt::TestWriter; /// Test case credentials /// @@ -84,7 +90,7 @@ impl AsyncSleep for InstantSleep { } } -#[derive(Deserialize)] +#[derive(Deserialize, Debug)] pub(crate) enum GenericTestResult { Ok(T), ErrorContains(String), @@ -129,6 +135,79 @@ pub(crate) struct Metadata { name: String, } +struct Tee { + buf: Arc>>, + quiet: bool, + inner: W, +} + +/// Capture logs from this test. +/// +/// The logs will be captured until the `DefaultGuard` is dropped. +/// +/// *Why use this instead of traced_test?* +/// This captures _all_ logs, not just logs produced by the current crate. +fn capture_test_logs() -> (DefaultGuard, Rx) { + // it may be helpful to upstream this at some point + let (mut writer, rx) = Tee::stdout(); + if env::var("VERBOSE_TEST_LOGS").is_ok() { + writer.loud(); + } else { + eprintln!("To see full logs from this test set VERBOSE_TEST_LOGS=true"); + } + let subscriber = tracing_subscriber::fmt() + .with_max_level(Level::TRACE) + .with_writer(Mutex::new(writer)) + .finish(); + let guard = tracing::subscriber::set_default(subscriber); + (guard, rx) +} + +struct Rx(Arc>>); +impl Rx { + pub(crate) fn contents(&self) -> String { + String::from_utf8(self.0.lock().unwrap().clone()).unwrap() + } +} + +impl Tee { + fn stdout() -> (Self, Rx) { + let buf: Arc>> = Default::default(); + ( + Tee { + buf: buf.clone(), + quiet: true, + inner: TestWriter::new(), + }, + Rx(buf), + ) + } +} + +impl Tee { + fn loud(&mut self) { + self.quiet = false; + } +} + +impl Write for Tee +where + W: Write, +{ + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.buf.lock().unwrap().extend_from_slice(buf); + if !self.quiet { + self.inner.write(buf) + } else { + Ok(buf.len()) + } + } + + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} + impl TestEnvironment { pub(crate) async fn from_dir(dir: impl AsRef) -> Result> { let dir = dir.as_ref(); @@ -232,12 +311,26 @@ impl TestEnvironment { eprintln!("test case: {}. {}", self.metadata.name, self.metadata.docs); } + fn lines_with_secrets<'a>(&'a self, logs: &'a str) -> Vec<&'a str> { + logs.lines().filter(|l| self.contains_secret(l)).collect() + } + + fn contains_secret(&self, log_line: &str) -> bool { + assert!(log_line.lines().count() <= 1); + match &self.metadata.result { + // NOTE: we aren't currently erroring if the session token is leaked, that is in the canonical request among other things + TestResult::Ok(creds) => log_line.contains(&creds.secret_access_key), + TestResult::ErrorContains(_) => false, + } + } + /// Execute a test case. Failures lead to panics. pub(crate) async fn execute(&self, make_provider: impl Fn(ProviderConfig) -> F) where F: Future, P: ProvideCredentials, { + let (_guard, rx) = capture_test_logs(); let provider = make_provider(self.provider_config.clone()).await; let result = provider.provide_credentials().await; tokio::time::pause(); @@ -256,6 +349,14 @@ impl TestEnvironment { Ok(()) => {} Err(e) => panic!("{}", e), } + let contents = rx.contents(); + let leaking_lines = self.lines_with_secrets(&contents); + assert!( + leaking_lines.is_empty(), + "secret was exposed\n{:?}\nSee the following log lines:\n {}", + self.metadata.result, + leaking_lines.join("\n ") + ) } #[track_caller] diff --git a/aws/rust-runtime/aws-config/test-data/profile-provider/credential_process/fs/home/.aws/config b/aws/rust-runtime/aws-config/test-data/profile-provider/credential_process/fs/home/.aws/config index aaec123f3f..6fee217076 100644 --- a/aws/rust-runtime/aws-config/test-data/profile-provider/credential_process/fs/home/.aws/config +++ b/aws/rust-runtime/aws-config/test-data/profile-provider/credential_process/fs/home/.aws/config @@ -1,6 +1,7 @@ [default] source_profile = base -credential_process = echo '{ "Version": 1, "AccessKeyId": "ASIARTESTID", "SecretAccessKey": "TESTSECRETKEY", "SessionToken": "TESTSESSIONTOKEN", "Expiration": "2022-05-02T18:36:00+00:00" }' +# these fake credentials are base64 encoded to prevent triggering the unit test that scans logs for secrets +credential_process = echo eyAiVmVyc2lvbiI6IDEsICJBY2Nlc3NLZXlJZCI6ICJBU0lBUlRFU1RJRCIsICJTZWNyZXRBY2Nlc3NLZXkiOiAiVEVTVFNFQ1JFVEtFWSIsICJTZXNzaW9uVG9rZW4iOiAiVEVTVFNFU1NJT05UT0tFTiIsICJFeHBpcmF0aW9uIjogIjIwMjItMDUtMDJUMTg6MzY6MDArMDA6MDAiIH0K | base64 --decode [profile base] region = us-east-1 diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs b/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs index 97a41ad724..b716b9fd40 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs @@ -256,7 +256,7 @@ fn calculate_signing_headers<'a>( // Step 4: https://docs.aws.amazon.com/en_pv/general/latest/gr/sigv4-add-signature-to-request.html let values = creq.values.as_headers().expect("signing with headers"); let mut headers = HeaderMap::new(); - add_header(&mut headers, header::X_AMZ_DATE, &values.date_time); + add_header(&mut headers, header::X_AMZ_DATE, &values.date_time, false); headers.insert( "authorization", build_authorization_header(params.access_key, &creq, sts, &signature), @@ -266,18 +266,26 @@ fn calculate_signing_headers<'a>( &mut headers, header::X_AMZ_CONTENT_SHA_256, &values.content_sha256, + false, ); } if let Some(security_token) = params.security_token { - add_header(&mut headers, header::X_AMZ_SECURITY_TOKEN, security_token); + add_header( + &mut headers, + header::X_AMZ_SECURITY_TOKEN, + security_token, + true, + ); } Ok(SigningOutput::new(headers, signature)) } -fn add_header(map: &mut HeaderMap, key: &'static str, value: &str) { - map.insert(key, HeaderValue::try_from(value).expect(key)); +fn add_header(map: &mut HeaderMap, key: &'static str, value: &str, sensitive: bool) { + let mut value = HeaderValue::try_from(value).expect(key); + value.set_sensitive(sensitive); + map.insert(key, value); } // add signature to authorization header 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 21e113a3ca..7c245bb760 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 @@ -19,6 +19,7 @@ import software.amazon.smithy.rustsdk.customize.route53.Route53Decorator import software.amazon.smithy.rustsdk.customize.s3.S3Decorator import software.amazon.smithy.rustsdk.customize.s3.S3ExtendedRequestIdDecorator import software.amazon.smithy.rustsdk.customize.s3control.S3ControlDecorator +import software.amazon.smithy.rustsdk.customize.sso.SSODecorator import software.amazon.smithy.rustsdk.customize.sts.STSDecorator import software.amazon.smithy.rustsdk.endpoints.AwsEndpointsStdLib import software.amazon.smithy.rustsdk.endpoints.OperationInputTestDecorator @@ -65,6 +66,7 @@ val DECORATORS: List = listOf( ), S3ControlDecorator().onlyApplyTo("com.amazonaws.s3control#AWSS3ControlServiceV20180820"), STSDecorator().onlyApplyTo("com.amazonaws.sts#AWSSecurityTokenServiceV20110615"), + SSODecorator().onlyApplyTo("com.amazonaws.sso#SWBPortalService"), // Only build docs-rs for linux to reduce load on docs.rs listOf( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt new file mode 100644 index 0000000000..f4e14cbe4b --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk.customize.sso + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.model.traits.SensitiveTrait +import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.core.util.letIf +import java.util.logging.Logger + +class SSODecorator : ClientCodegenDecorator { + override val name: String = "SSO" + override val order: Byte = 0 + private val logger: Logger = Logger.getLogger(javaClass.name) + + private fun isAwsCredentials(shape: Shape): Boolean = shape.id == ShapeId.from("com.amazonaws.sso#RoleCredentials") + + override fun transformModel(service: ServiceShape, model: Model): Model = + ModelTransformer.create().mapShapes(model) { shape -> + shape.letIf(isAwsCredentials(shape)) { + (shape as StructureShape).toBuilder().addTrait(SensitiveTrait()).build() + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt index a332dd3035..9c83a266b5 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt @@ -7,9 +7,11 @@ package software.amazon.smithy.rustsdk.customize.sts import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.RetryableTrait +import software.amazon.smithy.model.traits.SensitiveTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -25,6 +27,8 @@ class STSDecorator : ClientCodegenDecorator { shape is StructureShape && shape.hasTrait() && shape.id.namespace == "com.amazonaws.sts" && shape.id.name == "IDPCommunicationErrorException" + private fun isAwsCredentials(shape: Shape): Boolean = shape.id == ShapeId.from("com.amazonaws.sts#Credentials") + override fun transformModel(service: ServiceShape, model: Model): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isIdpCommunicationError(shape)) { @@ -33,6 +37,8 @@ class STSDecorator : ClientCodegenDecorator { .removeTrait(ErrorTrait.ID) .addTrait(ErrorTrait("server")) .addTrait(RetryableTrait.builder().build()).build() + }.letIf(isAwsCredentials(shape)) { + (shape as StructureShape).toBuilder().addTrait(SensitiveTrait()).build() } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndex.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndex.kt new file mode 100644 index 0000000000..15e8d5361c --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndex.kt @@ -0,0 +1,29 @@ +/* + * 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 + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.knowledge.KnowledgeIndex +import software.amazon.smithy.model.selector.Selector +import software.amazon.smithy.model.shapes.OperationShape + +class SensitiveIndex(model: Model) : KnowledgeIndex { + private val sensitiveInputSelector = Selector.parse("operation:test(-[input]-> ~> [trait|sensitive])") + private val sensitiveOutputSelector = Selector.parse("operation:test(-[output]-> ~> [trait|sensitive])") + private val sensitiveInputs = sensitiveInputSelector.select(model).map { it.id }.toSet() + private val sensitiveOutputs = sensitiveOutputSelector.select(model).map { it.id }.toSet() + + fun hasSensitiveInput(operationShape: OperationShape): Boolean = sensitiveInputs.contains(operationShape.id) + fun hasSensitiveOutput(operationShape: OperationShape): Boolean = sensitiveOutputs.contains(operationShape.id) + + companion object { + fun of(model: Model): SensitiveIndex { + return model.getKnowledge(SensitiveIndex::class.java) { + SensitiveIndex(it) + } + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index 45ea108242..e6f3e6e1f7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.protocols import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.SensitiveIndex import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolParserGenerator @@ -68,6 +69,8 @@ open class HttpBoundProtocolTraitImplGenerator( "SdkBody" to RuntimeType.sdkBody(runtimeConfig), ) + private val sensitiveIndex = SensitiveIndex.of(model) + open fun generateTraitImpls( operationWriter: RustWriter, operationShape: OperationShape, @@ -103,6 +106,11 @@ open class HttpBoundProtocolTraitImplGenerator( writeCustomizations(customizations, OperationSection.BeforeParseResponse(customizations, "response")) }, ) + val sensitive = writable { + if (sensitiveIndex.hasSensitiveOutput(operationShape)) { + rust("fn sensitive(&self) -> bool { true }") + } + } rustTemplate( """ impl #{ParseStrict} for $operationName { @@ -118,9 +126,11 @@ open class HttpBoundProtocolTraitImplGenerator( #{parse_response}(status, headers, body) } } + #{sensitive} }""", *codegenScope, *localScope, + "sensitive" to sensitive, ) } @@ -160,7 +170,10 @@ open class HttpBoundProtocolTraitImplGenerator( ) } - private fun parseStreamingResponse(operationShape: OperationShape, customizations: List): RuntimeType { + private fun parseStreamingResponse( + operationShape: OperationShape, + customizations: List, + ): RuntimeType { val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) val errorSymbol = symbolProvider.symbolForOperationError(operationShape) @@ -179,7 +192,11 @@ open class HttpBoundProtocolTraitImplGenerator( """ #{parse_streaming_response}(response, &properties) """, - "parse_streaming_response" to parserGenerator.parseStreamingResponseFn(operationShape, true, customizations), + "parse_streaming_response" to parserGenerator.parseStreamingResponseFn( + operationShape, + true, + customizations, + ), ) } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndexTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndexTest.kt new file mode 100644 index 0000000000..cecf423588 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/SensitiveIndexTest.kt @@ -0,0 +1,81 @@ +/* + * 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 + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.util.lookup + +class SensitiveIndexTest { + val model = """ + namespace com.example + service TestService { + operations: [ + NotSensitive, + SensitiveInput, + SensitiveOutput, + NestedSensitiveInput, + NestedSensitiveOutput + ] + } + + @sensitive + structure Credentials { + username: String, + password: String + } + + operation NotSensitive { + input := { userId: String } + + output := { response: String } + } + + operation SensitiveInput { + input := { credentials: Credentials } + } + + operation SensitiveOutput { + output := { credentials: Credentials } + } + + operation NestedSensitiveInput { + input := { nested: Nested } + } + + operation NestedSensitiveOutput { + output := { nested: Nested } + } + + structure Nested { + inner: Inner + } + + structure Inner { + credentials: Credentials + } + """.asSmithyModel(smithyVersion = "2.0") + + @Test + fun `correctly identify operations`() { + val index = SensitiveIndex.of(model) + + data class TestCase(val shape: String, val sensitiveInput: Boolean, val sensitiveOutput: Boolean) + + val testCases = listOf( + TestCase("NotSensitive", sensitiveInput = false, sensitiveOutput = false), + TestCase("SensitiveInput", sensitiveInput = true, sensitiveOutput = false), + TestCase("SensitiveOutput", sensitiveInput = false, sensitiveOutput = true), + TestCase("NestedSensitiveInput", sensitiveInput = true, sensitiveOutput = false), + TestCase("NestedSensitiveOutput", sensitiveInput = false, sensitiveOutput = true), + ) + testCases.forEach { tc -> + assertEquals(tc.sensitiveInput, index.hasSensitiveInput(model.lookup("com.example#${tc.shape}")), "input: $tc") + assertEquals(tc.sensitiveOutput, index.hasSensitiveOutput(model.lookup("com.example#${tc.shape}")), "output: $tc ") + } + } +} diff --git a/rust-runtime/aws-smithy-http/src/middleware.rs b/rust-runtime/aws-smithy-http/src/middleware.rs index b77c65b935..d3da9dac2d 100644 --- a/rust-runtime/aws-smithy-http/src/middleware.rs +++ b/rust-runtime/aws-smithy-http/src/middleware.rs @@ -21,6 +21,8 @@ use tracing::{debug_span, trace, Instrument}; type BoxError = Box; +const LOG_SENSITIVE_BODIES: &str = "LOG_SENSITIVE_BODIES"; + /// [`AsyncMapRequest`] defines an asynchronous middleware that transforms an [`operation::Request`]. /// /// Typically, these middleware will read configuration from the `PropertyBag` and use it to @@ -132,7 +134,15 @@ where }; let http_response = http::Response::from_parts(parts, Bytes::from(body)); - trace!(http_response = ?http_response, "read HTTP response body"); + if !handler.sensitive() + || std::env::var(LOG_SENSITIVE_BODIES) + .map(|v| v.eq_ignore_ascii_case("true")) + .unwrap_or_default() + { + trace!(http_response = ?http_response, "read HTTP response body"); + } else { + trace!(http_response = "** REDACTED **. To print, set LOG_SENSITIVE_BODIES=true") + } debug_span!("parse_loaded").in_scope(move || { let parsed = handler.parse_loaded(&http_response); sdk_result( diff --git a/rust-runtime/aws-smithy-http/src/response.rs b/rust-runtime/aws-smithy-http/src/response.rs index 9102fcbab4..7ba039f419 100644 --- a/rust-runtime/aws-smithy-http/src/response.rs +++ b/rust-runtime/aws-smithy-http/src/response.rs @@ -60,6 +60,13 @@ pub trait ParseHttpResponse { /// if `parse_unloaded` will never return `None`, however, it may make your code easier to test if an /// implementation is provided. fn parse_loaded(&self, response: &http::Response) -> Self::Output; + + /// Returns whether the contents of this response are sensitive + /// + /// When this is set to true, wire logging will be disabled + fn sensitive(&self) -> bool { + false + } } /// Convenience Trait for non-streaming APIs @@ -72,6 +79,13 @@ pub trait ParseStrictResponse { /// Parse an [`http::Response`] into `Self::Output`. fn parse(&self, response: &http::Response) -> Self::Output; + + /// Returns whether the contents of this response are sensitive + /// + /// When this is set to true, wire logging will be disabled + fn sensitive(&self) -> bool { + false + } } impl ParseHttpResponse for T { @@ -84,6 +98,10 @@ impl ParseHttpResponse for T { fn parse_loaded(&self, response: &http::Response) -> Self::Output { self.parse(response) } + + fn sensitive(&self) -> bool { + ParseStrictResponse::sensitive(self) + } } #[cfg(test)] From 437ae5bc2051957713c31a732c084107b9fb138a Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Thu, 20 Apr 2023 13:02:42 -0500 Subject: [PATCH 041/253] Allow "Update GitHub Pages" to opt-in to cargo sparse registry (#2610) ## Motivation and Context Addresses a CI failure within `Update GitHub Pages` due to the use of the cargo sparse registry ([example](https://github.com/awslabs/smithy-rs/actions/runs/4746548497/jobs/8430328515)). ## Description The PR allows the action to configure whether the cargo sparse registry is used via the environment variable [CARGO_UNSTABLE_SPARSE_REGISTRY](https://blog.rust-lang.org/2022/06/22/sparse-registry-testing.html#overview): Screenshot 2023-04-20 at 11 55 06 AM ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito Co-authored-by: Zelda Hessler --- .github/workflows/github-pages.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 83926c09ac..809955c7a0 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -1,5 +1,11 @@ on: workflow_dispatch: + inputs: + opt-in-to-sparse-registry: + description: Enable/disable CARGO_UNSTABLE_SPARSE_REGISTRY (https://blog.rust-lang.org/2022/06/22/sparse-registry-testing.html) + required: true + type: boolean + default: false push: branches: [main] paths: @@ -24,6 +30,7 @@ jobs: - name: Generate docs env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_UNSTABLE_SPARSE_REGISTRY: ${{ inputs.opt-in-to-sparse-registry }} run: | git config --local user.name "AWS SDK Rust Bot" git config --local user.email "aws-sdk-rust-primary@amazon.com" From 92b0704d207572d7b35e296edb728366997a60d1 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Thu, 20 Apr 2023 14:45:09 -0500 Subject: [PATCH 042/253] Allow SDK to be built with unknown traits in models (upgrading Smithy to 1.29.0 as part of it) (#2547) ## Motivation and Context Addresses https://github.com/awslabs/smithy-rs/issues/2104 ## Description This PR allows us to build `aws-sdk-rust` even with unknown traits present in the AWS models. This requires `Smithy` 1.29.0 that includes [support](https://github.com/awslabs/smithy/pull/1685) for an extension property, [allowUnknownTraits](https://smithy.io/2.0/guides/building-models/gradle-plugin.html?highlight=allowunknowntraits#smithy-extension-properties). ## Testing Manually added a unknown trait into an AWS model like so: ``` diff --git a/aws/sdk/aws-models/sts.json b/aws/sdk/aws-models/sts.json --- a/aws/sdk/aws-models/sts.json +++ b/aws/sdk/aws-models/sts.json @@ -69,6 +69,7 @@ "aws.auth#sigv4": { "name": "sts" }, + "aws.protocols#awsQuery2": {}, "aws.protocols#awsQuery": {}, ``` and confirmed `/gradlew :aws:sdk:assemble` built SDK successfully. Also verified that without `allowUnknownTraits = true` in `aws/sdk/build.gradle.kts`, `/gradlew :aws:sdk:assemble` failed with the above unknown trait. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- aws/sdk/build.gradle.kts | 1 + .../protocol/ServerProtocolTestGenerator.kt | 16 ++++++++++++++-- gradle.properties | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index 29c7e83254..06355aaab6 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -20,6 +20,7 @@ plugins { configure { smithyBuildConfigs = files(buildDir.resolve("smithy-build.json")) + allowUnknownTraits = true } val smithyVersion: String by project diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index 34270f2fdf..7288de8942 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -810,8 +810,16 @@ class ServerProtocolTestGenerator( FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsLongList", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsTimestampList", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsDateTimeList", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsHttpDateList_case0", TestType.MalformedRequest), - FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsHttpDateList_case1", TestType.MalformedRequest), + FailingTest( + RestJsonValidation, + "RestJsonMalformedUniqueItemsHttpDateList_case0", + TestType.MalformedRequest, + ), + FailingTest( + RestJsonValidation, + "RestJsonMalformedUniqueItemsHttpDateList_case1", + TestType.MalformedRequest, + ), FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsEnumList", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsIntEnumList", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedUniqueItemsListList", TestType.MalformedRequest), @@ -830,6 +838,10 @@ class ServerProtocolTestGenerator( FailingTest(RestJsonValidation, "RestJsonMalformedEnumString_case1", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedEnumUnion_case0", TestType.MalformedRequest), FailingTest(RestJsonValidation, "RestJsonMalformedEnumUnion_case1", TestType.MalformedRequest), + + // TODO(https://github.com/awslabs/smithy/issues/1737): Specs on @internal, @tags, and enum values need to be clarified + FailingTest(RestJsonValidation, "RestJsonMalformedEnumTraitString_case0", TestType.MalformedRequest), + FailingTest(RestJsonValidation, "RestJsonMalformedEnumTraitString_case1", TestType.MalformedRequest), ) private val RunOnly: Set? = null diff --git a/gradle.properties b/gradle.properties index ead4b8d783..8dbf0ddef9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ kotlin.code.style=official # codegen smithyGradlePluginVersion=0.6.0 -smithyVersion=1.28.1 +smithyVersion=1.29.0 # kotlin kotlinVersion=1.7.21 From 2b7a19ef54ee85d3c04ce5d40aef37a790696358 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 20 Apr 2023 13:53:49 -0700 Subject: [PATCH 043/253] Add a middleware vs. orchestrator performance comparison benchmark (#2593) ## Motivation and Context This PR adds a benchmark suite to the `aws-sdk-s3` test in `aws/sra-test` so that the performance of client middleware can be compared against the new orchestrator approach. The comparison is not yet apples to apples since the orchestrator isn't complete enough to be comparable, but this is a benchmark that we can check regularly as they become more comparable. Initially, the performance was really bad (43x slower than middleware), but that turned out to be a trivial mistake in orchestrator implementation that was causing some configuration JSON to get reparsed for every request. Fixing that makes the orchestrator only about 1.5x slower, and there is another trivial optimization that can be made to bring that down more. The benchmark can be run with the following: ```bash smithy-rs$ ./gradlew aws:sra-test:assemble $ cd aws/sra-test/integration-tests/aws-sdk-s3 $ cargo bench ``` ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../integration-tests/aws-sdk-s3/Cargo.toml | 32 +- .../benches/middleware_vs_orchestrator.rs | 285 ++++++++++++++++++ 2 files changed, 305 insertions(+), 12 deletions(-) create mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml index 51611304b2..70aaa61a75 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml +++ b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml @@ -6,20 +6,28 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -aws-credential-types = { path = "../../../sdk/build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } -aws-http = { path = "../../../sdk/build/aws-sdk/sdk/aws-http" } -aws-runtime = { path = "../../../sdk/build/aws-sdk/sdk/aws-runtime" } -aws-sdk-s3 = { path = "../../../sdk/build/aws-sdk/sdk/s3/", features = ["test-util"] } -aws-sigv4 = { path = "../../../sdk/build/aws-sdk/sdk/aws-sigv4" } -aws-types = { path = "../../../sdk/build/aws-sdk/sdk/aws-types" } -aws-smithy-async = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-async", features = ["rt-tokio"] } -aws-smithy-client = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-client", features = ["test-util"] } -aws-smithy-types = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-types" } -aws-smithy-http = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-http" } -aws-smithy-runtime = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-runtime", features = ["test-util"] } -aws-smithy-runtime-api = { path = "../../../sdk/build/aws-sdk/sdk/aws-smithy-runtime-api" } +aws-credential-types = { path = "../../../rust-runtime/aws-credential-types", features = ["test-util"] } +aws-http = { path = "../../../rust-runtime/aws-http" } +aws-runtime = { path = "../../../rust-runtime/aws-runtime" } +aws-sdk-s3 = { path = "../../build/sdk/aws-sdk-s3", features = ["test-util"] } +aws-sigv4 = { path = "../../../rust-runtime/aws-sigv4" } +aws-types = { path = "../../../rust-runtime/aws-types" } +aws-smithy-async = { path = "../../../../rust-runtime/aws-smithy-async", features = ["rt-tokio"] } +aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client", features = ["test-util"] } +aws-smithy-types = { path = "../../../../rust-runtime/aws-smithy-types" } +aws-smithy-http = { path = "../../../../rust-runtime/aws-smithy-http" } +aws-smithy-runtime = { path = "../../../../rust-runtime/aws-smithy-runtime", features = ["test-util"] } +aws-smithy-runtime-api = { path = "../../../../rust-runtime/aws-smithy-runtime-api" } +criterion = { version = "0.4", features = ["async_tokio"] } tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3.15", features = ["env-filter", "json"] } http = "0.2.3" http-body = "0.4.5" + +[profile.release] +debug = 1 + +[[bench]] +name = "middleware_vs_orchestrator" +harness = false diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs new file mode 100644 index 0000000000..634553466a --- /dev/null +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs @@ -0,0 +1,285 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[macro_use] +extern crate criterion; +use aws_sdk_s3 as s3; +use aws_smithy_client::erase::DynConnector; +use aws_smithy_client::test_connection::infallible_connection_fn; +use aws_smithy_runtime_api::type_erasure::TypedBox; +use criterion::Criterion; +use s3::operation::list_objects_v2::{ListObjectsV2Error, ListObjectsV2Input, ListObjectsV2Output}; + +async fn middleware(client: &s3::Client) { + client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .send() + .await + .expect("successful execution"); +} + +async fn orchestrator(connector: &DynConnector) { + // TODO(enableNewSmithyRuntime): benchmark with `send_v2` directly once it works + let runtime_plugins = aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins::new() + .with_client_plugin(orchestrator::ManualServiceRuntimePlugin(connector.clone())) + .with_operation_plugin(aws_sdk_s3::operation::list_objects_v2::ListObjectsV2::new()) + .with_operation_plugin(orchestrator::ManualOperationRuntimePlugin); + let input = ListObjectsV2Input::builder() + .bucket("test-bucket") + .prefix("prefix~") + .build() + .unwrap(); + let input = TypedBox::new(input).erase(); + let output = aws_smithy_runtime::client::orchestrator::invoke(input, &runtime_plugins) + .await + .map_err(|err| { + err.map_service_error(|err| { + TypedBox::::assume_from(err) + .expect("correct error type") + .unwrap() + }) + }) + .unwrap(); + TypedBox::::assume_from(output) + .expect("correct output type") + .unwrap(); +} + +fn test_connection() -> DynConnector { + infallible_connection_fn(|req| { + assert_eq!( + "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + req.uri().to_string() + ); + assert!(req.headers().contains_key("authorization")); + http::Response::builder() + .status(200) + .body( + r#" + + test-bucket + prefix~ + 1 + 1000 + false + + some-file.file + 2009-10-12T17:50:30.000Z + 434234 + STANDARD + + +"#, + ) + .unwrap() + }) +} + +fn middleware_bench(c: &mut Criterion) { + let conn = test_connection(); + let config = s3::Config::builder() + .credentials_provider(s3::config::Credentials::for_tests()) + .region(s3::config::Region::new("us-east-1")) + .http_connector(conn.clone()) + .build(); + let client = s3::Client::from_conf(config); + c.bench_function("middleware", move |b| { + b.to_async(tokio::runtime::Runtime::new().unwrap()) + .iter(|| async { middleware(&client).await }) + }); +} + +fn orchestrator_bench(c: &mut Criterion) { + let conn = test_connection(); + + c.bench_function("orchestrator", move |b| { + b.to_async(tokio::runtime::Runtime::new().unwrap()) + .iter(|| async { orchestrator(&conn).await }) + }); +} + +mod orchestrator { + use aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache}; + use aws_credential_types::provider::SharedCredentialsProvider; + use aws_credential_types::Credentials; + use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; + use aws_runtime::recursion_detection::RecursionDetectionInterceptor; + use aws_runtime::user_agent::UserAgentInterceptor; + use aws_sdk_s3::config::Region; + use aws_sdk_s3::operation::list_objects_v2::ListObjectsV2Input; + use aws_smithy_client::erase::DynConnector; + use aws_smithy_runtime::client::connections::adapter::DynConnectorAdapter; + use aws_smithy_runtime_api::client::endpoints::StaticUriEndpointResolver; + use aws_smithy_runtime_api::client::interceptors::{ + Interceptor, InterceptorContext, InterceptorError, Interceptors, + }; + use aws_smithy_runtime_api::client::orchestrator::{ + BoxError, ConfigBagAccessors, Connection, HttpRequest, HttpResponse, TraceProbe, + }; + use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; + use aws_smithy_runtime_api::config_bag::ConfigBag; + use aws_types::region::SigningRegion; + use aws_types::SigningService; + use http::Uri; + use std::sync::Arc; + + pub struct ManualServiceRuntimePlugin(pub DynConnector); + + impl RuntimePlugin for ManualServiceRuntimePlugin { + fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { + let identity_resolvers = + aws_smithy_runtime_api::client::orchestrator::IdentityResolvers::builder() + .identity_resolver( + aws_runtime::auth::sigv4::SCHEME_ID, + aws_runtime::identity::credentials::CredentialsIdentityResolver::new( + SharedCredentialsCache::new(CredentialsCache::lazy().create_cache( + SharedCredentialsProvider::new(Credentials::for_tests()), + )), + ), + ) + .identity_resolver( + "anonymous", + aws_smithy_runtime_api::client::identity::AnonymousIdentityResolver::new(), + ) + .build(); + cfg.set_identity_resolvers(identity_resolvers); + + let http_auth_schemes = + aws_smithy_runtime_api::client::orchestrator::HttpAuthSchemes::builder() + .auth_scheme( + aws_runtime::auth::sigv4::SCHEME_ID, + aws_runtime::auth::sigv4::SigV4HttpAuthScheme::new(), + ) + .build(); + cfg.set_http_auth_schemes(http_auth_schemes); + + cfg.set_auth_option_resolver( + aws_smithy_runtime_api::client::auth::option_resolver::AuthOptionListResolver::new( + Vec::new(), + ), + ); + + //cfg.set_endpoint_resolver(DefaultEndpointResolver::new( + // aws_smithy_http::endpoint::SharedEndpointResolver::new( + // aws_sdk_s3::endpoint::DefaultResolver::new(), + // ), + //)); + cfg.set_endpoint_resolver(StaticUriEndpointResolver::uri(Uri::from_static( + "https://test-bucket.s3.us-east-1.amazonaws.com/", + ))); + + let params_builder = aws_sdk_s3::endpoint::Params::builder() + .set_region(Some("us-east-1".to_owned())) + .set_endpoint(Some("https://s3.us-east-1.amazonaws.com/".to_owned())); + cfg.put(params_builder); + + cfg.set_retry_strategy( + aws_smithy_runtime_api::client::retries::NeverRetryStrategy::new(), + ); + + let connection: Box = + Box::new(DynConnectorAdapter::new(self.0.clone())); + cfg.set_connection(connection); + + cfg.set_trace_probe({ + #[derive(Debug)] + struct StubTraceProbe; + impl TraceProbe for StubTraceProbe { + fn dispatch_events(&self) { + // no-op + } + } + StubTraceProbe + }); + + cfg.put(SigningService::from_static("s3")); + cfg.put(SigningRegion::from(Region::from_static("us-east-1"))); + + cfg.put(ApiMetadata::new("unused", "unused")); + cfg.put(AwsUserAgent::for_tests()); // Override the user agent with the test UA + cfg.get::>() + .expect("interceptors set") + .register_client_interceptor(Arc::new(UserAgentInterceptor::new()) as _) + .register_client_interceptor(Arc::new(RecursionDetectionInterceptor::new()) as _); + Ok(()) + } + } + + // This is a temporary operation runtime plugin until EndpointParamsInterceptor and + // EndpointParamsFinalizerInterceptor have been fully implemented, in which case + // `.with_operation_plugin(ManualOperationRuntimePlugin)` can be removed. + pub struct ManualOperationRuntimePlugin; + + impl RuntimePlugin for ManualOperationRuntimePlugin { + fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { + #[derive(Debug)] + struct ListObjectsV2EndpointParamsInterceptor; + impl Interceptor for ListObjectsV2EndpointParamsInterceptor { + fn read_before_execution( + &self, + context: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let input = context.input()?; + let input = input + .downcast_ref::() + .ok_or_else(|| InterceptorError::invalid_input_access())?; + let mut params_builder = cfg + .get::() + .ok_or(InterceptorError::read_before_execution( + "missing endpoint params builder", + ))? + .clone(); + params_builder = params_builder.set_bucket(input.bucket.clone()); + cfg.put(params_builder); + + Ok(()) + } + } + + #[derive(Debug)] + struct ListObjectsV2EndpointParamsFinalizerInterceptor; + impl Interceptor for ListObjectsV2EndpointParamsFinalizerInterceptor { + fn read_before_execution( + &self, + _context: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let params_builder = cfg + .get::() + .ok_or(InterceptorError::read_before_execution( + "missing endpoint params builder", + ))? + .clone(); + let params = params_builder + .build() + .map_err(InterceptorError::read_before_execution)?; + cfg.put( + aws_smithy_runtime_api::client::orchestrator::EndpointResolverParams::new( + params, + ), + ); + + Ok(()) + } + } + + cfg.get::>() + .expect("interceptors set") + .register_operation_interceptor( + Arc::new(ListObjectsV2EndpointParamsInterceptor) as _ + ) + .register_operation_interceptor(Arc::new( + ListObjectsV2EndpointParamsFinalizerInterceptor, + ) as _); + Ok(()) + } + } +} + +criterion_group!(benches, middleware_bench, orchestrator_bench); +criterion_main!(benches); From dd517bc868faec8d90d20890cf147f5fcff15736 Mon Sep 17 00:00:00 2001 From: Julian Antonielli Date: Fri, 21 Apr 2023 16:37:02 +0100 Subject: [PATCH 044/253] Fix `tracing-subscriber` missing `regex` features issue (#2618) ## Description See #2619. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-credential-types/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/aws/rust-runtime/aws-credential-types/Cargo.toml b/aws/rust-runtime/aws-credential-types/Cargo.toml index 8fad933c2a..4a0b8a9390 100644 --- a/aws/rust-runtime/aws-credential-types/Cargo.toml +++ b/aws/rust-runtime/aws-credential-types/Cargo.toml @@ -28,6 +28,9 @@ env_logger = "0.9.0" tokio = { version = "1.23.1", features = ["full", "test-util", "rt"] } tracing-test = "0.2.1" +# TODO(https://github.com/awslabs/smithy-rs/issues/2619): Remove this +# workaround once the fixed is upstreamed. +regex = { version = "1.0", features = ["unicode-case", "unicode-perl"] } [package.metadata.docs.rs] all-features = true From 3d8c52a676f574ffbde12fb8b156e5b49406ad76 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 21 Apr 2023 12:16:28 -0500 Subject: [PATCH 045/253] Follow up on DefaultEndpointResolver in the orchestrator (#2592) ## Motivation and Context This PR incorporates post-merge feedback left in #2577. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- .../aws-sdk-s3/tests/sra_test.rs | 2 +- .../EndpointParamsInterceptorGenerator.kt | 11 +- .../ServiceRuntimePluginGenerator.kt | 2 +- .../aws-smithy-runtime-api/src/client.rs | 3 - .../src/client/endpoints.rs | 110 ------------------ .../src/client/interceptors/error.rs | 28 +++++ .../aws-smithy-runtime/external-types.toml | 2 +- .../src/client/orchestrator.rs | 3 +- .../src/client/orchestrator/endpoints.rs | 106 ++++++++++++++++- 9 files changed, 143 insertions(+), 124 deletions(-) delete mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index 2250e7927e..ff6ed15ef1 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -17,7 +17,7 @@ use aws_sdk_s3::primitives::SdkBody; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_runtime::client::connections::adapter::DynConnectorAdapter; -use aws_smithy_runtime_api::client::endpoints::DefaultEndpointResolver; +use aws_smithy_runtime::client::orchestrator::endpoints::DefaultEndpointResolver; use aws_smithy_runtime_api::client::interceptors::{ Interceptor, InterceptorContext, InterceptorError, Interceptors, }; diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 8343bbc9bd..0299eb21c7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -32,6 +32,7 @@ class EndpointParamsInterceptorGenerator( arrayOf( "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), + "ContextAttachedError" to interceptors.resolve("error::ContextAttachedError"), "EndpointResolverParams" to orchestrator.resolve("EndpointResolverParams"), "HttpResponse" to orchestrator.resolve("HttpResponse"), "HttpRequest" to orchestrator.resolve("HttpRequest"), @@ -83,10 +84,10 @@ class EndpointParamsInterceptorGenerator( let input = context.input()?; let _input = input .downcast_ref::<${operationInput.name}>() - .ok_or_else(|| #{InterceptorError}::invalid_input_access())?; + .ok_or_else(|| "failed to downcast to ${operationInput.name}")?; let params_builder = cfg .get::<#{ParamsBuilder}>() - .ok_or(#{InterceptorError}::read_before_execution("missing endpoint params builder"))? + .ok_or_else(|| "missing endpoint params builder")? .clone(); ${"" /* TODO(EndpointResolver): Call setters on `params_builder` to update its fields by using values from `_input` */} cfg.put(params_builder); @@ -111,7 +112,7 @@ class EndpointParamsInterceptorGenerator( ) withBlockTemplate( "let endpoint_prefix = ", - ".map_err(#{InterceptorError}::read_before_execution)?;", + """.map_err(|err| #{ContextAttachedError}::new("endpoint prefix could not be built", err))?;""", *codegenScope, ) { endpointTraitBindings.render( @@ -130,11 +131,11 @@ class EndpointParamsInterceptorGenerator( let _ = context; let params_builder = cfg .get::<#{ParamsBuilder}>() - .ok_or(#{InterceptorError}::read_before_execution("missing endpoint params builder"))? + .ok_or_else(|| "missing endpoint params builder")? .clone(); let params = params_builder .build() - .map_err(#{InterceptorError}::read_before_execution)?; + .map_err(|err| #{ContextAttachedError}::new("endpoint params could not be built", err))?; cfg.put( #{EndpointResolverParams}::new(params) ); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index acb93bbfa7..8b70a84785 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -86,7 +86,7 @@ class ServiceRuntimePluginGenerator( "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), "Connection" to runtimeApi.resolve("client::orchestrator::Connection"), "ConnectorSettings" to RuntimeType.smithyClient(rc).resolve("http_connector::ConnectorSettings"), - "DefaultEndpointResolver" to runtimeApi.resolve("client::endpoints::DefaultEndpointResolver"), + "DefaultEndpointResolver" to runtime.resolve("client::orchestrator::endpoints::DefaultEndpointResolver"), "DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"), "HttpAuthSchemes" to runtimeApi.resolve("client::orchestrator::HttpAuthSchemes"), "IdentityResolvers" to runtimeApi.resolve("client::orchestrator::IdentityResolvers"), diff --git a/rust-runtime/aws-smithy-runtime-api/src/client.rs b/rust-runtime/aws-smithy-runtime-api/src/client.rs index c6724ff251..192b97a5d4 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client.rs @@ -21,8 +21,5 @@ pub mod retries; /// Runtime plugin type definitions. pub mod runtime_plugin; -/// Smithy endpoint resolution runtime plugins -pub mod endpoints; - /// Smithy auth runtime plugins pub mod auth; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs b/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs deleted file mode 100644 index b9b4f4a512..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/endpoints.rs +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use crate::client::orchestrator::{ - BoxError, EndpointResolver, EndpointResolverParams, HttpRequest, -}; -use aws_smithy_http::endpoint::error::ResolveEndpointError; -use aws_smithy_http::endpoint::{ - apply_endpoint, EndpointPrefix, ResolveEndpoint, SharedEndpointResolver, -}; -use http::header::HeaderName; -use http::{HeaderValue, Uri}; -use std::fmt::Debug; -use std::str::FromStr; - -#[derive(Debug, Clone)] -pub struct StaticUriEndpointResolver { - endpoint: Uri, -} - -impl StaticUriEndpointResolver { - pub fn http_localhost(port: u16) -> Self { - Self { - endpoint: Uri::from_str(&format!("http://localhost:{port}")) - .expect("all u16 values are valid ports"), - } - } - - pub fn uri(endpoint: Uri) -> Self { - Self { endpoint } - } -} - -impl EndpointResolver for StaticUriEndpointResolver { - fn resolve_and_apply_endpoint( - &self, - _params: &EndpointResolverParams, - _endpoint_prefix: Option<&EndpointPrefix>, - request: &mut HttpRequest, - ) -> Result<(), BoxError> { - apply_endpoint(request.uri_mut(), &self.endpoint, None)?; - Ok(()) - } -} - -#[derive(Debug, Clone)] -pub struct DefaultEndpointResolver { - inner: SharedEndpointResolver, -} - -impl DefaultEndpointResolver { - pub fn new(resolve_endpoint: SharedEndpointResolver) -> Self { - Self { - inner: resolve_endpoint, - } - } -} - -impl EndpointResolver for DefaultEndpointResolver -where - Params: Debug + Send + Sync + 'static, -{ - fn resolve_and_apply_endpoint( - &self, - params: &EndpointResolverParams, - endpoint_prefix: Option<&EndpointPrefix>, - request: &mut HttpRequest, - ) -> Result<(), BoxError> { - let endpoint = match params.get::() { - Some(params) => self.inner.resolve_endpoint(params)?, - None => { - return Err(Box::new(ResolveEndpointError::message( - "params of expected type was not present", - ))); - } - }; - - let uri: Uri = endpoint.url().parse().map_err(|err| { - ResolveEndpointError::from_source("endpoint did not have a valid uri", err) - })?; - - apply_endpoint(request.uri_mut(), &uri, endpoint_prefix).map_err(|err| { - ResolveEndpointError::message(format!( - "failed to apply endpoint `{:?}` to request `{:?}`", - uri, request, - )) - .with_source(Some(err.into())) - })?; - - for (header_name, header_values) in endpoint.headers() { - request.headers_mut().remove(header_name); - for value in header_values { - request.headers_mut().insert( - HeaderName::from_str(header_name).map_err(|err| { - ResolveEndpointError::message("invalid header name") - .with_source(Some(err.into())) - })?, - HeaderValue::from_str(value).map_err(|err| { - ResolveEndpointError::message("invalid header value") - .with_source(Some(err.into())) - })?, - ); - } - } - - Ok(()) - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs index e25b797aeb..8d8b828971 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs @@ -169,3 +169,31 @@ impl std::error::Error for InterceptorError { self.source.as_ref().map(|err| err.as_ref() as _) } } + +/// A convenience error that allows for adding additional `context` to `source` +#[derive(Debug)] +pub struct ContextAttachedError { + context: String, + source: Option, +} + +impl ContextAttachedError { + pub fn new(context: impl Into, source: impl Into) -> Self { + Self { + context: context.into(), + source: Some(source.into()), + } + } +} + +impl fmt::Display for ContextAttachedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.context) + } +} + +impl std::error::Error for ContextAttachedError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.source.as_ref().map(|err| err.as_ref() as _) + } +} diff --git a/rust-runtime/aws-smithy-runtime/external-types.toml b/rust-runtime/aws-smithy-runtime/external-types.toml index 5ecb3a376b..a8a47f10d9 100644 --- a/rust-runtime/aws-smithy-runtime/external-types.toml +++ b/rust-runtime/aws-smithy-runtime/external-types.toml @@ -6,5 +6,5 @@ allowed_external_types = [ "http::header::name::HeaderName", "http::request::Request", "http::response::Response", - "uri::Uri", + "http::uri::Uri", ] diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 217a0eaae7..dff37ec7b0 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -18,7 +18,8 @@ use aws_smithy_runtime_api::config_bag::ConfigBag; use tracing::{debug_span, Instrument}; mod auth; -mod endpoints; +/// Defines types that implement a trait for endpoint resolution +pub mod endpoints; mod http; pub(self) mod phase; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 5da7bfe517..981ef3239c 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -3,12 +3,114 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_http::endpoint::EndpointPrefix; +use aws_smithy_http::endpoint::error::ResolveEndpointError; +use aws_smithy_http::endpoint::{ + apply_endpoint, EndpointPrefix, ResolveEndpoint, SharedEndpointResolver, +}; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, ConfigBagAccessors, HttpRequest, HttpResponse, + BoxError, ConfigBagAccessors, EndpointResolver, EndpointResolverParams, HttpRequest, + HttpResponse, }; use aws_smithy_runtime_api::config_bag::ConfigBag; +use http::header::HeaderName; +use http::{HeaderValue, Uri}; +use std::fmt::Debug; +use std::str::FromStr; + +#[derive(Debug, Clone)] +pub struct StaticUriEndpointResolver { + endpoint: Uri, +} + +impl StaticUriEndpointResolver { + pub fn http_localhost(port: u16) -> Self { + Self { + endpoint: Uri::from_str(&format!("http://localhost:{port}")) + .expect("all u16 values are valid ports"), + } + } + + pub fn uri(endpoint: Uri) -> Self { + Self { endpoint } + } +} + +impl EndpointResolver for StaticUriEndpointResolver { + fn resolve_and_apply_endpoint( + &self, + _params: &EndpointResolverParams, + _endpoint_prefix: Option<&EndpointPrefix>, + request: &mut HttpRequest, + ) -> Result<(), BoxError> { + apply_endpoint(request.uri_mut(), &self.endpoint, None)?; + Ok(()) + } +} + +#[derive(Debug, Clone)] +pub struct DefaultEndpointResolver { + inner: SharedEndpointResolver, +} + +impl DefaultEndpointResolver { + pub fn new(resolve_endpoint: SharedEndpointResolver) -> Self { + Self { + inner: resolve_endpoint, + } + } +} + +impl EndpointResolver for DefaultEndpointResolver +where + Params: Debug + Send + Sync + 'static, +{ + fn resolve_and_apply_endpoint( + &self, + params: &EndpointResolverParams, + endpoint_prefix: Option<&EndpointPrefix>, + request: &mut HttpRequest, + ) -> Result<(), BoxError> { + let endpoint = match params.get::() { + Some(params) => self.inner.resolve_endpoint(params)?, + None => { + return Err(Box::new(ResolveEndpointError::message( + "params of expected type was not present", + ))); + } + }; + + let uri: Uri = endpoint.url().parse().map_err(|err| { + ResolveEndpointError::from_source("endpoint did not have a valid uri", err) + })?; + + apply_endpoint(request.uri_mut(), &uri, endpoint_prefix).map_err(|err| { + ResolveEndpointError::message(format!( + "failed to apply endpoint `{:?}` to request `{:?}`", + uri, request, + )) + .with_source(Some(err.into())) + })?; + + for (header_name, header_values) in endpoint.headers() { + request.headers_mut().remove(header_name); + for value in header_values { + request.headers_mut().insert( + HeaderName::from_str(header_name).map_err(|err| { + ResolveEndpointError::message("invalid header name") + .with_source(Some(err.into())) + })?, + HeaderValue::from_str(value).map_err(|err| { + ResolveEndpointError::message("invalid header value") + .with_source(Some(err.into())) + })?, + ); + } + } + + Ok(()) + } +} pub(super) fn orchestrate_endpoint( ctx: &mut InterceptorContext, From 1a65a44e533896d9a950dc482a213fd6d8f8de97 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 21 Apr 2023 10:26:27 -0700 Subject: [PATCH 046/253] Upgrade MSRV to Rust 1.67.1 (#2611) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .github/workflows/ci-pr.yml | 3 +++ .github/workflows/ci.yml | 2 +- .github/workflows/claim-crate-names.yml | 2 +- .github/workflows/pull-request-bot.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/update-sdk-next.yml | 2 +- CHANGELOG.next.toml | 12 ++++++++++++ .../aws-config/src/profile/parser/normalize.rs | 2 +- .../integration-tests/dynamodb/tests/endpoints.rs | 1 - .../amazon/smithy/rust/codegen/core/testutil/Rust.kt | 2 +- gradle.properties | 2 +- rust-runtime/aws-smithy-client/src/lib.rs | 2 +- rust-runtime/inlineable/src/endpoint_lib/arn.rs | 2 +- .../inlineable/src/endpoint_lib/parse_url.rs | 2 +- .../inlineable/src/endpoint_lib/partition.rs | 2 +- .../inlineable/src/endpoint_lib/substring.rs | 4 ++-- .../inlineable/src/endpoint_lib/uri_encode.rs | 4 ++-- rust-toolchain.toml | 2 +- tools/ci-build/Dockerfile | 2 +- tools/ci-build/smithy-rs-tool-common/src/git.rs | 4 ++-- 20 files changed, 35 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 5b53c4dbf1..22f830e8bc 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -111,6 +111,9 @@ jobs: semver-checks: name: check the semver status of this PR runs-on: smithy_ubuntu-latest_8-core + needs: + - save-docker-login-token + - acquire-base-image steps: - uses: actions/checkout@v3 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c36d5ba08c..54ccda52ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ on: required: false env: - rust_version: 1.66.1 + rust_version: 1.67.1 rust_toolchain_components: clippy,rustfmt ENCRYPTED_DOCKER_PASSWORD: ${{ secrets.ENCRYPTED_DOCKER_PASSWORD }} DOCKER_LOGIN_TOKEN_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} diff --git a/.github/workflows/claim-crate-names.yml b/.github/workflows/claim-crate-names.yml index ef24446bb7..a7019823c8 100644 --- a/.github/workflows/claim-crate-names.yml +++ b/.github/workflows/claim-crate-names.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true env: - rust_version: 1.66.1 + rust_version: 1.67.1 name: Claim unpublished crate names on crates.io run-name: ${{ github.workflow }} diff --git a/.github/workflows/pull-request-bot.yml b/.github/workflows/pull-request-bot.yml index 221f520f1c..aafeaf773c 100644 --- a/.github/workflows/pull-request-bot.yml +++ b/.github/workflows/pull-request-bot.yml @@ -28,7 +28,7 @@ concurrency: env: java_version: 11 - rust_version: 1.66.1 + rust_version: 1.67.1 rust_toolchain_components: clippy,rustfmt apt_dependencies: libssl-dev gnuplot jq diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 52ddfdfe7a..db3af0cb9c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true env: - rust_version: 1.66.1 + rust_version: 1.67.1 name: Release smithy-rs run-name: ${{ github.workflow }} ${{ inputs.semantic_version }} (${{ inputs.commit_sha }}) - ${{ inputs.dry_run && 'Dry run' || 'Production run' }} diff --git a/.github/workflows/update-sdk-next.yml b/.github/workflows/update-sdk-next.yml index c96b09b73f..542abdde00 100644 --- a/.github/workflows/update-sdk-next.yml +++ b/.github/workflows/update-sdk-next.yml @@ -32,7 +32,7 @@ jobs: - name: Set up Rust uses: dtolnay/rust-toolchain@master with: - toolchain: 1.66.1 + toolchain: 1.67.1 - name: Delete old SDK run: | - name: Generate a fresh SDK diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index b9ca3ff1be..c52faf5882 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -144,3 +144,15 @@ message = "Add a sensitive method to `ParseHttpResponse`. When this returns true author = "rcoh" references = ["smithy-rs#2603"] meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } + +[[aws-sdk-rust]] +message = "Update MSRV to Rust 1.67.1" +references = ["smithy-rs#2611"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "jdisanti" + +[[smithy-rs]] +message = "Update MSRV to Rust 1.67.1" +references = ["smithy-rs#2611"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all"} +author = "jdisanti" diff --git a/aws/rust-runtime/aws-config/src/profile/parser/normalize.rs b/aws/rust-runtime/aws-config/src/profile/parser/normalize.rs index 36b469b729..5b3b5c1ea8 100644 --- a/aws/rust-runtime/aws-config/src/profile/parser/normalize.rs +++ b/aws/rust-runtime/aws-config/src/profile/parser/normalize.rs @@ -113,7 +113,7 @@ pub(super) fn merge_in( } } -fn merge_into_base<'a>(target: &mut Profile, profile: HashMap<&str, Cow<'a, str>>) { +fn merge_into_base(target: &mut Profile, profile: HashMap<&str, Cow<'_, str>>) { for (k, v) in profile { match validate_identifier(k) { Ok(k) => { diff --git a/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs b/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs index 6a01fedefd..25d65bb94c 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/endpoints.rs @@ -7,7 +7,6 @@ use aws_sdk_dynamodb::config::{self, Credentials, Region}; use aws_types::SdkConfig; use http::Uri; -#[track_caller] async fn expect_uri( conf: SdkConfig, uri: &'static str, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt index 5f9e612ee7..4e0b0f67f9 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt @@ -109,7 +109,7 @@ object TestWorkspace { // help rust select the right version when we run cargo test // TODO(https://github.com/awslabs/smithy-rs/issues/2048): load this from the msrv property using a // method as we do for runtime crate versions - "[toolchain]\nchannel = \"1.66.1\"\n", + "[toolchain]\nchannel = \"1.67.1\"\n", ) // ensure there at least an empty lib.rs file to avoid broken crates newProject.resolve("src").mkdirs() diff --git a/gradle.properties b/gradle.properties index 8dbf0ddef9..16f1511023 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ # # Rust MSRV (entered into the generated README) -rust.msrv=1.66.1 +rust.msrv=1.67.1 # To enable debug, swap out the two lines below. # When changing this value, be sure to run `./gradlew --stop` to kill the Gradle daemon. diff --git a/rust-runtime/aws-smithy-client/src/lib.rs b/rust-runtime/aws-smithy-client/src/lib.rs index 080e5ac9eb..35065c84b4 100644 --- a/rust-runtime/aws-smithy-client/src/lib.rs +++ b/rust-runtime/aws-smithy-client/src/lib.rs @@ -259,7 +259,7 @@ where > + Clone, { let _ = |o: static_tests::ValidTestOperation| { - let _ = self.call_raw(o); + drop(self.call_raw(o)); }; } } diff --git a/rust-runtime/inlineable/src/endpoint_lib/arn.rs b/rust-runtime/inlineable/src/endpoint_lib/arn.rs index a9b599e632..0fd68d73bc 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/arn.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/arn.rs @@ -89,7 +89,7 @@ impl<'a> Arn<'a> { } } -pub(crate) fn parse_arn<'a, 'b>(input: &'a str, e: &'b mut DiagnosticCollector) -> Option> { +pub(crate) fn parse_arn<'a>(input: &'a str, e: &mut DiagnosticCollector) -> Option> { e.capture(Arn::parse(input)) } diff --git a/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs b/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs index 5e437c9bfe..03e07f3e41 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/parse_url.rs @@ -45,7 +45,7 @@ impl<'a> Url<'a> { } } -pub(crate) fn parse_url<'a, 'b>(url: &'a str, e: &'b mut DiagnosticCollector) -> Option> { +pub(crate) fn parse_url<'a>(url: &'a str, e: &mut DiagnosticCollector) -> Option> { let raw = url; let uri: Uri = e.capture(url.parse())?; let url: ParsedUrl = e.capture(url.parse())?; diff --git a/rust-runtime/inlineable/src/endpoint_lib/partition.rs b/rust-runtime/inlineable/src/endpoint_lib/partition.rs index b9b4002ed1..02088d0f93 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/partition.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/partition.rs @@ -458,7 +458,7 @@ mod test { use regex::Regex; use std::collections::HashMap; - fn resolve<'a, 'b>(resolver: &'a PartitionResolver, region: &'b str) -> Partition<'a> { + fn resolve<'a>(resolver: &'a PartitionResolver, region: &str) -> Partition<'a> { resolver .resolve_partition(region, &mut DiagnosticCollector::new()) .expect("could not resolve partition") diff --git a/rust-runtime/inlineable/src/endpoint_lib/substring.rs b/rust-runtime/inlineable/src/endpoint_lib/substring.rs index 8512810c4e..27142e118e 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/substring.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/substring.rs @@ -13,12 +13,12 @@ use crate::endpoint_lib::diagnostic::DiagnosticCollector; /// - When `reverse` is false, indexes are evaluated from the beginning of the string /// - When `reverse` is true, indexes are evaluated from the end of the string (however, the result /// will still be "forwards" and `start` MUST be less than `end`. -pub(crate) fn substring<'a, 'b>( +pub(crate) fn substring<'a>( input: &'a str, start: usize, stop: usize, reverse: bool, - e: &'b mut DiagnosticCollector, + e: &mut DiagnosticCollector, ) -> Option<&'a str> { if start >= stop { e.capture(Err("start > stop"))?; diff --git a/rust-runtime/inlineable/src/endpoint_lib/uri_encode.rs b/rust-runtime/inlineable/src/endpoint_lib/uri_encode.rs index 58c0ef5742..de33264a9f 100644 --- a/rust-runtime/inlineable/src/endpoint_lib/uri_encode.rs +++ b/rust-runtime/inlineable/src/endpoint_lib/uri_encode.rs @@ -39,9 +39,9 @@ pub(crate) const BASE_SET: &AsciiSet = &CONTROLS .add(b'\\'); // Returns `Option` for forwards compatibility -pub(crate) fn uri_encode<'a, 'b>( +pub(crate) fn uri_encode<'a>( s: &'a str, - _e: &'b mut DiagnosticCollector, + _e: &mut DiagnosticCollector, ) -> std::borrow::Cow<'a, str> { utf8_percent_encode(s, BASE_SET).into() } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index f701aa5354..588ffd5788 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.66.1" +channel = "1.67.1" diff --git a/tools/ci-build/Dockerfile b/tools/ci-build/Dockerfile index 0e64f746a9..2bce2b0ec9 100644 --- a/tools/ci-build/Dockerfile +++ b/tools/ci-build/Dockerfile @@ -6,7 +6,7 @@ # This is the base Docker build image used by CI ARG base_image=public.ecr.aws/amazonlinux/amazonlinux:2 -ARG rust_stable_version=1.66.1 +ARG rust_stable_version=1.67.1 ARG rust_nightly_version=nightly-2022-11-16 FROM ${base_image} AS bare_base_image diff --git a/tools/ci-build/smithy-rs-tool-common/src/git.rs b/tools/ci-build/smithy-rs-tool-common/src/git.rs index b1b34aa263..c370529c91 100644 --- a/tools/ci-build/smithy-rs-tool-common/src/git.rs +++ b/tools/ci-build/smithy-rs-tool-common/src/git.rs @@ -104,11 +104,11 @@ pub trait Git: Send + Sync { /// Returns a list of commit hashes in reverse chronological order starting with /// `start_inclusive_revision` and ending before `end_exclusive_revision`. Both of /// these arguments can be any kind of Git revision (e.g., HEAD, HEAD~2, commit hash, etc). - fn rev_list<'a>( + fn rev_list( &self, start_inclusive_revision: &str, end_exclusive_revision: &str, - path: Option<&'a Path>, + path: Option<&Path>, ) -> Result>; /// Returns information about a given revision. From 44ece8e0f92e9d72c47f14d28fe519f0f7344e08 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Fri, 21 Apr 2023 18:37:39 +0100 Subject: [PATCH 047/253] Prevent build stalling with overlapping projections (#2602) ## Motivation and Context Smithy CLI runs projections concurrently, for some currently unknown reason this causes the build to stall. ## Description - Switch `defaultRustMetadata` in `BaseSymbolMetadataProvider` from a `val` to a `fun`. - Remove unused `RustType::TestModule` companion object. ## Notes A deadlock occurs when the client/server plugins both attempt to grab file locks. While this fixes this deadlock it is unknown whether there still exist conditions where it can happen. ## Testing 1. Checkout https://github.com/crisidev/smithy-rs-pokemon-service/commit/d276bb95bbd7bf5c5f029953b8f16cecfa8af24f, run `./gradlew assemble`, and observe the build halting. 2. Run the following commands to switch to this branch: ```bash git submodule update --init --recursive --remote cd smithy-rs git checkout harryb/remove-build-deadlock cd .. ./gradlew assemble ``` 3. Observe the build succeeding. --- .../smithy/rust/codegen/core/rustlang/RustType.kt | 9 --------- .../codegen/core/smithy/SymbolMetadataProvider.kt | 12 ++++++------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index 56f0b63c52..79dde7de12 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -422,15 +422,6 @@ data class RustMetadata( fun hasDebugDerive(): Boolean { return derives.contains(RuntimeType.Debug) } - - companion object { - val TestModule = RustMetadata( - visibility = Visibility.PRIVATE, - additionalAttributes = listOf( - Attribute.CfgTest, - ), - ) - } } data class Argument(val argument: String, val value: String, val type: String) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt index 880ac0510a..d78818601c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt @@ -137,13 +137,13 @@ class BaseSymbolMetadataProvider( // Only the server subproject uses these, so we provide a sane and conservative default implementation here so that // the rest of symbol metadata providers can just delegate to it. - private val defaultRustMetadata = RustMetadata(visibility = Visibility.PRIVATE) + private fun defaultRustMetadata() = RustMetadata(visibility = Visibility.PRIVATE) - override fun listMeta(listShape: ListShape) = defaultRustMetadata - override fun mapMeta(mapShape: MapShape) = defaultRustMetadata - override fun stringMeta(stringShape: StringShape) = defaultRustMetadata - override fun numberMeta(numberShape: NumberShape) = defaultRustMetadata - override fun blobMeta(blobShape: BlobShape) = defaultRustMetadata + override fun listMeta(listShape: ListShape) = defaultRustMetadata() + override fun mapMeta(mapShape: MapShape) = defaultRustMetadata() + override fun stringMeta(stringShape: StringShape) = defaultRustMetadata() + override fun numberMeta(numberShape: NumberShape) = defaultRustMetadata() + override fun blobMeta(blobShape: BlobShape) = defaultRustMetadata() } private const val META_KEY = "meta" From 982319ff5fc9947937dd6d41ca0532abe5098fab Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 21 Apr 2023 12:44:49 -0500 Subject: [PATCH 048/253] Switch to rust-toolchain@master in "Update GitHub Pages" (#2613) ## Motivation and Context The previous attempt https://github.com/awslabs/smithy-rs/pull/2610 did not work for properly running the workflow for `Update GitHub Pages`. This PR reverts it and uses a different fix to address the issue. ## Description Possibly with [this](https://users.rust-lang.org/t/sparse-registry-breaking-my-ci-and-i-dont-understand-why/89976) and `dtolnaly/rust-toolchain` having fixed a couple of issues [related to the sparse registry](https://github.com/dtolnay/rust-toolchain/issues?q=is%3Aissue+is%3Aclosed+sparse) recently, this PR will use `dtolnaly/rust-toolchain@master` rather than `stable` (most CIs in this repository have been using `master`). ## Testing The workflow in question has been verified to run successfully with this change ([link](https://github.com/awslabs/smithy-rs/actions/runs/4758820036)). ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito Co-authored-by: John DiSanti --- .github/workflows/github-pages.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 809955c7a0..9e0f52d9b6 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -1,11 +1,5 @@ on: workflow_dispatch: - inputs: - opt-in-to-sparse-registry: - description: Enable/disable CARGO_UNSTABLE_SPARSE_REGISTRY (https://blog.rust-lang.org/2022/06/22/sparse-registry-testing.html) - required: true - type: boolean - default: false push: branches: [main] paths: @@ -13,6 +7,9 @@ on: name: Update GitHub Pages +env: + rust_version: 1.67.1 + # Allow only one doc pages build to run at a time for the entire smithy-rs repo concurrency: group: github-pages-yml @@ -26,11 +23,12 @@ jobs: uses: actions/checkout@v3 with: persist-credentials: false - - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.rust_version }} - name: Generate docs env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CARGO_UNSTABLE_SPARSE_REGISTRY: ${{ inputs.opt-in-to-sparse-registry }} run: | git config --local user.name "AWS SDK Rust Bot" git config --local user.email "aws-sdk-rust-primary@amazon.com" From 07dd8d467fa51f9260b02cf271e10d30894008c0 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 21 Apr 2023 15:25:18 -0500 Subject: [PATCH 049/253] Create CredentialsCache and DefaultResolver only once in test/bench (#2620) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation and Context Addresses https://github.com/awslabs/smithy-rs/pull/2593#discussion_r1170703265 and https://github.com/awslabs/smithy-rs/pull/2593#discussion_r1170703694. Also ports the changes made in #2592. ## Description Prior to this PR, `sra_manual_test` and the bench `middleware_vs_orchestrator` created `CredentialsCache` and `aws_sdk_s3::endpoint::DefaultResolver` every time a request for `ListObjectsV2` was dispatched. This is not the case in production where those two types are created once during the construction of a service config ([here](https://github.com/awslabs/aws-sdk-rust/blob/main/sdk/s3/src/config.rs#L623-L625) and [here](https://github.com/awslabs/aws-sdk-rust/blob/main/sdk/s3/src/config.rs#L635-L652)) and reused for subsequent request dispatches. The PR will make `sra_manual_test` and `middleware_vs_orchestrator` do the same, creating `CredentialsCache` and `aws_sdk_s3::endpoint::DefaultResolver` only once before the test/benchmark starts running and reusing them thereafter. The change will help bring the performance for `orchestrator` closer to that for `middleware`. - In the `main` branch with `DefaultEndpointResolver` commented in and `StaticUriEndpointResolver` commented out: ``` middleware time: [20.924 µs 20.943 µs 20.964 µs] change: [-1.0107% -0.7357% -0.4827%] (p = 0.00 < 0.05) Change within noise threshold. Found 7 outliers among 100 measurements (7.00%) 1 (1.00%) high mild 6 (6.00%) high severe orchestrator time: [933.68 µs 940.11 µs 945.82 µs] change: [+2735.7% +2754.5% +2770.9%] (p = 0.00 < 0.05) Performance has regressed. Found 17 outliers among 100 measurements (17.00%) 14 (14.00%) low mild 2 (2.00%) high mild 1 (1.00%) high severe ``` - With the change in this PR: ``` middleware time: [21.161 µs 21.194 µs 21.232 µs] change: [-0.8973% -0.6397% -0.3758%] (p = 0.00 < 0.05) Change within noise threshold. Found 8 outliers among 100 measurements (8.00%) 5 (5.00%) high mild 3 (3.00%) high severe orchestrator time: [56.038 µs 56.182 µs 56.349 µs] change: [-0.7921% -0.5552% -0.3157%] (p = 0.00 < 0.05) Change within noise threshold. Found 5 outliers among 100 measurements (5.00%) 2 (2.00%) high mild 3 (3.00%) high severe ``` ## Testing Executed the following without any errors: ``` ➜ smithy-rs git:(ysaito/create-creds-cache-and-ep-resolver-only-once) ✗ ./gradlew aws:sra-test:assemble ➜ aws-sdk-s3 git:(ysaito/create-creds-cache-and-ep-resolver-only-once) cargo t ➜ aws-sdk-s3 git:(ysaito/create-creds-cache-and-ep-resolver-only-once) cargo bench ``` ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- .../benches/middleware_vs_orchestrator.rs | 73 +++++++++++-------- .../aws-sdk-s3/tests/sra_test.rs | 61 +++++++++------- 2 files changed, 77 insertions(+), 57 deletions(-) diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs index 634553466a..417b99c77b 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs @@ -5,9 +5,13 @@ #[macro_use] extern crate criterion; +use aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache}; +use aws_credential_types::provider::SharedCredentialsProvider; +use aws_credential_types::Credentials; use aws_sdk_s3 as s3; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::infallible_connection_fn; +use aws_smithy_http::endpoint::SharedEndpointResolver; use aws_smithy_runtime_api::type_erasure::TypedBox; use criterion::Criterion; use s3::operation::list_objects_v2::{ListObjectsV2Error, ListObjectsV2Input, ListObjectsV2Output}; @@ -22,10 +26,20 @@ async fn middleware(client: &s3::Client) { .expect("successful execution"); } -async fn orchestrator(connector: &DynConnector) { +async fn orchestrator( + connector: &DynConnector, + endpoint_resolver: SharedEndpointResolver, + credentials_cache: SharedCredentialsCache, +) { + let service_runtime_plugin = orchestrator::ManualServiceRuntimePlugin { + connector: connector.clone(), + endpoint_resolver: endpoint_resolver.clone(), + credentials_cache: credentials_cache.clone(), + }; + // TODO(enableNewSmithyRuntime): benchmark with `send_v2` directly once it works let runtime_plugins = aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins::new() - .with_client_plugin(orchestrator::ManualServiceRuntimePlugin(connector.clone())) + .with_client_plugin(service_runtime_plugin) .with_operation_plugin(aws_sdk_s3::operation::list_objects_v2::ListObjectsV2::new()) .with_operation_plugin(orchestrator::ManualOperationRuntimePlugin); let input = ListObjectsV2Input::builder() @@ -95,25 +109,32 @@ fn middleware_bench(c: &mut Criterion) { fn orchestrator_bench(c: &mut Criterion) { let conn = test_connection(); + let endpoint_resolver = SharedEndpointResolver::new(s3::endpoint::DefaultResolver::new()); + let credentials_cache = SharedCredentialsCache::new( + CredentialsCache::lazy() + .create_cache(SharedCredentialsProvider::new(Credentials::for_tests())), + ); c.bench_function("orchestrator", move |b| { b.to_async(tokio::runtime::Runtime::new().unwrap()) - .iter(|| async { orchestrator(&conn).await }) + .iter(|| async { + orchestrator(&conn, endpoint_resolver.clone(), credentials_cache.clone()).await + }) }); } mod orchestrator { - use aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache}; - use aws_credential_types::provider::SharedCredentialsProvider; - use aws_credential_types::Credentials; + use aws_credential_types::cache::SharedCredentialsCache; use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; use aws_runtime::recursion_detection::RecursionDetectionInterceptor; use aws_runtime::user_agent::UserAgentInterceptor; use aws_sdk_s3::config::Region; + use aws_sdk_s3::endpoint::Params; use aws_sdk_s3::operation::list_objects_v2::ListObjectsV2Input; use aws_smithy_client::erase::DynConnector; + use aws_smithy_http::endpoint::SharedEndpointResolver; use aws_smithy_runtime::client::connections::adapter::DynConnectorAdapter; - use aws_smithy_runtime_api::client::endpoints::StaticUriEndpointResolver; + use aws_smithy_runtime::client::orchestrator::endpoints::DefaultEndpointResolver; use aws_smithy_runtime_api::client::interceptors::{ Interceptor, InterceptorContext, InterceptorError, Interceptors, }; @@ -124,10 +145,13 @@ mod orchestrator { use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_types::region::SigningRegion; use aws_types::SigningService; - use http::Uri; use std::sync::Arc; - pub struct ManualServiceRuntimePlugin(pub DynConnector); + pub struct ManualServiceRuntimePlugin { + pub connector: DynConnector, + pub endpoint_resolver: SharedEndpointResolver, + pub credentials_cache: SharedCredentialsCache, + } impl RuntimePlugin for ManualServiceRuntimePlugin { fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { @@ -136,9 +160,7 @@ mod orchestrator { .identity_resolver( aws_runtime::auth::sigv4::SCHEME_ID, aws_runtime::identity::credentials::CredentialsIdentityResolver::new( - SharedCredentialsCache::new(CredentialsCache::lazy().create_cache( - SharedCredentialsProvider::new(Credentials::for_tests()), - )), + self.credentials_cache.clone(), ), ) .identity_resolver( @@ -163,14 +185,7 @@ mod orchestrator { ), ); - //cfg.set_endpoint_resolver(DefaultEndpointResolver::new( - // aws_smithy_http::endpoint::SharedEndpointResolver::new( - // aws_sdk_s3::endpoint::DefaultResolver::new(), - // ), - //)); - cfg.set_endpoint_resolver(StaticUriEndpointResolver::uri(Uri::from_static( - "https://test-bucket.s3.us-east-1.amazonaws.com/", - ))); + cfg.set_endpoint_resolver(DefaultEndpointResolver::new(self.endpoint_resolver.clone())); let params_builder = aws_sdk_s3::endpoint::Params::builder() .set_region(Some("us-east-1".to_owned())) @@ -182,7 +197,7 @@ mod orchestrator { ); let connection: Box = - Box::new(DynConnectorAdapter::new(self.0.clone())); + Box::new(DynConnectorAdapter::new(self.connector.clone())); cfg.set_connection(connection); cfg.set_trace_probe({ @@ -227,12 +242,10 @@ mod orchestrator { let input = context.input()?; let input = input .downcast_ref::() - .ok_or_else(|| InterceptorError::invalid_input_access())?; + .ok_or_else(|| "failed to downcast to ListObjectsV2Input")?; let mut params_builder = cfg .get::() - .ok_or(InterceptorError::read_before_execution( - "missing endpoint params builder", - ))? + .ok_or_else(|| "missing endpoint params builder")? .clone(); params_builder = params_builder.set_bucket(input.bucket.clone()); cfg.put(params_builder); @@ -251,13 +264,11 @@ mod orchestrator { ) -> Result<(), BoxError> { let params_builder = cfg .get::() - .ok_or(InterceptorError::read_before_execution( - "missing endpoint params builder", - ))? + .ok_or_else(|| "missing endpoint params builder")? .clone(); - let params = params_builder - .build() - .map_err(InterceptorError::read_before_execution)?; + let params = params_builder.build().map_err(|err| { + ContextAttachedError::new("endpoint params could not be built", err) + })?; cfg.put( aws_smithy_runtime_api::client::orchestrator::EndpointResolverParams::new( params, diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index ff6ed15ef1..77d576edfe 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -10,17 +10,18 @@ use aws_runtime::auth::sigv4::SigV4OperationSigningConfig; use aws_runtime::recursion_detection::RecursionDetectionInterceptor; use aws_runtime::user_agent::UserAgentInterceptor; use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::endpoint::Params; use aws_sdk_s3::operation::list_objects_v2::{ ListObjectsV2Error, ListObjectsV2Input, ListObjectsV2Output, }; use aws_sdk_s3::primitives::SdkBody; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::TestConnection; +use aws_smithy_http::endpoint::SharedEndpointResolver; use aws_smithy_runtime::client::connections::adapter::DynConnectorAdapter; use aws_smithy_runtime::client::orchestrator::endpoints::DefaultEndpointResolver; -use aws_smithy_runtime_api::client::interceptors::{ - Interceptor, InterceptorContext, InterceptorError, Interceptors, -}; +use aws_smithy_runtime_api::client::interceptors::error::ContextAttachedError; +use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext, Interceptors}; use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, Connection, HttpRequest, HttpResponse, TraceProbe, }; @@ -74,7 +75,11 @@ async fn sra_test() { async fn sra_manual_test() { tracing_subscriber::fmt::init(); - struct ManualServiceRuntimePlugin(TestConnection<&'static str>); + struct ManualServiceRuntimePlugin { + connector: TestConnection<&'static str>, + endpoint_resolver: SharedEndpointResolver, + credentials_cache: SharedCredentialsCache, + } impl RuntimePlugin for ManualServiceRuntimePlugin { fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { @@ -83,9 +88,7 @@ async fn sra_manual_test() { .identity_resolver( aws_runtime::auth::sigv4::SCHEME_ID, aws_runtime::identity::credentials::CredentialsIdentityResolver::new( - SharedCredentialsCache::new(CredentialsCache::lazy().create_cache( - SharedCredentialsProvider::new(Credentials::for_tests()), - )), + self.credentials_cache.clone(), ), ) .identity_resolver( @@ -110,13 +113,9 @@ async fn sra_manual_test() { ), ); - cfg.set_endpoint_resolver(DefaultEndpointResolver::new( - aws_smithy_http::endpoint::SharedEndpointResolver::new( - aws_sdk_s3::endpoint::DefaultResolver::new(), - ), - )); + cfg.set_endpoint_resolver(DefaultEndpointResolver::new(self.endpoint_resolver.clone())); - let params_builder = aws_sdk_s3::endpoint::Params::builder() + let params_builder = Params::builder() .set_region(Some("us-east-1".to_owned())) .set_endpoint(Some("https://s3.us-east-1.amazonaws.com/".to_owned())); cfg.put(params_builder); @@ -125,8 +124,9 @@ async fn sra_manual_test() { aws_smithy_runtime_api::client::retries::NeverRetryStrategy::new(), ); - let connection: Box = - Box::new(DynConnectorAdapter::new(DynConnector::new(self.0.clone()))); + let connection: Box = Box::new(DynConnectorAdapter::new( + DynConnector::new(self.connector.clone()), + )); cfg.set_connection(connection); cfg.set_trace_probe({ @@ -189,12 +189,10 @@ async fn sra_manual_test() { let input = context.input()?; let input = input .downcast_ref::() - .ok_or_else(|| InterceptorError::invalid_input_access())?; + .ok_or_else(|| "failed to downcast to ListObjectsV2Input")?; let mut params_builder = cfg .get::() - .ok_or(InterceptorError::read_before_execution( - "missing endpoint params builder", - ))? + .ok_or_else(|| "missing endpoint params builder")? .clone(); params_builder = params_builder.set_bucket(input.bucket.clone()); cfg.put(params_builder); @@ -213,13 +211,11 @@ async fn sra_manual_test() { ) -> Result<(), BoxError> { let params_builder = cfg .get::() - .ok_or(InterceptorError::read_before_execution( - "missing endpoint params builder", - ))? + .ok_or_else(|| "missing endpoint params builder")? .clone(); - let params = params_builder - .build() - .map_err(InterceptorError::read_before_execution)?; + let params = params_builder.build().map_err(|err| { + ContextAttachedError::new("endpoint params could not be built", err) + })?; cfg.put( aws_smithy_runtime_api::client::orchestrator::EndpointResolverParams::new( params, @@ -265,8 +261,21 @@ async fn sra_manual_test() { "#).unwrap(), )]); + let endpoint_resolver = + SharedEndpointResolver::new(aws_sdk_s3::endpoint::DefaultResolver::new()); + let credentials_cache = SharedCredentialsCache::new( + CredentialsCache::lazy() + .create_cache(SharedCredentialsProvider::new(Credentials::for_tests())), + ); + + let service_runtime_plugin = ManualServiceRuntimePlugin { + connector: conn.clone(), + endpoint_resolver, + credentials_cache, + }; + let runtime_plugins = aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins::new() - .with_client_plugin(ManualServiceRuntimePlugin(conn.clone())) + .with_client_plugin(service_runtime_plugin) .with_operation_plugin(aws_sdk_s3::operation::list_objects_v2::ListObjectsV2::new()) .with_operation_plugin(ManualOperationRuntimePlugin); From 2de6815c103af5b0b85f2e6e65a7b0d478fe0cb2 Mon Sep 17 00:00:00 2001 From: Julian Antonielli Date: Mon, 24 Apr 2023 11:25:27 +0100 Subject: [PATCH 050/253] Add `AlbHealthCheckLayer` (#2540) ## Motivation and Context Services often need the ability to report health status via health checks (see [ALB Health Checks](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html)). This PR adds a simple layer that allows configuring your service to respond to these health check requests. ## Description Adds `AlbHealthCheckLayer`, and `AlbHealthCheckService`. ## Testing Added this layer to the `pokemon-service` binary, and added an integration test for it. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- CHANGELOG.next.toml | 27 ++- examples/pokemon-service/Cargo.toml | 3 + examples/pokemon-service/src/main.rs | 15 +- examples/pokemon-service/tests/common/mod.rs | 4 + examples/pokemon-service/tests/simple.rs | 7 + .../src/plugin/alb_health_check.rs | 176 ++++++++++++++++++ .../aws-smithy-http-server/src/plugin/mod.rs | 1 + .../src/plugin/pipeline.rs | 7 + 8 files changed, 231 insertions(+), 9 deletions(-) create mode 100644 rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index c52faf5882..a03141576a 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -11,16 +11,31 @@ # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} # author = "rcoh" +[[smithy-rs]] +message = """ +Implement layer for servers to handle [ALB health checks](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html). +Take a look at `aws_smithy_http_server::plugin::alb_health_check` to learn about it. +""" +references = ["smithy-rs#2540"] +meta = { "breaking" = false, "tada" = true, "bug" = false, "target" = "server" } +author = "jjant" + +[[smithy-rs]] +message = "Implement `PluginPipeline::http_layer` which allows you to apply a `tower::Layer` to all operations." +references = ["smithy-rs#2540"] +meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "server" } +author = "jjant" + [[aws-sdk-rust]] -message = "Implement std::error::Error#source() properly for the service meta Error enum" +message = "Implement std::error::Error#source() properly for the service meta Error enum." references = ["aws-sdk-rust#784"] meta = { "breaking" = false, "tada" = false, "bug" = false } author = "abusch" [[smithy-rs]] -message = "Implement std::error::Error#source() properly for the service meta Error enum" +message = "Implement std::error::Error#source() properly for the service meta Error enum." references = ["aws-sdk-rust#784"] -meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client"} +meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } author = "abusch" [[aws-sdk-rust]] @@ -32,7 +47,7 @@ author = "jdisanti" [[smithy-rs]] message = "The outputs for event stream operations now implement the `Sync` auto-trait." references = ["smithy-rs#2496"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "all"} +meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "all" } author = "jdisanti" [[aws-sdk-rust]] @@ -44,7 +59,7 @@ author = "eduardomourar" [[smithy-rs]] message = "Clients now compile for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it!" references = ["smithy-rs#2254"] -meta = { "breaking" = false, "tada" = true, "bug" = false, "target" = "client"} +meta = { "breaking" = false, "tada" = true, "bug" = false, "target" = "client" } author = "eduardomourar" [[smithy-rs]] @@ -56,7 +71,7 @@ author = "jdisanti" [[smithy-rs]] message = "Streaming operations now emit the request ID at the `debug` log level like their non-streaming counterparts." references = ["smithy-rs#2495"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client"} +meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client" } author = "jdisanti" [[smithy-rs]] diff --git a/examples/pokemon-service/Cargo.toml b/examples/pokemon-service/Cargo.toml index 30839509e5..8324ffcc8c 100644 --- a/examples/pokemon-service/Cargo.toml +++ b/examples/pokemon-service/Cargo.toml @@ -24,6 +24,9 @@ async-stream = "0.3" rand = "0.8.5" serial_test = "1.0.0" +# We use hyper client in tests +hyper = {version = "0.14.25", features = ["server", "client"] } + # This dependency is only required for testing the `pokemon-service-tls` program. hyper-rustls = { version = "0.23.2", features = ["http2"] } diff --git a/examples/pokemon-service/src/main.rs b/examples/pokemon-service/src/main.rs index b62118c816..7a865abfaf 100644 --- a/examples/pokemon-service/src/main.rs +++ b/examples/pokemon-service/src/main.rs @@ -8,11 +8,15 @@ mod plugin; use std::{net::SocketAddr, sync::Arc}; use aws_smithy_http_server::{ - extension::OperationExtensionExt, instrumentation::InstrumentExt, plugin::PluginPipeline, - request::request_id::ServerRequestIdProviderLayer, AddExtensionLayer, + extension::OperationExtensionExt, + instrumentation::InstrumentExt, + plugin::{alb_health_check::AlbHealthCheckLayer, PluginPipeline}, + request::request_id::ServerRequestIdProviderLayer, + AddExtensionLayer, }; use clap::Parser; +use hyper::StatusCode; use plugin::PrintExt; use pokemon_service::{ @@ -47,7 +51,12 @@ pub async fn main() { // `Response::extensions`, or infer routing failure when it's missing. .insert_operation_extension() // Adds `tracing` spans and events to the request lifecycle. - .instrument(); + .instrument() + // Handle `/ping` health check requests. + .http_layer(AlbHealthCheckLayer::from_handler("/ping", |_req| async { + StatusCode::OK + })); + let app = PokemonService::builder_with_plugins(plugins) // Build a registry containing implementations to all the operations in the service. These // are async functions or async closures that take as input the operation's input and diff --git a/examples/pokemon-service/tests/common/mod.rs b/examples/pokemon-service/tests/common/mod.rs index 0cc34064df..d10430bfbd 100644 --- a/examples/pokemon-service/tests/common/mod.rs +++ b/examples/pokemon-service/tests/common/mod.rs @@ -23,6 +23,10 @@ pub async fn run_server() -> ChildDrop { ChildDrop(child) } +pub fn base_url() -> String { + format!("http://{DEFAULT_ADDRESS}:{DEFAULT_PORT}") +} + pub fn client() -> Client> { let authority = Authority::from_str(&format!("{DEFAULT_ADDRESS}:{DEFAULT_PORT}")) .expect("could not parse authority"); diff --git a/examples/pokemon-service/tests/simple.rs b/examples/pokemon-service/tests/simple.rs index 1c86d3cf10..af03bfee30 100644 --- a/examples/pokemon-service/tests/simple.rs +++ b/examples/pokemon-service/tests/simple.rs @@ -81,4 +81,11 @@ async fn simple_integration_test() { let service_statistics_out = client.get_server_statistics().send().await.unwrap(); assert_eq!(2, service_statistics_out.calls_count.unwrap()); + + let hyper_client = hyper::Client::new(); + let health_check_url = format!("{}/ping", common::base_url()); + let health_check_url = hyper::Uri::try_from(health_check_url).unwrap(); + let result = hyper_client.get(health_check_url).await.unwrap(); + + assert_eq!(result.status(), 200); } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs b/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs new file mode 100644 index 0000000000..33932ddd7d --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs @@ -0,0 +1,176 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Middleware for handling [ALB health +//! checks](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html). +//! +//! # Example +//! +//! ```no_run +//! # use aws_smithy_http_server::{body, plugin::{PluginPipeline, alb_health_check::AlbHealthCheckLayer}}; +//! # use hyper::{Body, Response, StatusCode}; +//! let plugins = PluginPipeline::new() +//! // Handle all `/ping` health check requests by returning a `200 OK`. +//! .http_layer(AlbHealthCheckLayer::from_handler("/ping", |_req| async { +//! StatusCode::OK +//! })); +//! +//! ``` + +use std::borrow::Cow; +use std::convert::Infallible; +use std::task::{Context, Poll}; + +use futures_util::{Future, FutureExt}; +use http::StatusCode; +use hyper::{Body, Request, Response}; +use pin_project_lite::pin_project; +use tower::{service_fn, util::Oneshot, Layer, Service, ServiceExt}; + +use crate::body::BoxBody; + +use super::either::EitherProj; +use super::Either; + +/// A [`tower::Layer`] used to apply [`AlbHealthCheckService`]. +#[derive(Clone, Debug)] +pub struct AlbHealthCheckLayer { + health_check_uri: Cow<'static, str>, + health_check_handler: HealthCheckHandler, +} + +impl AlbHealthCheckLayer<()> { + /// Handle health check requests at `health_check_uri` with the specified handler. + pub fn from_handler, H: Fn(Request) -> HandlerFuture + Clone>( + health_check_uri: impl Into>, + health_check_handler: H, + ) -> AlbHealthCheckLayer< + impl Service< + Request, + Response = StatusCode, + Error = Infallible, + Future = impl Future>, + > + Clone, + > { + let service = service_fn(move |req| health_check_handler(req).map(Ok)); + + AlbHealthCheckLayer::new(health_check_uri, service) + } + + /// Handle health check requests at `health_check_uri` with the specified service. + pub fn new, Response = StatusCode>>( + health_check_uri: impl Into>, + health_check_handler: H, + ) -> AlbHealthCheckLayer { + AlbHealthCheckLayer { + health_check_uri: health_check_uri.into(), + health_check_handler, + } + } +} + +impl Layer for AlbHealthCheckLayer { + type Service = AlbHealthCheckService; + + fn layer(&self, inner: S) -> Self::Service { + AlbHealthCheckService { + inner, + layer: self.clone(), + } + } +} + +/// A middleware [`Service`] responsible for handling health check requests. +#[derive(Clone, Debug)] +pub struct AlbHealthCheckService { + inner: S, + layer: AlbHealthCheckLayer, +} + +impl Service> for AlbHealthCheckService +where + S: Service, Response = Response> + Clone, + S::Future: std::marker::Send + 'static, + H: Service, Response = StatusCode, Error = Infallible> + Clone, +{ + type Response = S::Response; + + type Error = S::Error; + + type Future = AlbHealthCheckFuture; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + // The check that the service is ready is done by `Oneshot` below. + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: Request) -> Self::Future { + if req.uri() == self.layer.health_check_uri.as_ref() { + let clone = self.layer.health_check_handler.clone(); + let service = std::mem::replace(&mut self.layer.health_check_handler, clone); + let handler_future = service.oneshot(req); + + AlbHealthCheckFuture::handler_future(handler_future) + } else { + let clone = self.inner.clone(); + let service = std::mem::replace(&mut self.inner, clone); + let service_future = service.oneshot(req); + + AlbHealthCheckFuture::service_future(service_future) + } + } +} + +type HealthCheckFutureInner = Either>, Oneshot>>; + +pin_project! { + /// Future for [`AlbHealthCheckService`]. + pub struct AlbHealthCheckFuture, Response = StatusCode>, S: Service>> { + #[pin] + inner: HealthCheckFutureInner + } +} + +impl, Response = StatusCode>, S: Service>> AlbHealthCheckFuture { + fn handler_future(handler_future: Oneshot>) -> Self { + Self { + inner: Either::Left { value: handler_future }, + } + } + + fn service_future(service_future: Oneshot>) -> Self { + Self { + inner: Either::Right { value: service_future }, + } + } +} + +impl< + H: Service, Response = StatusCode, Error = Infallible>, + S: Service, Response = Response>, + > Future for AlbHealthCheckFuture +{ + type Output = Result; + + fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let either_proj = self.project().inner.project(); + + match either_proj { + EitherProj::Left { value } => { + let polled: Poll = value.poll(cx).map(|res| { + res.map(|status_code| { + Response::builder() + .status(status_code) + .body(crate::body::empty()) + .unwrap() + }) + .map_err(|never| match never {}) + }); + polled + } + EitherProj::Right { value } => value.poll(cx), + } + } +} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs index 72db50cbb4..ab3c385720 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -118,6 +118,7 @@ //! ``` //! +pub mod alb_health_check; mod closure; mod either; mod filter; diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs index 7b390fc903..2a956922e4 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs @@ -6,6 +6,8 @@ use crate::operation::Operation; use crate::plugin::{IdentityPlugin, Plugin, PluginStack}; +use super::HttpLayer; + /// A wrapper struct for composing [`Plugin`]s. /// It is used as input for the `builder_with_plugins` method on the generate service struct /// (e.g. `PokemonService::builder_with_plugins`). @@ -168,6 +170,11 @@ impl

PluginPipeline

{ pub fn push(self, new_plugin: NewPlugin) -> PluginPipeline> { PluginPipeline(PluginStack::new(new_plugin, self.0)) } + + /// Applies a single [`tower::Layer`] to all operations _before_ they are deserialized. + pub fn http_layer(self, layer: L) -> PluginPipeline, P>> { + PluginPipeline(PluginStack::new(HttpLayer(layer), self.0)) + } } impl Plugin for PluginPipeline From 2ebbfad498f428b945cba453ef6cd464d05d0ce2 Mon Sep 17 00:00:00 2001 From: Tim McNamara Date: Mon, 24 Apr 2023 23:40:04 +1200 Subject: [PATCH 051/253] Expand example documentation (#2570) ## Motivation and Context The current README for the example services is fairly spartan. ## Description I've expanded the README, specifically: - Clean up some markup - Document pre-reqs. - Document all make targets. - Avoid the $BINARY because it looks like a bash variable. - Expand intro ## Testing n/a ## Checklist n/a ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Matteo Bigoi <1781140+crisidev@users.noreply.github.com> Co-authored-by: david-perez --- examples/README.md | 79 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 20 deletions(-) diff --git a/examples/README.md b/examples/README.md index 7f08738455..a82327bc64 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,17 +1,35 @@ # Smithy Rust Server SDK examples -This folder contains an example services showcasing the service framework capabilities and to run benchmarks. +This folder contains some example services showcasing Smithy Rust Server SDK, +also known as the Rust service framework, capabilities and to run benchmarks. -- `/pokemon-service`, a HTTP server implementation demonstrating [middleware](https://awslabs.github.io/smithy-rs/design/server/middleware.html) -and [extractors](https://awslabs.github.io/smithy-rs/design/server/from_parts.html). -- `/pokemon-service-tls`, a minimal HTTPS server implementation. -- `/pokemon-service-lambda`, a minimal Lambda deployment. +Three server implementations are available: -The `/{binary}/tests` folders are integration tests involving the generated clients. +- `/pokemon-service`, a HTTP server demonstrating [middleware] and [extractors]. +- `/pokemon-service-tls`, a HTTPS server. This server can do + its own TLS negotiation, rather than relying on a load balancer. +- `/pokemon-service-lambda`, a server that can be deployed onto AWS Lambda. -## Build +These servers, and their clients, are generated using smithy-rs. You're invited +to benchmark the performance of these servers to see whether smithy-rs might be +a suitable choice for implementing your web service. -Since this example requires both the server and client SDK to be code-generated +[middleware]: https://awslabs.github.io/smithy-rs/design/server/middleware.html +[extractors]: https://awslabs.github.io/smithy-rs/design/server/from_parts.html + + +## Pre-requisites + +You will need install Java 11 to run the smithy-rs code generator and an +installation of Rust, including `cargo`, to compile the generated code. + +(Optional) The [Cargo Lambda](https://cargo-lambda.info/) sub-command for +`cargo` is required to support the AWS Lambda integration. + + +## Building + +Since these examples require both the server and client SDK to be code-generated from their [model](/codegen-server-test/model/pokemon.smithy), a Makefile is provided to build and run the service. Just run `make` to prepare the first build. @@ -19,31 +37,52 @@ build. Once the example has been built successfully the first time, idiomatic `cargo` can be used directly. -`make distclean` can be used for a complete cleanup of all artefacts. +### Make targets: + +- `codegen`: generates the Pokémon service crates (default) +- `build`: compiles the generated client and server +- `clean`: deletes build artifacts +- `clippy`: lints the code +- `distclean`: delete generated code and build artifacts +- `doc-open`: builds and opens the rustdoc documentation +- `lambda_invoke`: invokes a running server +- `lambda_watch`: runs the service on an emulated AWS Lambda environment +- `run`: runs the Pokémon service +- `test`: runs integration and unit tests + -## Run +## Running services -To run a binary use +To run one of the three server implementations locally, provide the appropriate +service name to the `--bin` flag: ```bash -cargo run -p $BINARY +cargo run --bin pokemon-service[(-lambda|-tls)] ``` -CLI arguments can be passed to the servers, use +CLI arguments can be passed to the server binaries by adding them after `--`. +For example, to see a service's help information, use the following: ```bash -cargo run -p $BINARY -- --help +cargo run --bin -- --help ``` -for more information. +## Testing -## Test +The `/pokemon-test*/tests` folders provide integration tests involving the +generated clients. -`cargo test` can be used to spawn a service and run some simple integration -tests against it. Use `-p $BINARY` to filter by package. +They can be invoked with `cargo test`. This will spawn each service in turn +and run some integration tests against it. Use `-p ` to filter by +package. More info can be found in the `tests` folder of each package. -## Benchmarks -Please see [BENCHMARKS.md](/examples/BENCHMARKS.md). +## Benchmarking + +Servers running locally (see "Running services") can be benchmarked with any +load testing tool, such as Artillery or `wrk`. + +Please see [BENCHMARKS.md](/examples/BENCHMARKS.md) for benchmarking results +produced by the smithy-rs team. From e07aa7660daf57279b229c5c04d9f9206b97c3e5 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 24 Apr 2023 14:06:31 -0700 Subject: [PATCH 052/253] Add HTTP basic and bearer auth support to the orchestrator (#2622) ## Motivation and Context This PR adds support for Smithy's `@httpBasicAuth` and `@httpBearerAuth` auth schemes, and ports the `@httpApiKeyAuth` scheme to the orchestrator. This is prerequisite work for supporting Amazon CodeCatalyst since that requires bearer auth. This PR also fixes a bug in auth orchestrator that caused an error if no identity is present for a scheme even when an identity for a lower priority scheme is available. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Zelda Hessler --- aws/rust-runtime/aws-runtime/src/auth.rs | 5 +- aws/rust-runtime/aws-runtime/src/identity.rs | 12 +- .../rustsdk/AwsFluentClientDecorator.kt | 35 +- .../smithy/rustsdk/CredentialProviders.kt | 16 +- .../HttpConnectorConfigCustomization.kt | 9 +- .../smithy/rustsdk/SigV4AuthDecorator.kt | 1 + .../benches/middleware_vs_orchestrator.rs | 5 +- .../aws-sdk-s3/tests/interceptors.rs | 103 ----- .../aws-sdk-s3/tests/sra_test.rs | 54 +-- .../client/smithy/RustClientCodegenPlugin.kt | 4 + .../customizations/ApiKeyAuthDecorator.kt | 6 +- .../customizations/HttpAuthDecorator.kt | 371 +++++++++++++++ .../HttpConnectorConfigDecorator.kt | 157 +++++++ .../ServiceRuntimePluginGenerator.kt | 24 +- .../client/CustomizableOperationGenerator.kt | 68 ++- .../client/FluentClientDecorator.kt | 18 + .../client/FluentClientGenerator.kt | 7 +- .../generators/client/FluentClientGenerics.kt | 33 ++ .../customizations/ApiKeyAuthDecoratorTest.kt | 2 +- .../customizations/HttpAuthDecoratorTest.kt | 433 ++++++++++++++++++ .../HttpVersionListGeneratorTest.kt | 2 +- .../ResiliencyConfigCustomizationTest.kt | 4 +- .../ClientContextConfigCustomizationTest.kt | 3 +- .../endpoint/EndpointParamsGeneratorTest.kt | 2 +- .../endpoint/EndpointResolverGeneratorTest.kt | 2 +- .../endpoint/EndpointsDecoratorTest.kt | 2 +- rust-runtime/aws-smithy-http-auth/src/lib.rs | 4 + .../aws-smithy-runtime-api/Cargo.toml | 6 + .../aws-smithy-runtime-api/additional-ci | 12 + .../external-types.toml | 1 + .../aws-smithy-runtime-api/src/client/auth.rs | 3 + .../src/client/auth/http.rs | 9 + .../src/client/identity.rs | 68 ++- .../src/client/identity/http.rs | 130 ++++++ .../src/client/orchestrator.rs | 95 ++-- rust-runtime/aws-smithy-runtime/Cargo.toml | 4 + rust-runtime/aws-smithy-runtime/src/client.rs | 2 + .../aws-smithy-runtime/src/client/auth.rs | 7 + .../src/client/auth/http.rs | 348 ++++++++++++++ .../src/client/connections.rs | 4 +- .../src/client/connections/test_connection.rs | 4 +- .../src/client/orchestrator/auth.rs | 215 ++++++++- .../ci-scripts/codegen-diff/semver-checks.py | 9 +- 43 files changed, 1960 insertions(+), 339 deletions(-) delete mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt rename codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/{ => smithy}/customizations/ApiKeyAuthDecoratorTest.kt (98%) create mode 100644 codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt rename codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/{ => smithy}/customizations/HttpVersionListGeneratorTest.kt (99%) rename codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/{ => smithy}/customizations/ResiliencyConfigCustomizationTest.kt (86%) rename codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/{ => smithy}/endpoint/ClientContextConfigCustomizationTest.kt (92%) rename codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/{ => smithy}/endpoint/EndpointParamsGeneratorTest.kt (95%) rename codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/{ => smithy}/endpoint/EndpointResolverGeneratorTest.kt (98%) rename codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/{ => smithy}/endpoint/EndpointsDecoratorTest.kt (99%) create mode 100755 rust-runtime/aws-smithy-runtime-api/additional-ci create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/auth.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/auth/http.rs diff --git a/aws/rust-runtime/aws-runtime/src/auth.rs b/aws/rust-runtime/aws-runtime/src/auth.rs index 503a4bf2af..e2b355f0be 100644 --- a/aws/rust-runtime/aws-runtime/src/auth.rs +++ b/aws/rust-runtime/aws-runtime/src/auth.rs @@ -12,10 +12,9 @@ pub mod sigv4 { UriPathNormalizationMode, }; use aws_smithy_http::property_bag::PropertyBag; - use aws_smithy_runtime_api::client::identity::Identity; + use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, HttpAuthScheme, HttpRequest, HttpRequestSigner, IdentityResolver, - IdentityResolvers, + BoxError, HttpAuthScheme, HttpRequest, HttpRequestSigner, }; use aws_types::region::SigningRegion; use aws_types::SigningService; diff --git a/aws/rust-runtime/aws-runtime/src/identity.rs b/aws/rust-runtime/aws-runtime/src/identity.rs index 0cf5b5101e..2e2eff1bf2 100644 --- a/aws/rust-runtime/aws-runtime/src/identity.rs +++ b/aws/rust-runtime/aws-runtime/src/identity.rs @@ -7,10 +7,8 @@ pub mod credentials { use aws_credential_types::cache::SharedCredentialsCache; use aws_smithy_http::property_bag::PropertyBag; - use aws_smithy_runtime_api::client::identity::Identity; - use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, BoxFallibleFut, IdentityResolver, - }; + use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver}; + use aws_smithy_runtime_api::client::orchestrator::{BoxError, Future}; /// Smithy identity resolver for AWS credentials. #[derive(Debug)] @@ -26,13 +24,13 @@ pub mod credentials { } impl IdentityResolver for CredentialsIdentityResolver { - fn resolve_identity(&self, _identity_properties: &PropertyBag) -> BoxFallibleFut { + fn resolve_identity(&self, _identity_properties: &PropertyBag) -> Future { let cache = self.credentials_cache.clone(); - Box::pin(async move { + Future::new(Box::pin(async move { let credentials = cache.as_ref().provide_cached_credentials().await?; let expiration = credentials.expiry(); Result::<_, BoxError>::Ok(Identity::new(credentials, expiration)) - }) + })) } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index 5fbe721e1e..cca5c9f910 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rustsdk -import software.amazon.smithy.codegen.core.Symbol 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.customize.ClientCodegenDecorator @@ -14,6 +13,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.client.Fluen import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerics import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.client.NoClientGenerics import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.rustlang.GenericTypeArg @@ -50,37 +50,6 @@ private class Types(runtimeConfig: RuntimeConfig) { val timeoutConfig = smithyTypes.resolve("timeout::TimeoutConfig") } -private class AwsClientGenerics(private val types: Types) : FluentClientGenerics { - /** Declaration with defaults set */ - override val decl = writable { } - - /** Instantiation of the Smithy client generics */ - override val smithyInst = writable { - rustTemplate( - "<#{DynConnector}, #{DynMiddleware}<#{DynConnector}>>", - "DynConnector" to types.dynConnector, - "DynMiddleware" to types.dynMiddleware, - ) - } - - /** Instantiation */ - override val inst = "" - - /** Trait bounds */ - override val bounds = writable { } - - /** Bounds for generated `send()` functions */ - override fun sendBounds( - operation: Symbol, - operationOutput: Symbol, - operationError: Symbol, - retryClassifier: RuntimeType, - ): Writable = - writable { } - - override fun toRustGenerics() = RustGenerics() -} - class AwsFluentClientDecorator : ClientCodegenDecorator { override val name: String = "FluentClient" @@ -90,7 +59,7 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { val runtimeConfig = codegenContext.runtimeConfig val types = Types(runtimeConfig) - val generics = AwsClientGenerics(types) + val generics = NoClientGenerics(runtimeConfig) FluentClientGenerator( codegenContext, reexportSmithyClientBuilder = false, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index c07590f1e2..84d644a43c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -112,18 +112,24 @@ class CredentialsIdentityResolverRegistration( override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { - is ServiceRuntimePluginSection.IdentityResolver -> { + is ServiceRuntimePluginSection.AdditionalConfig -> { rustTemplate( """ - .identity_resolver( - #{SIGV4_SCHEME_ID}, - #{CredentialsIdentityResolver}::new(self.handle.conf.credentials_cache()) - ) + cfg.set_identity_resolvers( + #{IdentityResolvers}::builder() + .identity_resolver( + #{SIGV4_SCHEME_ID}, + #{CredentialsIdentityResolver}::new(self.handle.conf.credentials_cache()) + ) + .build() + ); """, "SIGV4_SCHEME_ID" to AwsRuntimeType.awsRuntime(runtimeConfig) .resolve("auth::sigv4::SCHEME_ID"), "CredentialsIdentityResolver" to AwsRuntimeType.awsRuntime(runtimeConfig) .resolve("identity::credentials::CredentialsIdentityResolver"), + "IdentityResolvers" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::identity::IdentityResolvers"), ) } else -> {} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt index fcf3cc0c2b..75c252d0fb 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt @@ -15,7 +15,9 @@ 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.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.util.letIf +// TODO(enableNewSmithyRuntime): Delete this decorator since it's now in `codegen-client` class HttpConnectorDecorator : ClientCodegenDecorator { override val name: String = "HttpConnectorDecorator" override val order: Byte = 0 @@ -23,9 +25,10 @@ class HttpConnectorDecorator : ClientCodegenDecorator { override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, - ): List { - return baseCustomizations + HttpConnectorConfigCustomization(codegenContext) - } + ): List = + baseCustomizations.letIf(!codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + it + HttpConnectorConfigCustomization(codegenContext) + } } class HttpConnectorConfigCustomization( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 751788df29..7ae7582e69 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -136,6 +136,7 @@ private class AuthOperationRuntimePluginCustomization(private val codegenContext signing_options.normalize_uri_path = $normalizeUrlPath; signing_options.signing_optional = $signingOptional; signing_options.payload_override = #{payload_override}; + signing_options.request_timestamp = cfg.request_time().unwrap_or_default().system_time(); let mut sigv4_properties = #{PropertyBag}::new(); sigv4_properties.insert(#{SigV4OperationSigningConfig} { diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs index 417b99c77b..d587cdb0b4 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs @@ -135,8 +135,9 @@ mod orchestrator { use aws_smithy_http::endpoint::SharedEndpointResolver; use aws_smithy_runtime::client::connections::adapter::DynConnectorAdapter; use aws_smithy_runtime::client::orchestrator::endpoints::DefaultEndpointResolver; + use aws_smithy_runtime_api::client::interceptors::error::ContextAttachedError; use aws_smithy_runtime_api::client::interceptors::{ - Interceptor, InterceptorContext, InterceptorError, Interceptors, + Interceptor, InterceptorContext, Interceptors, }; use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, Connection, HttpRequest, HttpResponse, TraceProbe, @@ -156,7 +157,7 @@ mod orchestrator { impl RuntimePlugin for ManualServiceRuntimePlugin { fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { let identity_resolvers = - aws_smithy_runtime_api::client::orchestrator::IdentityResolvers::builder() + aws_smithy_runtime_api::client::identity::IdentityResolvers::builder() .identity_resolver( aws_runtime::auth::sigv4::SCHEME_ID, aws_runtime::identity::credentials::CredentialsIdentityResolver::new( diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs deleted file mode 100644 index fd4c8649c0..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -// type TxReq = http::Request; -// type TxRes = http::Response; -// -// pub struct SigV4SigningConfigInterceptor { -// pub signing_service: &'static str, -// pub signing_region: Option, -// } - -// // Mount the interceptors -// let mut interceptors = Interceptors::new(); -// let sig_v4_signing_config_interceptor = SigV4SigningConfigInterceptor { -// signing_region: service_config.region.clone(), -// signing_service: service_config.signing_service(), -// }; -// let credentials_cache_interceptor = CredentialsCacheInterceptor { -// shared_credentials_cache: service_config.credentials_cache.clone(), -// }; -// let checksum_interceptor = ChecksumInterceptor { -// checksum_mode: input.checksum_mode().cloned(), -// }; -// interceptors -// .with_interceptor(sig_v4_signing_config_interceptor) -// .with_interceptor(credentials_cache_interceptor) -// .with_interceptor(checksum_interceptor); - -// let token_bucket = Box::new(standard::TokenBucket::builder().max_tokens(500).build()); -// -// impl Interceptor for SigV4SigningConfigInterceptor { -// fn modify_before_signing( -// &mut self, -// context: &mut InterceptorContext, -// ) -> Result<(), InterceptorError> { -// let mut props = context.properties_mut(); -// -// let mut signing_config = OperationSigningConfig::default_config(); -// signing_config.signing_options.content_sha256_header = true; -// signing_config.signing_options.double_uri_encode = false; -// signing_config.signing_options.normalize_uri_path = false; -// props.insert(signing_config); -// props.insert(aws_types::SigningService::from_static(self.signing_service)); -// -// if let Some(signing_region) = self.signing_region.as_ref() { -// props.insert(aws_types::region::SigningRegion::from( -// signing_region.clone(), -// )); -// } -// -// Ok(()) -// } -// } -// -// pub struct CredentialsCacheInterceptor { -// pub shared_credentials_cache: SharedCredentialsCache, -// } -// -// impl Interceptor for CredentialsCacheInterceptor { -// fn modify_before_signing( -// &mut self, -// context: &mut InterceptorContext, -// ) -> Result<(), InterceptorError> { -// match self -// .shared_credentials_cache -// .as_ref() -// .provide_cached_credentials() -// .now_or_never() -// { -// Some(Ok(creds)) => { -// context.properties_mut().insert(creds); -// } -// // ignore the case where there is no credentials cache wired up -// Some(Err(CredentialsError::CredentialsNotLoaded { .. })) => { -// tracing::info!("credentials cache returned CredentialsNotLoaded, ignoring") -// } -// // if we get another error class, there is probably something actually wrong that the user will -// // want to know about -// Some(Err(other)) => return Err(InterceptorError::ModifyBeforeSigning(other.into())), -// None => unreachable!("fingers crossed that creds are always available"), -// } -// -// Ok(()) -// } -// } -// -// pub struct ChecksumInterceptor { -// pub checksum_mode: Option, -// } -// -// impl Interceptor for ChecksumInterceptor { -// fn modify_before_serialization( -// &mut self, -// context: &mut InterceptorContext, -// ) -> Result<(), InterceptorError> { -// let mut props = context.properties_mut(); -// props.insert(self.checksum_mode.clone()); -// -// Ok(()) -// } -// } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index 77d576edfe..70dd23d400 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -6,7 +6,6 @@ use aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache}; use aws_credential_types::provider::SharedCredentialsProvider; use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; -use aws_runtime::auth::sigv4::SigV4OperationSigningConfig; use aws_runtime::recursion_detection::RecursionDetectionInterceptor; use aws_runtime::user_agent::UserAgentInterceptor; use aws_sdk_s3::config::{Credentials, Region}; @@ -23,7 +22,7 @@ use aws_smithy_runtime::client::orchestrator::endpoints::DefaultEndpointResolver use aws_smithy_runtime_api::client::interceptors::error::ContextAttachedError; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext, Interceptors}; use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, ConfigBagAccessors, Connection, HttpRequest, HttpResponse, TraceProbe, + BoxError, ConfigBagAccessors, Connection, HttpRequest, HttpResponse, RequestTime, TraceProbe, }; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::config_bag::ConfigBag; @@ -33,8 +32,6 @@ use aws_types::SigningService; use std::sync::Arc; use std::time::{Duration, UNIX_EPOCH}; -mod interceptors; - // TODO(orchestrator-test): unignore #[ignore] #[tokio::test] @@ -83,21 +80,6 @@ async fn sra_manual_test() { impl RuntimePlugin for ManualServiceRuntimePlugin { fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { - let identity_resolvers = - aws_smithy_runtime_api::client::orchestrator::IdentityResolvers::builder() - .identity_resolver( - aws_runtime::auth::sigv4::SCHEME_ID, - aws_runtime::identity::credentials::CredentialsIdentityResolver::new( - self.credentials_cache.clone(), - ), - ) - .identity_resolver( - "anonymous", - aws_smithy_runtime_api::client::identity::AnonymousIdentityResolver::new(), - ) - .build(); - cfg.set_identity_resolvers(identity_resolvers); - let http_auth_schemes = aws_smithy_runtime_api::client::orchestrator::HttpAuthSchemes::builder() .auth_scheme( @@ -107,6 +89,7 @@ async fn sra_manual_test() { .build(); cfg.set_http_auth_schemes(http_auth_schemes); + // Set an empty auth option resolver to be overridden by operations that need auth. cfg.set_auth_option_resolver( aws_smithy_runtime_api::client::auth::option_resolver::AuthOptionListResolver::new( Vec::new(), @@ -142,31 +125,26 @@ async fn sra_manual_test() { cfg.put(SigningService::from_static("s3")); cfg.put(SigningRegion::from(Region::from_static("us-east-1"))); - - #[derive(Debug)] - struct OverrideSigningTimeInterceptor; - impl Interceptor for OverrideSigningTimeInterceptor { - fn read_before_signing( - &self, - _context: &InterceptorContext, - cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { - let mut signing_config = - cfg.get::().unwrap().clone(); - signing_config.signing_options.request_timestamp = - UNIX_EPOCH + Duration::from_secs(1624036048); - cfg.put(signing_config); - Ok(()) - } - } + cfg.set_request_time(RequestTime::new( + UNIX_EPOCH + Duration::from_secs(1624036048), + )); cfg.put(ApiMetadata::new("unused", "unused")); cfg.put(AwsUserAgent::for_tests()); // Override the user agent with the test UA cfg.get::>() .expect("interceptors set") .register_client_interceptor(Arc::new(UserAgentInterceptor::new()) as _) - .register_client_interceptor(Arc::new(RecursionDetectionInterceptor::new()) as _) - .register_client_interceptor(Arc::new(OverrideSigningTimeInterceptor) as _); + .register_client_interceptor(Arc::new(RecursionDetectionInterceptor::new()) as _); + cfg.set_identity_resolvers( + aws_smithy_runtime_api::client::identity::IdentityResolvers::builder() + .identity_resolver( + aws_runtime::auth::sigv4::SCHEME_ID, + aws_runtime::identity::credentials::CredentialsIdentityResolver::new( + self.credentials_cache.clone(), + ), + ) + .build(), + ); Ok(()) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt index 2e72185f9d..faa3a01c5d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt @@ -11,6 +11,8 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.customizations.ApiKeyAuthDecorator import software.amazon.smithy.rust.codegen.client.smithy.customizations.ClientCustomizations +import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpAuthDecorator +import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpConnectorConfigDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.NoOpEventStreamSigningDecorator @@ -62,6 +64,8 @@ class RustClientCodegenPlugin : ClientDecoratableBuildPlugin() { EndpointParamsDecorator(), NoOpEventStreamSigningDecorator(), ApiKeyAuthDecorator(), + HttpAuthDecorator(), + HttpConnectorConfigDecorator(), *decorator, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt index 0d9f5bde46..58fd14e6a5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt @@ -26,9 +26,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection -import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.letIf +// TODO(enableNewSmithyRuntime): Delete this decorator when switching to the orchestrator + /** * Inserts a ApiKeyAuth configuration into the operation */ @@ -37,7 +38,8 @@ class ApiKeyAuthDecorator : ClientCodegenDecorator { override val order: Byte = 10 private fun applies(codegenContext: ClientCodegenContext) = - isSupportedApiKeyAuth(codegenContext) + !codegenContext.settings.codegenConfig.enableNewSmithyRuntime && + isSupportedApiKeyAuth(codegenContext) override fun configCustomizations( codegenContext: ClientCodegenContext, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt new file mode 100644 index 0000000000..243f563d4e --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -0,0 +1,371 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.model.knowledge.ServiceIndex +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.traits.AuthTrait +import software.amazon.smithy.model.traits.HttpApiKeyAuthTrait +import software.amazon.smithy.model.traits.HttpBasicAuthTrait +import software.amazon.smithy.model.traits.HttpBearerAuthTrait +import software.amazon.smithy.model.traits.HttpDigestAuthTrait +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.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +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.withBlockTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.getTrait +import software.amazon.smithy.rust.codegen.core.util.letIf + +fun codegenScope(runtimeConfig: RuntimeConfig): Array> { + val smithyRuntime = + CargoDependency.smithyRuntime(runtimeConfig).withFeature("http-auth").toType() + val smithyRuntimeApi = CargoDependency.smithyRuntimeApi(runtimeConfig).withFeature("http-auth").toType() + val authHttp = smithyRuntime.resolve("client::auth::http") + val authHttpApi = smithyRuntimeApi.resolve("client::auth::http") + return arrayOf( + "ApiKeyAuthScheme" to authHttp.resolve("ApiKeyAuthScheme"), + "ApiKeyLocation" to authHttp.resolve("ApiKeyLocation"), + "AuthOptionListResolver" to smithyRuntimeApi.resolve("client::auth::option_resolver::AuthOptionListResolver"), + "BasicAuthScheme" to authHttp.resolve("BasicAuthScheme"), + "BearerAuthScheme" to authHttp.resolve("BearerAuthScheme"), + "DigestAuthScheme" to authHttp.resolve("DigestAuthScheme"), + "HTTP_API_KEY_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_API_KEY_AUTH_SCHEME_ID"), + "HTTP_BASIC_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_BASIC_AUTH_SCHEME_ID"), + "HTTP_BEARER_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_BEARER_AUTH_SCHEME_ID"), + "HTTP_DIGEST_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_DIGEST_AUTH_SCHEME_ID"), + "HttpAuthOption" to smithyRuntimeApi.resolve("client::orchestrator::HttpAuthOption"), + "IdentityResolver" to smithyRuntimeApi.resolve("client::identity::IdentityResolver"), + "IdentityResolvers" to smithyRuntimeApi.resolve("client::identity::IdentityResolvers"), + "Login" to smithyRuntimeApi.resolve("client::identity::http::Login"), + "PropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::PropertyBag"), + "Token" to smithyRuntimeApi.resolve("client::identity::http::Token"), + ) +} + +private data class HttpAuthSchemes( + val apiKey: Boolean, + val basic: Boolean, + val bearer: Boolean, + val digest: Boolean, +) { + companion object { + fun from(codegenContext: ClientCodegenContext): HttpAuthSchemes { + val authSchemes = ServiceIndex.of(codegenContext.model).getAuthSchemes(codegenContext.serviceShape).keys + val newRuntimeEnabled = codegenContext.settings.codegenConfig.enableNewSmithyRuntime + return HttpAuthSchemes( + apiKey = newRuntimeEnabled && authSchemes.contains(HttpApiKeyAuthTrait.ID), + basic = newRuntimeEnabled && authSchemes.contains(HttpBasicAuthTrait.ID), + bearer = newRuntimeEnabled && authSchemes.contains(HttpBearerAuthTrait.ID), + digest = newRuntimeEnabled && authSchemes.contains(HttpDigestAuthTrait.ID), + ) + } + } + + fun anyEnabled(): Boolean = isTokenBased() || isLoginBased() + fun isTokenBased(): Boolean = apiKey || bearer + fun isLoginBased(): Boolean = basic || digest +} + +class HttpAuthDecorator : ClientCodegenDecorator { + override val name: String get() = "HttpAuthDecorator" + override val order: Byte = 0 + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + HttpAuthSchemes.from(codegenContext).let { authSchemes -> + baseCustomizations.letIf(authSchemes.anyEnabled()) { + it + HttpAuthConfigCustomization(codegenContext, authSchemes) + } + } + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + HttpAuthSchemes.from(codegenContext).let { authSchemes -> + baseCustomizations.letIf(authSchemes.anyEnabled()) { + it + HttpAuthServiceRuntimePluginCustomization(codegenContext, authSchemes) + } + } + + override fun operationRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = + HttpAuthSchemes.from(codegenContext).let { authSchemes -> + baseCustomizations.letIf(authSchemes.anyEnabled()) { + it + HttpAuthOperationRuntimePluginCustomization(codegenContext) + } + } + + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + val authSchemes = HttpAuthSchemes.from(codegenContext) + if (authSchemes.anyEnabled()) { + rustCrate.withModule(ClientRustModule.Config) { + val codegenScope = codegenScope(codegenContext.runtimeConfig) + if (authSchemes.isTokenBased()) { + rustTemplate("pub use #{Token};", *codegenScope) + } + if (authSchemes.isLoginBased()) { + rustTemplate("pub use #{Login};", *codegenScope) + } + } + } + } +} + +private class HttpAuthServiceRuntimePluginCustomization( + codegenContext: ClientCodegenContext, + private val authSchemes: HttpAuthSchemes, +) : ServiceRuntimePluginCustomization() { + private val serviceShape = codegenContext.serviceShape + private val codegenScope = codegenScope(codegenContext.runtimeConfig) + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + when (section) { + is ServiceRuntimePluginSection.HttpAuthScheme -> { + if (authSchemes.apiKey) { + val trait = serviceShape.getTrait()!! + val location = when (trait.`in`) { + HttpApiKeyAuthTrait.Location.HEADER -> { + check(trait.scheme.isPresent) { + "A scheme is required for `@httpApiKey` when `in` is set to `header`" + } + "Header" + } + + HttpApiKeyAuthTrait.Location.QUERY -> "Query" + } + + rustTemplate( + """ + .auth_scheme( + #{HTTP_API_KEY_AUTH_SCHEME_ID}, + #{ApiKeyAuthScheme}::new( + ${trait.scheme.orElse("").dq()}, + #{ApiKeyLocation}::$location, + ${trait.name.dq()}, + ) + ) + """, + *codegenScope, + ) + } + if (authSchemes.basic) { + rustTemplate(".auth_scheme(#{HTTP_BASIC_AUTH_SCHEME_ID}, #{BasicAuthScheme}::new())", *codegenScope) + } + if (authSchemes.bearer) { + rustTemplate( + ".auth_scheme(#{HTTP_BEARER_AUTH_SCHEME_ID}, #{BearerAuthScheme}::new())", + *codegenScope, + ) + } + if (authSchemes.digest) { + rustTemplate( + ".auth_scheme(#{HTTP_DIGEST_AUTH_SCHEME_ID}, #{DigestAuthScheme}::new())", + *codegenScope, + ) + } + } + + is ServiceRuntimePluginSection.AdditionalConfig -> { + if (authSchemes.anyEnabled()) { + rust("cfg.set_identity_resolvers(self.handle.conf.identity_resolvers().clone());") + } + } + } + } +} + +private class HttpAuthOperationRuntimePluginCustomization( + codegenContext: ClientCodegenContext, +) : OperationRuntimePluginCustomization() { + private val serviceShape = codegenContext.serviceShape + private val codegenScope = codegenScope(codegenContext.runtimeConfig) + + override fun section(section: OperationRuntimePluginSection): Writable = writable { + when (section) { + is OperationRuntimePluginSection.AdditionalConfig -> { + withBlockTemplate( + "let auth_option_resolver = #{AuthOptionListResolver}::new(vec![", + "]);", + *codegenScope, + ) { + val authTrait: AuthTrait? = section.operationShape.getTrait() ?: serviceShape.getTrait() + for (authScheme in authTrait?.valueSet ?: emptySet()) { + when (authScheme) { + HttpApiKeyAuthTrait.ID -> { + rustTemplate( + "#{HttpAuthOption}::new(#{HTTP_API_KEY_AUTH_SCHEME_ID}, std::sync::Arc::new(#{PropertyBag}::new())),", + *codegenScope, + ) + } + + HttpBasicAuthTrait.ID -> { + rustTemplate( + "#{HttpAuthOption}::new(#{HTTP_BASIC_AUTH_SCHEME_ID}, std::sync::Arc::new(#{PropertyBag}::new())),", + *codegenScope, + ) + } + + HttpBearerAuthTrait.ID -> { + rustTemplate( + "#{HttpAuthOption}::new(#{HTTP_BEARER_AUTH_SCHEME_ID}, std::sync::Arc::new(#{PropertyBag}::new())),", + *codegenScope, + ) + } + + HttpDigestAuthTrait.ID -> { + rustTemplate( + "#{HttpAuthOption}::new(#{HTTP_DIGEST_AUTH_SCHEME_ID}, std::sync::Arc::new(#{PropertyBag}::new())),", + *codegenScope, + ) + } + + else -> {} + } + } + } + + rustTemplate("${section.configBagName}.set_auth_option_resolver(auth_option_resolver);", *codegenScope) + } + } + } +} + +private class HttpAuthConfigCustomization( + codegenContext: ClientCodegenContext, + private val authSchemes: HttpAuthSchemes, +) : ConfigCustomization() { + private val codegenScope = codegenScope(codegenContext.runtimeConfig) + + override fun section(section: ServiceConfig): Writable = writable { + when (section) { + is ServiceConfig.BuilderStruct -> { + rustTemplate("identity_resolvers: #{IdentityResolvers},", *codegenScope) + } + + is ServiceConfig.BuilderImpl -> { + if (authSchemes.apiKey) { + rustTemplate( + """ + /// Sets the API key that will be used for authentication. + pub fn api_key(self, api_key: #{Token}) -> Self { + self.api_key_resolver(api_key) + } + + /// Sets an API key resolver will be used for authentication. + pub fn api_key_resolver(mut self, api_key_resolver: impl #{IdentityResolver} + 'static) -> Self { + self.identity_resolvers = self.identity_resolvers.to_builder() + .identity_resolver(#{HTTP_API_KEY_AUTH_SCHEME_ID}, api_key_resolver) + .build(); + self + } + """, + *codegenScope, + ) + } + if (authSchemes.bearer) { + rustTemplate( + """ + /// Sets the bearer token that will be used for HTTP bearer auth. + pub fn bearer_token(self, bearer_token: #{Token}) -> Self { + self.bearer_token_resolver(bearer_token) + } + + /// Sets a bearer token provider that will be used for HTTP bearer auth. + pub fn bearer_token_resolver(mut self, bearer_token_resolver: impl #{IdentityResolver} + 'static) -> Self { + self.identity_resolvers = self.identity_resolvers.to_builder() + .identity_resolver(#{HTTP_BEARER_AUTH_SCHEME_ID}, bearer_token_resolver) + .build(); + self + } + """, + *codegenScope, + ) + } + if (authSchemes.basic) { + rustTemplate( + """ + /// Sets the login that will be used for HTTP basic auth. + pub fn basic_auth_login(self, basic_auth_login: #{Login}) -> Self { + self.basic_auth_login_resolver(basic_auth_login) + } + + /// Sets a login resolver that will be used for HTTP basic auth. + pub fn basic_auth_login_resolver(mut self, basic_auth_resolver: impl #{IdentityResolver} + 'static) -> Self { + self.identity_resolvers = self.identity_resolvers.to_builder() + .identity_resolver(#{HTTP_BASIC_AUTH_SCHEME_ID}, basic_auth_resolver) + .build(); + self + } + """, + *codegenScope, + ) + } + if (authSchemes.digest) { + rustTemplate( + """ + /// Sets the login that will be used for HTTP digest auth. + pub fn digest_auth_login(self, digest_auth_login: #{Login}) -> Self { + self.digest_auth_login_resolver(digest_auth_login) + } + + /// Sets a login resolver that will be used for HTTP digest auth. + pub fn digest_auth_login_resolver(mut self, digest_auth_resolver: impl #{IdentityResolver} + 'static) -> Self { + self.identity_resolvers = self.identity_resolvers.to_builder() + .identity_resolver(#{HTTP_DIGEST_AUTH_SCHEME_ID}, digest_auth_resolver) + .build(); + self + } + """, + *codegenScope, + ) + } + } + + is ServiceConfig.BuilderBuild -> { + rust("identity_resolvers: self.identity_resolvers,") + } + + is ServiceConfig.ConfigStruct -> { + rustTemplate("identity_resolvers: #{IdentityResolvers},", *codegenScope) + } + + is ServiceConfig.ConfigImpl -> { + rustTemplate( + """ + /// Returns the identity resolvers. + pub fn identity_resolvers(&self) -> &#{IdentityResolvers} { + &self.identity_resolvers + } + """, + *codegenScope, + ) + } + + else -> {} + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt new file mode 100644 index 0000000000..102c50e718 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -0,0 +1,157 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +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.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.util.letIf + +class HttpConnectorConfigDecorator : ClientCodegenDecorator { + override val name: String = "HttpConnectorConfigDecorator" + override val order: Byte = 0 + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + it + HttpConnectorConfigCustomization(codegenContext) + } +} + +private class HttpConnectorConfigCustomization( + codegenContext: CodegenContext, +) : ConfigCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val moduleUseName = codegenContext.moduleUseName() + private val codegenScope = arrayOf( + "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), + ) + + override fun section(section: ServiceConfig): Writable { + return when (section) { + is ServiceConfig.ConfigStruct -> writable { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } + + is ServiceConfig.ConfigImpl -> writable { + rustTemplate( + """ + /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. + pub fn http_connector(&self) -> Option<&#{HttpConnector}> { + self.http_connector.as_ref() + } + """, + *codegenScope, + ) + } + + is ServiceConfig.BuilderStruct -> writable { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } + + ServiceConfig.BuilderImpl -> writable { + rustTemplate( + """ + /// Sets the HTTP connector to use when making requests. + /// + /// ## Examples + /// ```no_run + /// ## ##[cfg(test)] + /// ## mod tests { + /// ## ##[test] + /// ## fn example() { + /// use std::time::Duration; + /// use aws_smithy_client::{Client, hyper_ext}; + /// use aws_smithy_client::erase::DynConnector; + /// use aws_smithy_client::http_connector::ConnectorSettings; + /// use $moduleUseName::config::Config; + /// + /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + /// .with_webpki_roots() + /// .https_only() + /// .enable_http1() + /// .enable_http2() + /// .build(); + /// let smithy_connector = hyper_ext::Adapter::builder() + /// // Optionally set things like timeouts as well + /// .connector_settings( + /// ConnectorSettings::builder() + /// .connect_timeout(Duration::from_secs(5)) + /// .build() + /// ) + /// .build(https_connector); + /// ## } + /// ## } + /// ``` + pub fn http_connector(mut self, http_connector: impl Into<#{HttpConnector}>) -> Self { + self.http_connector = Some(http_connector.into()); + self + } + + /// Sets the HTTP connector to use when making requests. + /// + /// ## Examples + /// ```no_run + /// ## ##[cfg(test)] + /// ## mod tests { + /// ## ##[test] + /// ## fn example() { + /// use std::time::Duration; + /// use aws_smithy_client::hyper_ext; + /// use aws_smithy_client::http_connector::ConnectorSettings; + /// use crate::sdk_config::{SdkConfig, Builder}; + /// use $moduleUseName::config::{Builder, Config}; + /// + /// fn override_http_connector(builder: &mut Builder) { + /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + /// .with_webpki_roots() + /// .https_only() + /// .enable_http1() + /// .enable_http2() + /// .build(); + /// let smithy_connector = hyper_ext::Adapter::builder() + /// // Optionally set things like timeouts as well + /// .connector_settings( + /// ConnectorSettings::builder() + /// .connect_timeout(Duration::from_secs(5)) + /// .build() + /// ) + /// .build(https_connector); + /// builder.set_http_connector(Some(smithy_connector)); + /// } + /// + /// let mut builder = $moduleUseName::Config::builder(); + /// override_http_connector(&mut builder); + /// let config = builder.build(); + /// ## } + /// ## } + /// ``` + pub fn set_http_connector(&mut self, http_connector: Option>) -> &mut Self { + self.http_connector = http_connector.map(|inner| inner.into()); + self + } + """, + *codegenScope, + ) + } + + is ServiceConfig.BuilderBuild -> writable { + rust("http_connector: self.http_connector,") + } + + else -> emptySection + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 8b70a84785..c9955e5fad 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -19,16 +19,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations sealed class ServiceRuntimePluginSection(name: String) : Section(name) { - /** - * Hook for adding identity resolvers. - * - * Should emit code that looks like the following: - * ``` - * .identity_resolver("name", path::to::MyIdentityResolver::new()) - * ``` - */ - data class IdentityResolver(val configBagName: String) : ServiceRuntimePluginSection("IdentityResolver") - /** * Hook for adding HTTP auth schemes. * @@ -89,13 +79,12 @@ class ServiceRuntimePluginGenerator( "DefaultEndpointResolver" to runtime.resolve("client::orchestrator::endpoints::DefaultEndpointResolver"), "DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"), "HttpAuthSchemes" to runtimeApi.resolve("client::orchestrator::HttpAuthSchemes"), - "IdentityResolvers" to runtimeApi.resolve("client::orchestrator::IdentityResolvers"), + "IdentityResolvers" to runtimeApi.resolve("client::identity::IdentityResolvers"), "NeverRetryStrategy" to runtimeApi.resolve("client::retries::NeverRetryStrategy"), "Params" to endpointTypesGenerator.paramsStruct(), "ResolveEndpoint" to http.resolve("endpoint::ResolveEndpoint"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), "SharedEndpointResolver" to http.resolve("endpoint::SharedEndpointResolver"), - "TestConnection" to runtime.resolve("client::connections::test_connection::TestConnection"), "TraceProbe" to runtimeApi.resolve("client::orchestrator::TraceProbe"), ) } @@ -117,12 +106,6 @@ class ServiceRuntimePluginGenerator( fn configure(&self, cfg: &mut #{ConfigBag}) -> Result<(), #{BoxError}> { use #{ConfigBagAccessors}; - let identity_resolvers = #{IdentityResolvers}::builder() - #{identity_resolver_customizations} - .identity_resolver("anonymous", #{AnonymousIdentityResolver}::new()) - .build(); - cfg.set_identity_resolvers(identity_resolvers); - let http_auth_schemes = #{HttpAuthSchemes}::builder() #{http_auth_scheme_customizations} .build(); @@ -146,7 +129,7 @@ class ServiceRuntimePluginGenerator( let connection: Box = self.handle.conf.http_connector() .and_then(move |c| c.connector(&#{ConnectorSettings}::default(), sleep_impl)) .map(|c| Box::new(#{DynConnectorAdapter}::new(c)) as _) - .unwrap_or_else(|| Box::new(#{TestConnection}::new(vec![])) as _); + .expect("connection set"); cfg.set_connection(connection); // TODO(RuntimePlugins): Add the TraceProbe to the config bag @@ -167,9 +150,6 @@ class ServiceRuntimePluginGenerator( } """, *codegenScope, - "identity_resolver_customizations" to writable { - writeCustomizations(customizations, ServiceRuntimePluginSection.IdentityResolver("cfg")) - }, "http_auth_scheme_customizations" to writable { writeCustomizations(customizations, ServiceRuntimePluginSection.HttpAuthScheme("cfg")) }, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index ed24d439b7..a45bad43d0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.GenericTypeArg import software.amazon.smithy.rust.codegen.core.rustlang.RustGenerics import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate @@ -20,10 +21,9 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustCrate * fluent client builders. */ class CustomizableOperationGenerator( - private val codegenContext: ClientCodegenContext, + codegenContext: ClientCodegenContext, private val generics: FluentClientGenerics, ) { - private val includeFluentClient = codegenContext.settings.codegenConfig.includeFluentClient private val runtimeConfig = codegenContext.runtimeConfig private val smithyHttp = CargoDependency.smithyHttp(runtimeConfig).toType() private val smithyTypes = CargoDependency.smithyTypes(runtimeConfig).toType() @@ -45,10 +45,6 @@ class CustomizableOperationGenerator( "RetryKind" to smithyTypes.resolve("retry::RetryKind"), ) renderCustomizableOperationModule(this) - - if (includeFluentClient) { - renderCustomizableOperationSend(this) - } } } @@ -135,26 +131,28 @@ class CustomizableOperationGenerator( *codegenScope, ) } +} - private fun renderCustomizableOperationSend(writer: RustWriter) { - val smithyHttp = CargoDependency.smithyHttp(runtimeConfig).toType() - val smithyClient = CargoDependency.smithyClient(runtimeConfig).toType() - - val operationGenerics = RustGenerics(GenericTypeArg("O"), GenericTypeArg("Retry")) - val handleGenerics = generics.toRustGenerics() - val combinedGenerics = operationGenerics + handleGenerics - - val codegenScope = arrayOf( - "combined_generics_decl" to combinedGenerics.declaration(), - "handle_generics_bounds" to handleGenerics.bounds(), - "ParseHttpResponse" to smithyHttp.resolve("response::ParseHttpResponse"), - "NewRequestPolicy" to smithyClient.resolve("retry::NewRequestPolicy"), - "SmithyRetryPolicy" to smithyClient.resolve("bounds::SmithyRetryPolicy"), - "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), - "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), - "SdkError" to RuntimeType.sdkError(runtimeConfig), - ) - +fun renderCustomizableOperationSend(runtimeConfig: RuntimeConfig, generics: FluentClientGenerics, writer: RustWriter) { + val smithyHttp = CargoDependency.smithyHttp(runtimeConfig).toType() + val smithyClient = CargoDependency.smithyClient(runtimeConfig).toType() + + val operationGenerics = RustGenerics(GenericTypeArg("O"), GenericTypeArg("Retry")) + val handleGenerics = generics.toRustGenerics() + val combinedGenerics = operationGenerics + handleGenerics + + val codegenScope = arrayOf( + "combined_generics_decl" to combinedGenerics.declaration(), + "handle_generics_bounds" to handleGenerics.bounds(), + "ParseHttpResponse" to smithyHttp.resolve("response::ParseHttpResponse"), + "NewRequestPolicy" to smithyClient.resolve("retry::NewRequestPolicy"), + "SmithyRetryPolicy" to smithyClient.resolve("bounds::SmithyRetryPolicy"), + "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), + "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + ) + + if (generics is FlexibleClientGenerics) { writer.rustTemplate( """ impl#{combined_generics_decl:W} CustomizableOperation#{combined_generics_decl:W} @@ -176,5 +174,25 @@ class CustomizableOperationGenerator( """, *codegenScope, ) + } else { + writer.rustTemplate( + """ + impl#{combined_generics_decl:W} CustomizableOperation#{combined_generics_decl:W} + where + #{handle_generics_bounds:W} + { + /// Sends this operation's request + pub async fn send(self) -> Result> + where + E: std::error::Error + Send + Sync + 'static, + O: #{ParseHttpResponse}> + Send + Sync + Clone + 'static, + Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + Send + Sync + Clone, + { + self.handle.client.call(self.operation).await + } + } + """, + *codegenScope, + ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt index bc959d4a74..e5e2760cee 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt @@ -9,12 +9,14 @@ import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape 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.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.rust 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.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section @@ -34,10 +36,26 @@ class FluentClientDecorator : ClientCodegenDecorator { return } + val generics = if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + NoClientGenerics(codegenContext.runtimeConfig) + } else { + FlexibleClientGenerics( + connectorDefault = null, + middlewareDefault = null, + retryDefault = RuntimeType.smithyClient(codegenContext.runtimeConfig).resolve("retry::Standard"), + client = RuntimeType.smithyClient(codegenContext.runtimeConfig), + ) + } + FluentClientGenerator( codegenContext, + generics = generics, customizations = listOf(GenericFluentClient(codegenContext)), ).render(rustCrate) + rustCrate.withModule(ClientRustModule.Client.customize) { + renderCustomizableOperationSend(codegenContext.runtimeConfig, generics, this) + } + rustCrate.mergeFeature(Feature("rustls", default = true, listOf("aws-smithy-client/rustls"))) rustCrate.mergeFeature(Feature("native-tls", default = false, listOf("aws-smithy-client/native-tls"))) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 062e9d3d0a..8cdc6d8562 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -55,12 +55,7 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase class FluentClientGenerator( private val codegenContext: ClientCodegenContext, private val reexportSmithyClientBuilder: Boolean = true, - private val generics: FluentClientGenerics = FlexibleClientGenerics( - connectorDefault = null, - middlewareDefault = null, - retryDefault = RuntimeType.smithyClient(codegenContext.runtimeConfig).resolve("retry::Standard"), - client = RuntimeType.smithyClient(codegenContext.runtimeConfig), - ), + private val generics: FluentClientGenerics, private val customizations: List = emptyList(), private val retryClassifier: RuntimeType = RuntimeType.smithyHttp(codegenContext.runtimeConfig) .resolve("retry::DefaultResponseRetryClassifier"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt index 399085d5e5..d37fb89057 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt @@ -12,8 +12,10 @@ 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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +// TODO(enableNewSmithyRuntime): Delete this client generics on/off switch headache interface FluentClientGenerics { /** Declaration with defaults set */ val decl: Writable @@ -34,6 +36,37 @@ interface FluentClientGenerics { fun toRustGenerics(): RustGenerics } +class NoClientGenerics(private val runtimeConfig: RuntimeConfig) : FluentClientGenerics { + /** Declaration with defaults set */ + override val decl = writable { } + + /** Instantiation of the Smithy client generics */ + override val smithyInst = writable { + rustTemplate( + "<#{DynConnector}, #{DynMiddleware}<#{DynConnector}>>", + "DynConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("erase::DynConnector"), + "DynMiddleware" to RuntimeType.smithyClient(runtimeConfig).resolve("erase::DynMiddleware"), + ) + } + + /** Instantiation */ + override val inst = "" + + /** Trait bounds */ + override val bounds = writable { } + + /** Bounds for generated `send()` functions */ + override fun sendBounds( + operation: Symbol, + operationOutput: Symbol, + operationError: Symbol, + retryClassifier: RuntimeType, + ): Writable = + writable { } + + override fun toRustGenerics() = RustGenerics() +} + data class FlexibleClientGenerics( val connectorDefault: RuntimeType?, val middlewareDefault: RuntimeType?, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ApiKeyAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecoratorTest.kt similarity index 98% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ApiKeyAuthDecoratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecoratorTest.kt index b44e427777..1673ac299f 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ApiKeyAuthDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecoratorTest.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.customizations +package software.amazon.smithy.rust.codegen.client.smithy.customizations import org.junit.jupiter.api.Test import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt new file mode 100644 index 0000000000..29c3b2e853 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt @@ -0,0 +1,433 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.node.BooleanNode +import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +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.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.integrationTest + +private fun additionalSettings(): ObjectNode = ObjectNode.objectNodeBuilder() + .withMember( + "codegen", + ObjectNode.objectNodeBuilder() + .withMember("enableNewSmithyRuntime", BooleanNode.from(true)).build(), + ) + .build() + +class HttpAuthDecoratorTest { + private fun codegenScope(runtimeConfig: RuntimeConfig): Array> = arrayOf( + "TestConnection" to CargoDependency.smithyClient(runtimeConfig) + .withFeature("test-util").toType() + .resolve("test_connection::TestConnection"), + "SdkBody" to RuntimeType.sdkBody(runtimeConfig), + ) + + @Test + fun multipleAuthSchemesSchemeSelection() { + clientIntegrationTest( + TestModels.allSchemes, + IntegrationTestParams(additionalSettings = additionalSettings()), + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("tests") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn use_api_key_auth_when_api_key_provided() { + use aws_smithy_runtime_api::client::identity::http::Token; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .uri("http://localhost:1234/SomeOperation?api_key=some-api-key") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .api_key(Token::new("some-api-key", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send_v2() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn use_basic_auth_when_basic_auth_login_provided() { + use aws_smithy_runtime_api::client::identity::http::Login; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .header("authorization", "Basic c29tZS11c2VyOnNvbWUtcGFzcw==") + .uri("http://localhost:1234/SomeOperation") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .basic_auth_login(Login::new("some-user", "some-pass", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send_v2() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } + + @Test + fun apiKeyInQueryString() { + clientIntegrationTest( + TestModels.apiKeyInQueryString, + IntegrationTestParams(additionalSettings = additionalSettings()), + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("api_key_applied_to_query_string") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn api_key_applied_to_query_string() { + use aws_smithy_runtime_api::client::identity::http::Token; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .uri("http://localhost:1234/SomeOperation?api_key=some-api-key") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .api_key(Token::new("some-api-key", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send_v2() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } + + @Test + fun apiKeyInHeaders() { + clientIntegrationTest( + TestModels.apiKeyInHeaders, + IntegrationTestParams(additionalSettings = additionalSettings()), + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("api_key_applied_to_headers") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn api_key_applied_to_headers() { + use aws_smithy_runtime_api::client::identity::http::Token; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .header("authorization", "ApiKey some-api-key") + .uri("http://localhost:1234/SomeOperation") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .api_key(Token::new("some-api-key", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send_v2() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } + + @Test + fun basicAuth() { + clientIntegrationTest( + TestModels.basicAuth, + IntegrationTestParams(additionalSettings = additionalSettings()), + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("basic_auth") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn basic_auth() { + use aws_smithy_runtime_api::client::identity::http::Login; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .header("authorization", "Basic c29tZS11c2VyOnNvbWUtcGFzcw==") + .uri("http://localhost:1234/SomeOperation") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .basic_auth_login(Login::new("some-user", "some-pass", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send_v2() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } + + @Test + fun bearerAuth() { + clientIntegrationTest( + TestModels.bearerAuth, + IntegrationTestParams(additionalSettings = additionalSettings()), + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("bearer_auth") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn basic_auth() { + use aws_smithy_runtime_api::client::identity::http::Token; + + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .header("authorization", "Bearer some-token") + .uri("http://localhost:1234/SomeOperation") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .bearer_token(Token::new("some-token", None)) + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send_v2() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } +} + +private object TestModels { + val allSchemes = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpApiKeyAuth(name: "api_key", in: "query") + @httpBasicAuth + @httpBearerAuth + @httpDigestAuth + @auth([httpApiKeyAuth, httpBasicAuth, httpBearerAuth, httpDigestAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + + val apiKeyInQueryString = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpApiKeyAuth(name: "api_key", in: "query") + @auth([httpApiKeyAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + + val apiKeyInHeaders = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpApiKeyAuth(name: "authorization", in: "header", scheme: "ApiKey") + @auth([httpApiKeyAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + + val basicAuth = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpBasicAuth + @auth([httpBasicAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + + val bearerAuth = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpBearerAuth + @auth([httpBearerAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/HttpVersionListGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt similarity index 99% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/HttpVersionListGeneratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt index 7536629578..3b5fc70a6f 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/HttpVersionListGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.customizations +package software.amazon.smithy.rust.codegen.client.smithy.customizations import org.junit.jupiter.api.Test import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ResiliencyConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt similarity index 86% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ResiliencyConfigCustomizationTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt index 612bf8e333..6ebc6ad759 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/customizations/ResiliencyConfigCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt @@ -3,12 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.customizations +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.customizations.ResiliencyConfigCustomization -import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization 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 diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt similarity index 92% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt index 8c3a4122e9..32cbbddeb4 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/ClientContextConfigCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt @@ -3,10 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.endpoint +package software.amazon.smithy.rust.codegen.client.smithy.endpoint import org.junit.jupiter.api.Test -import software.amazon.smithy.rust.codegen.client.smithy.endpoint.ClientContextConfigCustomization 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.rustlang.rust diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointParamsGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsGeneratorTest.kt similarity index 95% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointParamsGeneratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsGeneratorTest.kt index fedc249794..6397e117e1 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointParamsGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsGeneratorTest.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.endpoint +package software.amazon.smithy.rust.codegen.client.smithy.endpoint import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointResolverGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointResolverGeneratorTest.kt similarity index 98% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointResolverGeneratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointResolverGeneratorTest.kt index 179cbbc944..b842705f99 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointResolverGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointResolverGeneratorTest.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.endpoint +package software.amazon.smithy.rust.codegen.client.smithy.endpoint import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointsDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt similarity index 99% rename from codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointsDecoratorTest.kt rename to codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt index fc564c2696..56cd00f0ee 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/endpoint/EndpointsDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.endpoint +package software.amazon.smithy.rust.codegen.client.smithy.endpoint import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.string.shouldContain diff --git a/rust-runtime/aws-smithy-http-auth/src/lib.rs b/rust-runtime/aws-smithy-http-auth/src/lib.rs index b245385253..9b9977d2ec 100644 --- a/rust-runtime/aws-smithy-http-auth/src/lib.rs +++ b/rust-runtime/aws-smithy-http-auth/src/lib.rs @@ -3,6 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +// TODO(enableNewSmithyRuntime): The contents of this crate are moving into aws-smithy-runtime. +// This crate is kept to continue sorting the middleware implementation until it is removed. +// When removing the old implementation, clear out this crate and deprecate it. + #![allow(clippy::derive_partial_eq_without_eq)] #![warn( missing_docs, diff --git a/rust-runtime/aws-smithy-runtime-api/Cargo.toml b/rust-runtime/aws-smithy-runtime-api/Cargo.toml index a03bac307b..8fe513faf1 100644 --- a/rust-runtime/aws-smithy-runtime-api/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime-api/Cargo.toml @@ -9,12 +9,18 @@ repository = "https://github.com/awslabs/smithy-rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = [] +http-auth = ["dep:zeroize"] + [dependencies] +aws-smithy-async = { path = "../aws-smithy-async" } aws-smithy-http = { path = "../aws-smithy-http" } aws-smithy-types = { path = "../aws-smithy-types" } http = "0.2.3" tokio = { version = "1.25", features = ["sync"] } tracing = "0.1" +zeroize = { version = "1", optional = true } [package.metadata.docs.rs] all-features = true diff --git a/rust-runtime/aws-smithy-runtime-api/additional-ci b/rust-runtime/aws-smithy-runtime-api/additional-ci new file mode 100755 index 0000000000..b44c6c05be --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/additional-ci @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +# This script contains additional CI checks to run for this specific package + +set -e + +echo "### Testing every combination of features (excluding --all-features)" +cargo hack test --feature-powerset --exclude-all-features diff --git a/rust-runtime/aws-smithy-runtime-api/external-types.toml b/rust-runtime/aws-smithy-runtime-api/external-types.toml index f752bef762..ab041b9f7e 100644 --- a/rust-runtime/aws-smithy-runtime-api/external-types.toml +++ b/rust-runtime/aws-smithy-runtime-api/external-types.toml @@ -1,4 +1,5 @@ allowed_external_types = [ + "aws_smithy_async::*", "aws_smithy_types::*", "aws_smithy_http::*", diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 278e7e0410..0438c1bdfa 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -3,4 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#[cfg(feature = "http-auth")] +pub mod http; + pub mod option_resolver; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs new file mode 100644 index 0000000000..2f93a2eb40 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs @@ -0,0 +1,9 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +pub const HTTP_API_KEY_AUTH_SCHEME_ID: &str = "http-api-key-auth"; +pub const HTTP_BASIC_AUTH_SCHEME_ID: &str = "http-basic-auth"; +pub const HTTP_BEARER_AUTH_SCHEME_ID: &str = "http-bearer-auth"; +pub const HTTP_DIGEST_AUTH_SCHEME_ID: &str = "http-digest-auth"; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index 3d9c7e829d..11a54af820 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -3,13 +3,44 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::orchestrator::{BoxFallibleFut, IdentityResolver}; +use crate::client::orchestrator::Future; use aws_smithy_http::property_bag::PropertyBag; use std::any::Any; use std::fmt::Debug; use std::sync::Arc; use std::time::SystemTime; +#[cfg(feature = "http-auth")] +pub mod http; + +pub trait IdentityResolver: Send + Sync + Debug { + fn resolve_identity(&self, identity_properties: &PropertyBag) -> Future; +} + +#[derive(Clone, Debug, Default)] +pub struct IdentityResolvers { + identity_resolvers: Vec<(&'static str, Arc)>, +} + +impl IdentityResolvers { + pub fn builder() -> builders::IdentityResolversBuilder { + builders::IdentityResolversBuilder::new() + } + + pub fn identity_resolver(&self, identity_type: &'static str) -> Option<&dyn IdentityResolver> { + self.identity_resolvers + .iter() + .find(|resolver| resolver.0 == identity_type) + .map(|resolver| &*resolver.1) + } + + pub fn to_builder(self) -> builders::IdentityResolversBuilder { + builders::IdentityResolversBuilder { + identity_resolvers: self.identity_resolvers, + } + } +} + #[derive(Clone, Debug)] pub struct Identity { data: Arc, @@ -52,8 +83,39 @@ impl AnonymousIdentityResolver { } impl IdentityResolver for AnonymousIdentityResolver { - fn resolve_identity(&self, _: &PropertyBag) -> BoxFallibleFut { - Box::pin(async { Ok(Identity::new(AnonymousIdentity::new(), None)) }) + fn resolve_identity(&self, _: &PropertyBag) -> Future { + Future::ready(Ok(Identity::new(AnonymousIdentity::new(), None))) + } +} + +pub mod builders { + use super::*; + + #[derive(Debug, Default)] + pub struct IdentityResolversBuilder { + pub(super) identity_resolvers: Vec<(&'static str, Arc)>, + } + + impl IdentityResolversBuilder { + pub fn new() -> Self { + Default::default() + } + + pub fn identity_resolver( + mut self, + name: &'static str, + resolver: impl IdentityResolver + 'static, + ) -> Self { + self.identity_resolvers + .push((name, Arc::new(resolver) as _)); + self + } + + pub fn build(self) -> IdentityResolvers { + IdentityResolvers { + identity_resolvers: self.identity_resolvers, + } + } } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs new file mode 100644 index 0000000000..5a58588b02 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs @@ -0,0 +1,130 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Identity types for HTTP auth + +use crate::client::identity::{Identity, IdentityResolver}; +use crate::client::orchestrator::Future; +use aws_smithy_http::property_bag::PropertyBag; +use std::fmt::Debug; +use std::sync::Arc; +use std::time::SystemTime; +use zeroize::Zeroizing; + +/// Identity type required to sign requests using Smithy's token-based HTTP auth schemes +/// +/// This `Token` type is used with Smithy's `@httpApiKeyAuth` and `@httpBearerAuth` +/// auth traits. +#[derive(Clone, Eq, PartialEq)] +pub struct Token(Arc); + +#[derive(Eq, PartialEq)] +struct TokenInner { + token: Zeroizing, + expiration: Option, +} + +impl Debug for Token { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Token") + .field("token", &"** redacted **") + .finish() + } +} + +impl Token { + /// Constructs a new identity token for HTTP auth. + pub fn new(token: impl Into, expiration: Option) -> Self { + Self(Arc::new(TokenInner { + token: Zeroizing::new(token.into()), + expiration, + })) + } + + /// Returns the underlying identity token. + pub fn token(&self) -> &str { + &self.0.token + } +} + +impl From<&str> for Token { + fn from(token: &str) -> Self { + Self::from(token.to_owned()) + } +} + +impl From for Token { + fn from(api_key: String) -> Self { + Self(Arc::new(TokenInner { + token: Zeroizing::new(api_key), + expiration: None, + })) + } +} + +impl IdentityResolver for Token { + fn resolve_identity(&self, _identity_properties: &PropertyBag) -> Future { + Future::ready(Ok(Identity::new(self.clone(), self.0.expiration))) + } +} + +/// Identity type required to sign requests using Smithy's login-based HTTP auth schemes +/// +/// This `Login` type is used with Smithy's `@httpBasicAuth` and `@httpDigestAuth` +/// auth traits. +#[derive(Clone, Eq, PartialEq)] +pub struct Login(Arc); + +#[derive(Eq, PartialEq)] +struct LoginInner { + user: String, + password: Zeroizing, + expiration: Option, +} + +impl Debug for Login { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Login") + .field("user", &self.0.user) + .field("password", &"** redacted **") + .finish() + } +} + +impl Login { + /// Constructs a new identity login for HTTP auth. + pub fn new( + user: impl Into, + password: impl Into, + expiration: Option, + ) -> Self { + Self(Arc::new(LoginInner { + user: user.into(), + password: Zeroizing::new(password.into()), + expiration, + })) + } + + /// Returns the login user. + pub fn user(&self) -> &str { + &self.0.user + } + + /// Returns the login password. + pub fn password(&self) -> &str { + &self.0.password + } + + /// Returns the expiration time of this login (if any) + pub fn expiration(&self) -> Option { + self.0.expiration + } +} + +impl IdentityResolver for Login { + fn resolve_identity(&self, _identity_properties: &PropertyBag) -> Future { + Future::ready(Ok(Identity::new(self.clone(), self.0.expiration))) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 35b85e3bcb..bd2dcb3289 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -3,25 +3,29 @@ * SPDX-License-Identifier: Apache-2.0 */ +use super::identity::{IdentityResolver, IdentityResolvers}; use crate::client::identity::Identity; use crate::client::interceptors::context::{Input, OutputOrError}; use crate::client::interceptors::InterceptorContext; use crate::config_bag::ConfigBag; use crate::type_erasure::{TypeErasedBox, TypedBox}; +use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_http::body::SdkBody; use aws_smithy_http::endpoint::EndpointPrefix; use aws_smithy_http::property_bag::PropertyBag; use std::any::Any; use std::borrow::Cow; use std::fmt::Debug; -use std::future::Future; +use std::future::Future as StdFuture; use std::pin::Pin; use std::sync::Arc; +use std::time::SystemTime; pub type HttpRequest = http::Request; pub type HttpResponse = http::Response; pub type BoxError = Box; -pub type BoxFallibleFut = Pin>>>; +pub type BoxFuture = Pin>>>; +pub type Future = NowOrLater, BoxFuture>; pub trait TraceProbe: Send + Sync + Debug { fn dispatch_events(&self); @@ -41,11 +45,11 @@ pub trait ResponseDeserializer: Send + Sync + Debug { } pub trait Connection: Send + Sync + Debug { - fn call(&self, request: HttpRequest) -> BoxFallibleFut; + fn call(&self, request: HttpRequest) -> BoxFuture; } impl Connection for Box { - fn call(&self, request: HttpRequest) -> BoxFallibleFut { + fn call(&self, request: HttpRequest) -> BoxFuture { (**self).call(request) } } @@ -112,28 +116,6 @@ impl HttpAuthOption { } } -pub trait IdentityResolver: Send + Sync + Debug { - fn resolve_identity(&self, identity_properties: &PropertyBag) -> BoxFallibleFut; -} - -#[derive(Debug)] -pub struct IdentityResolvers { - identity_resolvers: Vec<(&'static str, Box)>, -} - -impl IdentityResolvers { - pub fn builder() -> builders::IdentityResolversBuilder { - builders::IdentityResolversBuilder::new() - } - - pub fn identity_resolver(&self, identity_type: &'static str) -> Option<&dyn IdentityResolver> { - self.identity_resolvers - .iter() - .find(|resolver| resolver.0 == identity_type) - .map(|resolver| &*resolver.1) - } -} - #[derive(Debug)] struct HttpAuthSchemesInner { schemes: Vec<(&'static str, Box)>, @@ -202,6 +184,29 @@ pub trait EndpointResolver: Send + Sync + Debug { ) -> Result<(), BoxError>; } +/// Time that the request is being made (so that time can be overridden in the [`ConfigBag`]). +#[non_exhaustive] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct RequestTime(SystemTime); + +impl Default for RequestTime { + fn default() -> Self { + Self(SystemTime::now()) + } +} + +impl RequestTime { + /// Create a new [`RequestTime`]. + pub fn new(time: SystemTime) -> Self { + Self(time) + } + + /// Returns the request time as a [`SystemTime`]. + pub fn system_time(&self) -> SystemTime { + self.0 + } +} + pub trait ConfigBagAccessors { fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams; fn set_auth_option_resolver_params( @@ -241,6 +246,9 @@ pub trait ConfigBagAccessors { fn trace_probe(&self) -> &dyn TraceProbe; fn set_trace_probe(&mut self, trace_probe: impl TraceProbe + 'static); + + fn request_time(&self) -> Option; + fn set_request_time(&mut self, request_time: RequestTime); } impl ConfigBagAccessors for ConfigBag { @@ -358,37 +366,18 @@ impl ConfigBagAccessors for ConfigBag { fn set_trace_probe(&mut self, trace_probe: impl TraceProbe + 'static) { self.put::>(Box::new(trace_probe)); } -} - -pub mod builders { - use super::*; - #[derive(Debug, Default)] - pub struct IdentityResolversBuilder { - identity_resolvers: Vec<(&'static str, Box)>, + fn request_time(&self) -> Option { + self.get::().cloned() } - impl IdentityResolversBuilder { - pub fn new() -> Self { - Default::default() - } - - pub fn identity_resolver( - mut self, - name: &'static str, - resolver: impl IdentityResolver + 'static, - ) -> Self { - self.identity_resolvers - .push((name, Box::new(resolver) as _)); - self - } - - pub fn build(self) -> IdentityResolvers { - IdentityResolvers { - identity_resolvers: self.identity_resolvers, - } - } + fn set_request_time(&mut self, request_time: RequestTime) { + self.put::(request_time); } +} + +pub mod builders { + use super::*; #[derive(Debug, Default)] pub struct HttpAuthSchemesBuilder { diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 4ff7e7613d..5c8e098462 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/awslabs/smithy-rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] +http-auth = ["aws-smithy-runtime-api/http-auth"] test-util = ["dep:aws-smithy-protocol-test"] [dependencies] @@ -25,6 +26,9 @@ pin-utils = "0.1.0" tokio = { version = "1.25", features = [] } tracing = "0.1" +[dev-dependencies] +tokio = { version = "1.25", features = ["macros", "rt"] } + [package.metadata.docs.rs] all-features = true targets = ["x86_64-unknown-linux-gnu"] diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index e41bda8ed2..25c86c7788 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +pub mod auth; + pub mod orchestrator; /// Smithy connector runtime plugins diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/auth.rs new file mode 100644 index 0000000000..d06c1e4e86 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/auth.rs @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(feature = "http-auth")] +pub mod http; diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs new file mode 100644 index 0000000000..8bacee21f0 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs @@ -0,0 +1,348 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::property_bag::PropertyBag; +use aws_smithy_http::query_writer::QueryWriter; +use aws_smithy_runtime_api::client::auth::http::{ + HTTP_API_KEY_AUTH_SCHEME_ID, HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID, + HTTP_DIGEST_AUTH_SCHEME_ID, +}; +use aws_smithy_runtime_api::client::identity::http::{Login, Token}; +use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; +use aws_smithy_runtime_api::client::orchestrator::{ + BoxError, HttpAuthScheme, HttpRequest, HttpRequestSigner, +}; +use aws_smithy_types::base64::encode; +use http::header::HeaderName; +use http::HeaderValue; + +/// Destination for the API key +#[derive(Copy, Clone, Debug)] +pub enum ApiKeyLocation { + Query, + Header, +} + +/// Auth implementation for Smithy's `@httpApiKey` auth scheme +#[derive(Debug)] +pub struct ApiKeyAuthScheme { + signer: ApiKeySigner, +} + +impl ApiKeyAuthScheme { + /// Creates a new `ApiKeyAuthScheme`. + pub fn new( + scheme: impl Into, + location: ApiKeyLocation, + name: impl Into, + ) -> Self { + Self { + signer: ApiKeySigner { + scheme: scheme.into(), + location, + name: name.into(), + }, + } + } +} + +impl HttpAuthScheme for ApiKeyAuthScheme { + fn scheme_id(&self) -> &'static str { + HTTP_API_KEY_AUTH_SCHEME_ID + } + + fn identity_resolver<'a>( + &self, + identity_resolvers: &'a IdentityResolvers, + ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + &self.signer + } +} + +#[derive(Debug)] +struct ApiKeySigner { + scheme: String, + location: ApiKeyLocation, + name: String, +} + +impl HttpRequestSigner for ApiKeySigner { + fn sign_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + _signing_properties: &PropertyBag, + ) -> Result<(), BoxError> { + let api_key = identity + .data::() + .ok_or("HTTP ApiKey auth requires a `Token` identity")?; + match self.location { + ApiKeyLocation::Header => { + request.headers_mut().append( + HeaderName::try_from(&self.name).expect("valid API key header name"), + HeaderValue::try_from(format!("{} {}", self.scheme, api_key.token())).map_err( + |_| "API key contains characters that can't be included in a HTTP header", + )?, + ); + } + ApiKeyLocation::Query => { + let mut query = QueryWriter::new(request.uri()); + query.insert(&self.name, api_key.token()); + *request.uri_mut() = query.build_uri(); + } + } + + Ok(()) + } +} + +/// Auth implementation for Smithy's `@httpBasicAuth` auth scheme +#[derive(Debug, Default)] +pub struct BasicAuthScheme { + signer: BasicAuthSigner, +} + +impl BasicAuthScheme { + /// Creates a new `BasicAuthScheme`. + pub fn new() -> Self { + Self { + signer: BasicAuthSigner, + } + } +} + +impl HttpAuthScheme for BasicAuthScheme { + fn scheme_id(&self) -> &'static str { + HTTP_BASIC_AUTH_SCHEME_ID + } + + fn identity_resolver<'a>( + &self, + identity_resolvers: &'a IdentityResolvers, + ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + &self.signer + } +} + +#[derive(Debug, Default)] +struct BasicAuthSigner; + +impl HttpRequestSigner for BasicAuthSigner { + fn sign_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + _signing_properties: &PropertyBag, + ) -> Result<(), BoxError> { + let login = identity + .data::() + .ok_or("HTTP basic auth requires a `Login` identity")?; + request.headers_mut().insert( + http::header::AUTHORIZATION, + HeaderValue::from_str(&format!( + "Basic {}", + encode(format!("{}:{}", login.user(), login.password())) + )) + .expect("valid header value"), + ); + Ok(()) + } +} + +/// Auth implementation for Smithy's `@httpBearerAuth` auth scheme +#[derive(Debug, Default)] +pub struct BearerAuthScheme { + signer: BearerAuthSigner, +} + +impl BearerAuthScheme { + /// Creates a new `BearerAuthScheme`. + pub fn new() -> Self { + Self { + signer: BearerAuthSigner, + } + } +} + +impl HttpAuthScheme for BearerAuthScheme { + fn scheme_id(&self) -> &'static str { + HTTP_BEARER_AUTH_SCHEME_ID + } + + fn identity_resolver<'a>( + &self, + identity_resolvers: &'a IdentityResolvers, + ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + &self.signer + } +} + +#[derive(Debug, Default)] +struct BearerAuthSigner; + +impl HttpRequestSigner for BearerAuthSigner { + fn sign_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + _signing_properties: &PropertyBag, + ) -> Result<(), BoxError> { + let token = identity + .data::() + .ok_or("HTTP bearer auth requires a `Token` identity")?; + request.headers_mut().insert( + http::header::AUTHORIZATION, + HeaderValue::from_str(&format!("Bearer {}", token.token())).map_err(|_| { + "Bearer token contains characters that can't be included in a HTTP header" + })?, + ); + Ok(()) + } +} + +/// Auth implementation for Smithy's `@httpDigestAuth` auth scheme +#[derive(Debug, Default)] +pub struct DigestAuthScheme { + signer: DigestAuthSigner, +} + +impl DigestAuthScheme { + /// Creates a new `DigestAuthScheme`. + pub fn new() -> Self { + Self { + signer: DigestAuthSigner, + } + } +} + +impl HttpAuthScheme for DigestAuthScheme { + fn scheme_id(&self) -> &'static str { + HTTP_DIGEST_AUTH_SCHEME_ID + } + + fn identity_resolver<'a>( + &self, + identity_resolvers: &'a IdentityResolvers, + ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + &self.signer + } +} + +#[derive(Debug, Default)] +struct DigestAuthSigner; + +impl HttpRequestSigner for DigestAuthSigner { + fn sign_request( + &self, + _request: &mut HttpRequest, + _identity: &Identity, + _signing_properties: &PropertyBag, + ) -> Result<(), BoxError> { + unimplemented!( + "support for signing with Smithy's `@httpDigestAuth` auth scheme is not implemented yet" + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::identity::http::Login; + + #[test] + fn test_api_key_signing_headers() { + let signer = ApiKeySigner { + scheme: "SomeSchemeName".into(), + location: ApiKeyLocation::Header, + name: "some-header-name".into(), + }; + let signing_properties = PropertyBag::new(); + let identity = Identity::new(Token::new("some-token", None), None); + let mut request = http::Request::builder() + .uri("http://example.com/Foobaz") + .body(SdkBody::empty()) + .unwrap(); + signer + .sign_request(&mut request, &identity, &signing_properties) + .expect("success"); + assert_eq!( + "SomeSchemeName some-token", + request.headers().get("some-header-name").unwrap() + ); + assert_eq!("http://example.com/Foobaz", request.uri().to_string()); + } + + #[test] + fn test_api_key_signing_query() { + let signer = ApiKeySigner { + scheme: "".into(), + location: ApiKeyLocation::Query, + name: "some-query-name".into(), + }; + let signing_properties = PropertyBag::new(); + let identity = Identity::new(Token::new("some-token", None), None); + let mut request = http::Request::builder() + .uri("http://example.com/Foobaz") + .body(SdkBody::empty()) + .unwrap(); + signer + .sign_request(&mut request, &identity, &signing_properties) + .expect("success"); + assert!(request.headers().get("some-query-name").is_none()); + assert_eq!( + "http://example.com/Foobaz?some-query-name=some-token", + request.uri().to_string() + ); + } + + #[test] + fn test_basic_auth() { + let signer = BasicAuthSigner; + let signing_properties = PropertyBag::new(); + let identity = Identity::new(Login::new("Aladdin", "open sesame", None), None); + let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); + + signer + .sign_request(&mut request, &identity, &signing_properties) + .expect("success"); + assert_eq!( + "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", + request.headers().get("Authorization").unwrap() + ); + } + + #[test] + fn test_bearer_auth() { + let signer = BearerAuthSigner; + + let signing_properties = PropertyBag::new(); + let identity = Identity::new(Token::new("some-token", None), None); + let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); + signer + .sign_request(&mut request, &identity, &signing_properties) + .expect("success"); + assert_eq!( + "Bearer some-token", + request.headers().get("Authorization").unwrap() + ); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/connections.rs b/rust-runtime/aws-smithy-runtime/src/client/connections.rs index d1d8b55658..e05eedf2e8 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connections.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connections.rs @@ -9,7 +9,7 @@ pub mod test_connection; pub mod adapter { use aws_smithy_client::erase::DynConnector; use aws_smithy_runtime_api::client::orchestrator::{ - BoxFallibleFut, Connection, HttpRequest, HttpResponse, + BoxFuture, Connection, HttpRequest, HttpResponse, }; use std::sync::{Arc, Mutex}; @@ -28,7 +28,7 @@ pub mod adapter { } impl Connection for DynConnectorAdapter { - fn call(&self, request: HttpRequest) -> BoxFallibleFut { + fn call(&self, request: HttpRequest) -> BoxFuture { let future = self.dyn_connector.lock().unwrap().call_lite(request); future } diff --git a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs b/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs index 7366615ab8..f948daf746 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs @@ -9,7 +9,7 @@ use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; use aws_smithy_runtime_api::client::orchestrator::{ - BoxFallibleFut, Connection, HttpRequest, HttpResponse, + BoxFuture, Connection, HttpRequest, HttpResponse, }; use http::header::{HeaderName, CONTENT_TYPE}; use std::fmt::Debug; @@ -187,7 +187,7 @@ impl TestConnection { } impl Connection for TestConnection { - fn call(&self, request: HttpRequest) -> BoxFallibleFut { + fn call(&self, request: HttpRequest) -> BoxFuture { // TODO(orchestrator) Validate request let res = if let Some((expected, resp)) = self.data.lock().unwrap().pop() { diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 4a99001920..77c6b6f21d 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -24,29 +24,29 @@ pub(super) async fn orchestrate_auth( .map_err(construction_failure)?; let identity_resolvers = cfg.identity_resolvers(); + tracing::trace!( + auth_option_resolver_params = ?params, + auth_options = ?auth_options, + identity_resolvers = ?identity_resolvers, + "orchestrating auth", + ); for option in auth_options.as_ref() { let scheme_id = option.scheme_id(); let scheme_properties = option.properties(); if let Some(auth_scheme) = cfg.http_auth_schemes().scheme(scheme_id) { - let identity_resolver = auth_scheme - .identity_resolver(identity_resolvers) - .ok_or_else(|| { - construction_failure(format!( - "no identity resolver found for auth scheme {id}. This is a bug. Please file an issue.", - id = auth_scheme.scheme_id() - )) - })?; - let request_signer = auth_scheme.request_signer(); - - let identity = identity_resolver - .resolve_identity(scheme_properties) - .await - .map_err(construction_failure)?; - return dispatch_phase.include_mut(|ctx| { - let request = ctx.request_mut()?; - request_signer.sign_request(request, &identity, scheme_properties)?; - Result::<_, BoxError>::Ok(()) - }); + if let Some(identity_resolver) = auth_scheme.identity_resolver(identity_resolvers) { + let request_signer = auth_scheme.request_signer(); + + let identity = identity_resolver + .resolve_identity(scheme_properties) + .await + .map_err(construction_failure)?; + return dispatch_phase.include_mut(|ctx| { + let request = ctx.request_mut()?; + request_signer.sign_request(request, &identity, scheme_properties)?; + Result::<_, BoxError>::Ok(()) + }); + } } } @@ -54,3 +54,180 @@ pub(super) async fn orchestrate_auth( "no auth scheme matched auth options. This is a bug. Please file an issue.", )) } + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::property_bag::PropertyBag; + use aws_smithy_runtime_api::client::auth::option_resolver::AuthOptionListResolver; + use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; + use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::client::orchestrator::{ + AuthOptionResolverParams, Future, HttpAuthOption, HttpAuthScheme, HttpAuthSchemes, + HttpRequest, HttpRequestSigner, + }; + use aws_smithy_runtime_api::type_erasure::TypedBox; + use std::sync::Arc; + + #[tokio::test] + async fn basic_case() { + #[derive(Debug)] + struct TestIdentityResolver; + impl IdentityResolver for TestIdentityResolver { + fn resolve_identity(&self, _identity_properties: &PropertyBag) -> Future { + Future::ready(Ok(Identity::new("doesntmatter", None))) + } + } + + #[derive(Debug)] + struct TestSigner; + + impl HttpRequestSigner for TestSigner { + fn sign_request( + &self, + request: &mut HttpRequest, + _identity: &Identity, + _signing_properties: &PropertyBag, + ) -> Result<(), BoxError> { + request + .headers_mut() + .insert(http::header::AUTHORIZATION, "success!".parse().unwrap()); + Ok(()) + } + } + + #[derive(Debug)] + struct TestAuthScheme { + signer: TestSigner, + } + impl HttpAuthScheme for TestAuthScheme { + fn scheme_id(&self) -> &'static str { + "test-scheme" + } + + fn identity_resolver<'a>( + &self, + identity_resolvers: &'a IdentityResolvers, + ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + &self.signer + } + } + + let input = TypedBox::new("doesnt-matter").erase(); + let mut context = InterceptorContext::new(input); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + let mut cfg = ConfigBag::base(); + cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); + cfg.set_auth_option_resolver(AuthOptionListResolver::new(vec![HttpAuthOption::new( + "test-scheme", + Arc::new(PropertyBag::new()), + )])); + cfg.set_identity_resolvers( + IdentityResolvers::builder() + .identity_resolver("test-scheme", TestIdentityResolver) + .build(), + ); + cfg.set_http_auth_schemes( + HttpAuthSchemes::builder() + .auth_scheme("test-scheme", TestAuthScheme { signer: TestSigner }) + .build(), + ); + + let phase = Phase::dispatch(context); + let context = orchestrate_auth(phase, &cfg) + .await + .expect("success") + .finish(); + + assert_eq!( + "success!", + context + .request() + .unwrap() + .headers() + .get("Authorization") + .unwrap() + ); + } + + #[cfg(feature = "http-auth")] + #[tokio::test] + async fn select_best_scheme_for_available_identity_resolvers() { + use crate::client::auth::http::{BasicAuthScheme, BearerAuthScheme}; + use aws_smithy_runtime_api::client::auth::http::{ + HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID, + }; + use aws_smithy_runtime_api::client::identity::http::{Login, Token}; + + let mut context = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + let mut cfg = ConfigBag::base(); + cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); + cfg.set_auth_option_resolver(AuthOptionListResolver::new(vec![ + HttpAuthOption::new(HTTP_BASIC_AUTH_SCHEME_ID, Arc::new(PropertyBag::new())), + HttpAuthOption::new(HTTP_BEARER_AUTH_SCHEME_ID, Arc::new(PropertyBag::new())), + ])); + cfg.set_http_auth_schemes( + HttpAuthSchemes::builder() + .auth_scheme(HTTP_BASIC_AUTH_SCHEME_ID, BasicAuthScheme::new()) + .auth_scheme(HTTP_BEARER_AUTH_SCHEME_ID, BearerAuthScheme::new()) + .build(), + ); + + // First, test the presence of a basic auth login and absence of a bearer token + cfg.set_identity_resolvers( + IdentityResolvers::builder() + .identity_resolver(HTTP_BASIC_AUTH_SCHEME_ID, Login::new("a", "b", None)) + .build(), + ); + + let phase = Phase::dispatch(context); + let context = orchestrate_auth(phase, &cfg) + .await + .expect("success") + .finish(); + + assert_eq!( + // "YTpi" == "a:b" in base64 + "Basic YTpi", + context + .request() + .unwrap() + .headers() + .get("Authorization") + .unwrap() + ); + + // Next, test the presence of a bearer token and absence of basic auth + cfg.set_identity_resolvers( + IdentityResolvers::builder() + .identity_resolver(HTTP_BEARER_AUTH_SCHEME_ID, Token::new("t", None)) + .build(), + ); + + let mut context = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + let context = orchestrate_auth(Phase::dispatch(context), &cfg) + .await + .expect("success") + .finish(); + + assert_eq!( + "Bearer t", + context + .request() + .unwrap() + .headers() + .get("Authorization") + .unwrap() + ); + } +} diff --git a/tools/ci-scripts/codegen-diff/semver-checks.py b/tools/ci-scripts/codegen-diff/semver-checks.py index cda783ce75..69ba9726d1 100755 --- a/tools/ci-scripts/codegen-diff/semver-checks.py +++ b/tools/ci-scripts/codegen-diff/semver-checks.py @@ -34,9 +34,16 @@ def main(skip_generation=False): os.chdir(sdk_directory) failed = False + # TODO(enableNewSmithyRuntime): Remove the deny list below + deny_list = [ + "aws-runtime", + "aws-runtime-api", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + ] for path in os.listdir(): eprint(f'checking {path}...', end='') - if get_cmd_status(f'git cat-file -e base:{sdk_directory}/{path}/Cargo.toml') == 0: + if path not in deny_list and get_cmd_status(f'git cat-file -e base:{sdk_directory}/{path}/Cargo.toml') == 0: (status, out, err) = get_cmd_output(f'cargo semver-checks check-release ' f'--baseline-rev {BASE_BRANCH} ' # in order to get semver-checks to work with publish-false crates, need to specify From f271da4cf52eb2a8d77ddfe95bedf61b1de0b89f Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Tue, 25 Apr 2023 09:51:59 -0500 Subject: [PATCH 053/253] feature: Invocation ID interceptor (#2626) ## Motivation and Context part of #1793 ## Description This adds an interceptor for AWS SDK requests. The interceptor is run just before the retry loop and adds a header with name `amz-sdk-invocation-id` and value that's a UUID. AWS services use this identifier to more efficiently process requests. ## Testing This change includes tests ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti --- aws/rust-runtime/aws-runtime/Cargo.toml | 1 + .../aws-runtime/src/invocation_id.rs | 91 +++++++++++++++++++ aws/rust-runtime/aws-runtime/src/lib.rs | 3 + .../smithy/rustsdk/AwsCodegenDecorator.kt | 1 + .../smithy/rustsdk/InvocationIdDecorator.kt | 43 +++++++++ 5 files changed, 139 insertions(+) create mode 100644 aws/rust-runtime/aws-runtime/src/invocation_id.rs create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index 7fa3274cb4..ccb3d45542 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -18,6 +18,7 @@ aws-types = { path = "../aws-types" } http = "0.2.3" percent-encoding = "2.1.0" tracing = "0.1" +uuid = { version = "1", features = ["v4", "fast-rng"] } [dev-dependencies] aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-test" } diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs new file mode 100644 index 0000000000..ecdf16ed5f --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -0,0 +1,91 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::interceptors::error::BoxError; +use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use http::{HeaderName, HeaderValue}; +use uuid::Uuid; + +#[allow(clippy::declare_interior_mutable_const)] // we will never mutate this +const AMZ_SDK_INVOCATION_ID: HeaderName = HeaderName::from_static("amz-sdk-invocation-id"); + +/// This interceptor generates a UUID and attaches it to all request attempts made as part of this operation. +#[non_exhaustive] +#[derive(Debug)] +pub struct InvocationIdInterceptor { + id: HeaderValue, +} + +impl InvocationIdInterceptor { + /// Creates a new `InvocationIdInterceptor` + pub fn new() -> Self { + Self::default() + } +} + +impl Default for InvocationIdInterceptor { + fn default() -> Self { + let id = Uuid::new_v4(); + let id = id + .to_string() + .parse() + .expect("UUIDs always produce a valid header value"); + Self { id } + } +} + +impl Interceptor for InvocationIdInterceptor { + fn modify_before_retry_loop( + &self, + context: &mut InterceptorContext, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let headers = context.request_mut()?.headers_mut(); + headers.append(AMZ_SDK_INVOCATION_ID, self.id.clone()); + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::invocation_id::InvocationIdInterceptor; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; + use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; + use aws_smithy_runtime_api::config_bag::ConfigBag; + use aws_smithy_runtime_api::type_erasure::TypedBox; + use http::HeaderValue; + + fn expect_header<'a>( + context: &'a InterceptorContext, + header_name: &str, + ) -> &'a HeaderValue { + context + .request() + .unwrap() + .headers() + .get(header_name) + .unwrap() + } + + #[test] + fn test_id_is_generated_and_set() { + let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + let mut config = ConfigBag::base(); + let interceptor = InvocationIdInterceptor::new(); + interceptor + .modify_before_retry_loop(&mut context, &mut config) + .unwrap(); + + let header = expect_header(&context, "amz-sdk-invocation-id"); + assert_eq!(&interceptor.id, header); + // UUID should include 32 chars and 4 dashes + assert_eq!(interceptor.id.len(), 36); + } +} diff --git a/aws/rust-runtime/aws-runtime/src/lib.rs b/aws/rust-runtime/aws-runtime/src/lib.rs index 3112279431..e218303269 100644 --- a/aws/rust-runtime/aws-runtime/src/lib.rs +++ b/aws/rust-runtime/aws-runtime/src/lib.rs @@ -24,3 +24,6 @@ pub mod recursion_detection; /// Supporting code for user agent headers in the AWS SDK. pub mod user_agent; + +/// Supporting code for invocation ID headers in the AWS SDK. +pub mod invocation_id; 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 7c245bb760..6d445f271d 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 @@ -53,6 +53,7 @@ val DECORATORS: List = listOf( AwsRequestIdDecorator(), DisabledAuthDecorator(), RecursionDetectionDecorator(), + InvocationIdDecorator(), ), // Service specific decorators diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt new file mode 100644 index 0000000000..abf317570f --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt @@ -0,0 +1,43 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.util.letIf + +class InvocationIdDecorator : ClientCodegenDecorator { + override val name: String get() = "InvocationIdDecorator" + override val order: Byte get() = 0 + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + it + listOf(InvocationIdRuntimePluginCustomization(codegenContext)) + } +} + +private class InvocationIdRuntimePluginCustomization( + private val codegenContext: ClientCodegenContext, +) : ServiceRuntimePluginCustomization() { + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.AdditionalConfig) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rust( + "#T::new()", + AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig) + .resolve("invocation_id::InvocationIdInterceptor"), + ) + } + } + } +} From 21249b0bb3497b864a02b4ff3e6eff84c8b95a87 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 25 Apr 2023 11:09:00 -0400 Subject: [PATCH 054/253] Upgrade cargo semver checks to 0.20.0 (#2631) ## Motivation and Context cargo semver-checks 0.20.0 is something like 100x faster ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- tools/ci-build/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci-build/Dockerfile b/tools/ci-build/Dockerfile index 2bce2b0ec9..f5ed4869d9 100644 --- a/tools/ci-build/Dockerfile +++ b/tools/ci-build/Dockerfile @@ -139,7 +139,7 @@ ARG rust_nightly_version RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-wasi --locked --version ${cargo_wasi_version} FROM install_rust AS cargo_semver_checks -ARG cargo_semver_checks_version=0.19.0 +ARG cargo_semver_checks_version=0.20.0 ARG rust_nightly_version RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-semver-checks --locked --version ${cargo_semver_checks_version} # From ae995fbadb3ca52c7d64968445f7dcda4532ea38 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Tue, 25 Apr 2023 15:51:30 -0500 Subject: [PATCH 055/253] feature: orchestrator retry classifiers (#2621) ## Motivation and Context To retry a response, we must first classify it as retryable. ## Description feature: add AWS error code classifier feature: add x-amz-retry-after header classifier feature: add smithy modeled retry classifier feature: add error type classifier feature: add HTTP status code classifier add: tests for classifiers remove: redundant `http` dep from `aws-http` move: `NeverRetryStrategy` to smithy-runtime crate add: RuntimePluginImpls codegen section for operation-specific runtime plugin definitions update: orchestrator retries to work with `ShouldAttempt` add: retry classifier config bag accessor add: raw response getter to SdkError update: RetryStrategy trait signatures to use `ShouldAttempt` add: `RetryClassifiers` struct for holding and calling retry classifiers update: `RetryClassifierDecorator` to define orchestrator classifiers add: `default_retry_classifiers` fn to codegen update: `ServiceGenerator` to add feature flag for aws-smithy-runtime/test-util update: SRA integration test to insert retry classifier plugin ## Testing this change includes tests ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-http/Cargo.toml | 1 - .../aws-runtime/external-types.toml | 3 +- aws/rust-runtime/aws-runtime/src/lib.rs | 3 + aws/rust-runtime/aws-runtime/src/retries.rs | 7 + .../aws-runtime/src/retries/classifier.rs | 174 ++++++++++++++++ .../rustsdk/RetryClassifierDecorator.kt | 158 +++++++++++++- .../aws-sdk-s3/tests/sra_test.rs | 8 +- .../customizations/HttpAuthDecorator.kt | 2 + .../EndpointParamsInterceptorGenerator.kt | 6 +- .../OperationRuntimePluginGenerator.kt | 40 ++++ .../smithy/generators/ServiceGenerator.kt | 4 + .../ServiceRuntimePluginGenerator.kt | 4 +- rust-runtime/aws-smithy-http/src/result.rs | 21 +- .../aws-smithy-runtime-api/src/client.rs | 1 + .../src/client/orchestrator.rs | 63 +++--- .../src/client/retries.rs | 74 +++++-- .../aws-smithy-runtime/external-types.toml | 1 + rust-runtime/aws-smithy-runtime/src/client.rs | 6 + .../src/client/orchestrator.rs | 19 +- .../aws-smithy-runtime/src/client/retries.rs | 7 + .../src/client/retries/classifier.rs | 195 ++++++++++++++++++ .../src/client/retries/strategy.rs | 8 + .../src/client/retries/strategy/never.rs | 32 +++ .../aws-smithy-types/src/error/metadata.rs | 6 + 24 files changed, 774 insertions(+), 69 deletions(-) create mode 100644 aws/rust-runtime/aws-runtime/src/retries.rs create mode 100644 aws/rust-runtime/aws-runtime/src/retries/classifier.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/retries.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs diff --git a/aws/rust-runtime/aws-http/Cargo.toml b/aws/rust-runtime/aws-http/Cargo.toml index be5cedd5d8..f09f8bc8e3 100644 --- a/aws/rust-runtime/aws-http/Cargo.toml +++ b/aws/rust-runtime/aws-http/Cargo.toml @@ -28,7 +28,6 @@ aws-smithy-checksums = { path = "../../../rust-runtime/aws-smithy-checksums" } aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-test" } bytes-utils = "0.1.2" env_logger = "0.9" -http = "0.2.3" tokio = { version = "1.23.1", features = ["macros", "rt", "rt-multi-thread", "test-util", "time"] } tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } proptest = "1" diff --git a/aws/rust-runtime/aws-runtime/external-types.toml b/aws/rust-runtime/aws-runtime/external-types.toml index 90b77e92e0..a38a0e0b57 100644 --- a/aws/rust-runtime/aws-runtime/external-types.toml +++ b/aws/rust-runtime/aws-runtime/external-types.toml @@ -1,7 +1,8 @@ allowed_external_types = [ "aws_credential_types::*", "aws_sigv4::*", - "aws_smithy_http::body::SdkBody", + "aws_smithy_http::*", + "aws_smithy_types::*", "aws_smithy_runtime_api::*", "aws_types::*", "http::request::Request", diff --git a/aws/rust-runtime/aws-runtime/src/lib.rs b/aws/rust-runtime/aws-runtime/src/lib.rs index e218303269..9df20ae3bc 100644 --- a/aws/rust-runtime/aws-runtime/src/lib.rs +++ b/aws/rust-runtime/aws-runtime/src/lib.rs @@ -25,5 +25,8 @@ pub mod recursion_detection; /// Supporting code for user agent headers in the AWS SDK. pub mod user_agent; +/// Supporting code for retry behavior specific to the AWS SDK. +pub mod retries; + /// Supporting code for invocation ID headers in the AWS SDK. pub mod invocation_id; diff --git a/aws/rust-runtime/aws-runtime/src/retries.rs b/aws/rust-runtime/aws-runtime/src/retries.rs new file mode 100644 index 0000000000..ed12aa9cde --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/retries.rs @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Classifiers that can inspect a response and determine if it should be retried. +pub mod classifier; diff --git a/aws/rust-runtime/aws-runtime/src/retries/classifier.rs b/aws/rust-runtime/aws-runtime/src/retries/classifier.rs new file mode 100644 index 0000000000..7e9c10d59b --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/retries/classifier.rs @@ -0,0 +1,174 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::http::HttpHeaders; +use aws_smithy_http::result::SdkError; +use aws_smithy_runtime_api::client::retries::RetryReason; +use aws_smithy_types::error::metadata::ProvideErrorMetadata; +use aws_smithy_types::retry::ErrorKind; + +/// AWS error codes that represent throttling errors. +pub const THROTTLING_ERRORS: &[&str] = &[ + "Throttling", + "ThrottlingException", + "ThrottledException", + "RequestThrottledException", + "TooManyRequestsException", + "ProvisionedThroughputExceededException", + "TransactionInProgressException", + "RequestLimitExceeded", + "BandwidthLimitExceeded", + "LimitExceededException", + "RequestThrottled", + "SlowDown", + "PriorRequestNotComplete", + "EC2ThrottledException", +]; + +/// AWS error codes that represent transient errors. +pub const TRANSIENT_ERRORS: &[&str] = &["RequestTimeout", "RequestTimeoutException"]; + +/// A retry classifier for determining if the response sent by an AWS service requires a retry. +#[derive(Debug)] +pub struct AwsErrorCodeClassifier; + +impl AwsErrorCodeClassifier { + /// Classify an error code to check if represents a retryable error. The codes of retryable + /// errors are defined [here](THROTTLING_ERRORS) and [here](TRANSIENT_ERRORS). + pub fn classify_error( + &self, + error: &SdkError, + ) -> Option { + if let Some(error_code) = error.code() { + if THROTTLING_ERRORS.contains(&error_code) { + return Some(RetryReason::Error(ErrorKind::ThrottlingError)); + } else if TRANSIENT_ERRORS.contains(&error_code) { + return Some(RetryReason::Error(ErrorKind::TransientError)); + } + }; + + None + } +} + +/// A retry classifier that checks for `x-amz-retry-after` headers. If one is found, a +/// [`RetryReason::Explicit`] is returned containing the duration to wait before retrying. +#[derive(Debug)] +pub struct AmzRetryAfterHeaderClassifier; + +impl AmzRetryAfterHeaderClassifier { + /// Classify an AWS responses error code to determine how (and if) it should be retried. + pub fn classify_error(&self, error: &SdkError) -> Option { + error + .raw_response() + .and_then(|res| res.http_headers().get("x-amz-retry-after")) + .and_then(|header| header.to_str().ok()) + .and_then(|header| header.parse::().ok()) + .map(|retry_after_delay| { + RetryReason::Explicit(std::time::Duration::from_millis(retry_after_delay)) + }) + } +} + +#[cfg(test)] +mod test { + use super::{AmzRetryAfterHeaderClassifier, AwsErrorCodeClassifier}; + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::operation; + use aws_smithy_http::result::SdkError; + use aws_smithy_runtime_api::client::retries::RetryReason; + use aws_smithy_types::error::metadata::ProvideErrorMetadata; + use aws_smithy_types::error::ErrorMetadata; + use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; + use std::fmt; + use std::time::Duration; + + #[derive(Debug)] + struct UnmodeledError; + + impl fmt::Display for UnmodeledError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UnmodeledError") + } + } + + impl std::error::Error for UnmodeledError {} + + struct CodedError { + metadata: ErrorMetadata, + } + + impl CodedError { + fn new(code: &'static str) -> Self { + Self { + metadata: ErrorMetadata::builder().code(code).build(), + } + } + } + + impl ProvideErrorKind for UnmodeledError { + fn retryable_error_kind(&self) -> Option { + None + } + + fn code(&self) -> Option<&str> { + None + } + } + + impl ProvideErrorMetadata for CodedError { + fn meta(&self) -> &ErrorMetadata { + &self.metadata + } + } + + #[test] + fn classify_by_error_code() { + let policy = AwsErrorCodeClassifier; + let res = http::Response::new("OK"); + let err = SdkError::service_error(CodedError::new("Throttling"), res); + + assert_eq!( + policy.classify_error(&err), + Some(RetryReason::Error(ErrorKind::ThrottlingError)) + ); + + let res = http::Response::new("OK"); + let err = SdkError::service_error(CodedError::new("RequestTimeout"), res); + assert_eq!( + policy.classify_error(&err), + Some(RetryReason::Error(ErrorKind::TransientError)) + ) + } + + #[test] + fn classify_generic() { + let policy = AwsErrorCodeClassifier; + let res = http::Response::new("OK"); + let err = aws_smithy_types::Error::builder().code("SlowDown").build(); + let err = SdkError::service_error(err, res); + assert_eq!( + policy.classify_error(&err), + Some(RetryReason::Error(ErrorKind::ThrottlingError)) + ); + } + + #[test] + fn test_retry_after_header() { + let policy = AmzRetryAfterHeaderClassifier; + let res = http::Response::builder() + .header("x-amz-retry-after", "5000") + .body("retry later") + .unwrap() + .map(SdkBody::from); + let res = operation::Response::new(res); + let err = SdkError::service_error(UnmodeledError, res); + + assert_eq!( + policy.classify_error(&err), + Some(RetryReason::Explicit(Duration::from_millis(5000))), + ); + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt index 0d488fb21f..ef730207a7 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt @@ -8,7 +8,12 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginSection +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.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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType @@ -23,13 +28,21 @@ class RetryClassifierDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List { - return baseCustomizations + RetryClassifierFeature(codegenContext.runtimeConfig) - } + ): List = + baseCustomizations + RetryClassifierFeature(codegenContext.runtimeConfig) + + override fun operationRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = + baseCustomizations + OperationRetryClassifiersFeature(codegenContext, operation) } class RetryClassifierFeature(private val runtimeConfig: RuntimeConfig) : OperationCustomization() { - override fun retryType(): RuntimeType = AwsRuntimeType.awsHttp(runtimeConfig).resolve("retry::AwsResponseRetryClassifier") + override fun retryType(): RuntimeType = + AwsRuntimeType.awsHttp(runtimeConfig).resolve("retry::AwsResponseRetryClassifier") + override fun section(section: OperationSection) = when (section) { is OperationSection.FinalizeOperation -> writable { rust( @@ -41,3 +54,140 @@ class RetryClassifierFeature(private val runtimeConfig: RuntimeConfig) : Operati else -> emptySection } } + +class OperationRetryClassifiersFeature( + codegenContext: ClientCodegenContext, + operation: OperationShape, +) : OperationRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + private val smithyRuntime = RuntimeType.smithyRuntime(runtimeConfig) + private val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) + private val codegenScope = arrayOf( + "HttpStatusCodeClassifier" to smithyRuntime.resolve("client::retries::classifier::HttpStatusCodeClassifier"), + "AwsErrorCodeClassifier" to awsRuntime.resolve("retries::classifier::AwsErrorCodeClassifier"), + "ModeledAsRetryableClassifier" to smithyRuntime.resolve("client::retries::classifier::ModeledAsRetryableClassifier"), + "AmzRetryAfterHeaderClassifier" to awsRuntime.resolve("retries::classifier::AmzRetryAfterHeaderClassifier"), + "SmithyErrorClassifier" to smithyRuntime.resolve("client::retries::classifier::SmithyErrorClassifier"), + "RetryReason" to smithyRuntimeApi.resolve("client::retries::RetryReason"), + "ClassifyRetry" to smithyRuntimeApi.resolve("client::retries::ClassifyRetry"), + "RetryClassifiers" to smithyRuntimeApi.resolve("client::retries::RetryClassifiers"), + "OperationError" to codegenContext.symbolProvider.symbolForOperationError(operation), + "SdkError" to RuntimeType.smithyHttp(runtimeConfig).resolve("result::SdkError"), + "ErasedError" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypeErasedBox"), + ) + + override fun section(section: OperationRuntimePluginSection) = when (section) { + is OperationRuntimePluginSection.RuntimePluginSupportingTypes -> writable { + Attribute(derive(RuntimeType.Debug)).render(this) + rustTemplate( + """ + struct HttpStatusCodeClassifier(#{HttpStatusCodeClassifier}); + impl HttpStatusCodeClassifier { + fn new() -> Self { + Self(#{HttpStatusCodeClassifier}::default()) + } + } + impl #{ClassifyRetry} for HttpStatusCodeClassifier { + fn classify_retry(&self, error: &#{ErasedError}) -> Option<#{RetryReason}> { + let error = error.downcast_ref::<#{SdkError}<#{OperationError}>>().expect("The error type is always known"); + self.0.classify_error(error) + } + } + """, + *codegenScope, + ) + + Attribute(derive(RuntimeType.Debug)).render(this) + rustTemplate( + """ + struct AwsErrorCodeClassifier(#{AwsErrorCodeClassifier}); + impl AwsErrorCodeClassifier { + fn new() -> Self { + Self(#{AwsErrorCodeClassifier}) + } + } + impl #{ClassifyRetry} for AwsErrorCodeClassifier { + fn classify_retry(&self, error: &#{ErasedError}) -> Option<#{RetryReason}> { + let error = error.downcast_ref::<#{SdkError}<#{OperationError}>>().expect("The error type is always known"); + self.0.classify_error(error) + } + } + """, + *codegenScope, + ) + + Attribute(derive(RuntimeType.Debug)).render(this) + rustTemplate( + """ + struct ModeledAsRetryableClassifier(#{ModeledAsRetryableClassifier}); + impl ModeledAsRetryableClassifier { + fn new() -> Self { + Self(#{ModeledAsRetryableClassifier}) + } + } + impl #{ClassifyRetry} for ModeledAsRetryableClassifier { + fn classify_retry(&self, error: &#{ErasedError}) -> Option<#{RetryReason}> { + let error = error.downcast_ref::<#{SdkError}<#{OperationError}>>().expect("The error type is always known"); + self.0.classify_error(error) + } + } + """, + *codegenScope, + ) + + Attribute(derive(RuntimeType.Debug)).render(this) + rustTemplate( + """ + struct AmzRetryAfterHeaderClassifier(#{AmzRetryAfterHeaderClassifier}); + impl AmzRetryAfterHeaderClassifier { + fn new() -> Self { + Self(#{AmzRetryAfterHeaderClassifier}) + } + } + impl #{ClassifyRetry} for AmzRetryAfterHeaderClassifier { + fn classify_retry(&self, error: &#{ErasedError}) -> Option<#{RetryReason}> { + let error = error.downcast_ref::<#{SdkError}<#{OperationError}>>().expect("The error type is always known"); + self.0.classify_error(error) + } + } + """, + *codegenScope, + ) + + Attribute(derive(RuntimeType.Debug)).render(this) + rustTemplate( + """ + struct SmithyErrorClassifier(#{SmithyErrorClassifier}); + impl SmithyErrorClassifier { + fn new() -> Self { + Self(#{SmithyErrorClassifier}) + } + } + impl #{ClassifyRetry} for SmithyErrorClassifier { + fn classify_retry(&self, error: &#{ErasedError}) -> Option<#{RetryReason}> { + let error = error.downcast_ref::<#{SdkError}<#{OperationError}>>().expect("The error type is always known"); + self.0.classify_error(error) + } + } + """, + *codegenScope, + ) + } + + is OperationRuntimePluginSection.RetryClassifier -> writable { + rustTemplate( + """ + .with_classifier(SmithyErrorClassifier::new()) + .with_classifier(AmzRetryAfterHeaderClassifier::new()) + .with_classifier(ModeledAsRetryableClassifier::new()) + .with_classifier(AwsErrorCodeClassifier::new()) + .with_classifier(HttpStatusCodeClassifier::new()) + """, + *codegenScope, + ) + } + + else -> emptySection + } +} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index 70dd23d400..28d8d9a3b7 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -24,11 +24,13 @@ use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorConte use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, Connection, HttpRequest, HttpResponse, RequestTime, TraceProbe, }; +use aws_smithy_runtime_api::client::retries::RetryClassifiers; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_runtime_api::type_erasure::TypedBox; use aws_types::region::SigningRegion; use aws_types::SigningService; +use http::Uri; use std::sync::Arc; use std::time::{Duration, UNIX_EPOCH}; @@ -104,7 +106,7 @@ async fn sra_manual_test() { cfg.put(params_builder); cfg.set_retry_strategy( - aws_smithy_runtime_api::client::retries::NeverRetryStrategy::new(), + aws_smithy_runtime::client::retries::strategy::StandardRetryStrategy::default(), ); let connection: Box = Box::new(DynConnectorAdapter::new( @@ -170,7 +172,7 @@ async fn sra_manual_test() { .ok_or_else(|| "failed to downcast to ListObjectsV2Input")?; let mut params_builder = cfg .get::() - .ok_or_else(|| "missing endpoint params builder")? + .ok_or("missing endpoint params builder")? .clone(); params_builder = params_builder.set_bucket(input.bucket.clone()); cfg.put(params_builder); @@ -189,7 +191,7 @@ async fn sra_manual_test() { ) -> Result<(), BoxError> { let params_builder = cfg .get::() - .ok_or_else(|| "missing endpoint params builder")? + .ok_or("missing endpoint params builder")? .clone(); let params = params_builder.build().map_err(|err| { ContextAttachedError::new("endpoint params could not be built", err) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index 243f563d4e..7f23f7a853 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -250,6 +250,8 @@ private class HttpAuthOperationRuntimePluginCustomization( rustTemplate("${section.configBagName}.set_auth_option_resolver(auth_option_resolver);", *codegenScope) } + + else -> emptySection } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 0299eb21c7..b00a020508 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -84,10 +84,10 @@ class EndpointParamsInterceptorGenerator( let input = context.input()?; let _input = input .downcast_ref::<${operationInput.name}>() - .ok_or_else(|| "failed to downcast to ${operationInput.name}")?; + .ok_or("failed to downcast to ${operationInput.name}")?; let params_builder = cfg .get::<#{ParamsBuilder}>() - .ok_or_else(|| "missing endpoint params builder")? + .ok_or("missing endpoint params builder")? .clone(); ${"" /* TODO(EndpointResolver): Call setters on `params_builder` to update its fields by using values from `_input` */} cfg.put(params_builder); @@ -131,7 +131,7 @@ class EndpointParamsInterceptorGenerator( let _ = context; let params_builder = cfg .get::<#{ParamsBuilder}>() - .ok_or_else(|| "missing endpoint params builder")? + .ok_or("missing endpoint params builder")? .clone(); let params = params_builder .build() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index 2ad5d454a2..7c2d1e7662 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -40,6 +40,29 @@ sealed class OperationRuntimePluginSection(name: String) : Section(name) { ) } } + + /** + * Hook for adding retry classifiers to an operation's `RetryClassifiers` bundle. + * + * Should emit 1+ lines of code that look like the following: + * ```rust + * .with_classifier(AwsErrorCodeClassifier::new()) + * .with_classifier(HttpStatusCodeClassifier::new()) + * ``` + */ + data class RetryClassifier( + val configBagName: String, + val operationShape: OperationShape, + ) : OperationRuntimePluginSection("RetryClassifier") + + /** + * Hook for adding supporting types for operation-specific runtime plugins. + * Examples include various operation-specific types (retry classifiers, config bag types, etc.) + */ + data class RuntimePluginSupportingTypes( + val configBagName: String, + val operationShape: OperationShape, + ) : OperationRuntimePluginSection("RuntimePluginSupportingTypes") } typealias OperationRuntimePluginCustomization = NamedCustomization @@ -58,6 +81,7 @@ class OperationRuntimePluginGenerator( "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), + "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), ) } @@ -79,10 +103,17 @@ class OperationRuntimePluginGenerator( ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{AuthOptionListResolverParams}::new())); + // Retry classifiers are operation-specific because they need to downcast operation-specific error types. + let retry_classifiers = #{RetryClassifiers}::new() + #{retry_classifier_customizations}; + cfg.set_retry_classifiers(retry_classifiers); + #{additional_config} Ok(()) } } + + #{runtime_plugin_supporting_types} """, *codegenScope, "additional_config" to writable { @@ -91,6 +122,15 @@ class OperationRuntimePluginGenerator( OperationRuntimePluginSection.AdditionalConfig("cfg", operationShape), ) }, + "retry_classifier_customizations" to writable { + writeCustomizations(customizations, OperationRuntimePluginSection.RetryClassifier("cfg", operationShape)) + }, + "runtime_plugin_supporting_types" to writable { + writeCustomizations( + customizations, + OperationRuntimePluginSection.RuntimePluginSupportingTypes("cfg", operationShape), + ) + }, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index ac5804aed9..764efb35df 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.model.knowledge.TopDownIndex 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.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfigGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ServiceErrorGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute @@ -47,6 +48,9 @@ class ServiceGenerator( serviceConfigGenerator.render(this) if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + // Enable users to opt in to the test-utils in the runtime crate + rustCrate.mergeFeature(TestUtilFeature.copy(deps = listOf("aws-smithy-runtime/test-util"))) + ServiceRuntimePluginGenerator(codegenContext) .render(this, decorator.serviceRuntimePluginCustomizations(codegenContext, emptyList())) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index c9955e5fad..7f5963c572 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -66,8 +66,8 @@ class ServiceRuntimePluginGenerator( private val endpointTypesGenerator = EndpointTypesGenerator.fromContext(codegenContext) private val codegenScope = codegenContext.runtimeConfig.let { rc -> val http = RuntimeType.smithyHttp(rc) - val runtimeApi = RuntimeType.smithyRuntimeApi(rc) val runtime = RuntimeType.smithyRuntime(rc) + val runtimeApi = RuntimeType.smithyRuntimeApi(rc) arrayOf( "AnonymousIdentityResolver" to runtimeApi.resolve("client::identity::AnonymousIdentityResolver"), "AuthOptionListResolver" to runtimeApi.resolve("client::auth::option_resolver::AuthOptionListResolver"), @@ -80,7 +80,7 @@ class ServiceRuntimePluginGenerator( "DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"), "HttpAuthSchemes" to runtimeApi.resolve("client::orchestrator::HttpAuthSchemes"), "IdentityResolvers" to runtimeApi.resolve("client::identity::IdentityResolvers"), - "NeverRetryStrategy" to runtimeApi.resolve("client::retries::NeverRetryStrategy"), + "NeverRetryStrategy" to runtime.resolve("client::retries::strategy::NeverRetryStrategy"), "Params" to endpointTypesGenerator.paramsStruct(), "ResolveEndpoint" to http.resolve("endpoint::ResolveEndpoint"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), diff --git a/rust-runtime/aws-smithy-http/src/result.rs b/rust-runtime/aws-smithy-http/src/result.rs index 0ce42c7cbd..bd6d2f1f3b 100644 --- a/rust-runtime/aws-smithy-http/src/result.rs +++ b/rust-runtime/aws-smithy-http/src/result.rs @@ -5,15 +5,17 @@ //! `Result` wrapper types for [success](SdkSuccess) and [failure](SdkError) responses. -use crate::connection::ConnectionMetadata; -use crate::operation; -use aws_smithy_types::error::metadata::{ProvideErrorMetadata, EMPTY_ERROR_METADATA}; -use aws_smithy_types::error::ErrorMetadata; -use aws_smithy_types::retry::ErrorKind; use std::error::Error; use std::fmt; use std::fmt::{Debug, Display, Formatter}; +use aws_smithy_types::error::metadata::{ProvideErrorMetadata, EMPTY_ERROR_METADATA}; +use aws_smithy_types::error::ErrorMetadata; +use aws_smithy_types::retry::ErrorKind; + +use crate::connection::ConnectionMetadata; +use crate::operation; + type BoxError = Box; /// Successful SDK Result @@ -434,6 +436,15 @@ impl SdkError { } } + /// Return a reference to this error's raw response, if it contains one. Otherwise, return `None`. + pub fn raw_response(&self) -> Option<&R> { + match self { + Self::ServiceError(inner) => Some(inner.raw()), + Self::ResponseError(inner) => Some(inner.raw()), + _ => None, + } + } + /// Maps the service error type in `SdkError::ServiceError` #[doc(hidden)] pub fn map_service_error(self, map: impl FnOnce(E) -> E2) -> SdkError { diff --git a/rust-runtime/aws-smithy-runtime-api/src/client.rs b/rust-runtime/aws-smithy-runtime-api/src/client.rs index 192b97a5d4..1c4a97a62b 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client.rs @@ -18,6 +18,7 @@ pub mod orchestrator; /// This code defines when and how failed requests should be retried. It also defines the behavior /// used to limit the rate that requests are sent. pub mod retries; + /// Runtime plugin type definitions. pub mod runtime_plugin; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index bd2dcb3289..85233f12ce 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -6,7 +6,8 @@ use super::identity::{IdentityResolver, IdentityResolvers}; use crate::client::identity::Identity; use crate::client::interceptors::context::{Input, OutputOrError}; -use crate::client::interceptors::InterceptorContext; +use crate::client::retries::RetryClassifiers; +use crate::client::retries::RetryStrategy; use crate::config_bag::ConfigBag; use crate::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_async::future::now_or_later::NowOrLater; @@ -54,16 +55,6 @@ impl Connection for Box { } } -pub trait RetryStrategy: Send + Sync + Debug { - fn should_attempt_initial_request(&self, cfg: &ConfigBag) -> Result<(), BoxError>; - - fn should_attempt_retry( - &self, - context: &InterceptorContext, - cfg: &ConfigBag, - ) -> Result; -} - #[derive(Debug)] pub struct AuthOptionResolverParams(TypeErasedBox); @@ -241,6 +232,9 @@ pub trait ConfigBagAccessors { response_serializer: impl ResponseDeserializer + 'static, ); + fn retry_classifiers(&self) -> &RetryClassifiers; + fn set_retry_classifiers(&mut self, retry_classifier: RetryClassifiers); + fn retry_strategy(&self) -> &dyn RetryStrategy; fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static); @@ -277,25 +271,6 @@ impl ConfigBagAccessors for ConfigBag { self.put::>(Box::new(auth_option_resolver)); } - fn http_auth_schemes(&self) -> &HttpAuthSchemes { - self.get::() - .expect("auth schemes must be set") - } - - fn set_http_auth_schemes(&mut self, http_auth_schemes: HttpAuthSchemes) { - self.put::(http_auth_schemes); - } - - fn retry_strategy(&self) -> &dyn RetryStrategy { - &**self - .get::>() - .expect("a retry strategy must be set") - } - - fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static) { - self.put::>(Box::new(retry_strategy)); - } - fn endpoint_resolver_params(&self) -> &EndpointResolverParams { self.get::() .expect("endpoint resolver params must be set") @@ -334,6 +309,15 @@ impl ConfigBagAccessors for ConfigBag { self.put::>(Box::new(connection)); } + fn http_auth_schemes(&self) -> &HttpAuthSchemes { + self.get::() + .expect("auth schemes must be set") + } + + fn set_http_auth_schemes(&mut self, http_auth_schemes: HttpAuthSchemes) { + self.put::(http_auth_schemes); + } + fn request_serializer(&self) -> &dyn RequestSerializer { &**self .get::>() @@ -357,6 +341,25 @@ impl ConfigBagAccessors for ConfigBag { self.put::>(Box::new(response_deserializer)); } + fn retry_classifiers(&self) -> &RetryClassifiers { + self.get::() + .expect("retry classifiers must be set") + } + + fn set_retry_classifiers(&mut self, retry_classifiers: RetryClassifiers) { + self.put::(retry_classifiers); + } + + fn retry_strategy(&self) -> &dyn RetryStrategy { + &**self + .get::>() + .expect("a retry strategy must be set") + } + + fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static) { + self.put::>(Box::new(retry_strategy)); + } + fn trace_probe(&self) -> &dyn TraceProbe { &**self .get::>() diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index fd840a7bc9..a53f1d242c 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -3,32 +3,72 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::client::interceptors::context::Error; use crate::client::interceptors::InterceptorContext; -use crate::client::orchestrator::{BoxError, RetryStrategy}; +use crate::client::orchestrator::{BoxError, HttpRequest, HttpResponse}; use crate::config_bag::ConfigBag; -use aws_smithy_http::body::SdkBody; +use aws_smithy_types::retry::ErrorKind; +use std::fmt::Debug; +use std::time::Duration; -pub mod rate_limiting; +/// An answer to the question "should I make a request attempt?" +pub enum ShouldAttempt { + Yes, + No, + YesAfterDelay(Duration), +} + +pub trait RetryStrategy: Send + Sync + Debug { + fn should_attempt_initial_request(&self, cfg: &ConfigBag) -> Result; -#[derive(Debug, Clone)] -pub struct NeverRetryStrategy {} + fn should_attempt_retry( + &self, + context: &InterceptorContext, + cfg: &ConfigBag, + ) -> Result; +} + +#[non_exhaustive] +#[derive(Eq, PartialEq, Debug)] +pub enum RetryReason { + Error(ErrorKind), + Explicit(Duration), +} + +/// Classifies what kind of retry is needed for a given [`Error`]. +pub trait ClassifyRetry: Send + Sync + Debug { + /// Run this classifier against an error to determine if it should be retried. Returns + /// `Some(RetryKind)` if the error should be retried; Otherwise returns `None`. + fn classify_retry(&self, error: &Error) -> Option; +} -impl NeverRetryStrategy { +#[derive(Debug)] +pub struct RetryClassifiers { + inner: Vec>, +} + +impl RetryClassifiers { pub fn new() -> Self { - Self {} + Self { + // It's always expected that at least one classifier will be defined, + // so we eagerly allocate for it. + inner: Vec::with_capacity(1), + } } -} -impl RetryStrategy for NeverRetryStrategy { - fn should_attempt_initial_request(&self, _cfg: &ConfigBag) -> Result<(), BoxError> { - Ok(()) + pub fn with_classifier(mut self, retry_classifier: impl ClassifyRetry + 'static) -> Self { + self.inner.push(Box::new(retry_classifier)); + + self } - fn should_attempt_retry( - &self, - _context: &InterceptorContext, http::Response>, - _cfg: &ConfigBag, - ) -> Result { - Ok(false) + // TODO(https://github.com/awslabs/smithy-rs/issues/2632) make a map function so users can front-run or second-guess the classifier's decision + // pub fn map_classifiers(mut self, fun: Fn() -> RetryClassifiers) +} + +impl ClassifyRetry for RetryClassifiers { + fn classify_retry(&self, error: &Error) -> Option { + // return the first non-None result + self.inner.iter().find_map(|cr| cr.classify_retry(error)) } } diff --git a/rust-runtime/aws-smithy-runtime/external-types.toml b/rust-runtime/aws-smithy-runtime/external-types.toml index a8a47f10d9..92360e722a 100644 --- a/rust-runtime/aws-smithy-runtime/external-types.toml +++ b/rust-runtime/aws-smithy-runtime/external-types.toml @@ -1,6 +1,7 @@ allowed_external_types = [ "aws_smithy_runtime_api::*", "aws_smithy_http::*", + "aws_smithy_types::*", "aws_smithy_client::erase::DynConnector", # TODO(audit-external-type-usage) We should newtype these or otherwise avoid exposing them "http::header::name::HeaderName", diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index 25c86c7788..d477ce9eae 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -9,3 +9,9 @@ pub mod orchestrator; /// Smithy connector runtime plugins pub mod connections; + +/// Smithy code related to retry handling and token buckets. +/// +/// This code defines when and how failed requests should be retried. It also defines the behavior +/// used to limit the rate at which requests are sent. +pub mod retries; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index dff37ec7b0..adba3dfb04 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -13,6 +13,7 @@ use aws_smithy_runtime_api::client::interceptors::{InterceptorContext, Intercept use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, HttpRequest, HttpResponse, }; +use aws_smithy_runtime_api::client::retries::ShouldAttempt; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; use aws_smithy_runtime_api::config_bag::ConfigBag; use tracing::{debug_span, Instrument}; @@ -61,9 +62,18 @@ pub async fn invoke( let retry_strategy = cfg.retry_strategy(); match retry_strategy.should_attempt_initial_request(cfg) { // Yes, let's make a request - Ok(_) => {} + Ok(ShouldAttempt::Yes) => {} + // No, this request shouldn't be sent + Ok(ShouldAttempt::No) => { + return Err(Phase::dispatch(context).fail( + "The retry strategy indicates that an initial request shouldn't be made, but it didn't specify why.", + )) + } // No, we shouldn't make a request because... Err(err) => return Err(Phase::dispatch(context).fail(err)), + Ok(ShouldAttempt::YesAfterDelay(_)) => { + unreachable!("Delaying the initial request is currently unsupported. If this feature is important to you, please file an issue in GitHub.") + } } } @@ -79,9 +89,12 @@ pub async fn invoke( let retry_strategy = cfg.retry_strategy(); match retry_strategy.should_attempt_retry(&context, cfg) { // Yes, let's retry the request - Ok(true) => continue, + Ok(ShouldAttempt::Yes) => continue, // No, this request shouldn't be retried - Ok(false) => {} + Ok(ShouldAttempt::No) => {} + Ok(ShouldAttempt::YesAfterDelay(_delay)) => { + todo!("implement retries with an explicit delay.") + } // I couldn't determine if the request should be retried because an error occurred. Err(err) => { return Err(Phase::response_handling(context).fail(err)); diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries.rs b/rust-runtime/aws-smithy-runtime/src/client/retries.rs new file mode 100644 index 0000000000..f8bbe70060 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries.rs @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +pub mod classifier; +pub mod strategy; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs new file mode 100644 index 0000000000..64960707ad --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs @@ -0,0 +1,195 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::result::SdkError; +use aws_smithy_runtime_api::client::retries::RetryReason; +use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; +use std::borrow::Cow; + +/// A retry classifier for checking if an error is modeled as retryable. +#[derive(Debug)] +pub struct ModeledAsRetryableClassifier; + +impl ModeledAsRetryableClassifier { + /// Check if an error is modeled as retryable, returning a [`RetryReason::Error`] if it is. + pub fn classify_error( + &self, + error: &SdkError, + ) -> Option { + match error { + SdkError::ServiceError(inner) => { + inner.err().retryable_error_kind().map(RetryReason::Error) + } + _ => None, + } + } +} + +#[derive(Debug)] +pub struct SmithyErrorClassifier; + +impl SmithyErrorClassifier { + pub fn classify_error(&self, result: &SdkError) -> Option { + match result { + SdkError::TimeoutError(_err) => Some(RetryReason::Error(ErrorKind::TransientError)), + SdkError::ResponseError { .. } => Some(RetryReason::Error(ErrorKind::TransientError)), + SdkError::DispatchFailure(err) if (err.is_timeout() || err.is_io()) => { + Some(RetryReason::Error(ErrorKind::TransientError)) + } + SdkError::DispatchFailure(err) => err.is_other().map(RetryReason::Error), + _ => None, + } + } +} + +const TRANSIENT_ERROR_STATUS_CODES: &[u16] = &[500, 502, 503, 504]; + +/// A retry classifier that will treat HTTP response with those status codes as retryable. +/// The `Default` version will retry 500, 502, 503, and 504 errors. +#[derive(Debug)] +pub struct HttpStatusCodeClassifier { + retryable_status_codes: Cow<'static, [u16]>, +} + +impl HttpStatusCodeClassifier { + /// Given a `Vec` where the `u16`s represent status codes, create a retry classifier that will + /// treat HTTP response with those status codes as retryable. The `Default` version will retry + /// 500, 502, 503, and 504 errors. + pub fn new_from_codes(retryable_status_codes: impl Into>) -> Self { + Self { + retryable_status_codes: retryable_status_codes.into(), + } + } + + /// Classify an HTTP response based on its status code. + pub fn classify_error(&self, error: &SdkError) -> Option { + error + .raw_response() + .map(|res| res.http().status().as_u16()) + .map(|status| self.retryable_status_codes.contains(&status)) + .unwrap_or_default() + .then_some(RetryReason::Error(ErrorKind::TransientError)) + } +} + +impl Default for HttpStatusCodeClassifier { + fn default() -> Self { + Self::new_from_codes(TRANSIENT_ERROR_STATUS_CODES.to_owned()) + } +} + +// Generic smithy clients would have something like this: +// pub fn default_retry_classifiers() -> RetryClassifiers { +// RetryClassifiers::new() +// .with_classifier(SmithyErrorClassifier::new()) +// .with_classifier(ModeledAsRetryableClassifier::new()) +// .with_classifier(HttpStatusCodeClassifier::new()) +// } +// This ordering is different than the default AWS ordering because the old generic client classifer +// was the same. + +#[cfg(test)] +mod test { + use std::fmt; + + use crate::client::retries::classifier::{ + HttpStatusCodeClassifier, ModeledAsRetryableClassifier, + }; + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::operation; + use aws_smithy_http::result::SdkError; + use aws_smithy_runtime_api::client::retries::RetryReason; + use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; + + use super::SmithyErrorClassifier; + + #[derive(Debug)] + struct UnmodeledError; + + impl fmt::Display for UnmodeledError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "UnmodeledError") + } + } + + impl std::error::Error for UnmodeledError {} + + #[test] + fn classify_by_response_status() { + let policy = HttpStatusCodeClassifier::default(); + let res = http::Response::builder() + .status(500) + .body("error!") + .unwrap() + .map(SdkBody::from); + let res = operation::Response::new(res); + let err = SdkError::service_error(UnmodeledError, res); + assert_eq!( + policy.classify_error(&err), + Some(RetryReason::Error(ErrorKind::TransientError)) + ); + } + + #[test] + fn classify_by_response_status_not_retryable() { + let policy = HttpStatusCodeClassifier::default(); + let res = http::Response::builder() + .status(408) + .body("error!") + .unwrap() + .map(SdkBody::from); + let res = operation::Response::new(res); + let err = SdkError::service_error(UnmodeledError, res); + + assert_eq!(policy.classify_error(&err), None); + } + + #[test] + fn classify_by_error_kind() { + struct ModeledRetries; + + impl ProvideErrorKind for ModeledRetries { + fn retryable_error_kind(&self) -> Option { + Some(ErrorKind::ClientError) + } + + fn code(&self) -> Option<&str> { + // code should not be called when `error_kind` is provided + unimplemented!() + } + } + + let policy = ModeledAsRetryableClassifier; + let res = http::Response::new("OK"); + let err = SdkError::service_error(ModeledRetries, res); + + assert_eq!( + policy.classify_error(&err), + Some(RetryReason::Error(ErrorKind::ClientError)), + ); + } + + #[test] + fn classify_response_error() { + let policy = SmithyErrorClassifier; + let test_response = http::Response::new("OK").map(SdkBody::from); + let err: SdkError = + SdkError::response_error(UnmodeledError, operation::Response::new(test_response)); + assert_eq!( + policy.classify_error(&err), + Some(RetryReason::Error(ErrorKind::TransientError)), + ); + } + + #[test] + fn test_timeout_error() { + let policy = SmithyErrorClassifier; + let err: SdkError = SdkError::timeout_error("blah"); + assert_eq!( + policy.classify_error(&err), + Some(RetryReason::Error(ErrorKind::TransientError)), + ); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs new file mode 100644 index 0000000000..6b7854fc2c --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs @@ -0,0 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +mod never; + +pub use never::NeverRetryStrategy; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs new file mode 100644 index 0000000000..49366a273d --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpRequest, HttpResponse}; +use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; +use aws_smithy_runtime_api::config_bag::ConfigBag; + +#[derive(Debug, Clone, Default)] +pub struct NeverRetryStrategy {} + +impl NeverRetryStrategy { + pub fn new() -> Self { + Self::default() + } +} + +impl RetryStrategy for NeverRetryStrategy { + fn should_attempt_initial_request(&self, _cfg: &ConfigBag) -> Result { + Ok(ShouldAttempt::Yes) + } + + fn should_attempt_retry( + &self, + _context: &InterceptorContext, + _cfg: &ConfigBag, + ) -> Result { + Ok(ShouldAttempt::No) + } +} diff --git a/rust-runtime/aws-smithy-types/src/error/metadata.rs b/rust-runtime/aws-smithy-types/src/error/metadata.rs index 06925e13f9..6629753733 100644 --- a/rust-runtime/aws-smithy-types/src/error/metadata.rs +++ b/rust-runtime/aws-smithy-types/src/error/metadata.rs @@ -46,6 +46,12 @@ pub struct ErrorMetadata { extras: Option>, } +impl ProvideErrorMetadata for ErrorMetadata { + fn meta(&self) -> &ErrorMetadata { + self + } +} + /// Builder for [`ErrorMetadata`]. #[derive(Debug, Default)] pub struct Builder { From b50f1e92e63d9d9de7c82f50f8b93c6276539892 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 25 Apr 2023 17:17:53 -0400 Subject: [PATCH 056/253] Update to latest models (#2625) ## Motivation and Context Update smithy-rs model snapshot ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/sdk/aws-models/config.json | 1804 ++++++++++++++---- aws/sdk/aws-models/dynamodb.json | 246 ++- aws/sdk/aws-models/ec2.json | 1153 +++++++++-- aws/sdk/aws-models/ecs.json | 350 ++-- aws/sdk/aws-models/glacier.json | 276 +-- aws/sdk/aws-models/iam.json | 1235 +++--------- aws/sdk/aws-models/kms.json | 316 +-- aws/sdk/aws-models/lambda.json | 735 +++++-- aws/sdk/aws-models/polly.json | 218 ++- aws/sdk/aws-models/qldb-session.json | 170 +- aws/sdk/aws-models/route53.json | 1219 +++--------- aws/sdk/aws-models/s3.json | 263 ++- aws/sdk/aws-models/s3control.json | 326 +++- aws/sdk/aws-models/sso.json | 597 ++---- aws/sdk/aws-models/sts.json | 350 ++-- aws/sdk/aws-models/transcribe-streaming.json | 170 +- 16 files changed, 5455 insertions(+), 3973 deletions(-) diff --git a/aws/sdk/aws-models/config.json b/aws/sdk/aws-models/config.json index 7ca3f50448..92170f2532 100644 --- a/aws/sdk/aws-models/config.json +++ b/aws/sdk/aws-models/config.json @@ -118,7 +118,7 @@ } }, "traits": { - "smithy.api#documentation": "

Indicates whether an Config rule is compliant based on\n\t\t\taccount ID, region, compliance, and rule name.

\n\t\t

A rule is compliant if all of the resources that the rule\n\t\t\tevaluated comply with it. It is noncompliant if any of these\n\t\t\tresources do not comply.

" + "smithy.api#documentation": "

Indicates whether an Config rule is compliant based on\n\t\t\taccount ID, region, compliance, and rule name.

\n

A rule is compliant if all of the resources that the rule\n\t\t\tevaluated comply with it. It is noncompliant if any of these\n\t\t\tresources do not comply.

" } }, "com.amazonaws.configservice#AggregateComplianceByConfigRuleList": { @@ -156,7 +156,7 @@ } }, "traits": { - "smithy.api#documentation": "

Provides aggregate compliance of the conformance pack. Indicates whether a conformance pack is compliant based on the name of the conformance pack, account ID, and region.

\n\t\t

A conformance pack is compliant if all of the rules in a conformance packs are compliant. It is noncompliant if any of the rules are not compliant.\n\t\t\tThe compliance status of a conformance pack is INSUFFICIENT_DATA only if all rules within a conformance pack cannot be evaluated due to insufficient data.\n\t\t\tIf some of the rules in a conformance pack are compliant but the compliance status of other rules in that same conformance pack is INSUFFICIENT_DATA, the conformance pack shows \n\t\t\tcompliant.

" + "smithy.api#documentation": "

Provides aggregate compliance of the conformance pack. Indicates whether a conformance pack is compliant based on the name of the conformance pack, account ID, and region.

\n

A conformance pack is compliant if all of the rules in a conformance packs are compliant. It is noncompliant if any of the rules are not compliant.\n\t\t\tThe compliance status of a conformance pack is INSUFFICIENT_DATA only if all rules within a conformance pack cannot be evaluated due to insufficient data.\n\t\t\tIf some of the rules in a conformance pack are compliant but the compliance status of other rules in that same conformance pack is INSUFFICIENT_DATA, the conformance pack shows \n\t\t\tcompliant.

" } }, "com.amazonaws.configservice#AggregateComplianceByConformancePackList": { @@ -223,7 +223,7 @@ } }, "traits": { - "smithy.api#documentation": "

Provides the number of compliant and noncompliant rules within a conformance pack.\n\t\t\tAlso provides the compliance status of the conformance pack and the total rule count which includes compliant rules, noncompliant rules, and rules that cannot be evaluated due to insufficient data.

\n\t\t\n\t\t

A conformance pack is compliant if all of the rules in a conformance packs are compliant. It is noncompliant if any of the rules are not compliant.\n\t\t\tThe compliance status of a conformance pack is INSUFFICIENT_DATA only if all rules within a conformance pack cannot be evaluated due to insufficient data.\n\t\t\tIf some of the rules in a conformance pack are compliant but the compliance status of other rules in that same conformance pack is INSUFFICIENT_DATA, the conformance pack shows compliant.

" + "smithy.api#documentation": "

Provides the number of compliant and noncompliant rules within a conformance pack.\n\t\t\tAlso provides the compliance status of the conformance pack and the total rule count which includes compliant rules, noncompliant rules, and rules that cannot be evaluated due to insufficient data.

\n

A conformance pack is compliant if all of the rules in a conformance packs are compliant. It is noncompliant if any of the rules are not compliant.\n\t\t\tThe compliance status of a conformance pack is INSUFFICIENT_DATA only if all rules within a conformance pack cannot be evaluated due to insufficient data.\n\t\t\tIf some of the rules in a conformance pack are compliant but the compliance status of other rules in that same conformance pack is INSUFFICIENT_DATA, the conformance pack shows compliant.

" } }, "com.amazonaws.configservice#AggregateConformancePackComplianceCount": { @@ -355,7 +355,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

The resource compliance status.

\n\t\t

For the AggregationEvaluationResult data type, Config supports only the COMPLIANT and\n\t\t\t\tNON_COMPLIANT. Config does not support the\n\t\t\t\tNOT_APPLICABLE and INSUFFICIENT_DATA\n\t\t\tvalue.

" + "smithy.api#documentation": "

The resource compliance status.

\n

For the AggregationEvaluationResult data type, Config supports only the COMPLIANT and\n\t\t\t\tNON_COMPLIANT. Config does not support the\n\t\t\t\tNOT_APPLICABLE and INSUFFICIENT_DATA\n\t\t\tvalue.

" } }, "ResultRecordedTime": { @@ -465,7 +465,7 @@ "LastUpdateStatus": { "target": "com.amazonaws.configservice#AggregatedSourceStatusType", "traits": { - "smithy.api#documentation": "

Filters the last updated status type.

\n\t\t
    \n
  • \n\t\t\t\t

    Valid value FAILED indicates errors while moving\n\t\t\t\t\tdata.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Valid value SUCCEEDED indicates the data was\n\t\t\t\t\tsuccessfully moved.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Valid value OUTDATED indicates the data is not the most\n\t\t\t\t\trecent.

    \n\t\t\t
  • \n
" + "smithy.api#documentation": "

Filters the last updated status type.

\n
    \n
  • \n

    Valid value FAILED indicates errors while moving\n\t\t\t\t\tdata.

    \n
  • \n
  • \n

    Valid value SUCCEEDED indicates the data was\n\t\t\t\t\tsuccessfully moved.

    \n
  • \n
  • \n

    Valid value OUTDATED indicates the data is not the most\n\t\t\t\t\trecent.

    \n
  • \n
" } }, "LastUpdateTime": { @@ -675,7 +675,7 @@ "configurationItemStatus": { "target": "com.amazonaws.configservice#ConfigurationItemStatus", "traits": { - "smithy.api#documentation": "

The configuration item status. The valid values are:

\n\t\t\n\t\t
    \n
  • \n

    OK – The resource configuration has been updated

    \n
  • \n
  • \n

    ResourceDiscovered – The resource was newly discovered

    \n
  • \n
  • \n

    ResourceNotRecorded – The resource was discovered but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
  • \n

    ResourceDeleted – The resource was deleted

    \n
  • \n
  • \n

    ResourceDeletedNotRecorded – The resource was deleted but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
\n\t\t \n

The CIs do not incur any cost.

\n
" + "smithy.api#documentation": "

The configuration item status. The valid values are:

\n
    \n
  • \n

    OK – The resource configuration has been updated

    \n
  • \n
  • \n

    ResourceDiscovered – The resource was newly discovered

    \n
  • \n
  • \n

    ResourceNotRecorded – The resource was discovered but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
  • \n

    ResourceDeleted – The resource was deleted

    \n
  • \n
  • \n

    ResourceDeletedNotRecorded – The resource was deleted but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
\n \n

The CIs do not incur any cost.

\n
" } }, "configurationStateId": { @@ -775,7 +775,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the current configuration items for resources that are present in your Config aggregator. The operation also returns a list of resources that are not processed in the current request. \n\t\t\tIf there are no unprocessed resources, the operation returns an empty unprocessedResourceIdentifiers list.

\n\t\t\n\t\t \n
    \n
  • \n

    The API does not return results for deleted resources.

    \n
  • \n
  • \n

    The API does not return tags and relationships.

    \n
  • \n
\n
" + "smithy.api#documentation": "

Returns the current configuration items for resources that are present in your Config aggregator. The operation also returns a list of resources that are not processed in the current request. \n\t\t\tIf there are no unprocessed resources, the operation returns an empty unprocessedResourceIdentifiers list.

\n \n
    \n
  • \n

    The API does not return results for deleted resources.

    \n
  • \n
  • \n

    The API does not return tags and relationships.

    \n
  • \n
\n
" } }, "com.amazonaws.configservice#BatchGetAggregateResourceConfigRequest": { @@ -795,6 +795,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#BatchGetAggregateResourceConfigResponse": { @@ -812,6 +815,9 @@ "smithy.api#documentation": "

A list of resource identifiers that were not processed with current scope. The list is empty if all the resources are processed.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#BatchGetResourceConfig": { @@ -831,7 +837,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the BaseConfigurationItem for one or more requested resources.\n\t\t\tThe operation also returns a list of resources that are\n\t\t\tnot processed in the current request. If there are no unprocessed\n\t\t\tresources, the operation returns an empty unprocessedResourceKeys\n\t\t\tlist.

\n\t\t \n\t\t\t
    \n
  • \n\t\t\t\t\t

    The API does not return results for deleted\n\t\t\t\t\t\tresources.

    \n\t\t\t\t
  • \n
  • \n\t\t\t\t\t

    The API does not return any tags for the requested\n\t\t\t\t\t\tresources. This information is filtered out of the\n\t\t\t\t\t\tsupplementaryConfiguration section of the API\n\t\t\t\t\t\tresponse.

    \n\t\t\t\t
  • \n
\n\t\t
" + "smithy.api#documentation": "

Returns the BaseConfigurationItem for one or more requested resources.\n\t\t\tThe operation also returns a list of resources that are\n\t\t\tnot processed in the current request. If there are no unprocessed\n\t\t\tresources, the operation returns an empty unprocessedResourceKeys\n\t\t\tlist.

\n \n
    \n
  • \n

    The API does not return results for deleted\n\t\t\t\t\t\tresources.

    \n
  • \n
  • \n

    The API does not return any tags for the requested\n\t\t\t\t\t\tresources. This information is filtered out of the\n\t\t\t\t\t\tsupplementaryConfiguration section of the API\n\t\t\t\t\t\tresponse.

    \n
  • \n
\n
" } }, "com.amazonaws.configservice#BatchGetResourceConfigRequest": { @@ -844,6 +850,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#BatchGetResourceConfigResponse": { @@ -861,6 +870,9 @@ "smithy.api#documentation": "

A list of resource keys that were not processed with the\n\t\t\tcurrent response. The unprocessesResourceKeys value is in the same\n\t\t\tform as ResourceKeys, so the value can be directly provided to a\n\t\t\tsubsequent BatchGetResourceConfig operation.\n\t\t\t\n\t\t\tIf there are no unprocessed resource keys, the response contains an\n\t\t\tempty unprocessedResourceKeys list.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#Boolean": { @@ -910,7 +922,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

Indicates whether an Amazon Web Services resource or Config rule is\n\t\t\tcompliant.

\n\t\t

A resource is compliant if it complies with all of the Config rules that evaluate it. A resource is noncompliant if it does\n\t\t\tnot comply with one or more of these rules.

\n\t\t

A rule is compliant if all of the resources that the rule\n\t\t\tevaluates comply with it. A rule is noncompliant if any of these\n\t\t\tresources do not comply.

\n\t\t

Config returns the INSUFFICIENT_DATA value\n\t\t\twhen no evaluation results are available for the Amazon Web Services resource or Config rule.

\n\t\t

For the Compliance data type, Config supports\n\t\t\tonly COMPLIANT, NON_COMPLIANT, and\n\t\t\t\tINSUFFICIENT_DATA values. Config does not\n\t\t\tsupport the NOT_APPLICABLE value for the\n\t\t\t\tCompliance data type.

" + "smithy.api#documentation": "

Indicates whether an Amazon Web Services resource or Config rule is\n\t\t\tcompliant.

\n

A resource is compliant if it complies with all of the Config rules that evaluate it. A resource is noncompliant if it does\n\t\t\tnot comply with one or more of these rules.

\n

A rule is compliant if all of the resources that the rule\n\t\t\tevaluates comply with it. A rule is noncompliant if any of these\n\t\t\tresources do not comply.

\n

Config returns the INSUFFICIENT_DATA value\n\t\t\twhen no evaluation results are available for the Amazon Web Services resource or Config rule.

\n

For the Compliance data type, Config supports\n\t\t\tonly COMPLIANT, NON_COMPLIANT, and\n\t\t\t\tINSUFFICIENT_DATA values. Config does not\n\t\t\tsupport the NOT_APPLICABLE value for the\n\t\t\t\tCompliance data type.

" } }, "ComplianceContributorCount": { @@ -1186,7 +1198,7 @@ "Scope": { "target": "com.amazonaws.configservice#Scope", "traits": { - "smithy.api#documentation": "

Defines which resources can trigger an evaluation for the rule.\n\t\t\tThe scope can include one or more resource types, a combination of\n\t\t\tone resource type and one resource ID, or a combination of a tag key\n\t\t\tand value. Specify a scope to constrain the resources that can\n\t\t\ttrigger an evaluation for the rule. If you do not specify a scope,\n\t\t\tevaluations are triggered when any resource in the recording group\n\t\t\tchanges.

\n\t\t \n

The scope can be empty.

\n
" + "smithy.api#documentation": "

Defines which resources can trigger an evaluation for the rule.\n\t\t\tThe scope can include one or more resource types, a combination of\n\t\t\tone resource type and one resource ID, or a combination of a tag key\n\t\t\tand value. Specify a scope to constrain the resources that can\n\t\t\ttrigger an evaluation for the rule. If you do not specify a scope,\n\t\t\tevaluations are triggered when any resource in the recording group\n\t\t\tchanges.

\n \n

The scope can be empty.

\n
" } }, "Source": { @@ -1205,19 +1217,19 @@ "MaximumExecutionFrequency": { "target": "com.amazonaws.configservice#MaximumExecutionFrequency", "traits": { - "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations\n\t\t\tfor a rule. You can specify a value for\n\t\t\t\tMaximumExecutionFrequency when:

\n\t\t
    \n
  • \n\t\t\t\t

    This is for an Config managed rule that is triggered at\n\t\t\t\t\ta periodic frequency.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Your custom rule is triggered when Config delivers\n\t\t\t\t\tthe configuration snapshot. For more information, see ConfigSnapshotDeliveryProperties.

    \n\t\t\t
  • \n
\n\n\n\n\t\t \n\t\t\t

By default, rules with a periodic trigger are evaluated\n\t\t\t\tevery 24 hours. To change the frequency, specify a valid value\n\t\t\t\tfor the MaximumExecutionFrequency\n\t\t\t\tparameter.

\n\t\t
" + "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations\n\t\t\tfor a rule. You can specify a value for\n\t\t\t\tMaximumExecutionFrequency when:

\n
    \n
  • \n

    This is for an Config managed rule that is triggered at\n\t\t\t\t\ta periodic frequency.

    \n
  • \n
  • \n

    Your custom rule is triggered when Config delivers\n\t\t\t\t\tthe configuration snapshot. For more information, see ConfigSnapshotDeliveryProperties.

    \n
  • \n
\n \n

By default, rules with a periodic trigger are evaluated\n\t\t\t\tevery 24 hours. To change the frequency, specify a valid value\n\t\t\t\tfor the MaximumExecutionFrequency\n\t\t\t\tparameter.

\n
" } }, "ConfigRuleState": { "target": "com.amazonaws.configservice#ConfigRuleState", "traits": { - "smithy.api#documentation": "

Indicates whether the Config rule is active or is currently\n\t\t\tbeing deleted by Config. It can also indicate the evaluation\n\t\t\tstatus for the Config rule.

\n\n\t\t

Config sets the state of the rule to\n\t\t\t\tEVALUATING temporarily after you use the\n\t\t\t\tStartConfigRulesEvaluation request to evaluate your\n\t\t\tresources against the Config rule.

\n\n\t\t

Config sets the state of the rule to\n\t\t\t\tDELETING_RESULTS temporarily after you use the\n\t\t\t\tDeleteEvaluationResults request to delete the\n\t\t\tcurrent evaluation results for the Config rule.

\n\n\t\t

Config temporarily sets the state of a rule to\n\t\t\t\tDELETING after you use the\n\t\t\t\tDeleteConfigRule request to delete the rule. After\n\t\t\tConfig deletes the rule, the rule and all of its evaluations are\n\t\t\terased and are no longer available.

" + "smithy.api#documentation": "

Indicates whether the Config rule is active or is currently\n\t\t\tbeing deleted by Config. It can also indicate the evaluation\n\t\t\tstatus for the Config rule.

\n

Config sets the state of the rule to\n\t\t\t\tEVALUATING temporarily after you use the\n\t\t\t\tStartConfigRulesEvaluation request to evaluate your\n\t\t\tresources against the Config rule.

\n

Config sets the state of the rule to\n\t\t\t\tDELETING_RESULTS temporarily after you use the\n\t\t\t\tDeleteEvaluationResults request to delete the\n\t\t\tcurrent evaluation results for the Config rule.

\n

Config temporarily sets the state of a rule to\n\t\t\t\tDELETING after you use the\n\t\t\t\tDeleteConfigRule request to delete the rule. After\n\t\t\tConfig deletes the rule, the rule and all of its evaluations are\n\t\t\terased and are no longer available.

" } }, "CreatedBy": { "target": "com.amazonaws.configservice#StringWithCharLimit256", "traits": { - "smithy.api#documentation": "

Service principal name of the service that created the\n\t\t\trule.

\n\t\t \n\t\t\t

The field is populated only if the service-linked rule is\n\t\t\t\tcreated by a service. The field is empty if you create your own\n\t\t\t\trule.

\n\t\t
" + "smithy.api#documentation": "

Service principal name of the service that created the\n\t\t\trule.

\n \n

The field is populated only if the service-linked rule is\n\t\t\t\tcreated by a service. The field is empty if you create your own\n\t\t\t\trule.

\n
" } }, "EvaluationModes": { @@ -1228,7 +1240,7 @@ } }, "traits": { - "smithy.api#documentation": "

Config rules evaluate the configuration settings of your Amazon Web Services resources. A rule can run when Config detects a configuration change to\n\t\t\tan Amazon Web Services resource or at a periodic frequency that you choose (for\n\t\t\texample, every 24 hours). There are two types of rules: Config Managed Rules and Config Custom Rules.\n\t\t\tManaged rules are predefined, customizable rules created by Config. For a list of managed rules, see\n\t\t\t\tList of Config\n\t\t\t\t\tManaged Rules.

\n\t\t\n\t\t

Custom rules are rules that you can create using either Guard or Lambda functions.\n\t\t\tGuard (Guard GitHub\n\t\t\t\tRepository) is a policy-as-code language that allows you to write policies that\n\t\t\tare enforced by Config Custom Policy rules. Lambda uses custom code that you upload to\n\t\t\tevaluate a custom rule. It is invoked by events that are published to it by an event source, which Config invokes when the custom rule is initiated.

\n\t\t\n\t\t

For more information about developing and using Config\n\t\t\trules, see Evaluating Amazon Web Services resource Configurations with Config\n\t\t\tin the Config Developer Guide.

\n\n\t\t \n\t\t\t

You can use the Amazon Web Services CLI and Amazon Web Services SDKs if you want to create\n\t\t\t\ta rule that triggers evaluations for your resources when Config delivers the configuration snapshot. For more\n\t\t\t\tinformation, see ConfigSnapshotDeliveryProperties.

\n\t\t
" + "smithy.api#documentation": "

Config rules evaluate the configuration settings of your Amazon Web Services resources. A rule can run when Config detects a configuration change to\n\t\t\tan Amazon Web Services resource or at a periodic frequency that you choose (for\n\t\t\texample, every 24 hours). There are two types of rules: Config Managed Rules and Config Custom Rules.

\n

Config Managed Rules are predefined,\n\t\t\t\tcustomizable rules created by Config. For a list of managed rules, see\n\t\t\t\tList of Config\n\t\t\t\t\tManaged Rules.

\n

Config Custom Rules are rules that you create from scratch. There are two ways to create Config custom rules: with Lambda functions\n\t\t\t\t( Lambda Developer Guide) and with Guard (Guard GitHub\n\t\t\t\t\t\tRepository), a policy-as-code language.\n\t\t\t\t\n\t\t\t\tConfig custom rules created with Lambda\n\t\t\t\tare called Config Custom Lambda Rules and Config custom rules created with\n\t\t\t\tGuard are called Config Custom Policy Rules.

\n

For more information about developing and using Config\n\t\t\trules, see Evaluating Resource with Config Rules\n\t\t\tin the Config Developer Guide.

\n \n

You can use the Amazon Web Services CLI and Amazon Web Services SDKs if you want to create\n\t\t\t\ta rule that triggers evaluations for your resources when Config delivers the configuration snapshot. For more\n\t\t\t\tinformation, see ConfigSnapshotDeliveryProperties.

\n
" } }, "com.amazonaws.configservice#ConfigRuleComplianceFilters": { @@ -1243,7 +1255,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

The rule compliance status.

\n\t\t

For the ConfigRuleComplianceFilters data type, Config supports only COMPLIANT and\n\t\t\t\tNON_COMPLIANT. Config does not support the\n\t\t\t\tNOT_APPLICABLE and the\n\t\t\t\tINSUFFICIENT_DATA values.

" + "smithy.api#documentation": "

The rule compliance status.

\n

For the ConfigRuleComplianceFilters data type, Config supports only COMPLIANT and\n\t\t\t\tNON_COMPLIANT. Config does not support the\n\t\t\t\tNOT_APPLICABLE and the\n\t\t\t\tINSUFFICIENT_DATA values.

" } }, "AccountId": { @@ -1373,7 +1385,7 @@ "target": "com.amazonaws.configservice#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether Config has evaluated your resources\n\t\t\tagainst the rule at least once.

\n\t\t
    \n
  • \n\t\t\t\t

    \n\t\t\t\t\t true - Config has evaluated your Amazon Web Services\n\t\t\t\t\tresources against the rule at least once.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    \n\t\t\t\t\t false - Config has not finished evaluating your Amazon Web Services resources against the\n\t\t\t\t\trule\n\t\t\t\t\tat least once.

    \n\t\t\t
  • \n
" + "smithy.api#documentation": "

Indicates whether Config has evaluated your resources\n\t\t\tagainst the rule at least once.

\n
    \n
  • \n

    \n true - Config has evaluated your Amazon Web Services\n\t\t\t\t\tresources against the rule at least once.

    \n
  • \n
  • \n

    \n false - Config has not finished evaluating your Amazon Web Services resources against the\n\t\t\t\t\trule\n\t\t\t\t\tat least once.

    \n
  • \n
" } }, "LastDebugLogDeliveryStatus": { @@ -1396,7 +1408,7 @@ } }, "traits": { - "smithy.api#documentation": "

Status information for your Config Managed rules and Config Custom Policy rules. The\n\t\t\tstatus includes information such as the last time the rule ran, the\n\t\t\tlast time it failed, and the related error for the last\n\t\t\tfailure.

\n\t\t

This action does not return status information about Config Custom Lambda rules.

" + "smithy.api#documentation": "

Status information for your Config Managed rules and Config Custom Policy rules. The\n\t\t\tstatus includes information such as the last time the rule ran, the\n\t\t\tlast time it failed, and the related error for the last\n\t\t\tfailure.

\n

This action does not return status information about Config Custom Lambda rules.

" } }, "com.amazonaws.configservice#ConfigRuleEvaluationStatusList": { @@ -1473,7 +1485,7 @@ } }, "traits": { - "smithy.api#documentation": "

Provides options for how often Config delivers\n\t\t\tconfiguration snapshots to the Amazon S3 bucket in your delivery\n\t\t\tchannel.

\n\n\t\t\n\t\t\n\n\t\t

The frequency for a rule that triggers evaluations for your\n\t\t\tresources when Config delivers the configuration snapshot is set\n\t\t\tby one of two values, depending on which is less frequent:

\n\n\t\t
    \n
  • \n\t\t\t\t

    The value for the deliveryFrequency\n\t\t\t\t\tparameter within the delivery channel configuration, which\n\t\t\t\t\tsets how often Config delivers configuration snapshots.\n\t\t\t\t\tThis value also sets how often Config invokes\n\t\t\t\t\tevaluations for Config rules.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    The value for the\n\t\t\t\t\t\tMaximumExecutionFrequency parameter, which\n\t\t\t\t\tsets the maximum frequency with which Config invokes\n\t\t\t\t\tevaluations for the rule. For more information, see ConfigRule.

    \n\t\t\t
  • \n
\n\n\t\t

If the deliveryFrequency value is less frequent\n\t\t\tthan the MaximumExecutionFrequency value for a rule,\n\t\t\tConfig invokes the rule only as often as the\n\t\t\t\tdeliveryFrequency value.

\n\n\t\t
    \n
  1. \n\t\t\t\t

    For example, you want your rule to run evaluations when\n\t\t\t\t\tConfig delivers the configuration snapshot.

    \n\t\t\t
  2. \n
  3. \n\t\t\t\t

    You specify the MaximumExecutionFrequency\n\t\t\t\t\tvalue for Six_Hours.

    \n\t\t\t
  4. \n
  5. \n\t\t\t\t

    You then specify the delivery channel\n\t\t\t\t\t\tdeliveryFrequency value for\n\t\t\t\t\t\tTwentyFour_Hours.

    \n\t\t\t
  6. \n
  7. \n\t\t\t\t

    Because the value for deliveryFrequency is\n\t\t\t\t\tless frequent than MaximumExecutionFrequency,\n\t\t\t\t\tConfig invokes evaluations for the rule every 24 hours.\n\t\t\t\t

    \n\t\t\t
  8. \n
\n\n\n\t\t

You should set the MaximumExecutionFrequency value\n\t\t\tto be at least as frequent as the deliveryFrequency\n\t\t\tvalue. You can view the deliveryFrequency value by\n\t\t\tusing the DescribeDeliveryChannnels action.

\n\n\t\t

To update the deliveryFrequency with which Config delivers your configuration snapshots, use the\n\t\t\t\tPutDeliveryChannel action.

" + "smithy.api#documentation": "

Provides options for how often Config delivers\n\t\t\tconfiguration snapshots to the Amazon S3 bucket in your delivery\n\t\t\tchannel.

\n

The frequency for a rule that triggers evaluations for your\n\t\t\tresources when Config delivers the configuration snapshot is set\n\t\t\tby one of two values, depending on which is less frequent:

\n
    \n
  • \n

    The value for the deliveryFrequency\n\t\t\t\t\tparameter within the delivery channel configuration, which\n\t\t\t\t\tsets how often Config delivers configuration snapshots.\n\t\t\t\t\tThis value also sets how often Config invokes\n\t\t\t\t\tevaluations for Config rules.

    \n
  • \n
  • \n

    The value for the\n\t\t\t\t\t\tMaximumExecutionFrequency parameter, which\n\t\t\t\t\tsets the maximum frequency with which Config invokes\n\t\t\t\t\tevaluations for the rule. For more information, see ConfigRule.

    \n
  • \n
\n

If the deliveryFrequency value is less frequent\n\t\t\tthan the MaximumExecutionFrequency value for a rule,\n\t\t\tConfig invokes the rule only as often as the\n\t\t\t\tdeliveryFrequency value.

\n
    \n
  1. \n

    For example, you want your rule to run evaluations when\n\t\t\t\t\tConfig delivers the configuration snapshot.

    \n
  2. \n
  3. \n

    You specify the MaximumExecutionFrequency\n\t\t\t\t\tvalue for Six_Hours.

    \n
  4. \n
  5. \n

    You then specify the delivery channel\n\t\t\t\t\t\tdeliveryFrequency value for\n\t\t\t\t\t\tTwentyFour_Hours.

    \n
  6. \n
  7. \n

    Because the value for deliveryFrequency is\n\t\t\t\t\tless frequent than MaximumExecutionFrequency,\n\t\t\t\t\tConfig invokes evaluations for the rule every 24 hours.\n\t\t\t\t

    \n
  8. \n
\n

You should set the MaximumExecutionFrequency value\n\t\t\tto be at least as frequent as the deliveryFrequency\n\t\t\tvalue. You can view the deliveryFrequency value by\n\t\t\tusing the DescribeDeliveryChannnels action.

\n

To update the deliveryFrequency with which Config delivers your configuration snapshots, use the\n\t\t\t\tPutDeliveryChannel action.

" } }, "com.amazonaws.configservice#ConfigStreamDeliveryInfo": { @@ -1482,7 +1494,7 @@ "lastStatus": { "target": "com.amazonaws.configservice#DeliveryStatus", "traits": { - "smithy.api#documentation": "

Status of the last attempted delivery.

\n\t\t

\n\t\t\t Note Providing an SNS topic on a\n\t\t\t\tDeliveryChannel for Config is optional. If the SNS\n\t\t\tdelivery is turned off, the last status will be Not_Applicable.

" + "smithy.api#documentation": "

Status of the last attempted delivery.

\n

\n Note Providing an SNS topic on a\n\t\t\t\tDeliveryChannel for Config is optional. If the SNS\n\t\t\tdelivery is turned off, the last status will be Not_Applicable.

" } }, "lastErrorCode": { @@ -1619,7 +1631,7 @@ "configurationItemStatus": { "target": "com.amazonaws.configservice#ConfigurationItemStatus", "traits": { - "smithy.api#documentation": "

The configuration item status. The valid values are:

\n\t\t\n\t\t
    \n
  • \n

    OK – The resource configuration has been updated

    \n
  • \n
  • \n

    ResourceDiscovered – The resource was newly discovered

    \n
  • \n
  • \n

    ResourceNotRecorded – The resource was discovered but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
  • \n

    ResourceDeleted – The resource was deleted

    \n
  • \n
  • \n

    ResourceDeletedNotRecorded – The resource was deleted but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
\n\t\t \n

The CIs do not incur any cost.

\n
" + "smithy.api#documentation": "

The configuration item status. The valid values are:

\n
    \n
  • \n

    OK – The resource configuration has been updated

    \n
  • \n
  • \n

    ResourceDiscovered – The resource was newly discovered

    \n
  • \n
  • \n

    ResourceNotRecorded – The resource was discovered but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
  • \n

    ResourceDeleted – The resource was deleted

    \n
  • \n
  • \n

    ResourceDeletedNotRecorded – The resource was deleted but its configuration was not recorded since the recorder excludes the recording of resources of this type

    \n
  • \n
\n \n

The CIs do not incur any cost.

\n
" } }, "configurationStateId": { @@ -1631,7 +1643,7 @@ "configurationItemMD5Hash": { "target": "com.amazonaws.configservice#ConfigurationItemMD5Hash", "traits": { - "smithy.api#documentation": "

Unique MD5 hash that represents the configuration item's\n\t\t\tstate.

\n\t\t

You can use MD5 hash to compare the states of two or more\n\t\t\tconfiguration items that are associated with the same\n\t\t\tresource.

" + "smithy.api#documentation": "

Unique MD5 hash that represents the configuration item's\n\t\t\tstate.

\n

You can use MD5 hash to compare the states of two or more\n\t\t\tconfiguration items that are associated with the same\n\t\t\tresource.

" } }, "arn": { @@ -1685,7 +1697,7 @@ "relatedEvents": { "target": "com.amazonaws.configservice#RelatedEventList", "traits": { - "smithy.api#documentation": "

A list of CloudTrail event IDs.

\n\t\t

A populated field indicates that the current configuration was\n\t\t\tinitiated by the events recorded in the CloudTrail log. For more\n\t\t\tinformation about CloudTrail, see What Is CloudTrail.

\n\t\t

An empty field indicates that the current configuration was not\n\t\t\tinitiated by any event. As of Version 1.3, the relatedEvents field is empty. \n\t\t\tYou can access the LookupEvents API in the CloudTrail API Reference to retrieve the events for the resource.

" + "smithy.api#documentation": "

A list of CloudTrail event IDs.

\n

A populated field indicates that the current configuration was\n\t\t\tinitiated by the events recorded in the CloudTrail log. For more\n\t\t\tinformation about CloudTrail, see What Is CloudTrail.

\n

An empty field indicates that the current configuration was not\n\t\t\tinitiated by any event. As of Version 1.3, the relatedEvents field is empty. \n\t\t\tYou can access the LookupEvents API in the CloudTrail API Reference to retrieve the events for the resource.

" } }, "relationships": { @@ -1770,7 +1782,7 @@ "roleARN": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

Amazon Resource Name (ARN) of the IAM role used to describe the\n\t\t\tAmazon Web Services resources associated with the account.

\n\t\t \n

While the API model does not require this field, the server will reject a request without a defined roleARN for the configuration recorder.

\n
" + "smithy.api#documentation": "

Amazon Resource Name (ARN) of the IAM role used to describe the\n\t\t\tAmazon Web Services resources associated with the account.

\n \n

While the API model does not require this field, the server will reject a request without a defined roleARN for the configuration recorder.

\n
" } }, "recordingGroup": { @@ -1827,30 +1839,30 @@ "lastStatus": { "target": "com.amazonaws.configservice#RecorderStatus", "traits": { - "smithy.api#documentation": "

The last (previous) status of the recorder.

" + "smithy.api#documentation": "

The status of the latest recording event processed by the recorder.

" } }, "lastErrorCode": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

The error code indicating that the recording failed.

" + "smithy.api#documentation": "

The latest error code from when the recorder last failed.

" } }, "lastErrorMessage": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

The message indicating that the recording failed due to an\n\t\t\terror.

" + "smithy.api#documentation": "

The latest error message from when the recorder last failed.

" } }, "lastStatusChangeTime": { "target": "com.amazonaws.configservice#Date", "traits": { - "smithy.api#documentation": "

The time when the status was last changed.

" + "smithy.api#documentation": "

The time of the latest change in status of an recording event processed by the recorder.

" } } }, "traits": { - "smithy.api#documentation": "

The current status of the configuration recorder.

" + "smithy.api#documentation": "

The current status of the configuration recorder.

\n \n

For a detailed status of recording events over time, add your Config events to CloudWatch metrics and use CloudWatch metrics.

\n
" } }, "com.amazonaws.configservice#ConfigurationRecorderStatusList": { @@ -1883,7 +1895,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ConformancePackComplianceType", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT and NON_COMPLIANT. INSUFFICIENT_DATA is not supported.

" + "smithy.api#documentation": "

Filters the results by compliance.

\n

The allowed values are COMPLIANT and NON_COMPLIANT. INSUFFICIENT_DATA is not supported.

" } } }, @@ -1963,7 +1975,7 @@ "ConformancePackComplianceStatus": { "target": "com.amazonaws.configservice#ConformancePackComplianceType", "traits": { - "smithy.api#documentation": "

The status of the conformance pack. The allowed values are COMPLIANT, NON_COMPLIANT and INSUFFICIENT_DATA.

", + "smithy.api#documentation": "

The status of the conformance pack.

", "smithy.api#required": {} } } @@ -2046,13 +2058,13 @@ "DeliveryS3Bucket": { "target": "com.amazonaws.configservice#DeliveryS3Bucket", "traits": { - "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n \n

This field is optional.

\n
" } }, "DeliveryS3KeyPrefix": { "target": "com.amazonaws.configservice#DeliveryS3KeyPrefix", "traits": { - "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n \n

This field is optional.

\n
" } }, "ConformancePackInputParameters": { @@ -2108,7 +2120,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ConformancePackComplianceType", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT and NON_COMPLIANT. INSUFFICIENT_DATA is not supported.

" + "smithy.api#documentation": "

Filters the results by compliance.

\n

The allowed values are COMPLIANT and NON_COMPLIANT. INSUFFICIENT_DATA is not supported.

" } }, "ResourceType": { @@ -2120,7 +2132,7 @@ "ResourceIds": { "target": "com.amazonaws.configservice#ConformancePackComplianceResourceIds", "traits": { - "smithy.api#documentation": "

Filters the results by resource IDs.

\n\t\t \n

This is valid only when you provide resource type. If there is no resource type, you will see an error.

\n
" + "smithy.api#documentation": "

Filters the results by resource IDs.

\n \n

This is valid only when you provide resource type. If there is no resource type, you will see an error.

\n
" } } }, @@ -2270,7 +2282,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ConformancePackComplianceType", "traits": { - "smithy.api#documentation": "

Compliance of the Config rule.

\n\t\t

The allowed values are COMPLIANT, NON_COMPLIANT, and INSUFFICIENT_DATA.

" + "smithy.api#documentation": "

Compliance of the Config rule.

" } }, "Controls": { @@ -2370,7 +2382,7 @@ "ConformancePackState": { "target": "com.amazonaws.configservice#ConformancePackState", "traits": { - "smithy.api#documentation": "

Indicates deployment status of conformance pack.

\n\t\t

Config sets the state of the conformance pack to:

\n\t\t
    \n
  • \n

    CREATE_IN_PROGRESS when a conformance pack creation is in progress for an account.

    \n
  • \n
  • \n

    CREATE_COMPLETE when a conformance pack has been successfully created in your account.

    \n
  • \n
  • \n

    CREATE_FAILED when a conformance pack creation failed in your account.

    \n
  • \n
  • \n

    DELETE_IN_PROGRESS when a conformance pack deletion is in progress.

    \n
  • \n
  • \n

    DELETE_FAILED when a conformance pack deletion failed in your account.

    \n
  • \n
", + "smithy.api#documentation": "

Indicates deployment status of conformance pack.

\n

Config sets the state of the conformance pack to:

\n
    \n
  • \n

    CREATE_IN_PROGRESS when a conformance pack creation is in progress for an account.

    \n
  • \n
  • \n

    CREATE_COMPLETE when a conformance pack has been successfully created in your account.

    \n
  • \n
  • \n

    CREATE_FAILED when a conformance pack creation failed in your account.

    \n
  • \n
  • \n

    DELETE_IN_PROGRESS when a conformance pack deletion is in progress.

    \n
  • \n
  • \n

    DELETE_FAILED when a conformance pack deletion failed in your account.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -2437,7 +2449,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have specified a template that is invalid or supported.

", + "smithy.api#documentation": "

You have specified a template that is not valid or supported.

", "smithy.api#error": "client" } }, @@ -2541,6 +2553,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteConfigRule": { @@ -2560,7 +2575,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified Config rule and all of its evaluation\n\t\t\tresults.

\n\t\t

Config sets the state of a rule to DELETING\n\t\t\tuntil the deletion is complete. You cannot update a rule while it is\n\t\t\tin this state. If you make a PutConfigRule or\n\t\t\t\tDeleteConfigRule request for the rule, you will\n\t\t\treceive a ResourceInUseException.

\n\t\t

You can check the state of a rule by using the\n\t\t\t\tDescribeConfigRules request.

" + "smithy.api#documentation": "

Deletes the specified Config rule and all of its evaluation\n\t\t\tresults.

\n

Config sets the state of a rule to DELETING\n\t\t\tuntil the deletion is complete. You cannot update a rule while it is\n\t\t\tin this state. If you make a PutConfigRule or\n\t\t\t\tDeleteConfigRule request for the rule, you will\n\t\t\treceive a ResourceInUseException.

\n

You can check the state of a rule by using the\n\t\t\t\tDescribeConfigRules request.

" } }, "com.amazonaws.configservice#DeleteConfigRuleRequest": { @@ -2575,7 +2590,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteConfigurationAggregator": { @@ -2605,6 +2621,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteConfigurationRecorder": { @@ -2621,7 +2640,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the configuration recorder.

\n\t\t

After the configuration recorder is deleted, Config will\n\t\t\tnot record resource configuration changes until you create a new\n\t\t\tconfiguration recorder.

\n\t\t

This action does not delete the configuration information that\n\t\t\twas previously recorded. You will be able to access the previously\n\t\t\trecorded information by using the\n\t\t\t\tGetResourceConfigHistory action, but you will not\n\t\t\tbe able to access this information in the Config console until\n\t\t\tyou create a new configuration recorder.

" + "smithy.api#documentation": "

Deletes the configuration recorder.

\n

After the configuration recorder is deleted, Config will\n\t\t\tnot record resource configuration changes until you create a new\n\t\t\tconfiguration recorder.

\n

This action does not delete the configuration information that\n\t\t\twas previously recorded. You will be able to access the previously\n\t\t\trecorded information by using the\n\t\t\t\tGetResourceConfigHistory action, but you will not\n\t\t\tbe able to access this information in the Config console until\n\t\t\tyou create a new configuration recorder.

" } }, "com.amazonaws.configservice#DeleteConfigurationRecorderRequest": { @@ -2636,7 +2655,8 @@ } }, "traits": { - "smithy.api#documentation": "

The request object for the\n\t\t\t\tDeleteConfigurationRecorder action.

" + "smithy.api#documentation": "

The request object for the\n\t\t\t\tDeleteConfigurationRecorder action.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteConformancePack": { @@ -2656,7 +2676,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified conformance pack and all the Config rules, remediation actions, and all evaluation results within that \n\t\t\tconformance pack.

\n\t\t

Config sets the conformance pack to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\tYou cannot update a conformance pack while it is in this state.

" + "smithy.api#documentation": "

Deletes the specified conformance pack and all the Config rules, remediation actions, and all evaluation results within that \n\t\t\tconformance pack.

\n

Config sets the conformance pack to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\tYou cannot update a conformance pack while it is in this state.

" } }, "com.amazonaws.configservice#DeleteConformancePackRequest": { @@ -2669,6 +2689,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteDeliveryChannel": { @@ -2688,7 +2711,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the delivery channel.

\n\t\t

Before you can delete the delivery channel, you must stop the\n\t\t\tconfiguration recorder by using the StopConfigurationRecorder action.

" + "smithy.api#documentation": "

Deletes the delivery channel.

\n

Before you can delete the delivery channel, you must stop the\n\t\t\tconfiguration recorder by using the StopConfigurationRecorder action.

" } }, "com.amazonaws.configservice#DeleteDeliveryChannelRequest": { @@ -2703,7 +2726,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DeleteDeliveryChannel\n\t\t\taction. The action accepts the following data, in JSON format.\n\t\t

" + "smithy.api#documentation": "

The input for the DeleteDeliveryChannel\n\t\t\taction. The action accepts the following data, in JSON format.\n\t\t

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteEvaluationResults": { @@ -2738,14 +2762,16 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteEvaluationResultsResponse": { "type": "structure", "members": {}, "traits": { - "smithy.api#documentation": "

The output when you delete the evaluation results for the\n\t\t\tspecified Config rule.

" + "smithy.api#documentation": "

The output when you delete the evaluation results for the\n\t\t\tspecified Config rule.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DeleteOrganizationConfigRule": { @@ -2768,7 +2794,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified organization Config rule and all of its evaluation results from all member accounts in that organization.

\n\t

Only a management account and a delegated administrator account can delete an organization Config rule.\n\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\t\tListDelegatedAdministrator permissions are added.

\n\t\t

Config sets the state of a rule to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\tYou cannot update a rule while it is in this state.

" + "smithy.api#documentation": "

Deletes the specified organization Config rule and all of its evaluation results from all member accounts in that organization.

\n

Only a management account and a delegated administrator account can delete an organization Config rule.\n\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\t\tListDelegatedAdministrator permissions are added.

\n

Config sets the state of a rule to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\tYou cannot update a rule while it is in this state.

" } }, "com.amazonaws.configservice#DeleteOrganizationConfigRuleRequest": { @@ -2781,6 +2807,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteOrganizationConformancePack": { @@ -2803,7 +2832,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified organization conformance pack and all of the Config rules and remediation actions from\n\t\t\tall member accounts in that organization.

\n

Only a management account or a delegated administrator account can delete an organization conformance pack.\n\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\tListDelegatedAdministrator permissions are added.

\n\t\t\t

Config sets the state of a conformance pack to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\t\tYou cannot update a conformance pack while it is in this state.

" + "smithy.api#documentation": "

Deletes the specified organization conformance pack and all of the Config rules and remediation actions from\n\t\t\tall member accounts in that organization.

\n

Only a management account or a delegated administrator account can delete an organization conformance pack.\n\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\tListDelegatedAdministrator permissions are added.

\n

Config sets the state of a conformance pack to DELETE_IN_PROGRESS until the deletion is complete. \n\t\t\t\tYou cannot update a conformance pack while it is in this state.

" } }, "com.amazonaws.configservice#DeleteOrganizationConformancePackRequest": { @@ -2816,6 +2845,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeletePendingAggregationRequest": { @@ -2852,6 +2884,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteRemediationConfiguration": { @@ -2896,11 +2931,17 @@ "smithy.api#documentation": "

The type of a resource.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteRemediationConfigurationResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.configservice#DeleteRemediationExceptions": { "type": "operation", @@ -2916,7 +2957,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes one or more remediation exceptions mentioned in the resource keys.

\n\t\t \n

Config generates a remediation exception when a problem occurs executing a remediation action to a specific resource. \n\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n
" + "smithy.api#documentation": "

Deletes one or more remediation exceptions mentioned in the resource keys.

\n \n

Config generates a remediation exception when a problem occurs executing a remediation action to a specific resource. \n\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n
" } }, "com.amazonaws.configservice#DeleteRemediationExceptionsRequest": { @@ -2936,6 +2977,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteRemediationExceptionsResponse": { @@ -2947,6 +2991,9 @@ "smithy.api#documentation": "

Returns a list of failed delete remediation exceptions batch objects. Each object in the batch consists of a list of failed items and failure messages.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DeleteResourceConfig": { @@ -2986,6 +3033,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteRetentionConfiguration": { @@ -3018,6 +3068,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteStoredQuery": { @@ -3050,11 +3103,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeleteStoredQueryResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.configservice#DeliverConfigSnapshot": { "type": "operation", @@ -3076,7 +3135,7 @@ } ], "traits": { - "smithy.api#documentation": "

Schedules delivery of a configuration snapshot to the Amazon S3\n\t\t\tbucket in the specified delivery channel. After the delivery has\n\t\t\tstarted, Config sends the following notifications using an\n\t\t\tAmazon SNS topic that you have specified.

\n\t\t
    \n
  • \n\t\t\t\t

    Notification of the start of the delivery.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Notification of the completion of the delivery, if the\n\t\t\t\t\tdelivery was successfully completed.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Notification of delivery failure, if the delivery\n\t\t\t\t\tfailed.

    \n\t\t\t
  • \n
" + "smithy.api#documentation": "

Schedules delivery of a configuration snapshot to the Amazon S3\n\t\t\tbucket in the specified delivery channel. After the delivery has\n\t\t\tstarted, Config sends the following notifications using an\n\t\t\tAmazon SNS topic that you have specified.

\n
    \n
  • \n

    Notification of the start of the delivery.

    \n
  • \n
  • \n

    Notification of the completion of the delivery, if the\n\t\t\t\t\tdelivery was successfully completed.

    \n
  • \n
  • \n

    Notification of delivery failure, if the delivery\n\t\t\t\t\tfailed.

    \n
  • \n
" } }, "com.amazonaws.configservice#DeliverConfigSnapshotRequest": { @@ -3091,7 +3150,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DeliverConfigSnapshot\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the DeliverConfigSnapshot\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DeliverConfigSnapshotResponse": { @@ -3105,7 +3165,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the DeliverConfigSnapshot\n\t\t\taction, in JSON format.

" + "smithy.api#documentation": "

The output for the DeliverConfigSnapshot\n\t\t\taction, in JSON format.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DeliveryChannel": { @@ -3120,7 +3181,7 @@ "s3BucketName": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

The name of the Amazon S3 bucket to which Config delivers\n\t\t\tconfiguration snapshots and configuration history files.

\n\t\t

If you specify a bucket that belongs to another Amazon Web Services account,\n\t\t\tthat bucket must have policies that grant access permissions to Config. For more information, see Permissions for the Amazon S3 Bucket in the Config\n\t\t\tDeveloper Guide.

" + "smithy.api#documentation": "

The name of the Amazon S3 bucket to which Config delivers\n\t\t\tconfiguration snapshots and configuration history files.

\n

If you specify a bucket that belongs to another Amazon Web Services account,\n\t\t\tthat bucket must have policies that grant access permissions to Config. For more information, see Permissions for the Amazon S3 Bucket in the Config\n\t\t\tDeveloper Guide.

" } }, "s3KeyPrefix": { @@ -3138,7 +3199,7 @@ "snsTopicARN": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the Amazon SNS topic to which\n\t\t\tConfig sends notifications about configuration\n\t\t\tchanges.

\n\t\t

If you choose a topic from another account, the topic must have\n\t\t\tpolicies that grant access permissions to Config. For more\n\t\t\tinformation, see Permissions for the Amazon SNS Topic in the Config\n\t\t\tDeveloper Guide.

" + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the Amazon SNS topic to which\n\t\t\tConfig sends notifications about configuration\n\t\t\tchanges.

\n

If you choose a topic from another account, the topic must have\n\t\t\tpolicies that grant access permissions to Config. For more\n\t\t\tinformation, see Permissions for the Amazon SNS Topic in the Config\n\t\t\tDeveloper Guide.

" } }, "configSnapshotDeliveryProperties": { @@ -3193,7 +3254,7 @@ } }, "traits": { - "smithy.api#documentation": "

The status of a specified delivery channel.

\n\t\t

Valid values: Success | Failure\n\t\t

" + "smithy.api#documentation": "

The status of a specified delivery channel.

\n

Valid values: Success | Failure\n

" } }, "com.amazonaws.configservice#DeliveryChannelStatusList": { @@ -3266,7 +3327,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of compliant and noncompliant rules with the\n\t\t\tnumber of resources for compliant and noncompliant rules. Does not display rules that do not have compliance results. \n\t\t\t

\n\t\t \n\t\t\t

The results can return an empty result page, but if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n\t\t
", + "smithy.api#documentation": "

Returns a list of compliant and noncompliant rules with the\n\t\t\tnumber of resources for compliant and noncompliant rules. Does not display rules that do not have compliance results. \n\t\t\t

\n \n

The results can return an empty result page, but if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -3303,6 +3364,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeAggregateComplianceByConfigRulesResponse": { @@ -3320,6 +3384,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeAggregateComplianceByConformancePacks": { @@ -3345,7 +3412,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of the conformance packs and their associated compliance status with the count of compliant and noncompliant Config rules within each \n\t\t\tconformance pack. Also returns the total rule count which includes compliant rules, noncompliant rules, and rules that cannot be evaluated due to insufficient data.

\n\t\t \n

The results can return an empty result page, but if you have a nextToken, the results are displayed on the next page.

\n
", + "smithy.api#documentation": "

Returns a list of the conformance packs and their associated compliance status with the count of compliant and noncompliant Config rules within each \n\t\t\tconformance pack. Also returns the total rule count which includes compliant rules, noncompliant rules, and rules that cannot be evaluated due to insufficient data.

\n \n

The results can return an empty result page, but if you have a nextToken, the results are displayed on the next page.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -3383,6 +3450,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeAggregateComplianceByConformancePacksResponse": { @@ -3400,6 +3470,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeAggregationAuthorizations": { @@ -3447,6 +3520,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeAggregationAuthorizationsResponse": { @@ -3464,6 +3540,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeComplianceByConfigRule": { @@ -3486,7 +3565,7 @@ } ], "traits": { - "smithy.api#documentation": "

Indicates whether the specified Config rules are compliant.\n\t\t\tIf a rule is noncompliant, this action returns the number of Amazon Web Services\n\t\t\tresources that do not comply with the rule.

\n\t\t

A rule is compliant if all of the evaluated resources comply\n\t\t\twith it. It is noncompliant if any of these resources do not\n\t\t\tcomply.

\n\t\t

If Config has no current evaluation results for the rule,\n\t\t\tit returns INSUFFICIENT_DATA. This result might\n\t\t\tindicate one of the following conditions:

\n\t\t
    \n
  • \n\t\t\t\t

    Config has never invoked an evaluation for the\n\t\t\t\t\trule. To check whether it has, use the\n\t\t\t\t\t\tDescribeConfigRuleEvaluationStatus action\n\t\t\t\t\tto get the LastSuccessfulInvocationTime and\n\t\t\t\t\t\tLastFailedInvocationTime.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    The rule's Lambda function is failing to send\n\t\t\t\t\tevaluation results to Config. Verify that the role you\n\t\t\t\t\tassigned to your configuration recorder includes the\n\t\t\t\t\t\tconfig:PutEvaluations permission. If the\n\t\t\t\t\trule is a custom rule, verify that the Lambda execution\n\t\t\t\t\trole includes the config:PutEvaluations\n\t\t\t\t\tpermission.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    The rule's Lambda function has returned\n\t\t\t\t\t\tNOT_APPLICABLE for all evaluation results.\n\t\t\t\t\tThis can occur if the resources were deleted or removed from\n\t\t\t\t\tthe rule's scope.

    \n\t\t\t
  • \n
", + "smithy.api#documentation": "

Indicates whether the specified Config rules are compliant.\n\t\t\tIf a rule is noncompliant, this action returns the number of Amazon Web Services\n\t\t\tresources that do not comply with the rule.

\n

A rule is compliant if all of the evaluated resources comply\n\t\t\twith it. It is noncompliant if any of these resources do not\n\t\t\tcomply.

\n

If Config has no current evaluation results for the rule,\n\t\t\tit returns INSUFFICIENT_DATA. This result might\n\t\t\tindicate one of the following conditions:

\n
    \n
  • \n

    Config has never invoked an evaluation for the\n\t\t\t\t\trule. To check whether it has, use the\n\t\t\t\t\t\tDescribeConfigRuleEvaluationStatus action\n\t\t\t\t\tto get the LastSuccessfulInvocationTime and\n\t\t\t\t\t\tLastFailedInvocationTime.

    \n
  • \n
  • \n

    The rule's Lambda function is failing to send\n\t\t\t\t\tevaluation results to Config. Verify that the role you\n\t\t\t\t\tassigned to your configuration recorder includes the\n\t\t\t\t\t\tconfig:PutEvaluations permission. If the\n\t\t\t\t\trule is a custom rule, verify that the Lambda execution\n\t\t\t\t\trole includes the config:PutEvaluations\n\t\t\t\t\tpermission.

    \n
  • \n
  • \n

    The rule's Lambda function has returned\n\t\t\t\t\t\tNOT_APPLICABLE for all evaluation results.\n\t\t\t\t\tThis can occur if the resources were deleted or removed from\n\t\t\t\t\tthe rule's scope.

    \n
  • \n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -3506,7 +3585,7 @@ "ComplianceTypes": { "target": "com.amazonaws.configservice#ComplianceTypes", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT and NON_COMPLIANT.

" + "smithy.api#documentation": "

Filters the results by compliance.

" } }, "NextToken": { @@ -3517,7 +3596,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeComplianceByConfigRuleResponse": { @@ -3537,7 +3617,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeComplianceByResource": { @@ -3557,7 +3638,7 @@ } ], "traits": { - "smithy.api#documentation": "

Indicates whether the specified Amazon Web Services resources are compliant. If\n\t\t\ta resource is noncompliant, this action returns the number of Config rules that the resource does not comply with.

\n\t\t

A resource is compliant if it complies with all the Config\n\t\t\trules that evaluate it. It is noncompliant if it does not comply\n\t\t\twith one or more of these rules.

\n\t\t

If Config has no current evaluation results for the\n\t\t\tresource, it returns INSUFFICIENT_DATA. This result\n\t\t\tmight indicate one of the following conditions about the rules that\n\t\t\tevaluate the resource:

\n\t\t
    \n
  • \n\t\t\t\t

    Config has never invoked an evaluation for the\n\t\t\t\t\trule. To check whether it has, use the\n\t\t\t\t\t\tDescribeConfigRuleEvaluationStatus action\n\t\t\t\t\tto get the LastSuccessfulInvocationTime and\n\t\t\t\t\t\tLastFailedInvocationTime.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    The rule's Lambda function is failing to send\n\t\t\t\t\tevaluation results to Config. Verify that the role that\n\t\t\t\t\tyou assigned to your configuration recorder includes the\n\t\t\t\t\t\tconfig:PutEvaluations permission. If the\n\t\t\t\t\trule is a custom rule, verify that the Lambda execution\n\t\t\t\t\trole includes the config:PutEvaluations\n\t\t\t\t\tpermission.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    The rule's Lambda function has returned\n\t\t\t\t\t\tNOT_APPLICABLE for all evaluation results.\n\t\t\t\t\tThis can occur if the resources were deleted or removed from\n\t\t\t\t\tthe rule's scope.

    \n\t\t\t
  • \n
", + "smithy.api#documentation": "

Indicates whether the specified Amazon Web Services resources are compliant. If\n\t\t\ta resource is noncompliant, this action returns the number of Config rules that the resource does not comply with.

\n

A resource is compliant if it complies with all the Config\n\t\t\trules that evaluate it. It is noncompliant if it does not comply\n\t\t\twith one or more of these rules.

\n

If Config has no current evaluation results for the\n\t\t\tresource, it returns INSUFFICIENT_DATA. This result\n\t\t\tmight indicate one of the following conditions about the rules that\n\t\t\tevaluate the resource:

\n
    \n
  • \n

    Config has never invoked an evaluation for the\n\t\t\t\t\trule. To check whether it has, use the\n\t\t\t\t\t\tDescribeConfigRuleEvaluationStatus action\n\t\t\t\t\tto get the LastSuccessfulInvocationTime and\n\t\t\t\t\t\tLastFailedInvocationTime.

    \n
  • \n
  • \n

    The rule's Lambda function is failing to send\n\t\t\t\t\tevaluation results to Config. Verify that the role that\n\t\t\t\t\tyou assigned to your configuration recorder includes the\n\t\t\t\t\t\tconfig:PutEvaluations permission. If the\n\t\t\t\t\trule is a custom rule, verify that the Lambda execution\n\t\t\t\t\trole includes the config:PutEvaluations\n\t\t\t\t\tpermission.

    \n
  • \n
  • \n

    The rule's Lambda function has returned\n\t\t\t\t\t\tNOT_APPLICABLE for all evaluation results.\n\t\t\t\t\tThis can occur if the resources were deleted or removed from\n\t\t\t\t\tthe rule's scope.

    \n
  • \n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -3584,7 +3665,7 @@ "ComplianceTypes": { "target": "com.amazonaws.configservice#ComplianceTypes", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT, NON_COMPLIANT, and INSUFFICIENT_DATA.

" + "smithy.api#documentation": "

Filters the results by compliance.

" } }, "Limit": { @@ -3602,7 +3683,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeComplianceByResourceResponse": { @@ -3622,7 +3704,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigRuleEvaluationStatus": { @@ -3673,12 +3756,13 @@ "target": "com.amazonaws.configservice#RuleLimit", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The number of rule evaluation results that you want\n\t\t\treturned.

\n\t\t\n\t\t

This parameter is required if the rule limit for your account\n\t\t\tis more than the default of 150 rules.

\n\t\t

For information about requesting a rule limit increase, see\n\t\t\t\tConfig Limits in the Amazon Web Services General\n\t\t\t\tReference Guide.

" + "smithy.api#documentation": "

The number of rule evaluation results that you want\n\t\t\treturned.

\n

This parameter is required if the rule limit for your account\n\t\t\tis more than the default of 150 rules.

\n

For information about requesting a rule limit increase, see\n\t\t\t\tConfig Limits in the Amazon Web Services General\n\t\t\t\tReference Guide.

" } } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigRuleEvaluationStatusResponse": { @@ -3698,7 +3782,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigRules": { @@ -3740,7 +3825,7 @@ } }, "traits": { - "smithy.api#documentation": "

Returns a filtered list of Detective or Proactive Config rules. By default, if the filter is not defined, this API returns an unfiltered list.

" + "smithy.api#documentation": "

Returns a filtered list of Detective or Proactive Config rules. By default, if the filter is not defined, this API returns an unfiltered list. For more information on Detective or Proactive Config rules,\n\t\t\tsee \n Evaluation Mode\n in the Config Developer Guide.

" } }, "com.amazonaws.configservice#DescribeConfigRulesRequest": { @@ -3761,12 +3846,13 @@ "Filters": { "target": "com.amazonaws.configservice#DescribeConfigRulesFilters", "traits": { - "smithy.api#documentation": "

Returns a list of Detecive or Proactive Config rules. By default, this API returns an unfiltered list.

" + "smithy.api#documentation": "

Returns a list of Detective or Proactive Config rules. By default, this API returns an unfiltered list. For more information on Detective or Proactive Config rules,\n\t\t\tsee \n Evaluation Mode\n in the Config Developer Guide.

" } } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigRulesResponse": { @@ -3786,7 +3872,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigurationAggregatorSourcesStatus": { @@ -3834,7 +3921,7 @@ "UpdateStatus": { "target": "com.amazonaws.configservice#AggregatedSourceStatusTypeList", "traits": { - "smithy.api#documentation": "

Filters the status type.

\n\t\t
    \n
  • \n\t\t\t\t

    Valid value FAILED indicates errors while moving\n\t\t\t\t\tdata.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Valid value SUCCEEDED indicates the data was\n\t\t\t\t\tsuccessfully moved.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    Valid value OUTDATED indicates the data is not the most\n\t\t\t\t\trecent.

    \n\t\t\t
  • \n
" + "smithy.api#documentation": "

Filters the status type.

\n
    \n
  • \n

    Valid value FAILED indicates errors while moving\n\t\t\t\t\tdata.

    \n
  • \n
  • \n

    Valid value SUCCEEDED indicates the data was\n\t\t\t\t\tsuccessfully moved.

    \n
  • \n
  • \n

    Valid value OUTDATED indicates the data is not the most\n\t\t\t\t\trecent.

    \n
  • \n
" } }, "NextToken": { @@ -3850,6 +3937,9 @@ "smithy.api#documentation": "

The maximum number of AggregatorSourceStatus returned on each\n\t\t\tpage. The default is maximum. If you specify 0, Config uses the\n\t\t\tdefault.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigurationAggregatorSourcesStatusResponse": { @@ -3867,6 +3957,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigurationAggregators": { @@ -3923,6 +4016,9 @@ "smithy.api#documentation": "

The maximum number of configuration aggregators returned on\n\t\t\teach page. The default is maximum. If you specify 0, Config uses\n\t\t\tthe default.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigurationAggregatorsResponse": { @@ -3940,6 +4036,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigurationRecorderStatus": { @@ -3956,7 +4055,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the current status of the specified configuration\n\t\t\trecorder. If a configuration recorder is not specified, this action\n\t\t\treturns the status of all configuration recorders associated with\n\t\t\tthe account.

\n\t\t \n\t\t\t

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account.

\n\t\t
" + "smithy.api#documentation": "

Returns the current status of the specified configuration\n\t\t\trecorder as well as the status of the last recording event for the recorder. If a configuration recorder is not specified, this action\n\t\t\treturns the status of all configuration recorders associated with\n\t\t\tthe account.

\n \n

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account. For a detailed status of recording events over time, add your Config events to Amazon CloudWatch metrics and use CloudWatch metrics.

\n
" } }, "com.amazonaws.configservice#DescribeConfigurationRecorderStatusRequest": { @@ -3970,7 +4069,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DescribeConfigurationRecorderStatus\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the DescribeConfigurationRecorderStatus\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigurationRecorderStatusResponse": { @@ -3984,7 +4084,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the DescribeConfigurationRecorderStatus action, in JSON\n\t\t\tformat.

" + "smithy.api#documentation": "

The output for the DescribeConfigurationRecorderStatus action, in JSON\n\t\t\tformat.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConfigurationRecorders": { @@ -4001,7 +4102,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the details for the specified configuration recorders.\n\t\t\tIf the configuration recorder is not specified, this action returns\n\t\t\tthe details for all configuration recorders associated with the\n\t\t\taccount.

\n\t\t \n\t\t\t

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account.

\n\t\t
" + "smithy.api#documentation": "

Returns the details for the specified configuration recorders.\n\t\t\tIf the configuration recorder is not specified, this action returns\n\t\t\tthe details for all configuration recorders associated with the\n\t\t\taccount.

\n \n

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account.

\n
" } }, "com.amazonaws.configservice#DescribeConfigurationRecordersRequest": { @@ -4015,7 +4116,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DescribeConfigurationRecorders action.

" + "smithy.api#documentation": "

The input for the DescribeConfigurationRecorders action.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConfigurationRecordersResponse": { @@ -4029,7 +4131,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the DescribeConfigurationRecorders action.

" + "smithy.api#documentation": "

The output for the DescribeConfigurationRecorders action.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConformancePackCompliance": { @@ -4058,7 +4161,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns compliance details for each rule in that conformance pack.

\n\t\t \n

You must provide exact rule names.

\n
", + "smithy.api#documentation": "

Returns compliance details for each rule in that conformance pack.

\n \n

You must provide exact rule names.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4105,6 +4208,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConformancePackComplianceResponse": { @@ -4130,6 +4236,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConformancePackStatus": { @@ -4152,7 +4261,7 @@ } ], "traits": { - "smithy.api#documentation": "

Provides one or more conformance packs deployment status.

\n\t\t \n

If there are no conformance packs then you will see an empty result.

\n
", + "smithy.api#documentation": "

Provides one or more conformance packs deployment status.

\n \n

If there are no conformance packs then you will see an empty result.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4183,6 +4292,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConformancePackStatusResponse": { @@ -4200,6 +4312,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeConformancePacks": { @@ -4256,6 +4371,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeConformancePacksResponse": { @@ -4273,6 +4391,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeDeliveryChannelStatus": { @@ -4289,7 +4410,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the current status of the specified delivery channel.\n\t\t\tIf a delivery channel is not specified, this action returns the\n\t\t\tcurrent status of all delivery channels associated with the\n\t\t\taccount.

\n\t\t \n\t\t\t

Currently, you can specify only one delivery channel per\n\t\t\t\tregion in your account.

\n\t\t
" + "smithy.api#documentation": "

Returns the current status of the specified delivery channel.\n\t\t\tIf a delivery channel is not specified, this action returns the\n\t\t\tcurrent status of all delivery channels associated with the\n\t\t\taccount.

\n \n

Currently, you can specify only one delivery channel per\n\t\t\t\tregion in your account.

\n
" } }, "com.amazonaws.configservice#DescribeDeliveryChannelStatusRequest": { @@ -4303,7 +4424,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DeliveryChannelStatus\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the DeliveryChannelStatus\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeDeliveryChannelStatusResponse": { @@ -4317,7 +4439,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the DescribeDeliveryChannelStatus action.

" + "smithy.api#documentation": "

The output for the DescribeDeliveryChannelStatus action.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeDeliveryChannels": { @@ -4334,7 +4457,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns details about the specified delivery channel. If a\n\t\t\tdelivery channel is not specified, this action returns the details\n\t\t\tof all delivery channels associated with the account.

\n\t\t \n\t\t\t

Currently, you can specify only one delivery channel per\n\t\t\t\tregion in your account.

\n\t\t
" + "smithy.api#documentation": "

Returns details about the specified delivery channel. If a\n\t\t\tdelivery channel is not specified, this action returns the details\n\t\t\tof all delivery channels associated with the account.

\n \n

Currently, you can specify only one delivery channel per\n\t\t\t\tregion in your account.

\n
" } }, "com.amazonaws.configservice#DescribeDeliveryChannelsRequest": { @@ -4348,7 +4471,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the DescribeDeliveryChannels\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the DescribeDeliveryChannels\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeDeliveryChannelsResponse": { @@ -4362,7 +4486,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the DescribeDeliveryChannels\n\t\t\taction.

" + "smithy.api#documentation": "

The output for the DescribeDeliveryChannels\n\t\t\taction.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeOrganizationConfigRuleStatuses": { @@ -4388,7 +4513,7 @@ } ], "traits": { - "smithy.api#documentation": "

Provides organization Config rule deployment status for an organization.

\n\t\t\n\t\t \n

The status is not considered successful until organization Config rule is successfully deployed in all the member \n\t\t\taccounts with an exception of excluded accounts.

\n\t\t\t

When you specify the limit and the next token, you receive a paginated response.\n\t\t\tLimit and next token are not applicable if you specify organization Config rule names. \n\t\t\tIt is only applicable, when you request all the organization Config rules.

\n\t\t\t
", + "smithy.api#documentation": "

Provides organization Config rule deployment status for an organization.

\n \n

The status is not considered successful until organization Config rule is successfully deployed in all the member \n\t\t\taccounts with an exception of excluded accounts.

\n

When you specify the limit and the next token, you receive a paginated response.\n\t\t\tLimit and next token are not applicable if you specify organization Config rule names. \n\t\t\tIt is only applicable, when you request all the organization Config rules.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4419,6 +4544,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeOrganizationConfigRuleStatusesResponse": { @@ -4436,6 +4564,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeOrganizationConfigRules": { @@ -4461,7 +4592,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of organization Config rules.

\n\t\t\t\n\t\t \n

When you specify the limit and the next token, you receive a paginated response.

\n\t\t\t

Limit and next token are not applicable if you specify organization Config rule names. \n\t\t\tIt is only applicable, when you request all the organization Config rules.

\n\t\t\t\n\t\t\t

\n For accounts within an organzation\n

\n\t\t\t\n\t\t\t

If you deploy an organizational rule or conformance pack in an organization\n\t\t\t\tadministrator account, and then establish a delegated administrator and deploy an\n\t\t\t\torganizational rule or conformance pack in the delegated administrator account, you\n\t\t\t\twon't be able to see the organizational rule or conformance pack in the organization\n\t\t\t\tadministrator account from the delegated administrator account or see the organizational\n\t\t\t\trule or conformance pack in the delegated administrator account from organization\n\t\t\t\tadministrator account. The DescribeOrganizationConfigRules and \n\t\t\t\tDescribeOrganizationConformancePacks APIs can only see and interact with\n\t\t\t\tthe organization-related resource that were deployed from within the account calling\n\t\t\t\tthose APIs.

\n\t\t
", + "smithy.api#documentation": "

Returns a list of organization Config rules.

\n \n

When you specify the limit and the next token, you receive a paginated response.

\n

Limit and next token are not applicable if you specify organization Config rule names. \n\t\t\tIt is only applicable, when you request all the organization Config rules.

\n

\n For accounts within an organzation\n

\n

If you deploy an organizational rule or conformance pack in an organization\n\t\t\t\tadministrator account, and then establish a delegated administrator and deploy an\n\t\t\t\torganizational rule or conformance pack in the delegated administrator account, you\n\t\t\t\twon't be able to see the organizational rule or conformance pack in the organization\n\t\t\t\tadministrator account from the delegated administrator account or see the organizational\n\t\t\t\trule or conformance pack in the delegated administrator account from organization\n\t\t\t\tadministrator account. The DescribeOrganizationConfigRules and \n\t\t\t\tDescribeOrganizationConformancePacks APIs can only see and interact with\n\t\t\t\tthe organization-related resource that were deployed from within the account calling\n\t\t\t\tthose APIs.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4492,6 +4623,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeOrganizationConfigRulesResponse": { @@ -4509,6 +4643,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeOrganizationConformancePackStatuses": { @@ -4534,7 +4671,7 @@ } ], "traits": { - "smithy.api#documentation": "

Provides organization conformance pack deployment status for an organization.

\n\t\t \n\t\t\t

The status is not considered successful until organization conformance pack is successfully \n\t\t\t\tdeployed in all the member accounts with an exception of excluded accounts.

\n\t\t\t

When you specify the limit and the next token, you receive a paginated response. \n\t\t\t\tLimit and next token are not applicable if you specify organization conformance pack names. \n\t\t\t\tThey are only applicable, when you request all the organization conformance packs.

\n
", + "smithy.api#documentation": "

Provides organization conformance pack deployment status for an organization.

\n \n

The status is not considered successful until organization conformance pack is successfully \n\t\t\t\tdeployed in all the member accounts with an exception of excluded accounts.

\n

When you specify the limit and the next token, you receive a paginated response. \n\t\t\t\tLimit and next token are not applicable if you specify organization conformance pack names. \n\t\t\t\tThey are only applicable, when you request all the organization conformance packs.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4565,6 +4702,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeOrganizationConformancePackStatusesResponse": { @@ -4582,6 +4722,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeOrganizationConformancePacks": { @@ -4607,7 +4750,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of organization conformance packs.

\n\t\t \n

When you specify the limit and the next token, you receive a paginated response.

\n\t\t\t

Limit and next token are not applicable if you specify organization conformance packs names. They are only applicable,\n\t\t\twhen you request all the organization conformance packs.

\n\t\t\n\t\t\t

\n For accounts within an organzation\n

\n\t\t\t\t\t\t\n\t\t\t

If you deploy an organizational rule or conformance pack in an organization\n\t\t\t\tadministrator account, and then establish a delegated administrator and deploy an\n\t\t\t\torganizational rule or conformance pack in the delegated administrator account, you\n\t\t\t\twon't be able to see the organizational rule or conformance pack in the organization\n\t\t\t\tadministrator account from the delegated administrator account or see the organizational\n\t\t\t\trule or conformance pack in the delegated administrator account from organization\n\t\t\t\tadministrator account. The DescribeOrganizationConfigRules and \n\t\t\t\tDescribeOrganizationConformancePacks APIs can only see and interact with\n\t\t\t\tthe organization-related resource that were deployed from within the account calling\n\t\t\t\tthose APIs.

\n\t\t
", + "smithy.api#documentation": "

Returns a list of organization conformance packs.

\n \n

When you specify the limit and the next token, you receive a paginated response.

\n

Limit and next token are not applicable if you specify organization conformance packs names. They are only applicable,\n\t\t\twhen you request all the organization conformance packs.

\n

\n For accounts within an organzation\n

\n

If you deploy an organizational rule or conformance pack in an organization\n\t\t\t\tadministrator account, and then establish a delegated administrator and deploy an\n\t\t\t\torganizational rule or conformance pack in the delegated administrator account, you\n\t\t\t\twon't be able to see the organizational rule or conformance pack in the organization\n\t\t\t\tadministrator account from the delegated administrator account or see the organizational\n\t\t\t\trule or conformance pack in the delegated administrator account from organization\n\t\t\t\tadministrator account. The DescribeOrganizationConfigRules and \n\t\t\t\tDescribeOrganizationConformancePacks APIs can only see and interact with\n\t\t\t\tthe organization-related resource that were deployed from within the account calling\n\t\t\t\tthose APIs.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4638,6 +4781,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a\n\t\t\tpaginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeOrganizationConformancePacksResponse": { @@ -4655,6 +4801,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a\n\t\t\tpaginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribePendingAggregationRequests": { @@ -4712,6 +4861,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribePendingAggregationRequestsResponse": { @@ -4729,6 +4881,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeRemediationConfigurations": { @@ -4753,6 +4908,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeRemediationConfigurationsResponse": { @@ -4764,6 +4922,9 @@ "smithy.api#documentation": "

Returns a remediation configuration object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeRemediationExceptions": { @@ -4783,7 +4944,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the details of one or more remediation exceptions. A detailed view of a remediation exception for a set of resources that includes an explanation of an exception and the time when the exception will be deleted. \n\t\t\tWhen you specify the limit and the next token, you receive a paginated response.

\n\t\t \n

Config generates a remediation exception when a problem occurs executing a remediation action to a specific resource. \n\t\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n\t\t\t

When you specify the limit and the next token, you receive a paginated response.

\n\t\t\t

Limit and next token are not applicable if you request resources in batch. It is only applicable, when you request all resources.

\n
", + "smithy.api#documentation": "

Returns the details of one or more remediation exceptions. A detailed view of a remediation exception for a set of resources that includes an explanation of an exception and the time when the exception will be deleted. \n\t\t\tWhen you specify the limit and the next token, you receive a paginated response.

\n \n

Config generates a remediation exception when a problem occurs executing a remediation action to a specific resource. \n\t\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n

When you specify the limit and the next token, you receive a paginated response.

\n

Limit and next token are not applicable if you request resources in batch. It is only applicable, when you request all resources.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4820,6 +4981,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeRemediationExceptionsResponse": { @@ -4837,6 +5001,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeRemediationExecutionStatus": { @@ -4897,6 +5064,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeRemediationExecutionStatusResponse": { @@ -4914,6 +5084,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DescribeRetentionConfigurations": { @@ -4936,7 +5109,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the details of one or more retention configurations. If\n\t\t\tthe retention configuration name is not specified, this action\n\t\t\treturns the details for all the retention configurations for that\n\t\t\taccount.

\n\t\t \n\t\t\t

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n\t\t
", + "smithy.api#documentation": "

Returns the details of one or more retention configurations. If\n\t\t\tthe retention configuration name is not specified, this action\n\t\t\treturns the details for all the retention configurations for that\n\t\t\taccount.

\n \n

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -4950,7 +5123,7 @@ "RetentionConfigurationNames": { "target": "com.amazonaws.configservice#RetentionConfigurationNameList", "traits": { - "smithy.api#documentation": "

A list of names of retention configurations for which you want\n\t\t\tdetails. If you do not specify a name, Config returns details\n\t\t\tfor all the retention configurations for that account.

\n\t\t \n\t\t\t

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n\t\t
" + "smithy.api#documentation": "

A list of names of retention configurations for which you want\n\t\t\tdetails. If you do not specify a name, Config returns details\n\t\t\tfor all the retention configurations for that account.

\n \n

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n
" } }, "NextToken": { @@ -4959,6 +5132,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page\n\t\t\tthat you use to get the next page of results in a paginated\n\t\t\tresponse.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#DescribeRetentionConfigurationsResponse": { @@ -4976,6 +5152,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page\n\t\t\tthat you use to get the next page of results in a paginated\n\t\t\tresponse.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#DiscoveredResourceIdentifierList": { @@ -5019,7 +5198,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

Indicates whether the Amazon Web Services resource complies with the Config\n\t\t\trule that it was evaluated against.

\n\t\t

For the Evaluation data type, Config supports\n\t\t\tonly the COMPLIANT, NON_COMPLIANT, and\n\t\t\t\tNOT_APPLICABLE values. Config does not support\n\t\t\tthe INSUFFICIENT_DATA value for this data\n\t\t\ttype.

\n\t\t

Similarly, Config does not accept\n\t\t\t\tINSUFFICIENT_DATA as the value for\n\t\t\t\tComplianceType from a PutEvaluations\n\t\t\trequest. For example, an Lambda function for a custom Config\n\t\t\trule cannot pass an INSUFFICIENT_DATA value to Config.

", + "smithy.api#documentation": "

Indicates whether the Amazon Web Services resource complies with the Config\n\t\t\trule that it was evaluated against.

\n

For the Evaluation data type, Config supports\n\t\t\tonly the COMPLIANT, NON_COMPLIANT, and\n\t\t\t\tNOT_APPLICABLE values. Config does not support\n\t\t\tthe INSUFFICIENT_DATA value for this data\n\t\t\ttype.

\n

Similarly, Config does not accept\n\t\t\t\tINSUFFICIENT_DATA as the value for\n\t\t\t\tComplianceType from a PutEvaluations\n\t\t\trequest. For example, an Lambda function for a custom Config\n\t\t\trule cannot pass an INSUFFICIENT_DATA value to Config.

", "smithy.api#required": {} } }, @@ -5113,7 +5292,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

Indicates whether the Amazon Web Services resource complies with the Config\n\t\t\trule that evaluated it.

\n\t\t

For the EvaluationResult data type, Config\n\t\t\tsupports only the COMPLIANT,\n\t\t\tNON_COMPLIANT, and NOT_APPLICABLE values.\n\t\t\tConfig does not support the INSUFFICIENT_DATA value\n\t\t\tfor the EvaluationResult data type.

" + "smithy.api#documentation": "

Indicates whether the Amazon Web Services resource complies with the Config\n\t\t\trule that evaluated it.

\n

For the EvaluationResult data type, Config\n\t\t\tsupports only the COMPLIANT,\n\t\t\tNON_COMPLIANT, and NOT_APPLICABLE values.\n\t\t\tConfig does not support the INSUFFICIENT_DATA value\n\t\t\tfor the EvaluationResult data type.

" } }, "ResultRecordedTime": { @@ -5464,7 +5643,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the evaluation results for the specified Config\n\t\t\trule for a specific resource in a rule. The results indicate which\n\t\t\tAmazon Web Services resources were evaluated by the rule, when each resource was\n\t\t\tlast evaluated, and whether each resource complies with the rule.

\n\t\t \n\t\t\t

The results can return an empty result page. But if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n\t\t
", + "smithy.api#documentation": "

Returns the evaluation results for the specified Config\n\t\t\trule for a specific resource in a rule. The results indicate which\n\t\t\tAmazon Web Services resources were evaluated by the rule, when each resource was\n\t\t\tlast evaluated, and whether each resource complies with the rule.

\n \n

The results can return an empty result page. But if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -5507,7 +5686,7 @@ "ComplianceType": { "target": "com.amazonaws.configservice#ComplianceType", "traits": { - "smithy.api#documentation": "

The resource compliance status.

\n\t\t \n\t\t\t

For the\n\t\t\t\t\tGetAggregateComplianceDetailsByConfigRuleRequest\n\t\t\t\tdata type, Config supports only the COMPLIANT\n\t\t\t\tand NON_COMPLIANT. Config does not support the\n\t\t\t\t\tNOT_APPLICABLE and\n\t\t\t\t\tINSUFFICIENT_DATA values.

\n\t\t
" + "smithy.api#documentation": "

The resource compliance status.

\n \n

For the\n\t\t\t\t\tGetAggregateComplianceDetailsByConfigRuleRequest\n\t\t\t\tdata type, Config supports only the COMPLIANT\n\t\t\t\tand NON_COMPLIANT. Config does not support the\n\t\t\t\t\tNOT_APPLICABLE and\n\t\t\t\t\tINSUFFICIENT_DATA values.

\n
" } }, "Limit": { @@ -5523,6 +5702,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetAggregateComplianceDetailsByConfigRuleResponse": { @@ -5540,6 +5722,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetAggregateConfigRuleComplianceSummary": { @@ -5565,7 +5750,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the number of compliant and noncompliant rules for one\n\t\t\tor more accounts and regions in an aggregator.

\n\t\t \n\t\t\t

The results can return an empty result page, but if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n\t\t
", + "smithy.api#documentation": "

Returns the number of compliant and noncompliant rules for one\n\t\t\tor more accounts and regions in an aggregator.

\n \n

The results can return an empty result page, but if you\n\t\t\t\thave a nextToken, the results are displayed on the next\n\t\t\t\tpage.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -5608,6 +5793,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetAggregateConfigRuleComplianceSummaryResponse": { @@ -5631,6 +5819,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use\n\t\t\tto get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetAggregateConformancePackComplianceSummary": { @@ -5656,7 +5847,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the count of compliant and noncompliant conformance packs across all Amazon Web Services accounts and Amazon Web Services Regions in an aggregator. You can filter based on Amazon Web Services account ID or Amazon Web Services Region.

\n\t\t \n

The results can return an empty result page, but if you have a nextToken, the results are displayed on the next page.

\n
", + "smithy.api#documentation": "

Returns the count of compliant and noncompliant conformance packs across all Amazon Web Services accounts and Amazon Web Services Regions in an aggregator. You can filter based on Amazon Web Services account ID or Amazon Web Services Region.

\n \n

The results can return an empty result page, but if you have a nextToken, the results are displayed on the next page.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -5699,6 +5890,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetAggregateConformancePackComplianceSummaryResponse": { @@ -5722,6 +5916,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetAggregateDiscoveredResourceCounts": { @@ -5747,7 +5944,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the resource counts across accounts and regions that are present in your Config aggregator. You can request the resource counts by providing filters and GroupByKey.

\n\t\t

For example, if the input contains accountID 12345678910 and region us-east-1 in filters, the API returns the count of resources in account ID 12345678910 and region us-east-1.\n\t\t\tIf the input contains ACCOUNT_ID as a GroupByKey, the API returns resource counts for all source accounts that are present in your aggregator.

", + "smithy.api#documentation": "

Returns the resource counts across accounts and regions that are present in your Config aggregator. You can request the resource counts by providing filters and GroupByKey.

\n

For example, if the input contains accountID 12345678910 and region us-east-1 in filters, the API returns the count of resources in account ID 12345678910 and region us-east-1.\n\t\t\tIf the input contains ACCOUNT_ID as a GroupByKey, the API returns resource counts for all source accounts that are present in your aggregator.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -5790,6 +5987,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetAggregateDiscoveredResourceCountsResponse": { @@ -5821,6 +6021,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetAggregateResourceConfig": { @@ -5866,6 +6069,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetAggregateResourceConfigResponse": { @@ -5877,6 +6083,9 @@ "smithy.api#documentation": "

Returns a ConfigurationItem object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetComplianceDetailsByConfigRule": { @@ -5921,7 +6130,7 @@ "ComplianceTypes": { "target": "com.amazonaws.configservice#ComplianceTypes", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT,\n\t\t\t\tNON_COMPLIANT, and\n\t\t\tNOT_APPLICABLE.

" + "smithy.api#documentation": "

Filters the results by compliance.

\n

\n INSUFFICIENT_DATA is a valid ComplianceType that is returned when an Config rule cannot be evaluated. However, INSUFFICIENT_DATA cannot be used as a ComplianceType for filtering results.

" } }, "Limit": { @@ -5939,7 +6148,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetComplianceDetailsByConfigRuleResponse": { @@ -5959,7 +6169,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetComplianceDetailsByResource": { @@ -6002,7 +6213,7 @@ "ComplianceTypes": { "target": "com.amazonaws.configservice#ComplianceTypes", "traits": { - "smithy.api#documentation": "

Filters the results by compliance.

\n\t\t

The allowed values are COMPLIANT,\n\t\t\t\tNON_COMPLIANT, and\n\t\t\tNOT_APPLICABLE.

" + "smithy.api#documentation": "

Filters the results by compliance.

\n

\n INSUFFICIENT_DATA is a valid ComplianceType that is returned when an Config rule cannot be evaluated. However, INSUFFICIENT_DATA cannot be used as a ComplianceType for filtering results.

" } }, "NextToken": { @@ -6014,12 +6225,13 @@ "ResourceEvaluationId": { "target": "com.amazonaws.configservice#ResourceEvaluationId", "traits": { - "smithy.api#documentation": "

The unique ID of Amazon Web Services resource execution for which you want to retrieve evaluation results.

\n\t\t \n

You need to only provide either a ResourceEvaluationID or a ResourceID and ResourceType.

\n
" + "smithy.api#documentation": "

The unique ID of Amazon Web Services resource execution for which you want to retrieve evaluation results.

\n \n

You need to only provide either a ResourceEvaluationID or a ResourceID and ResourceType.

\n
" } } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetComplianceDetailsByResourceResponse": { @@ -6039,7 +6251,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetComplianceSummaryByConfigRule": { @@ -6065,7 +6278,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetComplianceSummaryByResourceType": { @@ -6091,12 +6305,13 @@ "ResourceTypes": { "target": "com.amazonaws.configservice#ResourceTypes", "traits": { - "smithy.api#documentation": "

Specify one or more resource types to get the number of\n\t\t\tresources that are compliant and the number that are noncompliant\n\t\t\tfor each resource type.

\n\t\t

For this request, you can specify an Amazon Web Services resource type such as\n\t\t\t\tAWS::EC2::Instance. You can specify that the\n\t\t\tresource type is an Amazon Web Services account by specifying\n\t\t\t\tAWS::::Account.

" + "smithy.api#documentation": "

Specify one or more resource types to get the number of\n\t\t\tresources that are compliant and the number that are noncompliant\n\t\t\tfor each resource type.

\n

For this request, you can specify an Amazon Web Services resource type such as\n\t\t\t\tAWS::EC2::Instance. You can specify that the\n\t\t\tresource type is an Amazon Web Services account by specifying\n\t\t\t\tAWS::::Account.

" } } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetComplianceSummaryByResourceTypeResponse": { @@ -6110,7 +6325,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetConformancePackComplianceDetails": { @@ -6186,6 +6402,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetConformancePackComplianceDetailsResponse": { @@ -6210,6 +6429,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetConformancePackComplianceSummary": { @@ -6264,6 +6486,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetConformancePackComplianceSummaryResponse": { @@ -6281,6 +6506,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetCustomRulePolicy": { @@ -6309,6 +6537,9 @@ "smithy.api#documentation": "

The name of your Config Custom Policy rule.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetCustomRulePolicyResponse": { @@ -6320,6 +6551,9 @@ "smithy.api#documentation": "

The policy definition containing the logic for your Config Custom Policy rule.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetDiscoveredResourceCounts": { @@ -6342,7 +6576,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the resource types, the number of each resource type,\n\t\t\tand the total number of resources that Config is recording in\n\t\t\tthis region for your Amazon Web Services account.

\n\t\t

\n Example\n

\n
    \n
  1. \n\t\t\t\t

    Config is recording three resource types in the US\n\t\t\t\t\tEast (Ohio) Region for your account: 25 EC2 instances, 20\n\t\t\t\t\tIAM users, and 15 S3 buckets.

    \n\t\t\t
  2. \n
  3. \n\t\t\t\t

    You make a call to the\n\t\t\t\t\t\tGetDiscoveredResourceCounts action and\n\t\t\t\t\tspecify that you want all resource types.

    \n\t\t\t
  4. \n
  5. \n\t\t\t\t

    Config returns the following:

    \n\n\t\t\t\t
      \n
    • \n\t\t\t\t\t\t

      The resource types (EC2 instances, IAM users,\n\t\t\t\t\t\t\tand S3 buckets).

      \n\t\t\t\t\t
    • \n
    • \n\t\t\t\t\t\t

      The number of each resource type (25, 20, and\n\t\t\t\t\t\t\t15).

      \n\t\t\t\t\t
    • \n
    • \n\t\t\t\t\t\t

      The total number of all resources\n\t\t\t\t\t\t\t(60).

      \n\t\t\t\t\t
    • \n
    \n\n\t\t\t
  6. \n
\n\n\t\t

The response is paginated. By default, Config lists 100\n\t\t\t\tResourceCount objects on each page. You can\n\t\t\tcustomize this number with the limit parameter. The\n\t\t\tresponse includes a nextToken string. To get the next\n\t\t\tpage of results, run the request again and specify the string for\n\t\t\tthe nextToken parameter.

\n\n\t\t \n\t\t\t

If you make a call to the GetDiscoveredResourceCounts action, you might\n\t\t\t\tnot immediately receive resource counts in the following\n\t\t\t\tsituations:

\n\n\t\t\t
    \n
  • \n\t\t\t\t\t

    You are a new Config customer.

    \n\t\t\t\t
  • \n
  • \n\t\t\t\t\t

    You just enabled resource recording.

    \n\t\t\t\t
  • \n
\n\n\t\t\t

It might take a few minutes for Config to record and\n\t\t\t\tcount your resources. Wait a few minutes and then retry the\n\t\t\t\t\tGetDiscoveredResourceCounts action.\n\t\t\t

\n\t\t
", + "smithy.api#documentation": "

Returns the resource types, the number of each resource type,\n\t\t\tand the total number of resources that Config is recording in\n\t\t\tthis region for your Amazon Web Services account.

\n

\n Example\n

\n
    \n
  1. \n

    Config is recording three resource types in the US\n\t\t\t\t\tEast (Ohio) Region for your account: 25 EC2 instances, 20\n\t\t\t\t\tIAM users, and 15 S3 buckets.

    \n
  2. \n
  3. \n

    You make a call to the\n\t\t\t\t\t\tGetDiscoveredResourceCounts action and\n\t\t\t\t\tspecify that you want all resource types.

    \n
  4. \n
  5. \n

    Config returns the following:

    \n
      \n
    • \n

      The resource types (EC2 instances, IAM users,\n\t\t\t\t\t\t\tand S3 buckets).

      \n
    • \n
    • \n

      The number of each resource type (25, 20, and\n\t\t\t\t\t\t\t15).

      \n
    • \n
    • \n

      The total number of all resources\n\t\t\t\t\t\t\t(60).

      \n
    • \n
    \n
  6. \n
\n

The response is paginated. By default, Config lists 100\n\t\t\t\tResourceCount objects on each page. You can\n\t\t\tcustomize this number with the limit parameter. The\n\t\t\tresponse includes a nextToken string. To get the next\n\t\t\tpage of results, run the request again and specify the string for\n\t\t\tthe nextToken parameter.

\n \n

If you make a call to the GetDiscoveredResourceCounts action, you might\n\t\t\t\tnot immediately receive resource counts in the following\n\t\t\t\tsituations:

\n
    \n
  • \n

    You are a new Config customer.

    \n
  • \n
  • \n

    You just enabled resource recording.

    \n
  • \n
\n

It might take a few minutes for Config to record and\n\t\t\t\tcount your resources. Wait a few minutes and then retry the\n\t\t\t\t\tGetDiscoveredResourceCounts action.\n\t\t\t

\n
", "smithy.api#paginated": { "inputToken": "nextToken", "outputToken": "nextToken", @@ -6356,7 +6590,7 @@ "resourceTypes": { "target": "com.amazonaws.configservice#ResourceTypes", "traits": { - "smithy.api#documentation": "

The comma-separated list that specifies the resource types that\n\t\t\tyou want Config to return (for example,\n\t\t\t\t\"AWS::EC2::Instance\",\n\t\t\t\"AWS::IAM::User\").

\n\n\t\t

If a value for resourceTypes is not specified, Config returns all resource types that Config is recording in\n\t\t\tthe region for your account.

\n\t\t \n\t\t\t

If the configuration recorder is turned off, Config\n\t\t\t\treturns an empty list of ResourceCount\n\t\t\t\tobjects. If the configuration recorder is not recording a\n\t\t\t\tspecific resource type (for example, S3 buckets), that resource\n\t\t\t\ttype is not returned in the list of ResourceCount objects.

\n\t\t
" + "smithy.api#documentation": "

The comma-separated list that specifies the resource types that\n\t\t\tyou want Config to return (for example,\n\t\t\t\t\"AWS::EC2::Instance\",\n\t\t\t\"AWS::IAM::User\").

\n

If a value for resourceTypes is not specified, Config returns all resource types that Config is recording in\n\t\t\tthe region for your account.

\n \n

If the configuration recorder is turned off, Config\n\t\t\t\treturns an empty list of ResourceCount\n\t\t\t\tobjects. If the configuration recorder is not recording a\n\t\t\t\tspecific resource type (for example, S3 buckets), that resource\n\t\t\t\ttype is not returned in the list of ResourceCount objects.

\n
" } }, "limit": { @@ -6372,6 +6606,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page\n\t\t\tthat you use to get the next page of results in a paginated\n\t\t\tresponse.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetDiscoveredResourceCountsResponse": { @@ -6381,7 +6618,7 @@ "target": "com.amazonaws.configservice#Long", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The total number of resources that Config is recording in\n\t\t\tthe region for your account. If you specify resource types in the\n\t\t\trequest, Config returns only the total number of resources for\n\t\t\tthose resource types.

\n\n\n\t\t

\n Example\n

\n
    \n
  1. \n\t\t\t\t

    Config is recording three resource types in the US\n\t\t\t\t\tEast (Ohio) Region for your account: 25 EC2 instances, 20\n\t\t\t\t\tIAM users, and 15 S3 buckets, for a total of 60\n\t\t\t\t\tresources.

    \n\t\t\t
  2. \n
  3. \n\t\t\t\t

    You make a call to the\n\t\t\t\t\t\tGetDiscoveredResourceCounts action and\n\t\t\t\t\tspecify the resource type,\n\t\t\t\t\t\t\"AWS::EC2::Instances\", in the\n\t\t\t\t\trequest.

    \n\t\t\t
  4. \n
  5. \n\t\t\t\t

    Config returns 25 for\n\t\t\t\t\t\ttotalDiscoveredResources.

    \n\t\t\t
  6. \n
" + "smithy.api#documentation": "

The total number of resources that Config is recording in\n\t\t\tthe region for your account. If you specify resource types in the\n\t\t\trequest, Config returns only the total number of resources for\n\t\t\tthose resource types.

\n

\n Example\n

\n
    \n
  1. \n

    Config is recording three resource types in the US\n\t\t\t\t\tEast (Ohio) Region for your account: 25 EC2 instances, 20\n\t\t\t\t\tIAM users, and 15 S3 buckets, for a total of 60\n\t\t\t\t\tresources.

    \n
  2. \n
  3. \n

    You make a call to the\n\t\t\t\t\t\tGetDiscoveredResourceCounts action and\n\t\t\t\t\tspecify the resource type,\n\t\t\t\t\t\t\"AWS::EC2::Instances\", in the\n\t\t\t\t\trequest.

    \n
  4. \n
  5. \n

    Config returns 25 for\n\t\t\t\t\t\ttotalDiscoveredResources.

    \n
  6. \n
" } }, "resourceCounts": { @@ -6396,6 +6633,9 @@ "smithy.api#documentation": "

The string that you use in a subsequent request to get the next\n\t\t\tpage of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetOrganizationConfigRuleDetailedStatus": { @@ -6459,6 +6699,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetOrganizationConfigRuleDetailedStatusResponse": { @@ -6476,6 +6719,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetOrganizationConformancePackDetailedStatus": { @@ -6539,6 +6785,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetOrganizationConformancePackDetailedStatusResponse": { @@ -6556,6 +6805,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetOrganizationCustomRulePolicy": { @@ -6588,6 +6840,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetOrganizationCustomRulePolicyResponse": { @@ -6599,6 +6854,9 @@ "smithy.api#documentation": "

The policy definition containing the logic for your organization Config Custom Policy rule.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetResourceConfigHistory": { @@ -6630,7 +6888,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of ConfigurationItems for the specified resource.\n\t\t\tThe list contains details about each state of the resource\n\t\t\tduring the specified time interval. If you specified a retention\n\t\t\tperiod to retain your ConfigurationItems between a\n\t\t\tminimum of 30 days and a maximum of 7 years (2557 days), Config\n\t\t\treturns the ConfigurationItems for the specified\n\t\t\tretention period.

\n\t\t

The response is paginated. By default, Config returns a\n\t\t\tlimit of 10 configuration items per page. You can customize this\n\t\t\tnumber with the limit parameter. The response includes\n\t\t\ta nextToken string. To get the next page of results,\n\t\t\trun the request again and specify the string for the\n\t\t\t\tnextToken parameter.

\n\t\t \n\t\t\t

Each call to the API is limited to span a duration of seven\n\t\t\t\tdays. It is likely that the number of records returned is\n\t\t\t\tsmaller than the specified limit. In such cases,\n\t\t\t\tyou can make another call, using the\n\t\t\t\tnextToken.

\n\t\t
", + "smithy.api#documentation": "

Returns a list of ConfigurationItems for the specified resource.\n\t\t\tThe list contains details about each state of the resource\n\t\t\tduring the specified time interval. If you specified a retention\n\t\t\tperiod to retain your ConfigurationItems between a\n\t\t\tminimum of 30 days and a maximum of 7 years (2557 days), Config\n\t\t\treturns the ConfigurationItems for the specified\n\t\t\tretention period.

\n

The response is paginated. By default, Config returns a\n\t\t\tlimit of 10 configuration items per page. You can customize this\n\t\t\tnumber with the limit parameter. The response includes\n\t\t\ta nextToken string. To get the next page of results,\n\t\t\trun the request again and specify the string for the\n\t\t\t\tnextToken parameter.

\n \n

Each call to the API is limited to span a duration of seven\n\t\t\t\tdays. It is likely that the number of records returned is\n\t\t\t\tsmaller than the specified limit. In such cases,\n\t\t\t\tyou can make another call, using the\n\t\t\t\tnextToken.

\n
", "smithy.api#paginated": { "inputToken": "nextToken", "outputToken": "nextToken", @@ -6689,7 +6947,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the GetResourceConfigHistory\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the GetResourceConfigHistory\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetResourceConfigHistoryResponse": { @@ -6709,7 +6968,8 @@ } }, "traits": { - "smithy.api#documentation": "

The output for the GetResourceConfigHistory\n\t\t\taction.

" + "smithy.api#documentation": "

The output for the GetResourceConfigHistory\n\t\t\taction.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetResourceEvaluationSummary": { @@ -6726,7 +6986,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a summary of resource evaluation for the specified resource evaluation ID from the proactive rules that were run. \n\t\t\tThe results indicate which evaluation context was used to evaluate the rules, which resource details were evaluated,\n\t\t\tthe evaluation mode that was run, and whether the resource details comply with the configuration of the proactive rules.

" + "smithy.api#documentation": "

Returns a summary of resource evaluation for the specified resource evaluation ID from the proactive rules that were run. \n\t\t\tThe results indicate which evaluation context was used to evaluate the rules, which resource details were evaluated,\n\t\t\tthe evaluation mode that was run, and whether the resource details comply with the configuration of the proactive rules.

\n \n

To see additional information about the evaluation result, such as which rule flagged a resource as NON_COMPLIANT, use the GetComplianceDetailsByResource API.\n\t\t\tFor more information, see the Examples section.

\n
" } }, "com.amazonaws.configservice#GetResourceEvaluationSummaryRequest": { @@ -6739,6 +6999,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetResourceEvaluationSummaryResponse": { @@ -6786,6 +7049,9 @@ "smithy.api#documentation": "

Returns a ResourceDetails object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GetStoredQuery": { @@ -6818,6 +7084,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#GetStoredQueryResponse": { @@ -6829,6 +7098,9 @@ "smithy.api#documentation": "

Returns a StoredQuery object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#GroupByAPILimit": { @@ -6915,7 +7187,7 @@ } }, "traits": { - "smithy.api#documentation": "

Indicates one of the following errors:

\n\t\t
    \n
  • \n

    For PutConfigRule, the rule cannot be created because the IAM role assigned to Config lacks permissions to perform the config:Put* action.

    \n
  • \n
  • \n

    For PutConfigRule, the Lambda function cannot be invoked. Check the function ARN, and check the function's permissions.

    \n
  • \n
  • \n

    For PutOrganizationConfigRule, organization Config rule cannot be created because you do not have permissions to call IAM GetRole action or create a service-linked role.

    \n
  • \n
  • \n

    For PutConformancePack and PutOrganizationConformancePack, a conformance pack cannot be created because you do not have the following permissions:

    \n\t\t\t\t
      \n
    • \n

      You do not have permission to call IAM GetRole action or create a service-linked role.

      \n
    • \n
    • \n

      You do not have permission to read Amazon S3 bucket or call SSM:GetDocument.

      \n
    • \n
    \n\t\t\t
  • \n
", + "smithy.api#documentation": "

Indicates one of the following errors:

\n
    \n
  • \n

    For PutConfigRule, the rule cannot be created because the IAM role assigned to Config lacks permissions to perform the config:Put* action.

    \n
  • \n
  • \n

    For PutConfigRule, the Lambda function cannot be invoked. Check the function ARN, and check the function's permissions.

    \n
  • \n
  • \n

    For PutOrganizationConfigRule, organization Config rule cannot be created because you do not have permissions to call IAM GetRole action or create a service-linked role.

    \n
  • \n
  • \n

    For PutConformancePack and PutOrganizationConformancePack, a conformance pack cannot be created because you do not have the following permissions:

    \n
      \n
    • \n

      You do not have permission to call IAM GetRole action or create a service-linked role.

      \n
    • \n
    • \n

      You do not have permission to read Amazon S3 bucket or call SSM:GetDocument.

      \n
    • \n
    \n
  • \n
", "smithy.api#error": "client" } }, @@ -6951,7 +7223,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified delivery channel name is invalid.

", + "smithy.api#documentation": "

The specified delivery channel name is not valid.

", "smithy.api#error": "client" } }, @@ -6996,7 +7268,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified next token is invalid. Specify the\n\t\t\t\tnextToken string that was returned in the previous\n\t\t\tresponse to get the next page of results.

", + "smithy.api#documentation": "

The specified next token is not valid. Specify the\n\t\t\t\tnextToken string that was returned in the previous\n\t\t\tresponse to get the next page of results.

", "smithy.api#error": "client" } }, @@ -7011,7 +7283,7 @@ } }, "traits": { - "smithy.api#documentation": "

One or more of the specified parameters are invalid. Verify\n\t\t\tthat your parameters are valid and try again.

", + "smithy.api#documentation": "

One or more of the specified parameters are not valid. Verify\n\t\t\tthat your parameters are valid and try again.

", "smithy.api#error": "client" } }, @@ -7026,7 +7298,7 @@ } }, "traits": { - "smithy.api#documentation": "

Config throws an exception if the recording group does not contain a valid list of resource types. Invalid values might also be incorrectly formatted.

", + "smithy.api#documentation": "

Config throws an exception if the recording group does not contain a valid list of resource types. Values that are not valid might also be incorrectly formatted.

", "smithy.api#error": "client" } }, @@ -7041,7 +7313,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified ResultToken is invalid.

", + "smithy.api#documentation": "

The specified ResultToken is not valid.

", "smithy.api#error": "client" } }, @@ -7071,7 +7343,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified Amazon S3 key prefix is invalid.

", + "smithy.api#documentation": "

The specified Amazon S3 key prefix is not valid.

", "smithy.api#error": "client" } }, @@ -7086,7 +7358,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified Amazon KMS Key ARN is invalid.

", + "smithy.api#documentation": "

The specified Amazon KMS Key ARN is not valid.

", "smithy.api#error": "client" } }, @@ -7116,7 +7388,7 @@ } }, "traits": { - "smithy.api#documentation": "

The specified time range is invalid. The earlier time is not\n\t\t\tchronologically before the later time.

", + "smithy.api#documentation": "

The specified time range is not valid. The earlier time is not\n\t\t\tchronologically before the later time.

", "smithy.api#error": "client" } }, @@ -7162,7 +7434,7 @@ } }, "traits": { - "smithy.api#documentation": "

For StartConfigRulesEvaluation API, this exception\n\t\t\tis thrown if an evaluation is in progress or if you call the StartConfigRulesEvaluation API more than once per\n\t\t\tminute.

\n\t\t

For PutConfigurationAggregator API, this exception\n\t\t\tis thrown if the number of accounts and aggregators exceeds the\n\t\t\tlimit.

", + "smithy.api#documentation": "

For StartConfigRulesEvaluation API, this exception\n\t\t\tis thrown if an evaluation is in progress or if you call the StartConfigRulesEvaluation API more than once per\n\t\t\tminute.

\n

For PutConfigurationAggregator API, this exception\n\t\t\tis thrown if the number of accounts and aggregators exceeds the\n\t\t\tlimit.

", "smithy.api#error": "client" } }, @@ -7189,7 +7461,7 @@ } ], "traits": { - "smithy.api#documentation": "

Accepts a resource type and returns a list of resource identifiers that are aggregated for a specific resource type across accounts and regions. \n\t\t\tA resource identifier includes the resource type, ID, (if available) the custom resource name, source account, and source region. \n\t\t\tYou can narrow the results to include only resources that have specific resource IDs, or a resource name, or source account ID, or source region.

\n\t\t\t

For example, if the input consists of accountID 12345678910 and the region is us-east-1 for resource type AWS::EC2::Instance then the API returns all the EC2 instance identifiers of accountID 12345678910 and region us-east-1.

", + "smithy.api#documentation": "

Accepts a resource type and returns a list of resource identifiers that are aggregated for a specific resource type across accounts and regions. \n\t\t\tA resource identifier includes the resource type, ID, (if available) the custom resource name, source account, and source region. \n\t\t\tYou can narrow the results to include only resources that have specific resource IDs, or a resource name, or source account ID, or source region.

\n

For example, if the input consists of accountID 12345678910 and the region is us-east-1 for resource type AWS::EC2::Instance then the API returns all the EC2 instance identifiers of accountID 12345678910 and region us-east-1.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -7234,6 +7506,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListAggregateDiscoveredResourcesResponse": { @@ -7251,6 +7526,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#ListConformancePackComplianceScores": { @@ -7273,7 +7551,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a list of conformance pack compliance scores. \n\t\t\tA compliance score is the percentage of the number of compliant rule-resource combinations in a conformance pack compared to the number of total possible rule-resource combinations in the conformance pack.\n\t\t\tThis metric provides you with a high-level view of the compliance state of your conformance packs. You can use it to identify, investigate, and understand\n\t\t\tthe level of compliance in your conformance packs.

\n\t\t \n

Conformance packs with no evaluation results will have a compliance score of INSUFFICIENT_DATA.

\n
", + "smithy.api#documentation": "

Returns a list of conformance pack compliance scores. \n\t\t\tA compliance score is the percentage of the number of compliant rule-resource combinations in a conformance pack compared to the number of total possible rule-resource combinations in the conformance pack.\n\t\t\tThis metric provides you with a high-level view of the compliance state of your conformance packs. You can use it to identify, investigate, and understand\n\t\t\tthe level of compliance in your conformance packs.

\n \n

Conformance packs with no evaluation results will have a compliance score of INSUFFICIENT_DATA.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -7293,13 +7571,13 @@ "SortOrder": { "target": "com.amazonaws.configservice#SortOrder", "traits": { - "smithy.api#documentation": "

Determines the order in which conformance pack compliance scores are sorted. Either in ascending or descending order.

\n\t\t\n\t\t

By default, conformance pack compliance scores are sorted in alphabetical order by name of the conformance pack. Conformance pack compliance scores are sorted in reverse alphabetical order if you enter DESCENDING.

\n\t\t\n\t\t

You can sort conformance pack compliance scores by the numerical value of the compliance score by entering SCORE in the SortBy action. When compliance scores are sorted by SCORE, conformance packs with a compliance score of INSUFFICIENT_DATA will be last when sorting by ascending order and first when sorting by descending order.

" + "smithy.api#documentation": "

Determines the order in which conformance pack compliance scores are sorted. Either in ascending or descending order.

\n

By default, conformance pack compliance scores are sorted in alphabetical order by name of the conformance pack. Conformance pack compliance scores are sorted in reverse alphabetical order if you enter DESCENDING.

\n

You can sort conformance pack compliance scores by the numerical value of the compliance score by entering SCORE in the SortBy action. When compliance scores are sorted by SCORE, conformance packs with a compliance score of INSUFFICIENT_DATA will be last when sorting by ascending order and first when sorting by descending order.

" } }, "SortBy": { "target": "com.amazonaws.configservice#SortBy", "traits": { - "smithy.api#documentation": "

Sorts your conformance pack compliance scores in either ascending or descending order, depending on SortOrder.

\n\t\t

By default, conformance pack compliance scores are sorted in alphabetical order by name of the conformance pack.\n\t\t\tEnter SCORE, to sort conformance pack compliance scores by the numerical value of the compliance score.

" + "smithy.api#documentation": "

Sorts your conformance pack compliance scores in either ascending or descending order, depending on SortOrder.

\n

By default, conformance pack compliance scores are sorted in alphabetical order by name of the conformance pack.\n\t\t\tEnter SCORE, to sort conformance pack compliance scores by the numerical value of the compliance score.

" } }, "Limit": { @@ -7312,9 +7590,12 @@ "NextToken": { "target": "com.amazonaws.configservice#NextToken", "traits": { - "smithy.api#documentation": "

The nextToken string in a prior request that you can use to get the paginated response for next set of conformance pack compliance scores.

" + "smithy.api#documentation": "

The nextToken string in a prior request that you can use to get the paginated response for the next set of conformance pack compliance scores.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListConformancePackComplianceScoresResponse": { @@ -7333,6 +7614,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#ListDiscoveredResources": { @@ -7358,7 +7642,7 @@ } ], "traits": { - "smithy.api#documentation": "

Accepts a resource type and returns a list of resource\n\t\t\tidentifiers for the resources of that type. A resource identifier\n\t\t\tincludes the resource type, ID, and (if available) the custom\n\t\t\tresource name. The results consist of resources that Config has\n\t\t\tdiscovered, including those that Config is not currently\n\t\t\trecording. You can narrow the results to include only resources that\n\t\t\thave specific resource IDs or a resource name.

\n\t\t \n\t\t\t

You can specify either resource IDs or a resource name, but\n\t\t\t\tnot both, in the same request.

\n\t\t
\n\t\t

The response is paginated. By default, Config lists 100\n\t\t\tresource identifiers on each page. You can customize this number\n\t\t\twith the limit parameter. The response includes a\n\t\t\t\tnextToken string. To get the next page of results,\n\t\t\trun the request again and specify the string for the\n\t\t\t\tnextToken parameter.

", + "smithy.api#documentation": "

Accepts a resource type and returns a list of resource\n\t\t\tidentifiers for the resources of that type. A resource identifier\n\t\t\tincludes the resource type, ID, and (if available) the custom\n\t\t\tresource name. The results consist of resources that Config has\n\t\t\tdiscovered, including those that Config is not currently\n\t\t\trecording. You can narrow the results to include only resources that\n\t\t\thave specific resource IDs or a resource name.

\n \n

You can specify either resource IDs or a resource name, but\n\t\t\t\tnot both, in the same request.

\n
\n

The response is paginated. By default, Config lists 100\n\t\t\tresource identifiers on each page. You can customize this number\n\t\t\twith the limit parameter. The response includes a\n\t\t\t\tnextToken string. To get the next page of results,\n\t\t\trun the request again and specify the string for the\n\t\t\t\tnextToken parameter.

", "smithy.api#paginated": { "inputToken": "nextToken", "outputToken": "nextToken", @@ -7380,7 +7664,7 @@ "resourceIds": { "target": "com.amazonaws.configservice#ResourceIdList", "traits": { - "smithy.api#documentation": "

The IDs of only those resources that you want Config to\n\t\t\tlist in the response. If you do not specify this parameter, Config lists all resources of the specified type that it has\n\t\t\tdiscovered.

" + "smithy.api#documentation": "

The IDs of only those resources that you want Config to\n\t\t\tlist in the response. If you do not specify this parameter, Config lists all resources of the specified type that it has\n\t\t\tdiscovered. You can list a minimum of 1 resourceID and a maximum of 20 resourceIds.

" } }, "resourceName": { @@ -7411,7 +7695,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListDiscoveredResourcesResponse": { @@ -7431,7 +7716,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#ListResourceEvaluations": { @@ -7495,6 +7781,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListResourceEvaluationsResponse": { @@ -7512,6 +7801,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#ListStoredQueries": { @@ -7555,6 +7847,9 @@ "smithy.api#documentation": "

The maximum number of results to be returned with a single call.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListStoredQueriesResponse": { @@ -7572,6 +7867,9 @@ "smithy.api#documentation": "

If the previous paginated request didn't return all of the remaining results, the response object's NextToken parameter value is set to a token. \n\t\t\tTo retrieve the next set of results, call this action again and assign that token to the request object's NextToken parameter. \n\t\t\tIf there are no remaining results, the previous response object's NextToken parameter is set to null.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#ListTagsForResource": { @@ -7629,6 +7927,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ListTagsForResourceResponse": { @@ -7646,6 +7947,9 @@ "smithy.api#documentation": "

The nextToken string returned on a previous page that you use to get the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#Long": { @@ -7888,7 +8192,7 @@ "MemberAccountRuleStatus": { "target": "com.amazonaws.configservice#MemberAccountRuleStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status for Config rule in the member account.\n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in the member account. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n\t\t

Config sets the state of the rule to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when Config rule has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when Config rule is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when Config rule creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when Config rule is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when Config rule has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when Config rule has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when Config rule is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
", + "smithy.api#documentation": "

Indicates deployment status for Config rule in the member account.\n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in the member account. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n

Config sets the state of the rule to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when Config rule has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when Config rule is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when Config rule creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when Config rule is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when Config rule has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when Config rule has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when Config rule is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -8036,7 +8340,7 @@ } }, "traits": { - "smithy.api#documentation": "

The Config rule in the request is invalid. Verify that the rule is an Config Custom Policy rule, that the rule name is correct, and that valid Amazon Resouce Names (ARNs) are used before trying again.

", + "smithy.api#documentation": "

The Config rule in the request is not valid. Verify that the rule is an Config Process Check rule, that the rule name is correct, and that valid Amazon Resouce Names (ARNs) are used before trying again.

", "smithy.api#error": "client" } }, @@ -8126,7 +8430,7 @@ } }, "traits": { - "smithy.api#documentation": "

The Config rule in the request is invalid. Verify that the rule is an organization Config Custom Policy rule, that the rule name is correct, and that valid Amazon Resouce Names (ARNs) are used before trying again.

", + "smithy.api#documentation": "

The Config rule in the request is not valid. Verify that the rule is an organization Config Process Check rule, that the rule name is correct, and that valid Amazon Resouce Names (ARNs) are used before trying again.

", "smithy.api#error": "client" } }, @@ -8141,7 +8445,7 @@ } }, "traits": { - "smithy.api#documentation": "

Config organization conformance pack that you passed in the filter does not exist.

\n\t\t

For DeleteOrganizationConformancePack, you tried to delete an organization conformance pack that does not exist.

", + "smithy.api#documentation": "

Config organization conformance pack that you passed in the filter does not exist.

\n

For DeleteOrganizationConformancePack, you tried to delete an organization conformance pack that does not exist.

", "smithy.api#error": "client" } }, @@ -8204,7 +8508,7 @@ } }, "traits": { - "smithy.api#documentation": "

For PutConfigurationAggregator API, you can see this exception for the following reasons:

\n\t\t
    \n
  • \n

    No permission to call EnableAWSServiceAccess API

    \n
  • \n
  • \n

    The configuration aggregator cannot be updated because your Amazon Web Services Organization management account or the delegated administrator role changed. \n\t\t\t\tDelete this aggregator and create a new one with the current Amazon Web Services Organization.

    \n
  • \n
  • \n

    The configuration aggregator is associated with a previous Amazon Web Services Organization and Config cannot aggregate data with current Amazon Web Services Organization. \n\t\t\t\tDelete this aggregator and create a new one with the current Amazon Web Services Organization.

    \n
  • \n
  • \n

    You are not a registered delegated administrator for Config with permissions to call ListDelegatedAdministrators API. \n\t\t\tEnsure that the management account registers delagated administrator for Config service principle name before the delegated administrator creates an aggregator.

    \n
  • \n
\t\n\t\t

For all OrganizationConfigRule and OrganizationConformancePack APIs, Config throws an exception if APIs are called from member accounts. All APIs must be called from organization management account.

", + "smithy.api#documentation": "

For PutConfigurationAggregator API, you can see this exception for the following reasons:

\n
    \n
  • \n

    No permission to call EnableAWSServiceAccess API

    \n
  • \n
  • \n

    The configuration aggregator cannot be updated because your Amazon Web Services Organization management account or the delegated administrator role changed. \n\t\t\t\tDelete this aggregator and create a new one with the current Amazon Web Services Organization.

    \n
  • \n
  • \n

    The configuration aggregator is associated with a previous Amazon Web Services Organization and Config cannot aggregate data with current Amazon Web Services Organization. \n\t\t\t\tDelete this aggregator and create a new one with the current Amazon Web Services Organization.

    \n
  • \n
  • \n

    You are not a registered delegated administrator for Config with permissions to call ListDelegatedAdministrators API. \n\t\t\tEnsure that the management account registers delagated administrator for Config service principle name before the delegated administrator creates an aggregator.

    \n
  • \n
\n

For all OrganizationConfigRule and OrganizationConformancePack APIs, Config throws an exception if APIs are called from member accounts. All APIs must be called from organization management account.

", "smithy.api#error": "client" } }, @@ -8344,7 +8648,7 @@ "OrganizationRuleStatus": { "target": "com.amazonaws.configservice#OrganizationRuleStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status of an organization Config rule. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in all the member accounts. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in all the member accounts. Additionally, Config rule status is updated when one or more member accounts join or leave an organization. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule in all the member accounts and disables service access for config-multiaccountsetup.amazonaws.com.

\n\t\t\t

Config sets the state of the rule to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when an organization Config rule has been successfully created in all the member accounts.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when an organization Config rule creation is in progress.

    \n
  • \n
  • \n

    \n CREATE_FAILED when an organization Config rule creation failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_FAILED when an organization Config rule deletion failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when an organization Config rule deletion is in progress.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when an organization Config rule has been successfully deleted from all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when an organization Config rule has been successfully updated in all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when an organization Config rule update is in progress.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when an organization Config rule update failed in one or more member accounts within that organization.

    \n
  • \n
", + "smithy.api#documentation": "

Indicates deployment status of an organization Config rule. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in all the member accounts. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in all the member accounts. Additionally, Config rule status is updated when one or more member accounts join or leave an organization. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule in all the member accounts and disables service access for config-multiaccountsetup.amazonaws.com.

\n

Config sets the state of the rule to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when an organization Config rule has been successfully created in all the member accounts.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when an organization Config rule creation is in progress.

    \n
  • \n
  • \n

    \n CREATE_FAILED when an organization Config rule creation failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_FAILED when an organization Config rule deletion failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when an organization Config rule deletion is in progress.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when an organization Config rule has been successfully deleted from all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when an organization Config rule has been successfully updated in all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when an organization Config rule update is in progress.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when an organization Config rule update failed in one or more member accounts within that organization.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -8455,13 +8759,13 @@ "DeliveryS3Bucket": { "target": "com.amazonaws.configservice#DeliveryS3Bucket", "traits": { - "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n \n

This field is optional.

\n
" } }, "DeliveryS3KeyPrefix": { "target": "com.amazonaws.configservice#DeliveryS3KeyPrefix", "traits": { - "smithy.api#documentation": "

Any folder structure you want to add to an Amazon S3 bucket.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

Any folder structure you want to add to an Amazon S3 bucket.

\n \n

This field is optional.

\n
" } }, "ConformancePackInputParameters": { @@ -8508,7 +8812,7 @@ "Status": { "target": "com.amazonaws.configservice#OrganizationResourceDetailedStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status for conformance pack in a member account.\n\t\t\tWhen management account calls PutOrganizationConformancePack action for the first time, conformance pack status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConformancePack action for the second time, conformance pack status is updated in the member account. \n\t\t\tConformance pack status is deleted when the management account deletes OrganizationConformancePack and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n\t\t

Config sets the state of the conformance pack to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when conformance pack has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when conformance pack is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when conformance pack creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when conformance pack is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when conformance pack has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when conformance pack has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when conformance pack is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
", + "smithy.api#documentation": "

Indicates deployment status for conformance pack in a member account.\n\t\t\tWhen management account calls PutOrganizationConformancePack action for the first time, conformance pack status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConformancePack action for the second time, conformance pack status is updated in the member account. \n\t\t\tConformance pack status is deleted when the management account deletes OrganizationConformancePack and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n

Config sets the state of the conformance pack to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when conformance pack has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when conformance pack is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when conformance pack creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when conformance pack is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when conformance pack has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when conformance pack has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when conformance pack is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -8576,7 +8880,7 @@ "Status": { "target": "com.amazonaws.configservice#OrganizationResourceStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status of an organization conformance pack. \n\t\t\tWhen management account calls PutOrganizationConformancePack for the first time, \n\t\t\tconformance pack status is created in all the member accounts. \n\t\t\tWhen management account calls PutOrganizationConformancePack for the second time, \n\t\t\tconformance pack status is updated in all the member accounts. \n\t\t\tAdditionally, conformance pack status is updated when one or more member accounts join or leave an \n\t\t\torganization. \n\t\t\tConformance pack status is deleted when the management account deletes \n\t\t\tOrganizationConformancePack in all the member accounts and disables service \n\t\t\taccess for config-multiaccountsetup.amazonaws.com.

\n\t\t

Config sets the state of the conformance pack to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when an organization conformance pack has been successfully created in all the member accounts.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when an organization conformance pack creation is in progress.

    \n
  • \n
  • \n

    \n CREATE_FAILED when an organization conformance pack creation failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_FAILED when an organization conformance pack deletion failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when an organization conformance pack deletion is in progress.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when an organization conformance pack has been successfully deleted from all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when an organization conformance pack has been successfully updated in all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when an organization conformance pack update is in progress.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when an organization conformance pack update failed in one or more member accounts within that organization.

    \n
  • \n
", + "smithy.api#documentation": "

Indicates deployment status of an organization conformance pack. \n\t\t\tWhen management account calls PutOrganizationConformancePack for the first time, \n\t\t\tconformance pack status is created in all the member accounts. \n\t\t\tWhen management account calls PutOrganizationConformancePack for the second time, \n\t\t\tconformance pack status is updated in all the member accounts. \n\t\t\tAdditionally, conformance pack status is updated when one or more member accounts join or leave an \n\t\t\torganization. \n\t\t\tConformance pack status is deleted when the management account deletes \n\t\t\tOrganizationConformancePack in all the member accounts and disables service \n\t\t\taccess for config-multiaccountsetup.amazonaws.com.

\n

Config sets the state of the conformance pack to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when an organization conformance pack has been successfully created in all the member accounts.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when an organization conformance pack creation is in progress.

    \n
  • \n
  • \n

    \n CREATE_FAILED when an organization conformance pack creation failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_FAILED when an organization conformance pack deletion failed in one or more member accounts within that organization.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when an organization conformance pack deletion is in progress.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when an organization conformance pack has been successfully deleted from all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when an organization conformance pack has been successfully updated in all the member accounts.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when an organization conformance pack update is in progress.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when an organization conformance pack update failed in one or more member accounts within that organization.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -8620,7 +8924,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have specified a template that is invalid or supported.

", + "smithy.api#documentation": "

You have specified a template that is not valid or supported.

", "smithy.api#error": "client" } }, @@ -8642,7 +8946,7 @@ "OrganizationConfigRuleTriggerTypes": { "target": "com.amazonaws.configservice#OrganizationConfigRuleTriggerTypeNoSNs", "traits": { - "smithy.api#documentation": "

The type of notification that initiates Config to run an evaluation for a rule.\n\t\t\tFor Config Custom Policy rules, Config supports change-initiated notification types:

\n\t\t\n\t\t
    \n
  • \n

    \n ConfigurationItemChangeNotification - Initiates an evaluation when Config delivers a configuration item as a result of a resource\n\t\t\t\t\tchange.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Initiates an evaluation when\n\t\t\t\t\t\tConfig delivers an oversized configuration item. Config may generate this notification type when a resource changes and the\n\t\t\t\t\tnotification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
" + "smithy.api#documentation": "

The type of notification that initiates Config to run an evaluation for a rule.\n\t\t\tFor Config Custom Policy rules, Config supports change-initiated notification types:

\n
    \n
  • \n

    \n ConfigurationItemChangeNotification - Initiates an evaluation when Config delivers a configuration item as a result of a resource\n\t\t\t\t\tchange.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Initiates an evaluation when\n\t\t\t\t\t\tConfig delivers an oversized configuration item. Config may generate this notification type when a resource changes and the\n\t\t\t\t\tnotification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
" } }, "InputParameters": { @@ -8718,7 +9022,7 @@ "OrganizationConfigRuleTriggerTypes": { "target": "com.amazonaws.configservice#OrganizationConfigRuleTriggerTypeNoSNs", "traits": { - "smithy.api#documentation": "

The type of notification that triggers Config to run an evaluation for a rule.\n\t\t\tFor Config Custom Policy rules, Config supports change\n\t\t\ttriggered notification types:

\n\t\t\n\t\t
    \n
  • \n

    \n ConfigurationItemChangeNotification - Triggers an evaluation when Config delivers a configuration item as a result of a resource change.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Triggers an evaluation when Config delivers an oversized configuration item. \n\t\t\t\tConfig may generate this notification type when a resource changes and the notification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
" + "smithy.api#documentation": "

The type of notification that triggers Config to run an evaluation for a rule.\n\t\t\tFor Config Custom Policy rules, Config supports change\n\t\t\ttriggered notification types:

\n
    \n
  • \n

    \n ConfigurationItemChangeNotification - Triggers an evaluation when Config delivers a configuration item as a result of a resource change.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Triggers an evaluation when Config delivers an oversized configuration item. \n\t\t\t\tConfig may generate this notification type when a resource changes and the notification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
" } }, "InputParameters": { @@ -8793,7 +9097,7 @@ "OrganizationConfigRuleTriggerTypes": { "target": "com.amazonaws.configservice#OrganizationConfigRuleTriggerTypes", "traits": { - "smithy.api#documentation": "

The type of notification that triggers Config to run an evaluation for a rule. You can specify the following notification types:

\n\t\t\n\t\t
    \n
  • \n

    \n ConfigurationItemChangeNotification - Triggers an evaluation when Config delivers a configuration item as a result of a resource change.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Triggers an evaluation when Config delivers an oversized configuration item. \n\t\t\t \tConfig may generate this notification type when a resource changes and the notification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
  • \n

    \n ScheduledNotification - Triggers a periodic evaluation at the frequency specified for MaximumExecutionFrequency.

    \n
  • \n
", + "smithy.api#documentation": "

The type of notification that triggers Config to run an evaluation for a rule. You can specify the following notification types:

\n
    \n
  • \n

    \n ConfigurationItemChangeNotification - Triggers an evaluation when Config delivers a configuration item as a result of a resource change.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification - Triggers an evaluation when Config delivers an oversized configuration item. \n\t\t\t \tConfig may generate this notification type when a resource changes and the notification exceeds the maximum size allowed by Amazon SNS.

    \n
  • \n
  • \n

    \n ScheduledNotification - Triggers a periodic evaluation at the frequency specified for MaximumExecutionFrequency.

    \n
  • \n
", "smithy.api#required": {} } }, @@ -8806,7 +9110,7 @@ "MaximumExecutionFrequency": { "target": "com.amazonaws.configservice#MaximumExecutionFrequency", "traits": { - "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations for a rule. \n\t\t\tYour custom rule is triggered when Config delivers the configuration snapshot. For more information, see ConfigSnapshotDeliveryProperties.

\n\t\t \n

By default, rules with a periodic trigger are evaluated every 24 hours. To change the frequency, specify a valid \n\t\t\tvalue for the MaximumExecutionFrequency parameter.

\n
" + "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations for a rule. \n\t\t\tYour custom rule is triggered when Config delivers the configuration snapshot. For more information, see ConfigSnapshotDeliveryProperties.

\n \n

By default, rules with a periodic trigger are evaluated every 24 hours. To change the frequency, specify a valid \n\t\t\tvalue for the MaximumExecutionFrequency parameter.

\n
" } }, "ResourceTypesScope": { @@ -8863,7 +9167,7 @@ "MaximumExecutionFrequency": { "target": "com.amazonaws.configservice#MaximumExecutionFrequency", "traits": { - "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations for a rule. This is for an Config managed rule that is triggered at a periodic frequency.

\n\t\t \n

By default, rules with a periodic trigger are evaluated every 24 hours. To change the frequency, specify a valid \n\t\t\tvalue for the MaximumExecutionFrequency parameter.

\n
" + "smithy.api#documentation": "

The maximum frequency with which Config runs evaluations for a rule. This is for an Config managed rule that is triggered at a periodic frequency.

\n \n

By default, rules with a periodic trigger are evaluated every 24 hours. To change the frequency, specify a valid \n\t\t\tvalue for the MaximumExecutionFrequency parameter.

\n
" } }, "ResourceTypesScope": { @@ -8966,7 +9270,7 @@ "Status": { "target": "com.amazonaws.configservice#OrganizationResourceDetailedStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status for conformance pack in a member account.\n\t\t\tWhen management account calls PutOrganizationConformancePack action for the first time, conformance pack status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConformancePack action for the second time, conformance pack status is updated in the member account. \n\t\t\tConformance pack status is deleted when the management account deletes OrganizationConformancePack and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n\t\t

Config sets the state of the conformance pack to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when conformance pack has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when conformance pack is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when conformance pack creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when conformance pack is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when conformance pack has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when conformance pack has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when conformance pack is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
" + "smithy.api#documentation": "

Indicates deployment status for conformance pack in a member account.\n\t\t\tWhen management account calls PutOrganizationConformancePack action for the first time, conformance pack status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConformancePack action for the second time, conformance pack status is updated in the member account. \n\t\t\tConformance pack status is deleted when the management account deletes OrganizationConformancePack and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t

\n

Config sets the state of the conformance pack to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when conformance pack has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when conformance pack is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when conformance pack creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when conformance pack is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when conformance pack has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when conformance pack has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when conformance pack is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when conformance pack deletion has failed in the member account.

    \n
  • \n
" } } }, @@ -9226,7 +9530,7 @@ } ], "traits": { - "smithy.api#documentation": "

Authorizes the aggregator account and region to collect data\n\t\t\tfrom the source account and region.

" + "smithy.api#documentation": "

Authorizes the aggregator account and region to collect data\n\t\t\tfrom the source account and region.

\n \n

\n PutAggregationAuthorization is an idempotent API. Subsequent requests won’t create a duplicate resource if one was already created. If a following request has different tags values,\n\t\t\tConfig will ignore these differences and treat it as an idempotent request of the previous. In this case, tags will not be updated, even if they are different.

\n
" } }, "com.amazonaws.configservice#PutAggregationAuthorizationRequest": { @@ -9252,6 +9556,9 @@ "smithy.api#documentation": "

An array of tag object.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutAggregationAuthorizationResponse": { @@ -9263,6 +9570,9 @@ "smithy.api#documentation": "

Returns an AggregationAuthorization object.\n\t\t\t\n\t\t

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutConfigRule": { @@ -9291,7 +9601,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates an Config rule to evaluate if your\n\t\t\tAmazon Web Services resources comply with your desired configurations. For information on how many Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n\t\t\n\t\t

There are two types of rules: Config Custom Rules and Config Managed Rules.\n\t\t\tYou can use PutConfigRule to create both Config custom rules and Config managed rules.

\n\t\t\n\t\t

Custom rules are rules that you can create using either Guard or Lambda functions.\n\t\t\tGuard (Guard GitHub\n\t\t\t\tRepository) is a policy-as-code language that allows you to write policies that\n\t\t\tare enforced by Config Custom Policy rules. Lambda uses custom code that you upload to\n\t\t\tevaluate a custom rule. If you are adding a new Custom Lambda rule,\n\t\t\tyou first need to create an Lambda function that the rule invokes to evaluate\n\t\t\tyour resources. When you use PutConfigRule to add a Custom Lambda rule to Config, you must specify the Amazon Resource\n\t\t\tName (ARN) that Lambda assigns to the function. You specify the ARN\n\t\t\tin the SourceIdentifier key. This key is part of the\n\t\t\tSource object, which is part of the\n\t\t\tConfigRule object.

\n\t\t\n\t\t

Managed rules are predefined,\n\t\t\tcustomizable rules created by Config. For a list of managed rules, see\n\t\t\tList of Config\n\t\t\t\tManaged Rules. If you are adding an Config managed rule, you must specify the\n\t\t\trule's identifier for the SourceIdentifier key.

\n\t\t\n\t\t

For any new rule that you add, specify the\n\t\t\t\tConfigRuleName in the ConfigRule\n\t\t\tobject. Do not specify the ConfigRuleArn or the\n\t\t\tConfigRuleId. These values are generated by Config for new rules.

\n\t\t

If you are updating a rule that you added previously, you can\n\t\t\tspecify the rule by ConfigRuleName,\n\t\t\t\tConfigRuleId, or ConfigRuleArn in the\n\t\t\t\tConfigRule data type that you use in this\n\t\t\trequest.

\n\n\t\t

For more information about developing and using Config\n\t\t\trules, see Evaluating Amazon Web Services resource Configurations with Config\n\t\t\tin the Config Developer Guide.

" + "smithy.api#documentation": "

Adds or updates an Config rule to evaluate if your\n\t\t\tAmazon Web Services resources comply with your desired configurations. For information on how many Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

There are two types of rules: Config Managed Rules and Config Custom Rules.\n\t\t\tYou can use PutConfigRule to create both Config Managed Rules and Config Custom Rules.

\n

Config Managed Rules are predefined,\n\t\t\tcustomizable rules created by Config. For a list of managed rules, see\n\t\t\tList of Config\n\t\t\t\tManaged Rules. If you are adding an Config managed rule, you must specify the\n\t\t\trule's identifier for the SourceIdentifier key.

\n

Config Custom Rules are rules that you create from scratch. There are two ways to create Config custom rules: with Lambda functions\n\t\t\t( Lambda Developer Guide) and with Guard (Guard GitHub\n\t\t\t\t\tRepository), a policy-as-code language.\n\t\t\t\n\t\t\tConfig custom rules created with Lambda\n\t\t\tare called Config Custom Lambda Rules and Config custom rules created with\n\t\t\tGuard are called Config Custom Policy Rules.

\n

If you are adding a new Config Custom Lambda rule,\n\t\t\tyou first need to create an Lambda function that the rule invokes to evaluate\n\t\t\tyour resources. When you use PutConfigRule to add a Custom Lambda rule to Config, you must specify the Amazon Resource\n\t\t\tName (ARN) that Lambda assigns to the function. You specify the ARN\n\t\t\tin the SourceIdentifier key. This key is part of the\n\t\t\tSource object, which is part of the\n\t\t\tConfigRule object.

\n

For any new Config rule that you add, specify the\n\t\t\t\tConfigRuleName in the ConfigRule\n\t\t\tobject. Do not specify the ConfigRuleArn or the\n\t\t\tConfigRuleId. These values are generated by Config for new rules.

\n

If you are updating a rule that you added previously, you can\n\t\t\tspecify the rule by ConfigRuleName,\n\t\t\t\tConfigRuleId, or ConfigRuleArn in the\n\t\t\t\tConfigRule data type that you use in this\n\t\t\trequest.

\n

For more information about developing and using Config\n\t\t\trules, see Evaluating Resources with Config Rules\n\t\t\tin the Config Developer Guide.

\n \n

\n PutConfigRule is an idempotent API. Subsequent requests won’t create a duplicate resource if one was already created. If a following request has different tags values,\n\t\t\tConfig will ignore these differences and treat it as an idempotent request of the previous. In this case, tags will not be updated, even if they are different.

\n
" } }, "com.amazonaws.configservice#PutConfigRuleRequest": { @@ -9310,6 +9620,9 @@ "smithy.api#documentation": "

An array of tag object.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutConfigurationAggregator": { @@ -9341,7 +9654,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates and updates the configuration aggregator with the\n\t\t\tselected source accounts and regions. The source account can be\n\t\t\tindividual account(s) or an organization.

\n\t\t\n\t\t

\n accountIds that are passed will be replaced with existing accounts.\n\t\t\tIf you want to add additional accounts into the aggregator, call DescribeConfigurationAggregators to get the previous accounts and then append new ones.

\n\t\t \n\t\t\t

Config should be enabled in source accounts and regions\n\t\t\t\tyou want to aggregate.

\n\t\t\t\n\t\t\t

If your source type is an organization, you must be signed in to the management account or a registered delegated administrator and all the features must be enabled in your organization. \n\t\t\t\tIf the caller is a management account, Config calls EnableAwsServiceAccess API to enable integration between Config and Organizations.\n\t\t\t\tIf the caller is a registered delegated administrator, Config calls ListDelegatedAdministrators API to verify whether the caller is a valid delegated administrator.

\n\t\t\t

To register a delegated administrator, see Register a Delegated Administrator in the Config developer guide.

\n\t\t
" + "smithy.api#documentation": "

Creates and updates the configuration aggregator with the\n\t\t\tselected source accounts and regions. The source account can be\n\t\t\tindividual account(s) or an organization.

\n

\n accountIds that are passed will be replaced with existing accounts.\n\t\t\tIf you want to add additional accounts into the aggregator, call DescribeConfigurationAggregators to get the previous accounts and then append new ones.

\n \n

Config should be enabled in source accounts and regions\n\t\t\t\tyou want to aggregate.

\n

If your source type is an organization, you must be signed in to the management account or a registered delegated administrator and all the features must be enabled in your organization. \n\t\t\t\tIf the caller is a management account, Config calls EnableAwsServiceAccess API to enable integration between Config and Organizations.\n\t\t\t\tIf the caller is a registered delegated administrator, Config calls ListDelegatedAdministrators API to verify whether the caller is a valid delegated administrator.

\n

To register a delegated administrator, see Register a Delegated Administrator in the Config developer guide.

\n
\n \n

\n PutConfigurationAggregator is an idempotent API. Subsequent requests won’t create a duplicate resource if one was already created. If a following request has different tags values,\n\t\t\tConfig will ignore these differences and treat it as an idempotent request of the previous. In this case, tags will not be updated, even if they are different.

\n
" } }, "com.amazonaws.configservice#PutConfigurationAggregatorRequest": { @@ -9372,6 +9685,9 @@ "smithy.api#documentation": "

An array of tag object.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutConfigurationAggregatorResponse": { @@ -9383,6 +9699,9 @@ "smithy.api#documentation": "

Returns a ConfigurationAggregator object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutConfigurationRecorder": { @@ -9408,7 +9727,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new configuration recorder to record the selected\n\t\t\tresource configurations.

\n\t\t

You can use this action to change the role roleARN\n\t\t\tor the recordingGroup of an existing recorder. To\n\t\t\tchange the role, call the action on the existing configuration\n\t\t\trecorder and specify a role.

\n\t\t \n\t\t\t

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account.

\n\t\t\t

If ConfigurationRecorder does not have the\n\t\t\t\t\trecordingGroup parameter\n\t\t\t\tspecified, the default is to record all supported resource\n\t\t\t\ttypes.

\n\t\t
" + "smithy.api#documentation": "

Creates a new configuration recorder to record the selected\n\t\t\tresource configurations.

\n

You can use this action to change the role roleARN\n\t\t\tor the recordingGroup of an existing recorder. To\n\t\t\tchange the role, call the action on the existing configuration\n\t\t\trecorder and specify a role.

\n \n

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account.

\n

If ConfigurationRecorder does not have the\n\t\t\t\t\trecordingGroup parameter\n\t\t\t\tspecified, the default is to record all supported resource\n\t\t\t\ttypes.

\n
" } }, "com.amazonaws.configservice#PutConfigurationRecorderRequest": { @@ -9423,7 +9742,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the PutConfigurationRecorder\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the PutConfigurationRecorder\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutConformancePack": { @@ -9452,7 +9772,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates or updates a conformance pack. A conformance pack is a collection of Config rules that can be easily deployed in an account and a region and across an organization.\n\t\t\tFor information on how many conformance packs you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n\t\t

This API creates a service-linked role AWSServiceRoleForConfigConforms in your account. \n\t\tThe service-linked role is created only when the role does not exist in your account.

\n\t\t \n

You must specify only one of the follow parameters: TemplateS3Uri, TemplateBody or TemplateSSMDocumentDetails.

\n
" + "smithy.api#documentation": "

Creates or updates a conformance pack. A conformance pack is a collection of Config rules that can be easily deployed in an account and a region and across an organization.\n\t\t\tFor information on how many conformance packs you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

This API creates a service-linked role AWSServiceRoleForConfigConforms in your account. \n\t\tThe service-linked role is created only when the role does not exist in your account.

\n \n

You must specify only one of the follow parameters: TemplateS3Uri, TemplateBody or TemplateSSMDocumentDetails.

\n
" } }, "com.amazonaws.configservice#PutConformancePackRequest": { @@ -9468,25 +9788,25 @@ "TemplateS3Uri": { "target": "com.amazonaws.configservice#TemplateS3Uri", "traits": { - "smithy.api#documentation": "

The location of the file containing the template body (s3://bucketname/prefix). The uri must point to a conformance pack template (max size: 300 KB) that is located in an Amazon S3 bucket in the same Region as the conformance pack.

\n\t\t \n

You must have access to read Amazon S3 bucket.

\n
" + "smithy.api#documentation": "

The location of the file containing the template body (s3://bucketname/prefix). The uri must point to a conformance pack template (max size: 300 KB) that is located in an Amazon S3 bucket in the same Region as the conformance pack.

\n \n

You must have access to read Amazon S3 bucket.

\n
" } }, "TemplateBody": { "target": "com.amazonaws.configservice#TemplateBody", "traits": { - "smithy.api#documentation": "

A string containing the full conformance pack template body. The structure containing the template body has a minimum length of 1 byte and a maximum length of 51,200 bytes.

\n\t\t \n

You can use a YAML template with two resource types: Config rule (AWS::Config::ConfigRule) and remediation action (AWS::Config::RemediationConfiguration).

\n
" + "smithy.api#documentation": "

A string containing the full conformance pack template body. The structure containing the template body has a minimum length of 1 byte and a maximum length of 51,200 bytes.

\n \n

You can use a YAML template with two resource types: Config rule (AWS::Config::ConfigRule) and remediation action (AWS::Config::RemediationConfiguration).

\n
" } }, "DeliveryS3Bucket": { "target": "com.amazonaws.configservice#DeliveryS3Bucket", "traits": { - "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n \n

This field is optional.

\n
" } }, "DeliveryS3KeyPrefix": { "target": "com.amazonaws.configservice#DeliveryS3KeyPrefix", "traits": { - "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n \n

This field is optional.

\n
" } }, "ConformancePackInputParameters": { @@ -9501,6 +9821,9 @@ "smithy.api#documentation": "

An object of type TemplateSSMDocumentDetails, which contains the name or the Amazon Resource Name (ARN) of the Amazon Web Services Systems Manager document (SSM document) and the version of the SSM document that is used to create a conformance pack.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutConformancePackResponse": { @@ -9512,6 +9835,9 @@ "smithy.api#documentation": "

ARN of the conformance pack.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutDeliveryChannel": { @@ -9549,7 +9875,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a delivery channel object to deliver configuration\n\t\t\tinformation to an Amazon S3 bucket and Amazon SNS topic.

\n\t\t

Before you can create a delivery channel, you must create a\n\t\t\tconfiguration recorder.

\n\t\t

You can use this action to change the Amazon S3 bucket or an\n\t\t\tAmazon SNS topic of the existing delivery channel. To change the\n\t\t\tAmazon S3 bucket or an Amazon SNS topic, call this action and\n\t\t\tspecify the changed values for the S3 bucket and the SNS topic. If\n\t\t\tyou specify a different value for either the S3 bucket or the SNS\n\t\t\ttopic, this action will keep the existing value for the parameter\n\t\t\tthat is not changed.

\n\t\t \n\t\t\t

You can have only one delivery channel per region in your\n\t\t\t\taccount.

\n\t\t\t\n\n\t\t
" + "smithy.api#documentation": "

Creates a delivery channel object to deliver configuration\n\t\t\tinformation to an Amazon S3 bucket and Amazon SNS topic.

\n

Before you can create a delivery channel, you must create a\n\t\t\tconfiguration recorder.

\n

You can use this action to change the Amazon S3 bucket or an\n\t\t\tAmazon SNS topic of the existing delivery channel. To change the\n\t\t\tAmazon S3 bucket or an Amazon SNS topic, call this action and\n\t\t\tspecify the changed values for the S3 bucket and the SNS topic. If\n\t\t\tyou specify a different value for either the S3 bucket or the SNS\n\t\t\ttopic, this action will keep the existing value for the parameter\n\t\t\tthat is not changed.

\n \n

You can have only one delivery channel per region in your\n\t\t\t\taccount.

\n
" } }, "com.amazonaws.configservice#PutDeliveryChannelRequest": { @@ -9564,7 +9890,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the PutDeliveryChannel\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the PutDeliveryChannel\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutEvaluations": { @@ -9610,12 +9937,13 @@ "target": "com.amazonaws.configservice#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Use this parameter to specify a test run for\n\t\t\tPutEvaluations. You can verify whether your Lambda function will deliver evaluation results to Config. No\n\t\t\tupdates occur to your existing evaluations, and evaluation results\n\t\t\tare not sent to Config.

\n\n\t\t \n\t\t\t

When TestMode is true,\n\t\t\t\t\tPutEvaluations doesn't require a valid value\n\t\t\t\tfor the ResultToken parameter, but the value cannot\n\t\t\t\tbe null.

\n\t\t
" + "smithy.api#documentation": "

Use this parameter to specify a test run for\n\t\t\tPutEvaluations. You can verify whether your Lambda function will deliver evaluation results to Config. No\n\t\t\tupdates occur to your existing evaluations, and evaluation results\n\t\t\tare not sent to Config.

\n \n

When TestMode is true,\n\t\t\t\t\tPutEvaluations doesn't require a valid value\n\t\t\t\tfor the ResultToken parameter, but the value cannot\n\t\t\t\tbe null.

\n
" } } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutEvaluationsResponse": { @@ -9629,7 +9957,8 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutExternalEvaluation": { @@ -9669,11 +9998,17 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutExternalEvaluationResponse": { "type": "structure", - "members": {} + "members": {}, + "traits": { + "smithy.api#output": {} + } }, "com.amazonaws.configservice#PutOrganizationConfigRule": { "type": "operation", @@ -9710,7 +10045,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates an Config rule for your entire organization to evaluate if your Amazon Web Services resources comply with your \n\t\t\tdesired configurations. For information on how many organization Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n\t

Only a management account and a delegated administrator can create or update an organization Config rule.\n\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\tListDelegatedAdministrator permissions are added. An organization can have up to 3 delegated administrators.

\n\t\t

This API enables organization service access through the EnableAWSServiceAccess action and creates a service-linked \n\t\t\trole AWSServiceRoleForConfigMultiAccountSetup in the management or delegated administrator account of your organization. \n\t\t\tThe service-linked role is created only when the role does not exist in the caller account. \n\t\t\tConfig verifies the existence of role with GetRole action.

\n\t\t

To use this API with delegated administrator, register a delegated administrator by calling Amazon Web Services Organization\n\t\t\tregister-delegated-administrator for config-multiaccountsetup.amazonaws.com.

\n\t\n\t\t

There are two types of rules: Config Custom Rules and Config Managed Rules.\n\t\t\tYou can use PutOrganizationConfigRule to create both Config custom rules and Config managed rules.

\n\t\t\t\n\t\t

Custom rules are rules that you can create using either Guard or Lambda functions.\n\t\t\tGuard (Guard GitHub\n\t\t\t\tRepository) is a policy-as-code language that allows you to write policies that\n\t\t\tare enforced by Config Custom Policy rules. Lambda uses custom code that you upload to\n\t\t\tevaluate a custom rule. If you are adding a new Custom Lambda rule, you first need to create an Lambda function in the management account or a delegated \n\t\tadministrator that the rule invokes to evaluate your resources. You also need to create an IAM role in the managed account that can be assumed by the Lambda function.\n\t\tWhen you use PutOrganizationConfigRule to add a Custom Lambda rule to Config, you must \n\t\t\tspecify the Amazon Resource Name (ARN) that Lambda assigns to the function.

\n\t\t\n\t\t

Managed rules are predefined,\n\t\t\tcustomizable rules created by Config. For a list of managed rules, see\n\t\t\tList of Config\n\t\t\t\tManaged Rules. If you are adding an Config managed rule, you must specify the rule's identifier for the RuleIdentifier key.

\n\t\t\n\t\t\n\t\t \n

Prerequisite: Ensure you call EnableAllFeatures API to enable all features in an organization.

\n\t\t\t

Make sure to specify one of either OrganizationCustomPolicyRuleMetadata for Custom Policy rules, OrganizationCustomRuleMetadata for Custom Lambda rules, or OrganizationManagedRuleMetadata for managed rules.

\n\t\t\t
" + "smithy.api#documentation": "

Adds or updates an Config rule for your entire organization to evaluate if your Amazon Web Services resources comply with your \n\t\t\tdesired configurations. For information on how many organization Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

Only a management account and a delegated administrator can create or update an organization Config rule.\n\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\tListDelegatedAdministrator permissions are added. An organization can have up to 3 delegated administrators.

\n

This API enables organization service access through the EnableAWSServiceAccess action and creates a service-linked \n\t\t\trole AWSServiceRoleForConfigMultiAccountSetup in the management or delegated administrator account of your organization. \n\t\t\tThe service-linked role is created only when the role does not exist in the caller account. \n\t\t\tConfig verifies the existence of role with GetRole action.

\n

To use this API with delegated administrator, register a delegated administrator by calling Amazon Web Services Organization\n\t\t\tregister-delegated-administrator for config-multiaccountsetup.amazonaws.com.

\n

There are two types of rules: Config Managed Rules and Config Custom Rules. \n\t\t\tYou can use PutOrganizationConfigRule to create both Config Managed Rules and Config Custom Rules.

\n

Config Managed Rules are predefined,\n\t\t\tcustomizable rules created by Config. For a list of managed rules, see\n\t\t\tList of Config\n\t\t\t\tManaged Rules. If you are adding an Config managed rule, you must specify the rule's identifier for the RuleIdentifier key.

\n

Config Custom Rules are rules that you create from scratch. There are two ways to create Config custom rules: with Lambda functions\n\t\t\t( Lambda Developer Guide) and with Guard (Guard GitHub\n\t\t\t\t\tRepository), a policy-as-code language.\n\t\t\t\n\t\t\tConfig custom rules created with Lambda\n\t\t\tare called Config Custom Lambda Rules and Config custom rules created with\n\t\t\tGuard are called Config Custom Policy Rules.

\n

If you are adding a new Config Custom Lambda rule, you first need to create an Lambda function in the management account or a delegated \n\t\tadministrator that the rule invokes to evaluate your resources. You also need to create an IAM role in the managed account that can be assumed by the Lambda function.\n\t\tWhen you use PutOrganizationConfigRule to add a Custom Lambda rule to Config, you must \n\t\t\tspecify the Amazon Resource Name (ARN) that Lambda assigns to the function.

\n \n

Prerequisite: Ensure you call EnableAllFeatures API to enable all features in an organization.

\n

Make sure to specify one of either OrganizationCustomPolicyRuleMetadata for Custom Policy rules, OrganizationCustomRuleMetadata for Custom Lambda rules, or OrganizationManagedRuleMetadata for managed rules.

\n
" } }, "com.amazonaws.configservice#PutOrganizationConfigRuleRequest": { @@ -9747,6 +10082,9 @@ "smithy.api#documentation": "

An OrganizationCustomPolicyRuleMetadata object. This object specifies metadata for your organization's Config Custom Policy rule. The metadata includes the runtime system in use, which accounts have debug\n\t\t\tlogging enabled, and other custom rule metadata, such as resource type, resource ID of\n\t\t\tAmazon Web Services resource, and organization trigger types that initiate Config to evaluate Amazon Web Services resources against a rule.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutOrganizationConfigRuleResponse": { @@ -9758,6 +10096,9 @@ "smithy.api#documentation": "

The Amazon Resource Name (ARN) of an organization Config rule.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutOrganizationConformancePack": { @@ -9795,7 +10136,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deploys conformance packs across member accounts in an Amazon Web Services Organization. For information on how many organization conformance packs and how many Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n\t\t

Only a management account and a delegated administrator can call this API. \n\t\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\t\tListDelegatedAdministrator permissions are added. An organization can have up to 3 delegated administrators.

\n\t\t

This API enables organization service access for config-multiaccountsetup.amazonaws.com\n\t\t\tthrough the EnableAWSServiceAccess action and creates a \n\t\t\tservice-linked role AWSServiceRoleForConfigMultiAccountSetup in the management or delegated administrator account of your organization. \n\t\t\tThe service-linked role is created only when the role does not exist in the caller account. \n\t\t\tTo use this API with delegated administrator, register a delegated administrator by calling Amazon Web Services Organization \n\t\t\tregister-delegate-admin for config-multiaccountsetup.amazonaws.com.

\n\n\t\t\t\n\t\t\t \n\t\t \n\t\t\t

Prerequisite: Ensure you call EnableAllFeatures API to enable all features in an organization.

\n\t\t\t

You must specify either the TemplateS3Uri or the TemplateBody parameter, but not both. \n\t\t\tIf you provide both Config uses the TemplateS3Uri parameter and ignores the TemplateBody parameter.

\n\t\t\t

Config sets the state of a conformance pack to CREATE_IN_PROGRESS and UPDATE_IN_PROGRESS until the conformance pack is created or updated. \n\t\t\t\tYou cannot update a conformance pack while it is in this state.

\n
" + "smithy.api#documentation": "

Deploys conformance packs across member accounts in an Amazon Web Services Organization. For information on how many organization conformance packs and how many Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

Only a management account and a delegated administrator can call this API. \n\t\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\t\tListDelegatedAdministrator permissions are added. An organization can have up to 3 delegated administrators.

\n

This API enables organization service access for config-multiaccountsetup.amazonaws.com\n\t\t\tthrough the EnableAWSServiceAccess action and creates a \n\t\t\tservice-linked role AWSServiceRoleForConfigMultiAccountSetup in the management or delegated administrator account of your organization. \n\t\t\tThe service-linked role is created only when the role does not exist in the caller account. \n\t\t\tTo use this API with delegated administrator, register a delegated administrator by calling Amazon Web Services Organization \n\t\t\tregister-delegate-admin for config-multiaccountsetup.amazonaws.com.

\n \n

Prerequisite: Ensure you call EnableAllFeatures API to enable all features in an organization.

\n

You must specify either the TemplateS3Uri or the TemplateBody parameter, but not both. \n\t\t\tIf you provide both Config uses the TemplateS3Uri parameter and ignores the TemplateBody parameter.

\n

Config sets the state of a conformance pack to CREATE_IN_PROGRESS and UPDATE_IN_PROGRESS until the conformance pack is created or updated. \n\t\t\t\tYou cannot update a conformance pack while it is in this state.

\n
" } }, "com.amazonaws.configservice#PutOrganizationConformancePackRequest": { @@ -9811,7 +10152,7 @@ "TemplateS3Uri": { "target": "com.amazonaws.configservice#TemplateS3Uri", "traits": { - "smithy.api#documentation": "

Location of file containing the template body. The uri must point to the conformance pack template\n\t\t\t(max size: 300 KB).

\n\t\t \n

You must have access to read Amazon S3 bucket.

\n
" + "smithy.api#documentation": "

Location of file containing the template body. The uri must point to the conformance pack template\n\t\t\t(max size: 300 KB).

\n \n

You must have access to read Amazon S3 bucket.

\n
" } }, "TemplateBody": { @@ -9823,13 +10164,13 @@ "DeliveryS3Bucket": { "target": "com.amazonaws.configservice#DeliveryS3Bucket", "traits": { - "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n\t\t \n

This field is optional. If used, it must be prefixed with awsconfigconforms.

\n
" + "smithy.api#documentation": "

The name of the Amazon S3 bucket where Config stores conformance pack templates.

\n \n

This field is optional. If used, it must be prefixed with awsconfigconforms.

\n
" } }, "DeliveryS3KeyPrefix": { "target": "com.amazonaws.configservice#DeliveryS3KeyPrefix", "traits": { - "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The prefix for the Amazon S3 bucket.

\n \n

This field is optional.

\n
" } }, "ConformancePackInputParameters": { @@ -9844,6 +10185,9 @@ "smithy.api#documentation": "

A list of Amazon Web Services accounts to be excluded from an organization conformance pack while deploying a conformance pack.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutOrganizationConformancePackResponse": { @@ -9855,6 +10199,9 @@ "smithy.api#documentation": "

ARN of the organization conformance pack.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutRemediationConfigurations": { @@ -9874,7 +10221,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates the remediation configuration with a specific Config rule with the \n\t\t\tselected target or action. \n\t\t\tThe API creates the RemediationConfiguration object for the Config rule. \n\t\tThe Config rule must already exist for you to add a remediation configuration. \n\t\tThe target (SSM document) must exist and have permissions to use the target.

\n\t\t \n

If you make backward incompatible changes to the SSM document, \n\t\t\tyou must call this again to ensure the remediations can run.

\n\t\t\t

This API does not support adding remediation configurations for service-linked Config Rules such as Organization Config rules, \n\t\t\t\tthe rules deployed by conformance packs, and rules deployed by Amazon Web Services Security Hub.

\n
\n\t\t \n

For manual remediation configuration, you need to provide a value for automationAssumeRole or use a value in the assumeRolefield to remediate your resources. The SSM automation document can use either as long as it maps to a valid parameter.

\n\t\t\t

However, for automatic remediation configuration, the only valid assumeRole field value is AutomationAssumeRole and you need to provide a value for AutomationAssumeRole to remediate your resources.

\n\t\t
" + "smithy.api#documentation": "

Adds or updates the remediation configuration with a specific Config rule with the \n\t\t\tselected target or action. \n\t\t\tThe API creates the RemediationConfiguration object for the Config rule. \n\t\tThe Config rule must already exist for you to add a remediation configuration. \n\t\tThe target (SSM document) must exist and have permissions to use the target.

\n \n

If you make backward incompatible changes to the SSM document, \n\t\t\tyou must call this again to ensure the remediations can run.

\n

This API does not support adding remediation configurations for service-linked Config Rules such as Organization Config rules, \n\t\t\t\tthe rules deployed by conformance packs, and rules deployed by Amazon Web Services Security Hub.

\n
\n \n

For manual remediation configuration, you need to provide a value for automationAssumeRole or use a value in the assumeRolefield to remediate your resources. The SSM automation document can use either as long as it maps to a valid parameter.

\n

However, for automatic remediation configuration, the only valid assumeRole field value is AutomationAssumeRole and you need to provide a value for AutomationAssumeRole to remediate your resources.

\n
" } }, "com.amazonaws.configservice#PutRemediationConfigurationsRequest": { @@ -9887,6 +10234,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutRemediationConfigurationsResponse": { @@ -9898,6 +10248,9 @@ "smithy.api#documentation": "

Returns a list of failed remediation batch objects.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutRemediationExceptions": { @@ -9917,7 +10270,7 @@ } ], "traits": { - "smithy.api#documentation": "

A remediation exception is when a specific resource is no longer considered for auto-remediation. \n\t\t\tThis API adds a new exception or updates an existing exception for a specific resource with a specific Config rule.

\n\t\t \n

Config generates a remediation exception when a problem occurs executing a remediation action to a specific resource. \n\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n
\n\t\t \n

To place an exception on an Amazon Web Services resource, ensure remediation is set as manual remediation.

\n
" + "smithy.api#documentation": "

A remediation exception is when a specified resource is no longer considered for auto-remediation. \n\t\t\tThis API adds a new exception or updates an existing exception for a specified resource with a specified Config rule.

\n \n

Config generates a remediation exception when a problem occurs running a remediation action for a specified resource. \n\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n
\n \n

When placing an exception on an Amazon Web Services resource, it is recommended that remediation is set as manual remediation until\n\t\t\tthe given Config rule for the specified resource evaluates the resource as NON_COMPLIANT.\n\t\t\tOnce the resource has been evaluated as NON_COMPLIANT, you can add remediation exceptions and change the remediation type back from Manual to Auto if you want to use auto-remediation.\n\t\t\tOtherwise, using auto-remediation before a NON_COMPLIANT evaluation result can delete resources before the exception is applied.

\n
\n \n

Placing an exception can only be performed on resources that are NON_COMPLIANT.\n\t\t\tIf you use this API for COMPLIANT resources or resources that are NOT_APPLICABLE, a remediation exception will not be generated.\n\t\t\tFor more information on the conditions that initiate the possible Config evaluation results,\n\t\t\tsee Concepts | Config Rules in the Config Developer Guide.

\n
" } }, "com.amazonaws.configservice#PutRemediationExceptionsRequest": { @@ -9949,6 +10302,9 @@ "smithy.api#documentation": "

The exception is automatically deleted after the expiration date.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutRemediationExceptionsResponse": { @@ -9960,6 +10316,9 @@ "smithy.api#documentation": "

Returns a list of failed remediation exceptions batch objects. Each object in the batch consists of a list of failed items and failure messages.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutResourceConfig": { @@ -9985,7 +10344,7 @@ } ], "traits": { - "smithy.api#documentation": "

Records the configuration state for the resource provided in the request.\n\t\t\t \n\t\t\tThe configuration state of a resource is represented in Config as Configuration Items. \n\t\t\tOnce this API records the configuration item, you can retrieve the list of configuration items for the custom resource type using existing Config APIs.

\n\t\t \n

The custom resource type must be registered with CloudFormation. This API accepts the configuration item registered with CloudFormation.

\n\t\t\t

When you call this API, Config only stores configuration state of the resource provided in the request. This API does not change or remediate the configuration of the resource.\n\t\t\t\t

\n\t\t

Write-only schema properites are not recorded as part of the published configuration item.

\n
" + "smithy.api#documentation": "

Records the configuration state for the resource provided in the request.\n\t\t\t \n\t\t\tThe configuration state of a resource is represented in Config as Configuration Items. \n\t\t\tOnce this API records the configuration item, you can retrieve the list of configuration items for the custom resource type using existing Config APIs.

\n \n

The custom resource type must be registered with CloudFormation. This API accepts the configuration item registered with CloudFormation.

\n

When you call this API, Config only stores configuration state of the resource provided in the request. This API does not change or remediate the configuration of the resource.\n\t\t\t\t

\n

Write-only schema properites are not recorded as part of the published configuration item.

\n
" } }, "com.amazonaws.configservice#PutResourceConfigRequest": { @@ -9994,7 +10353,7 @@ "ResourceType": { "target": "com.amazonaws.configservice#ResourceTypeString", "traits": { - "smithy.api#documentation": "

The type of the resource. The custom resource type must be registered with CloudFormation.

\n\t\t \n

You cannot use the organization names “amzn”, “amazon”, “alexa”, “custom” with custom resource types. It is the first part of the ResourceType up to the first ::.

\n
", + "smithy.api#documentation": "

The type of the resource. The custom resource type must be registered with CloudFormation.

\n \n

You cannot use the organization names “amzn”, “amazon”, “alexa”, “custom” with custom resource types. It is the first part of the ResourceType up to the first ::.

\n
", "smithy.api#required": {} } }, @@ -10021,16 +10380,19 @@ "Configuration": { "target": "com.amazonaws.configservice#Configuration", "traits": { - "smithy.api#documentation": "

The configuration object of the resource in valid JSON format. It must match the schema registered with CloudFormation.

\n\t\t \n

The configuration JSON must not exceed 64 KB.

\n
", + "smithy.api#documentation": "

The configuration object of the resource in valid JSON format. It must match the schema registered with CloudFormation.

\n \n

The configuration JSON must not exceed 64 KB.

\n
", "smithy.api#required": {} } }, "Tags": { "target": "com.amazonaws.configservice#Tags", "traits": { - "smithy.api#documentation": "

Tags associated with the resource.

\n\t\t \n

This field is not to be confused with the Amazon Web Services-wide tag feature for Amazon Web Services resources.\n\t\t\tTags for PutResourceConfig are tags that you supply for the configuration items of your custom resources.

\n
" + "smithy.api#documentation": "

Tags associated with the resource.

\n \n

This field is not to be confused with the Amazon Web Services-wide tag feature for Amazon Web Services resources.\n\t\t\tTags for PutResourceConfig are tags that you supply for the configuration items of your custom resources.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutRetentionConfiguration": { @@ -10050,7 +10412,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates and updates the retention configuration with details\n\t\t\tabout retention period (number of days) that Config stores your\n\t\t\thistorical information. The API creates the\n\t\t\t\tRetentionConfiguration object and names the object\n\t\t\tas default. When you have a\n\t\t\t\tRetentionConfiguration object named default, calling the API modifies the\n\t\t\tdefault object.

\n\t\t \n\t\t\t

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n\t\t
" + "smithy.api#documentation": "

Creates and updates the retention configuration with details\n\t\t\tabout retention period (number of days) that Config stores your\n\t\t\thistorical information. The API creates the\n\t\t\t\tRetentionConfiguration object and names the object\n\t\t\tas default. When you have a\n\t\t\t\tRetentionConfiguration object named default, calling the API modifies the\n\t\t\tdefault object.

\n \n

Currently, Config supports only one retention\n\t\t\t\tconfiguration per region in your account.

\n
" } }, "com.amazonaws.configservice#PutRetentionConfigurationRequest": { @@ -10060,10 +10422,13 @@ "target": "com.amazonaws.configservice#RetentionPeriodInDays", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Number of days Config stores your historical\n\t\t\tinformation.

\n\t\t \n\t\t\t

Currently, only applicable to the configuration item\n\t\t\t\thistory.

\n\t\t
", + "smithy.api#documentation": "

Number of days Config stores your historical\n\t\t\tinformation.

\n \n

Currently, only applicable to the configuration item\n\t\t\t\thistory.

\n
", "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutRetentionConfigurationResponse": { @@ -10075,6 +10440,9 @@ "smithy.api#documentation": "

Returns a retention configuration object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#PutStoredQuery": { @@ -10097,7 +10465,7 @@ } ], "traits": { - "smithy.api#documentation": "

Saves a new query or updates an existing saved query. The QueryName must be unique for a single Amazon Web Services account and a single Amazon Web Services Region.\n\t\t\tYou can create upto 300 queries in a single Amazon Web Services account and a single Amazon Web Services Region.

" + "smithy.api#documentation": "

Saves a new query or updates an existing saved query. The QueryName must be unique for a single Amazon Web Services account and a single Amazon Web Services Region.\n\t\t\tYou can create upto 300 queries in a single Amazon Web Services account and a single Amazon Web Services Region.

\n \n

\n PutStoredQuery is an idempotent API. Subsequent requests won’t create a duplicate resource if one was already created. If a following request has different tags values,\n\t\t\tConfig will ignore these differences and treat it as an idempotent request of the previous. In this case, tags will not be updated, even if they are different.

\n
" } }, "com.amazonaws.configservice#PutStoredQueryRequest": { @@ -10106,7 +10474,7 @@ "StoredQuery": { "target": "com.amazonaws.configservice#StoredQuery", "traits": { - "smithy.api#documentation": "

A list of StoredQuery objects. \n\t\t\tThe mandatory fields are QueryName and Expression.

\n\t\t \n

When you are creating a query, you must provide a query name and an expression. \n\t\t\tWhen you are updating a query, you must provide a query name but updating the description is optional.

\n
", + "smithy.api#documentation": "

A list of StoredQuery objects. \n\t\t\tThe mandatory fields are QueryName and Expression.

\n \n

When you are creating a query, you must provide a query name and an expression. \n\t\t\tWhen you are updating a query, you must provide a query name but updating the description is optional.

\n
", "smithy.api#required": {} } }, @@ -10116,6 +10484,9 @@ "smithy.api#documentation": "

A list of Tags object.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#PutStoredQueryResponse": { @@ -10127,6 +10498,9 @@ "smithy.api#documentation": "

Amazon Resource Name (ARN) of the query. \n\t\t\tFor example, arn:partition:service:region:account-id:resource-type/resource-name/resource-id.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#QueryArn": { @@ -10232,25 +10606,25 @@ "target": "com.amazonaws.configservice#AllSupported", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Config records configuration changes for\n\t\t\tevery supported type of regional resource.

\n\t\t

If you set this option to true, when Config\n\t\t\tadds support for a new type of regional resource, it starts\n\t\t\trecording resources of that type automatically.

\n\t\t

If you set this option to true, you cannot\n\t\t\tenumerate a list of resourceTypes.

" + "smithy.api#documentation": "

Specifies whether Config records configuration changes for\n\t\t\tevery supported type of regional resource.

\n

If you set this option to true, when Config\n\t\t\tadds support for a new type of regional resource, it starts\n\t\t\trecording resources of that type automatically.

\n

If you set this option to true, you cannot\n\t\t\tenumerate a list of resourceTypes.

" } }, "includeGlobalResourceTypes": { "target": "com.amazonaws.configservice#IncludeGlobalResourceTypes", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Config includes all supported types of\n\t\t\tglobal resources (for example, IAM resources) with the resources\n\t\t\tthat it records.

\n\t\t

Before you can set this option to true, you must\n\t\t\tset the allSupported option to\n\t\t\ttrue.

\n\t\t

If you set this option to true, when Config\n\t\t\tadds support for a new type of global resource, it starts recording\n\t\t\tresources of that type automatically.

\n\t\t

The configuration details for any global resource are the same\n\t\t\tin all regions. To prevent duplicate configuration items, you should\n\t\t\tconsider customizing Config in only one region to record global\n\t\t\tresources.

" + "smithy.api#documentation": "

Specifies whether Config includes all supported types of\n\t\t\tglobal resources (for example, IAM resources) with the resources\n\t\t\tthat it records.

\n

Before you can set this option to true, you must\n\t\t\tset the allSupported option to\n\t\t\ttrue.

\n

If you set this option to true, when Config\n\t\t\tadds support for a new type of global resource, it starts recording\n\t\t\tresources of that type automatically.

\n

The configuration details for any global resource are the same\n\t\t\tin all regions. To prevent duplicate configuration items, you should\n\t\t\tconsider customizing Config in only one region to record global\n\t\t\tresources.

" } }, "resourceTypes": { "target": "com.amazonaws.configservice#ResourceTypeList", "traits": { - "smithy.api#documentation": "

A comma-separated list that specifies the types of Amazon Web Services\n\t\t\tresources for which Config records configuration changes (for\n\t\t\texample, AWS::EC2::Instance or\n\t\t\t\tAWS::CloudTrail::Trail).

\n\t\t

To record all configuration changes, you must\n\t\t\tset the allSupported option to\n\t\t\ttrue.

\n\t\t

If you set this option to false, when Config\n\t\t\tadds support for a new type of resource, it will not record\n\t\t\tresources of that type unless you manually add that type to your\n\t\t\trecording group.

\n\t\t

For a list of valid resourceTypes values, see the\n\t\t\t\tresourceType Value column in\n\t\t\t\tSupported Amazon Web Services resource Types.

" + "smithy.api#documentation": "

A comma-separated list that specifies the types of Amazon Web Services\n\t\t\tresources for which Config records configuration changes (for\n\t\t\texample, AWS::EC2::Instance or\n\t\t\t\tAWS::CloudTrail::Trail).

\n

To record all configuration changes, you must\n\t\t\tset the allSupported option to\n\t\t\ttrue.

\n

If you set the AllSupported option to false and populate the ResourceTypes option with values,\n\t\t\twhen Config adds support for a new type of resource,\n\t\t\tit will not record resources of that type unless you manually add that type to your recording group.

\n

For a list of valid resourceTypes values, see the\n\t\t\t\tresourceType Value column in\n\t\t\t\tSupported Amazon Web Services resource Types.

" } } }, "traits": { - "smithy.api#documentation": "

Specifies which Amazon Web Services resource types Config\n\t\t\trecords for configuration changes. In the recording group, you specify whether you want to record all supported resource types\n\t\t\tor only specific types of resources.

\n\t\t\n\t \t

By default, Config records the configuration changes for all supported types of\n\t\t\t\tregional resources that Config discovers in the region in which it is\n\t\t\t\trunning. Regional resources are tied to a region and can be used only in that region. Examples\n\t\t\t\tof regional resources are EC2 instances and EBS volumes.

\n\t\t\t

You can also have Config record supported types of global resources.\n\t\t\t\tGlobal resources are not tied to a specific region and can be used in all regions. The global\n\t\t\t\tresource types that Config supports include IAM users, groups, roles, and customer managed\n\t\t\t\tpolicies.

\n\t\t\n\t\t \n\t\t\t

Global resource types onboarded to Config recording after February 2022 will only be\n\t\t\t\trecorded in the service's home region for the commercial partition and\n\t\t\t\tAmazon Web Services GovCloud (US) West for the GovCloud partition. You can view the Configuration Items for\n\t\t\t\tthese new global resource types only in their home region and Amazon Web Services GovCloud (US) West.

\n\t\t\t\n\t\t\t

Supported global resource types onboarded before February 2022 such as\n\t\t\t\tAWS::IAM::Group, AWS::IAM::Policy, AWS::IAM::Role,\n\t\t\t\tAWS::IAM::User remain unchanged, and they will continue to deliver\n\t\t\t\tConfiguration Items in all supported regions in Config. The change will only affect new global\n\t\t\t\tresource types onboarded after February 2022.

\n\t\t\t\n\t\t\t

To record global resource types onboarded after February 2022,\n\t\t\t\tenable All Supported Resource Types in the home region of the global resource type you want to record.

\t\n\t\t
\n\t\t\n\t\t

If you don't want Config to record all resources, you can\n\t\t\tspecify which types of resources it will record with the\n\t\t\t\tresourceTypes parameter.

\n\t\t

For a list of supported resource types, see Supported Resource Types.

\n\t\t

For more information and a table of the Home Regions for Global Resource Types Onboarded after February 2022, see Selecting Which Resources Config Records.

" + "smithy.api#documentation": "

Specifies which Amazon Web Services resource types Config\n\t\t\trecords for configuration changes. In the recording group, you specify whether you want to record all supported resource types\n\t\t\tor only specific types of resources.

\n

By default, Config records the configuration changes for all supported types of\n\t\t\t\tregional resources that Config discovers in the region in which it is\n\t\t\t\trunning. Regional resources are tied to a region and can be used only in that region. Examples\n\t\t\t\tof regional resources are EC2 instances and EBS volumes.

\n

You can also have Config record supported types of global resources.\n\t\t\t\tGlobal resources are not tied to a specific region and can be used in all regions. The global\n\t\t\t\tresource types that Config supports include IAM users, groups, roles, and customer managed\n\t\t\t\tpolicies.

\n \n

Global resource types onboarded to Config recording after February 2022 will only be\n\t\t\t\trecorded in the service's home region for the commercial partition and\n\t\t\t\tAmazon Web Services GovCloud (US) West for the GovCloud partition. You can view the Configuration Items for\n\t\t\t\tthese new global resource types only in their home region and Amazon Web Services GovCloud (US) West.

\n

Supported global resource types onboarded before February 2022 such as\n\t\t\t\tAWS::IAM::Group, AWS::IAM::Policy, AWS::IAM::Role,\n\t\t\t\tAWS::IAM::User remain unchanged, and they will continue to deliver\n\t\t\t\tConfiguration Items in all supported regions in Config. The change will only affect new global\n\t\t\t\tresource types onboarded after February 2022.

\n

To record global resource types onboarded after February 2022,\n\t\t\t\tenable All Supported Resource Types in the home region of the global resource type you want to record.

\n
\n

If you don't want Config to record all resources, you can\n\t\t\tspecify which types of resources it will record with the\n\t\t\t\tresourceTypes parameter.

\n

For a list of supported resource types, see Supported Resource Types.

\n

For more information and a table of the Home Regions for Global Resource Types Onboarded after February 2022, see Selecting Which Resources Config Records.

" } }, "com.amazonaws.configservice#ReevaluateConfigRuleNames": { @@ -10335,14 +10709,14 @@ "TargetId": { "target": "com.amazonaws.configservice#StringWithCharLimit256", "traits": { - "smithy.api#documentation": "

Target ID is the name of the public document.

", + "smithy.api#documentation": "

Target ID is the name of the SSM document.

", "smithy.api#required": {} } }, "TargetVersion": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

Version of the target. For example, version of the SSM document.

\n\t\t \n

If you make backward incompatible changes to the SSM document, \n\t\t\tyou must call PutRemediationConfiguration API again to ensure the remediations can run.

\n
" + "smithy.api#documentation": "

Version of the target. For example, version of the SSM document.

\n \n

If you make backward incompatible changes to the SSM document, \n\t\t\tyou must call PutRemediationConfiguration API again to ensure the remediations can run.

\n
" } }, "Parameters": { @@ -10373,13 +10747,13 @@ "MaximumAutomaticAttempts": { "target": "com.amazonaws.configservice#AutoRemediationAttempts", "traits": { - "smithy.api#documentation": "

The maximum number of failed attempts for auto-remediation. If you do not select a number, the default is 5.

\n\t\t

For example, if you specify MaximumAutomaticAttempts as 5 with RetryAttemptSeconds as 50 seconds, \n\t\t\t\n\t\t\tConfig will put a RemediationException on your behalf for the failing resource after the 5th failed attempt within 50 seconds.

" + "smithy.api#documentation": "

The maximum number of failed attempts for auto-remediation. If you do not select a number, the default is 5.

\n

For example, if you specify MaximumAutomaticAttempts as 5 with RetryAttemptSeconds as 50 seconds, \n\t\t\t\n\t\t\tConfig will put a RemediationException on your behalf for the failing resource after the 5th failed attempt within 50 seconds.

" } }, "RetryAttemptSeconds": { "target": "com.amazonaws.configservice#AutoRemediationAttemptSeconds", "traits": { - "smithy.api#documentation": "

Maximum time in seconds that Config runs auto-remediation. If you do not select a number, the default is 60 seconds.

\n\t\t

For example, if you specify RetryAttemptSeconds as 50 seconds and MaximumAutomaticAttempts as 5, \n\t\tConfig will run auto-remediations 5 times within 50 seconds before throwing an exception.

" + "smithy.api#documentation": "

Maximum time in seconds that Config runs auto-remediation. If you do not select a number, the default is 60 seconds.

\n

For example, if you specify RetryAttemptSeconds as 50 seconds and MaximumAutomaticAttempts as 5, \n\t\tConfig will run auto-remediations 5 times within 50 seconds before throwing an exception.

" } }, "Arn": { @@ -10835,12 +11209,12 @@ "ResourceConfigurationSchemaType": { "target": "com.amazonaws.configservice#ResourceConfigurationSchemaType", "traits": { - "smithy.api#documentation": "

The schema type of the resource configuration.

" + "smithy.api#documentation": "

The schema type of the resource configuration.

\n \n

You can find the\n\t\t\tResource type schema, or CFN_RESOURCE_SCHEMA, in \"Amazon Web Services public extensions\" within the CloudFormation registry or with the following CLI commmand:\n\t\t\taws cloudformation describe-type --type-name \"AWS::S3::Bucket\" --type RESOURCE.

\n

For more information, see Managing extensions through the CloudFormation registry\n\t\t\t\tand Amazon Web Services resource and property types reference in the CloudFormation User Guide.

\n
" } } }, "traits": { - "smithy.api#documentation": "

Returns information about the resource being evaluated.

" + "smithy.api#documentation": "

Returns information about the resource being evaluated.

" } }, "com.amazonaws.configservice#ResourceEvaluation": { @@ -11041,7 +11415,7 @@ } }, "traits": { - "smithy.api#documentation": "

You see this exception in the following cases:

\n\t\t
    \n
  • \n

    For DeleteConfigRule, Config is deleting this rule. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConfigRule, the rule is deleting your evaluation results. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConfigRule, a remediation action is associated with the rule and Config cannot delete this rule. Delete the remediation action associated with the rule before deleting the rule and try your request again later.

    \n
  • \n
  • \n

    For PutConfigOrganizationRule, organization Config rule deletion is in progress. Try your request again later.

    \n
  • \n
  • \n

    For DeleteOrganizationConfigRule, organization Config rule creation is in progress. Try your request again later.

    \n
  • \n
  • \n

    For PutConformancePack and PutOrganizationConformancePack, a conformance pack creation, update, and deletion is in progress. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConformancePack, a conformance pack creation, update, and deletion is in progress. Try your request again later.

    \n
  • \n
", + "smithy.api#documentation": "

You see this exception in the following cases:

\n
    \n
  • \n

    For DeleteConfigRule, Config is deleting this rule. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConfigRule, the rule is deleting your evaluation results. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConfigRule, a remediation action is associated with the rule and Config cannot delete this rule. Delete the remediation action associated with the rule before deleting the rule and try your request again later.

    \n
  • \n
  • \n

    For PutConfigOrganizationRule, organization Config rule deletion is in progress. Try your request again later.

    \n
  • \n
  • \n

    For DeleteOrganizationConfigRule, organization Config rule creation is in progress. Try your request again later.

    \n
  • \n
  • \n

    For PutConformancePack and PutOrganizationConformancePack, a conformance pack creation, update, and deletion is in progress. Try your request again later.

    \n
  • \n
  • \n

    For DeleteConformancePack, a conformance pack creation, update, and deletion is in progress. Try your request again later.

    \n
  • \n
", "smithy.api#error": "client" } }, @@ -12098,35 +12472,695 @@ "traits": { "smithy.api#enumValue": "AWS::Route53::HostedZone" } - } - } - }, - "com.amazonaws.configservice#ResourceTypeList": { - "type": "list", - "member": { - "target": "com.amazonaws.configservice#ResourceType" - } - }, - "com.amazonaws.configservice#ResourceTypeString": { - "type": "string", - "traits": { - "smithy.api#length": { - "min": 1, - "max": 196 - } - } - }, - "com.amazonaws.configservice#ResourceTypes": { - "type": "list", - "member": { - "target": "com.amazonaws.configservice#StringWithCharLimit256" - }, - "traits": { - "smithy.api#length": { - "min": 0, - "max": 20 - } - } + }, + "IoTEventsInput": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTEvents::Input" + } + }, + "IoTEventsDetectorModel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTEvents::DetectorModel" + } + }, + "IoTEventsAlarmModel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTEvents::AlarmModel" + } + }, + "ServiceDiscoveryHttpNamespace": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ServiceDiscovery::HttpNamespace" + } + }, + "EventsEventBus": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Events::EventBus" + } + }, + "ImageBuilderContainerRecipe": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ImageBuilder::ContainerRecipe" + } + }, + "ImageBuilderDistributionConfiguration": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ImageBuilder::DistributionConfiguration" + } + }, + "ImageBuilderInfrastructureConfiguration": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ImageBuilder::InfrastructureConfiguration" + } + }, + "DataSyncLocationObjectStorage": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::LocationObjectStorage" + } + }, + "DataSyncLocationHDFS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::LocationHDFS" + } + }, + "GlueClassifier": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Glue::Classifier" + } + }, + "Route53RecoveryReadinessCell": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53RecoveryReadiness::Cell" + } + }, + "Route53RecoveryReadinessReadinessCheck": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53RecoveryReadiness::ReadinessCheck" + } + }, + "ECRRegistryPolicy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ECR::RegistryPolicy" + } + }, + "BackupReportPlan": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Backup::ReportPlan" + } + }, + "LightsailCertificate": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lightsail::Certificate" + } + }, + "RUMAppMonitor": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::RUM::AppMonitor" + } + }, + "EventsEndpoint": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Events::Endpoint" + } + }, + "SESReceiptRuleSet": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SES::ReceiptRuleSet" + } + }, + "EventsArchive": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Events::Archive" + } + }, + "EventsApiDestination": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Events::ApiDestination" + } + }, + "LightsailDisk": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lightsail::Disk" + } + }, + "FISExperimentTemplate": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::FIS::ExperimentTemplate" + } + }, + "DataSyncLocationFSxWindows": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DataSync::LocationFSxWindows" + } + }, + "SESReceiptFilter": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SES::ReceiptFilter" + } + }, + "GuardDutyFilter": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::GuardDuty::Filter" + } + }, + "SESTemplate": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SES::Template" + } + }, + "AmazonMQBroker": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AmazonMQ::Broker" + } + }, + "AppConfigEnvironment": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AppConfig::Environment" + } + }, + "AppConfigConfigurationProfile": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AppConfig::ConfigurationProfile" + } + }, + "Cloud9EnvironmentEC2": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Cloud9::EnvironmentEC2" + } + }, + "EventSchemasRegistry": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EventSchemas::Registry" + } + }, + "EventSchemasRegistryPolicy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EventSchemas::RegistryPolicy" + } + }, + "EventSchemasDiscoverer": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EventSchemas::Discoverer" + } + }, + "FraudDetectorLabel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::FraudDetector::Label" + } + }, + "FraudDetectorEntityType": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::FraudDetector::EntityType" + } + }, + "FraudDetectorVariable": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::FraudDetector::Variable" + } + }, + "FraudDetectorOutcome": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::FraudDetector::Outcome" + } + }, + "IoTAuthorizer": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::Authorizer" + } + }, + "IoTSecurityProfile": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::SecurityProfile" + } + }, + "IoTRoleAlias": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::RoleAlias" + } + }, + "IoTDimension": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::Dimension" + } + }, + "IoTAnalyticsDatastore": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTAnalytics::Datastore" + } + }, + "LightsailBucket": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lightsail::Bucket" + } + }, + "LightsailStaticIp": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lightsail::StaticIp" + } + }, + "MediaPackagePackagingGroup": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::MediaPackage::PackagingGroup" + } + }, + "Route53RecoveryReadinessRecoveryGroup": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53RecoveryReadiness::RecoveryGroup" + } + }, + "ResilienceHubResiliencyPolicy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ResilienceHub::ResiliencyPolicy" + } + }, + "TransferWorkflow": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Transfer::Workflow" + } + }, + "EKSIdentityProviderConfig": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EKS::IdentityProviderConfig" + } + }, + "EKSAddon": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EKS::Addon" + } + }, + "GlueMLTransform": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Glue::MLTransform" + } + }, + "IoTPolicy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::Policy" + } + }, + "IoTMitigationAction": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::MitigationAction" + } + }, + "IoTTwinMakerWorkspace": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTTwinMaker::Workspace" + } + }, + "IoTTwinMakerEntity": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTTwinMaker::Entity" + } + }, + "IoTAnalyticsDataset": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTAnalytics::Dataset" + } + }, + "IoTAnalyticsPipeline": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTAnalytics::Pipeline" + } + }, + "IoTAnalyticsChannel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTAnalytics::Channel" + } + }, + "IoTSiteWiseDashboard": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTSiteWise::Dashboard" + } + }, + "IoTSiteWiseProject": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTSiteWise::Project" + } + }, + "IoTSiteWisePortal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTSiteWise::Portal" + } + }, + "IoTSiteWiseAssetModel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTSiteWise::AssetModel" + } + }, + "IVSChannel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IVS::Channel" + } + }, + "IVSRecordingConfiguration": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IVS::RecordingConfiguration" + } + }, + "IVSPlaybackKeyPair": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IVS::PlaybackKeyPair" + } + }, + "KinesisAnalyticsV2Application": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::KinesisAnalyticsV2::Application" + } + }, + "RDSGlobalCluster": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::RDS::GlobalCluster" + } + }, + "S3MultiRegionAccessPoint": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::S3::MultiRegionAccessPoint" + } + }, + "DeviceFarmTestGridProject": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DeviceFarm::TestGridProject" + } + }, + "BudgetsBudgetsAction": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Budgets::BudgetsAction" + } + }, + "LexBot": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lex::Bot" + } + }, + "CodeGuruReviewerRepositoryAssociation": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::CodeGuruReviewer::RepositoryAssociation" + } + }, + "IoTCustomMetric": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::CustomMetric" + } + }, + "Route53ResolverFirewallDomainList": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53Resolver::FirewallDomainList" + } + }, + "RoboMakerRobotApplicationVersion": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::RoboMaker::RobotApplicationVersion" + } + }, + "EC2TrafficMirrorSession": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::TrafficMirrorSession" + } + }, + "IoTSiteWiseGateway": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTSiteWise::Gateway" + } + }, + "LexBotAlias": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Lex::BotAlias" + } + }, + "LookoutMetricsAlert": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::LookoutMetrics::Alert" + } + }, + "IoTAccountAuditConfiguration": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::AccountAuditConfiguration" + } + }, + "EC2TrafficMirrorTarget": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::TrafficMirrorTarget" + } + }, + "S3StorageLens": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::S3::StorageLens" + } + }, + "IoTScheduledAudit": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::ScheduledAudit" + } + }, + "EventsConnection": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Events::Connection" + } + }, + "EventSchemasSchema": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EventSchemas::Schema" + } + }, + "MediaPackagePackagingConfiguration": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::MediaPackage::PackagingConfiguration" + } + }, + "KinesisVideoSignalingChannel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::KinesisVideo::SignalingChannel" + } + }, + "AppStreamDirectoryConfig": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AppStream::DirectoryConfig" + } + }, + "LookoutVisionProject": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::LookoutVision::Project" + } + }, + "Route53RecoveryControlCluster": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53RecoveryControl::Cluster" + } + }, + "Route53RecoveryControlSafetyRule": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53RecoveryControl::SafetyRule" + } + }, + "Route53RecoveryControlControlPanel": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53RecoveryControl::ControlPanel" + } + }, + "Route53RecoveryControlRoutingControl": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53RecoveryControl::RoutingControl" + } + }, + "Route53RecoveryReadinessResourceSet": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53RecoveryReadiness::ResourceSet" + } + }, + "RoboMakerSimulationApplication": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::RoboMaker::SimulationApplication" + } + }, + "RoboMakerRobotApplication": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::RoboMaker::RobotApplication" + } + }, + "HealthLakeFHIRDatastore": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::HealthLake::FHIRDatastore" + } + }, + "PinpointSegment": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Pinpoint::Segment" + } + }, + "PinpointApplicationSettings": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Pinpoint::ApplicationSettings" + } + }, + "EventsRule": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Events::Rule" + } + }, + "EC2DHCPOptions": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::DHCPOptions" + } + }, + "EC2NetworkInsightsPath": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::NetworkInsightsPath" + } + }, + "EC2TrafficMirrorFilter": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::TrafficMirrorFilter" + } + }, + "EC2IPAM": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::IPAM" + } + }, + "IoTTwinMakerScene": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTTwinMaker::Scene" + } + }, + "NetworkManagerTransitGatewayRegistration": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::NetworkManager::TransitGatewayRegistration" + } + }, + "CustomerProfilesDomain": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::CustomerProfiles::Domain" + } + }, + "AutoScalingWarmPool": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AutoScaling::WarmPool" + } + }, + "ConnectPhoneNumber": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Connect::PhoneNumber" + } + } + } + }, + "com.amazonaws.configservice#ResourceTypeList": { + "type": "list", + "member": { + "target": "com.amazonaws.configservice#ResourceType" + } + }, + "com.amazonaws.configservice#ResourceTypeString": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 196 + } + } + }, + "com.amazonaws.configservice#ResourceTypes": { + "type": "list", + "member": { + "target": "com.amazonaws.configservice#StringWithCharLimit256" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 20 + } + } }, "com.amazonaws.configservice#ResourceTypesScope": { "type": "list", @@ -12186,7 +13220,7 @@ "target": "com.amazonaws.configservice#RetentionPeriodInDays", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Number of days Config stores your historical information.

\n\t\t \n

Currently, only applicable to the configuration item history.

\n
", + "smithy.api#documentation": "

Number of days Config stores your historical information.

\n \n

Currently, only applicable to the configuration item history.

\n
", "smithy.api#required": {} } } @@ -12320,7 +13354,7 @@ } ], "traits": { - "smithy.api#documentation": "

Accepts a structured query language (SQL) SELECT command and an aggregator to query configuration state of Amazon Web Services resources across multiple accounts and regions, \n\t\t\tperforms the corresponding search, and returns resource configurations matching the properties.

\n\t\t

For more information about query components, see the \n\t\t\t\n Query Components\n section in the Config Developer Guide.

\n\t\t\n\t\t \n\t\t\t

If you run an aggregation query (i.e., using GROUP BY or using aggregate functions such as COUNT; e.g., SELECT resourceId, COUNT(*) WHERE resourceType = 'AWS::IAM::Role' GROUP BY resourceId)\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 500.

\n\t\t\t\n\t\t\t

If you run a non-aggregation query (i.e., not using GROUP BY or aggregate function; e.g., SELECT * WHERE resourceType = 'AWS::IAM::Role')\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 25.

\n\t\t
", + "smithy.api#documentation": "

Accepts a structured query language (SQL) SELECT command and an aggregator to query configuration state of Amazon Web Services resources across multiple accounts and regions, \n\t\t\tperforms the corresponding search, and returns resource configurations matching the properties.

\n

For more information about query components, see the \n\t\t\t\n Query Components\n section in the Config Developer Guide.

\n \n

If you run an aggregation query (i.e., using GROUP BY or using aggregate functions such as COUNT; e.g., SELECT resourceId, COUNT(*) WHERE resourceType = 'AWS::IAM::Role' GROUP BY resourceId)\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 500.

\n

If you run a non-aggregation query (i.e., not using GROUP BY or aggregate function; e.g., SELECT * WHERE resourceType = 'AWS::IAM::Role')\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 25.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -12366,6 +13400,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#SelectAggregateResourceConfigResponse": { @@ -12386,6 +13423,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#SelectResourceConfig": { @@ -12408,7 +13448,7 @@ } ], "traits": { - "smithy.api#documentation": "

Accepts a structured query language (SQL) SELECT command, performs the corresponding search, and returns resource configurations matching the properties.

\n\t\t

For more information about query components, see the \n\t\t\t\n Query Components\n section in the Config Developer Guide.

", + "smithy.api#documentation": "

Accepts a structured query language (SQL) SELECT command, performs the corresponding search, and returns resource configurations matching the properties.

\n

For more information about query components, see the \n\t\t\t\n Query Components\n section in the Config Developer Guide.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -12440,6 +13480,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#SelectResourceConfigResponse": { @@ -12463,6 +13506,9 @@ "smithy.api#documentation": "

The nextToken string returned in a previous request that you use to request the next page of results in a paginated response.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#SortBy": { @@ -12499,20 +13545,20 @@ "Owner": { "target": "com.amazonaws.configservice#Owner", "traits": { - "smithy.api#documentation": "

Indicates whether Amazon Web Services or the customer owns and manages the Config rule.

\n\t\t\n\t\t

Config Managed Rules are predefined rules owned by Amazon Web Services. For more information, see Config Managed Rules in the Config developer guide.

\n\t\t\n\t\t

Config Custom Rules are rules that you can develop either with Guard (CUSTOM_POLICY) or Lambda (CUSTOM_LAMBDA). For more information, see Config Custom Rules in the Config developer guide.

", + "smithy.api#documentation": "

Indicates whether Amazon Web Services or the customer owns and manages the Config rule.

\n

Config Managed Rules are predefined rules owned by Amazon Web Services. For more information, see Config Managed Rules in the Config developer guide.

\n

Config Custom Rules are rules that you can develop either with Guard (CUSTOM_POLICY) or Lambda (CUSTOM_LAMBDA). For more information, see Config Custom Rules in the Config developer guide.

", "smithy.api#required": {} } }, "SourceIdentifier": { "target": "com.amazonaws.configservice#StringWithCharLimit256", "traits": { - "smithy.api#documentation": "

For Config Managed rules, a predefined identifier from a\n\t\t\tlist. For example, IAM_PASSWORD_POLICY is a managed\n\t\t\trule. To reference a managed rule, see List of Config Managed Rules.

\n\t\t

For Config Custom Lambda rules, the identifier is the Amazon Resource Name\n\t\t\t(ARN) of the rule's Lambda function, such as\n\t\t\tarn:aws:lambda:us-east-2:123456789012:function:custom_rule_name.

\n\t\t\n\t\t

For Config Custom Policy rules, this field will be ignored.

" + "smithy.api#documentation": "

For Config Managed rules, a predefined identifier from a\n\t\t\tlist. For example, IAM_PASSWORD_POLICY is a managed\n\t\t\trule. To reference a managed rule, see List of Config Managed Rules.

\n

For Config Custom Lambda rules, the identifier is the Amazon Resource Name\n\t\t\t(ARN) of the rule's Lambda function, such as\n\t\t\tarn:aws:lambda:us-east-2:123456789012:function:custom_rule_name.

\n

For Config Custom Policy rules, this field will be ignored.

" } }, "SourceDetails": { "target": "com.amazonaws.configservice#SourceDetails", "traits": { - "smithy.api#documentation": "

Provides the source and the message types that cause Config to evaluate your Amazon Web Services resources against a rule. It also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

\n\t\t\n\t\t

If the owner is set to CUSTOM_POLICY, the only acceptable values for the Config rule trigger message type are ConfigurationItemChangeNotification and OversizedConfigurationItemChangeNotification.

" + "smithy.api#documentation": "

Provides the source and the message types that cause Config to evaluate your Amazon Web Services resources against a rule. It also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

\n

If the owner is set to CUSTOM_POLICY, the only acceptable values for the Config rule trigger message type are ConfigurationItemChangeNotification and OversizedConfigurationItemChangeNotification.

" } }, "CustomPolicyDetails": { @@ -12538,13 +13584,13 @@ "MessageType": { "target": "com.amazonaws.configservice#MessageType", "traits": { - "smithy.api#documentation": "

The type of notification that triggers Config to run an\n\t\t\tevaluation for a rule. You can specify the following notification\n\t\t\ttypes:

\n\n\n\t\t
    \n
  • \n\t\t\t\t

    \n\t\t\t\t\t ConfigurationItemChangeNotification - Triggers\n\t\t\t\t\tan evaluation when Config delivers a configuration item\n\t\t\t\t\tas a result of a resource change.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    \n OversizedConfigurationItemChangeNotification\n\t\t\t\t\t- Triggers an evaluation when Config delivers an\n\t\t\t\t\toversized configuration item. Config may generate this\n\t\t\t\t\tnotification type when a resource changes and the\n\t\t\t\t\tnotification exceeds the maximum size allowed by Amazon\n\t\t\t\t\tSNS.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    \n ScheduledNotification - Triggers a\n\t\t\t\t\tperiodic evaluation at the frequency specified for\n\t\t\t\t\t\tMaximumExecutionFrequency.

    \n\t\t\t
  • \n
  • \n\t\t\t\t

    \n ConfigurationSnapshotDeliveryCompleted -\n\t\t\t\t\tTriggers a periodic evaluation when Config delivers a\n\t\t\t\t\tconfiguration snapshot.

    \n\t\t\t
  • \n
\n\n\t\t

If you want your custom rule to be triggered by configuration\n\t\t\tchanges, specify two SourceDetail objects, one for\n\t\t\t\tConfigurationItemChangeNotification and one for\n\t\t\t\tOversizedConfigurationItemChangeNotification.

" + "smithy.api#documentation": "

The type of notification that triggers Config to run an\n\t\t\tevaluation for a rule. You can specify the following notification\n\t\t\ttypes:

\n
    \n
  • \n

    \n ConfigurationItemChangeNotification - Triggers\n\t\t\t\t\tan evaluation when Config delivers a configuration item\n\t\t\t\t\tas a result of a resource change.

    \n
  • \n
  • \n

    \n OversizedConfigurationItemChangeNotification\n\t\t\t\t\t- Triggers an evaluation when Config delivers an\n\t\t\t\t\toversized configuration item. Config may generate this\n\t\t\t\t\tnotification type when a resource changes and the\n\t\t\t\t\tnotification exceeds the maximum size allowed by Amazon\n\t\t\t\t\tSNS.

    \n
  • \n
  • \n

    \n ScheduledNotification - Triggers a\n\t\t\t\t\tperiodic evaluation at the frequency specified for\n\t\t\t\t\t\tMaximumExecutionFrequency.

    \n
  • \n
  • \n

    \n ConfigurationSnapshotDeliveryCompleted -\n\t\t\t\t\tTriggers a periodic evaluation when Config delivers a\n\t\t\t\t\tconfiguration snapshot.

    \n
  • \n
\n

If you want your custom rule to be triggered by configuration\n\t\t\tchanges, specify two SourceDetail objects, one for\n\t\t\t\tConfigurationItemChangeNotification and one for\n\t\t\t\tOversizedConfigurationItemChangeNotification.

" } }, "MaximumExecutionFrequency": { "target": "com.amazonaws.configservice#MaximumExecutionFrequency", "traits": { - "smithy.api#documentation": "

The frequency at which you want Config to run evaluations\n\t\t\tfor a custom rule with a periodic trigger. If you specify a value\n\t\t\tfor MaximumExecutionFrequency, then\n\t\t\t\tMessageType must use the\n\t\t\t\tScheduledNotification value.

\n\n\n\t\t\n\n\t\t \n\t\t\t

By default, rules with a periodic trigger are evaluated\n\t\t\t\tevery 24 hours. To change the frequency, specify a valid value\n\t\t\t\tfor the MaximumExecutionFrequency\n\t\t\t\tparameter.

\n\t\t\t

Based on the valid value you choose, Config runs\n\t\t\t\tevaluations once for each valid value. For example, if you\n\t\t\t\tchoose Three_Hours, Config runs evaluations\n\t\t\t\tonce every three hours. In this case, Three_Hours\n\t\t\t\tis the frequency of this rule.

\n\t\t
" + "smithy.api#documentation": "

The frequency at which you want Config to run evaluations\n\t\t\tfor a custom rule with a periodic trigger. If you specify a value\n\t\t\tfor MaximumExecutionFrequency, then\n\t\t\t\tMessageType must use the\n\t\t\t\tScheduledNotification value.

\n \n

By default, rules with a periodic trigger are evaluated\n\t\t\t\tevery 24 hours. To change the frequency, specify a valid value\n\t\t\t\tfor the MaximumExecutionFrequency\n\t\t\t\tparameter.

\n

Based on the valid value you choose, Config runs\n\t\t\t\tevaluations once for each valid value. For example, if you\n\t\t\t\tchoose Three_Hours, Config runs evaluations\n\t\t\t\tonce every three hours. In this case, Three_Hours\n\t\t\t\tis the frequency of this rule.

\n
" } } }, @@ -12886,7 +13932,7 @@ "name": "config" }, "aws.protocols#awsJson1_1": {}, - "smithy.api#documentation": "Config\n\n\t\t

Config provides a way to keep track of the configurations\n\t\t\tof all the Amazon Web Services resources associated with your Amazon Web Services account. You can\n\t\t\tuse Config to get the current and historical configurations of\n\t\t\teach Amazon Web Services resource and also to get information about the relationship\n\t\t\tbetween the resources. An Amazon Web Services resource can be an Amazon Compute\n\t\t\tCloud (Amazon EC2) instance, an Elastic Block Store (EBS) volume, an\n\t\t\telastic network Interface (ENI), or a security group. For a complete\n\t\t\tlist of resources currently supported by Config, see Supported Amazon Web Services resources.

\n\n\t\t

You can access and manage Config through the Amazon Web Services Management\n\t\t\tConsole, the Amazon Web Services Command Line Interface (Amazon Web Services CLI), the Config\n\t\t\tAPI, or the Amazon Web Services SDKs for Config. This reference guide contains\n\t\t\tdocumentation for the Config API and the Amazon Web Services CLI commands that\n\t\t\tyou can use to manage Config. The Config API uses the\n\t\t\tSignature Version 4 protocol for signing requests. For more\n\t\t\tinformation about how to sign a request with this protocol, see\n\t\t\t\tSignature\n\t\t\t\tVersion 4 Signing Process. For detailed information\n\t\t\tabout Config features and their associated actions or commands,\n\t\t\tas well as how to work with Amazon Web Services Management Console, see What Is Config in the Config Developer\n\t\t\t\tGuide.

", + "smithy.api#documentation": "Config\n

Config provides a way to keep track of the configurations\n\t\t\tof all the Amazon Web Services resources associated with your Amazon Web Services account. You can\n\t\t\tuse Config to get the current and historical configurations of\n\t\t\teach Amazon Web Services resource and also to get information about the relationship\n\t\t\tbetween the resources. An Amazon Web Services resource can be an Amazon Compute\n\t\t\tCloud (Amazon EC2) instance, an Elastic Block Store (EBS) volume, an\n\t\t\telastic network Interface (ENI), or a security group. For a complete\n\t\t\tlist of resources currently supported by Config, see Supported Amazon Web Services resources.

\n

You can access and manage Config through the Amazon Web Services Management\n\t\t\tConsole, the Amazon Web Services Command Line Interface (Amazon Web Services CLI), the Config\n\t\t\tAPI, or the Amazon Web Services SDKs for Config. This reference guide contains\n\t\t\tdocumentation for the Config API and the Amazon Web Services CLI commands that\n\t\t\tyou can use to manage Config. The Config API uses the\n\t\t\tSignature Version 4 protocol for signing requests. For more\n\t\t\tinformation about how to sign a request with this protocol, see\n\t\t\t\tSignature\n\t\t\t\tVersion 4 Signing Process. For detailed information\n\t\t\tabout Config features and their associated actions or commands,\n\t\t\tas well as how to work with Amazon Web Services Management Console, see What Is Config in the Config Developer\n\t\t\t\tGuide.

", "smithy.api#title": "AWS Config", "smithy.api#xmlNamespace": { "uri": "http://config.amazonaws.com/doc/2014-11-12/" @@ -13276,9 +14322,9 @@ } }, "params": { - "Region": "af-south-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "af-south-1" } }, { @@ -13289,9 +14335,9 @@ } }, "params": { - "Region": "ap-east-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "ap-east-1" } }, { @@ -13302,9 +14348,9 @@ } }, "params": { - "Region": "ap-northeast-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "ap-northeast-1" } }, { @@ -13315,9 +14361,9 @@ } }, "params": { - "Region": "ap-northeast-2", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "ap-northeast-2" } }, { @@ -13328,9 +14374,9 @@ } }, "params": { - "Region": "ap-northeast-3", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "ap-northeast-3" } }, { @@ -13341,9 +14387,9 @@ } }, "params": { - "Region": "ap-south-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "ap-south-1" } }, { @@ -13354,9 +14400,9 @@ } }, "params": { - "Region": "ap-southeast-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "ap-southeast-1" } }, { @@ -13367,9 +14413,9 @@ } }, "params": { - "Region": "ap-southeast-2", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "ap-southeast-2" } }, { @@ -13380,9 +14426,9 @@ } }, "params": { - "Region": "ap-southeast-3", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "ap-southeast-3" } }, { @@ -13393,9 +14439,9 @@ } }, "params": { - "Region": "ca-central-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "ca-central-1" } }, { @@ -13406,9 +14452,9 @@ } }, "params": { - "Region": "eu-central-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "eu-central-1" } }, { @@ -13419,9 +14465,9 @@ } }, "params": { - "Region": "eu-north-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "eu-north-1" } }, { @@ -13432,9 +14478,9 @@ } }, "params": { - "Region": "eu-south-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "eu-south-1" } }, { @@ -13445,9 +14491,9 @@ } }, "params": { - "Region": "eu-west-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "eu-west-1" } }, { @@ -13458,9 +14504,9 @@ } }, "params": { - "Region": "eu-west-2", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "eu-west-2" } }, { @@ -13471,9 +14517,9 @@ } }, "params": { - "Region": "eu-west-3", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "eu-west-3" } }, { @@ -13484,9 +14530,9 @@ } }, "params": { - "Region": "me-south-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "me-south-1" } }, { @@ -13497,9 +14543,9 @@ } }, "params": { - "Region": "sa-east-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "sa-east-1" } }, { @@ -13510,9 +14556,9 @@ } }, "params": { - "Region": "us-east-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "us-east-1" } }, { @@ -13523,9 +14569,9 @@ } }, "params": { - "Region": "us-east-1", "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "Region": "us-east-1" } }, { @@ -13536,9 +14582,9 @@ } }, "params": { - "Region": "us-east-2", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "us-east-2" } }, { @@ -13549,9 +14595,9 @@ } }, "params": { - "Region": "us-east-2", "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "Region": "us-east-2" } }, { @@ -13562,9 +14608,9 @@ } }, "params": { - "Region": "us-west-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "us-west-1" } }, { @@ -13575,9 +14621,9 @@ } }, "params": { - "Region": "us-west-1", "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "Region": "us-west-1" } }, { @@ -13588,9 +14634,9 @@ } }, "params": { - "Region": "us-west-2", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "us-west-2" } }, { @@ -13601,9 +14647,9 @@ } }, "params": { - "Region": "us-west-2", "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "Region": "us-west-2" } }, { @@ -13614,9 +14660,9 @@ } }, "params": { - "Region": "us-east-1", "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "Region": "us-east-1" } }, { @@ -13627,9 +14673,9 @@ } }, "params": { - "Region": "us-east-1", "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "Region": "us-east-1" } }, { @@ -13640,9 +14686,9 @@ } }, "params": { - "Region": "cn-north-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "cn-north-1" } }, { @@ -13653,9 +14699,9 @@ } }, "params": { - "Region": "cn-northwest-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "cn-northwest-1" } }, { @@ -13666,9 +14712,9 @@ } }, "params": { - "Region": "cn-north-1", "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "Region": "cn-north-1" } }, { @@ -13679,9 +14725,9 @@ } }, "params": { - "Region": "cn-north-1", "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "Region": "cn-north-1" } }, { @@ -13692,9 +14738,9 @@ } }, "params": { - "Region": "cn-north-1", "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "Region": "cn-north-1" } }, { @@ -13705,9 +14751,9 @@ } }, "params": { - "Region": "us-gov-east-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "us-gov-east-1" } }, { @@ -13718,9 +14764,9 @@ } }, "params": { - "Region": "us-gov-east-1", "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "Region": "us-gov-east-1" } }, { @@ -13731,9 +14777,9 @@ } }, "params": { - "Region": "us-gov-west-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "us-gov-west-1" } }, { @@ -13744,9 +14790,9 @@ } }, "params": { - "Region": "us-gov-west-1", "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "Region": "us-gov-west-1" } }, { @@ -13757,9 +14803,9 @@ } }, "params": { - "Region": "us-gov-east-1", "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "Region": "us-gov-east-1" } }, { @@ -13770,9 +14816,9 @@ } }, "params": { - "Region": "us-gov-east-1", "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "Region": "us-gov-east-1" } }, { @@ -13783,9 +14829,9 @@ } }, "params": { - "Region": "us-iso-east-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "us-iso-east-1" } }, { @@ -13796,9 +14842,20 @@ } }, "params": { - "Region": "us-iso-west-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "us-iso-west-1" + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "UseDualStack": true, + "UseFIPS": true, + "Region": "us-iso-east-1" } }, { @@ -13809,9 +14866,20 @@ } }, "params": { - "Region": "us-iso-east-1", "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "Region": "us-iso-east-1" + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-iso-east-1" } }, { @@ -13822,9 +14890,20 @@ } }, "params": { - "Region": "us-isob-east-1", "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "Region": "us-isob-east-1" + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "UseDualStack": true, + "UseFIPS": true, + "Region": "us-isob-east-1" } }, { @@ -13835,9 +14914,20 @@ } }, "params": { - "Region": "us-isob-east-1", "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "Region": "us-isob-east-1" + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-isob-east-1" } }, { @@ -13848,9 +14938,9 @@ } }, "params": { - "Region": "us-east-1", "UseDualStack": false, "UseFIPS": false, + "Region": "us-east-1", "Endpoint": "https://example.com" } }, @@ -13873,9 +14963,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "Region": "us-east-1", "UseDualStack": false, "UseFIPS": true, + "Region": "us-east-1", "Endpoint": "https://example.com" } }, @@ -13885,11 +14975,17 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "Region": "us-east-1", "UseDualStack": true, "UseFIPS": false, + "Region": "us-east-1", "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -13919,7 +15015,7 @@ } ], "traits": { - "smithy.api#documentation": "

Runs an on-demand evaluation for the specified Config rules\n\t\t\tagainst the last known configuration state of the resources. Use\n\t\t\t\tStartConfigRulesEvaluation when you want to test\n\t\t\tthat a rule you updated is working as expected.\n\t\t\t\tStartConfigRulesEvaluation does not re-record the\n\t\t\tlatest configuration state for your resources. It re-runs an\n\t\t\tevaluation against the last known state of your resources.

\n\t\t

You can specify up to 25 Config rules per request.

\n\n\t\t\n\t\t

An existing StartConfigRulesEvaluation call for\n\t\t\tthe specified rules must complete before you can call the API again.\n\t\t\tIf you chose to have Config stream to an Amazon SNS topic, you\n\t\t\twill receive a ConfigRuleEvaluationStarted notification\n\t\t\twhen the evaluation starts.

\n\t\t \n\t\t\t

You don't need to call the\n\t\t\t\t\tStartConfigRulesEvaluation API to run an\n\t\t\t\tevaluation for a new rule. When you create a rule, Config\n\t\t\t\tevaluates your resources against the rule automatically.\n\t\t\t

\n\t\t
\n\t\t

The StartConfigRulesEvaluation API is useful if\n\t\t\tyou want to run on-demand evaluations, such as the following\n\t\t\texample:

\n\t\t
    \n
  1. \n\t\t\t\t

    You have a custom rule that evaluates your IAM\n\t\t\t\t\tresources every 24 hours.

    \n\t\t\t
  2. \n
  3. \n\t\t\t\t

    You update your Lambda function to add additional\n\t\t\t\t\tconditions to your rule.

    \n\t\t\t
  4. \n
  5. \n\t\t\t\t

    Instead of waiting for the next periodic evaluation,\n\t\t\t\t\tyou call the StartConfigRulesEvaluation\n\t\t\t\t\tAPI.

    \n\t\t\t
  6. \n
  7. \n\t\t\t\t

    Config invokes your Lambda function and evaluates\n\t\t\t\t\tyour IAM resources.

    \n\t\t\t
  8. \n
  9. \n\t\t\t\t

    Your custom rule will still run periodic evaluations\n\t\t\t\t\tevery 24 hours.

    \n\t\t\t
  10. \n
" + "smithy.api#documentation": "

Runs an on-demand evaluation for the specified Config rules\n\t\t\tagainst the last known configuration state of the resources. Use\n\t\t\t\tStartConfigRulesEvaluation when you want to test\n\t\t\tthat a rule you updated is working as expected.\n\t\t\t\tStartConfigRulesEvaluation does not re-record the\n\t\t\tlatest configuration state for your resources. It re-runs an\n\t\t\tevaluation against the last known state of your resources.

\n

You can specify up to 25 Config rules per request.

\n

An existing StartConfigRulesEvaluation call for\n\t\t\tthe specified rules must complete before you can call the API again.\n\t\t\tIf you chose to have Config stream to an Amazon SNS topic, you\n\t\t\twill receive a ConfigRuleEvaluationStarted notification\n\t\t\twhen the evaluation starts.

\n \n

You don't need to call the\n\t\t\t\t\tStartConfigRulesEvaluation API to run an\n\t\t\t\tevaluation for a new rule. When you create a rule, Config\n\t\t\t\tevaluates your resources against the rule automatically.\n\t\t\t

\n
\n

The StartConfigRulesEvaluation API is useful if\n\t\t\tyou want to run on-demand evaluations, such as the following\n\t\t\texample:

\n
    \n
  1. \n

    You have a custom rule that evaluates your IAM\n\t\t\t\t\tresources every 24 hours.

    \n
  2. \n
  3. \n

    You update your Lambda function to add additional\n\t\t\t\t\tconditions to your rule.

    \n
  4. \n
  5. \n

    Instead of waiting for the next periodic evaluation,\n\t\t\t\t\tyou call the StartConfigRulesEvaluation\n\t\t\t\t\tAPI.

    \n
  6. \n
  7. \n

    Config invokes your Lambda function and evaluates\n\t\t\t\t\tyour IAM resources.

    \n
  8. \n
  9. \n

    Your custom rule will still run periodic evaluations\n\t\t\t\t\tevery 24 hours.

    \n
  10. \n
" } }, "com.amazonaws.configservice#StartConfigRulesEvaluationRequest": { @@ -13933,14 +15029,16 @@ } }, "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#StartConfigRulesEvaluationResponse": { "type": "structure", "members": {}, "traits": { - "smithy.api#documentation": "

The output when you start the evaluation for the specified Config rule.

" + "smithy.api#documentation": "

The output when you start the evaluation for the specified Config rule.

", + "smithy.api#output": {} } }, "com.amazonaws.configservice#StartConfigurationRecorder": { @@ -13960,7 +15058,7 @@ } ], "traits": { - "smithy.api#documentation": "

Starts recording configurations of the Amazon Web Services resources you have\n\t\t\tselected to record in your Amazon Web Services account.

\n\t\t

You must have created at least one delivery channel to\n\t\t\tsuccessfully start the configuration recorder.

" + "smithy.api#documentation": "

Starts recording configurations of the Amazon Web Services resources you have\n\t\t\tselected to record in your Amazon Web Services account.

\n

You must have created at least one delivery channel to\n\t\t\tsuccessfully start the configuration recorder.

" } }, "com.amazonaws.configservice#StartConfigurationRecorderRequest": { @@ -13975,7 +15073,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the StartConfigurationRecorder\n\t\t\taction.

" + "smithy.api#documentation": "

The input for the StartConfigurationRecorder\n\t\t\taction.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#StartRemediationExecution": { @@ -13998,7 +15097,7 @@ } ], "traits": { - "smithy.api#documentation": "

Runs an on-demand remediation for the specified Config rules against the last known remediation configuration. It runs an execution against the current state of your resources. Remediation execution is asynchronous.

\n\t\t\t

You can specify up to 100 resource keys per request. An existing StartRemediationExecution call for the specified resource keys must complete before you can call the API again.

" + "smithy.api#documentation": "

Runs an on-demand remediation for the specified Config rules against the last known remediation configuration. It runs an execution against the current state of your resources. Remediation execution is asynchronous.

\n

You can specify up to 100 resource keys per request. An existing StartRemediationExecution call for the specified resource keys must complete before you can call the API again.

" } }, "com.amazonaws.configservice#StartRemediationExecutionRequest": { @@ -14018,6 +15117,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#StartRemediationExecutionResponse": { @@ -14035,6 +15137,9 @@ "smithy.api#documentation": "

For resources that have failed to start execution, the API returns a resource key object.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#StartResourceEvaluation": { @@ -14054,7 +15159,7 @@ } ], "traits": { - "smithy.api#documentation": "

Runs an on-demand evaluation for the specified resource to determine whether the resource details will comply with configured Config rules.\n\t\t\tYou can also use it for evaluation purposes. Config recommends using an evaluation context. It runs an execution against the resource details with all\n\t\t\tof the Config rules in your account that match with the specified proactive mode and resource type.

\n\t\t\n\t\t \n

Ensure you have the cloudformation:DescribeType role setup to validate the resource type schema.\n\t\t

\n
" + "smithy.api#documentation": "

Runs an on-demand evaluation for the specified resource to determine whether the resource details will comply with configured Config rules.\n\t\t\tYou can also use it for evaluation purposes. Config recommends using an evaluation context. It runs an execution against the resource details with all\n\t\t\tof the Config rules in your account that match with the specified proactive mode and resource type.

\n \n

Ensure you have the cloudformation:DescribeType role setup to validate the resource type schema.

\n

You can find the\n\t\t\t\tResource type schema in \"Amazon Web Services public extensions\" within the CloudFormation registry or with the following CLI commmand:\n\t\t\taws cloudformation describe-type --type-name \"AWS::S3::Bucket\" --type RESOURCE.

\n

For more information, see Managing extensions through the CloudFormation registry\n\t\t\tand Amazon Web Services resource and property types reference in the CloudFormation User Guide.

\n
" } }, "com.amazonaws.configservice#StartResourceEvaluationRequest": { @@ -14076,7 +15181,7 @@ "EvaluationMode": { "target": "com.amazonaws.configservice#EvaluationMode", "traits": { - "smithy.api#documentation": "

The mode of an evaluation. The valid value for this API is Proactive.

", + "smithy.api#documentation": "

The mode of an evaluation. The valid values for this API are DETECTIVE and PROACTIVE.

", "smithy.api#required": {} } }, @@ -14090,9 +15195,12 @@ "ClientToken": { "target": "com.amazonaws.configservice#ClientToken", "traits": { - "smithy.api#documentation": "

A client token is a unique, case-sensitive string of up to 64 ASCII characters. \n\t\t\tTo make an idempotent API request using one of these actions, specify a client token in the request.

\n\t\t \n

Avoid reusing the same client token for other API requests. If you retry\n\t\t\t\ta request that completed successfully using the same client token and the same\n\t\t\t\tparameters, the retry succeeds without performing any further actions. If you retry\n\t\t\t\ta successful request using the same client token, but one or more of the parameters\n\t\t\t\tare different, other than the Region or Availability Zone, the retry fails with an\n\t\t\t\tIdempotentParameterMismatch error.

\n
" + "smithy.api#documentation": "

A client token is a unique, case-sensitive string of up to 64 ASCII characters. \n\t\t\tTo make an idempotent API request using one of these actions, specify a client token in the request.

\n \n

Avoid reusing the same client token for other API requests. If you retry\n\t\t\t\ta request that completed successfully using the same client token and the same\n\t\t\t\tparameters, the retry succeeds without performing any further actions. If you retry\n\t\t\t\ta successful request using the same client token, but one or more of the parameters\n\t\t\t\tare different, other than the Region or Availability Zone, the retry fails with an\n\t\t\t\tIdempotentParameterMismatch error.

\n
" } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#StartResourceEvaluationResponse": { @@ -14104,6 +15212,9 @@ "smithy.api#documentation": "

A\n\t\t\tunique ResourceEvaluationId that is associated with a single execution.

" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.configservice#StaticParameterValues": { @@ -14145,7 +15256,7 @@ "MemberAccountRuleStatus": { "target": "com.amazonaws.configservice#MemberAccountRuleStatus", "traits": { - "smithy.api#documentation": "

Indicates deployment status for Config rule in the member account.\n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in the member account. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t\t

\n\t\t

Config sets the state of the rule to:

\n\t\t
    \n
  • \n

    \n CREATE_SUCCESSFUL when Config rule has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when Config rule is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when Config rule creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when Config rule is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when Config rule has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when Config rule has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when Config rule is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
" + "smithy.api#documentation": "

Indicates deployment status for Config rule in the member account.\n\t\t\tWhen management account calls PutOrganizationConfigRule action for the first time, Config rule status is created in the member account. \n\t\t\tWhen management account calls PutOrganizationConfigRule action for the second time, Config rule status is updated in the member account. \n\t\t\tConfig rule status is deleted when the management account deletes OrganizationConfigRule and disables service access for config-multiaccountsetup.amazonaws.com. \n\t\t\t

\n

Config sets the state of the rule to:

\n
    \n
  • \n

    \n CREATE_SUCCESSFUL when Config rule has been created in the member account.

    \n
  • \n
  • \n

    \n CREATE_IN_PROGRESS when Config rule is being created in the member account.

    \n
  • \n
  • \n

    \n CREATE_FAILED when Config rule creation has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
  • \n

    \n DELETE_IN_PROGRESS when Config rule is being deleted in the member account.

    \n
  • \n
  • \n

    \n DELETE_SUCCESSFUL when Config rule has been deleted in the member account.

    \n
  • \n
  • \n

    \n UPDATE_SUCCESSFUL when Config rule has been updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_IN_PROGRESS when Config rule is being updated in the member account.

    \n
  • \n
  • \n

    \n UPDATE_FAILED when Config rule deletion has failed in the member account.

    \n
  • \n
" } } }, @@ -14182,7 +15293,8 @@ } }, "traits": { - "smithy.api#documentation": "

The input for the StopConfigurationRecorder action.

" + "smithy.api#documentation": "

The input for the StopConfigurationRecorder action.

", + "smithy.api#input": {} } }, "com.amazonaws.configservice#StoredQuery": { @@ -14419,7 +15531,7 @@ } ], "traits": { - "smithy.api#documentation": "

Associates the specified tags to a resource with the specified resourceArn. If existing tags on a resource are not specified in the request parameters, they are not changed. \n\t\t\tWhen a resource is deleted, the tags associated with that resource are deleted as well.

" + "smithy.api#documentation": "

Associates the specified tags to a resource with the specified resourceArn. If existing tags on a resource are not specified in the request parameters, they are not changed.\n\t\t\tIf existing tags are specified, however, then their values will be updated. When a resource is deleted, the tags associated with that resource are deleted as well.

" } }, "com.amazonaws.configservice#TagResourceRequest": { @@ -14439,6 +15551,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#TagValue": { @@ -14503,12 +15618,12 @@ "DocumentVersion": { "target": "com.amazonaws.configservice#SSMDocumentVersion", "traits": { - "smithy.api#documentation": "

The version of the SSM document to use to create a conformance pack. By default, Config uses the latest version.

\n\t\t \n

This field is optional.

\n
" + "smithy.api#documentation": "

The version of the SSM document to use to create a conformance pack. By default, Config uses the latest version.

\n \n

This field is optional.

\n
" } } }, "traits": { - "smithy.api#documentation": "

This API allows you to create a conformance pack template with an Amazon Web Services Systems Manager document (SSM document). \n\t\t\tTo deploy a conformance pack using an SSM document, first create an SSM document with conformance pack content, and then provide the DocumentName in the PutConformancePack API. You can also provide the DocumentVersion.

\n\t\t\n\t\t

The TemplateSSMDocumentDetails object contains the name of the SSM document and the version of the SSM document.

" + "smithy.api#documentation": "

This API allows you to create a conformance pack template with an Amazon Web Services Systems Manager document (SSM document). \n\t\t\tTo deploy a conformance pack using an SSM document, first create an SSM document with conformance pack content, and then provide the DocumentName in the PutConformancePack API. You can also provide the DocumentVersion.

\n

The TemplateSSMDocumentDetails object contains the name of the SSM document and the version of the SSM document.

" } }, "com.amazonaws.configservice#TimeWindow": { @@ -14589,6 +15704,9 @@ "smithy.api#required": {} } } + }, + "traits": { + "smithy.api#input": {} } }, "com.amazonaws.configservice#ValidationException": { @@ -14602,7 +15720,7 @@ } }, "traits": { - "smithy.api#documentation": "

The requested action is invalid.

\n\t\t

For PutStoredQuery, you will see this exception if there are missing required fields or if the input value fails the validation, or if you are trying to create more than 300 queries.

\n\t\t

For GetStoredQuery, ListStoredQuery, and DeleteStoredQuery you will see this exception if there are missing required fields or if the input value fails the validation.

", + "smithy.api#documentation": "

The requested action is not valid.

\n

For PutStoredQuery, you will see this exception if there are missing required fields or if the input value fails the validation, or if you are trying to create more than 300 queries.

\n

For GetStoredQuery, ListStoredQuery, and DeleteStoredQuery you will see this exception if there are missing required fields or if the input value fails the validation.

", "smithy.api#error": "client" } }, diff --git a/aws/sdk/aws-models/dynamodb.json b/aws/sdk/aws-models/dynamodb.json index c6a6550e5b..adb4cf00eb 100644 --- a/aws/sdk/aws-models/dynamodb.json +++ b/aws/sdk/aws-models/dynamodb.json @@ -833,7 +833,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

The BatchGetItem operation returns the attributes of one or more items\n from one or more tables. You identify requested items by primary key.

\n

A single operation can retrieve up to 16 MB of data, which can contain as many as 100\n items. BatchGetItem returns a partial result if the response size limit is\n exceeded, the table's provisioned throughput is exceeded, or an internal processing\n failure occurs. If a partial result is returned, the operation returns a value for\n UnprocessedKeys. You can use this value to retry the operation starting\n with the next item to get.

\n \n

If you request more than 100 items, BatchGetItem returns a\n ValidationException with the message \"Too many items requested for\n the BatchGetItem call.\"

\n
\n

For example, if you ask to retrieve 100 items, but each individual item is 300 KB in\n size, the system returns 52 items (so as not to exceed the 16 MB limit). It also returns\n an appropriate UnprocessedKeys value so you can get the next page of\n results. If desired, your application can include its own logic to assemble the pages of\n results into one dataset.

\n

If none of the items can be processed due to insufficient\n provisioned throughput on all of the tables in the request, then\n BatchGetItem returns a\n ProvisionedThroughputExceededException. If at least\n one of the items is successfully processed, then\n BatchGetItem completes successfully, while returning the keys of the\n unread items in UnprocessedKeys.

\n \n

If DynamoDB returns any unprocessed items, you should retry the batch operation on\n those items. However, we strongly recommend that you use an exponential\n backoff algorithm. If you retry the batch operation immediately, the\n underlying read or write requests can still fail due to throttling on the individual\n tables. If you delay the batch operation using exponential backoff, the individual\n requests in the batch are much more likely to succeed.

\n

For more information, see Batch Operations and Error Handling in the Amazon DynamoDB\n Developer Guide.

\n
\n

By default, BatchGetItem performs eventually consistent reads on every\n table in the request. If you want strongly consistent reads instead, you can set\n ConsistentRead to true for any or all tables.

\n

In order to minimize response latency, BatchGetItem retrieves items in\n parallel.

\n

When designing your application, keep in mind that DynamoDB does not return items in\n any particular order. To help parse the response by item, include the primary key values\n for the items in your request in the ProjectionExpression parameter.

\n

If a requested item does not exist, it is not returned in the result. Requests for\n nonexistent items consume the minimum read capacity units according to the type of read.\n For more information, see Working with Tables in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

The BatchGetItem operation returns the attributes of one or more items\n from one or more tables. You identify requested items by primary key.

\n

A single operation can retrieve up to 16 MB of data, which can contain as many as 100\n items. BatchGetItem returns a partial result if the response size limit is\n exceeded, the table's provisioned throughput is exceeded, or an internal processing\n failure occurs. If a partial result is returned, the operation returns a value for\n UnprocessedKeys. You can use this value to retry the operation starting\n with the next item to get.

\n \n

If you request more than 100 items, BatchGetItem returns a\n ValidationException with the message \"Too many items requested for\n the BatchGetItem call.\"

\n
\n

For example, if you ask to retrieve 100 items, but each individual item is 300 KB in\n size, the system returns 52 items (so as not to exceed the 16 MB limit). It also returns\n an appropriate UnprocessedKeys value so you can get the next page of\n results. If desired, your application can include its own logic to assemble the pages of\n results into one dataset.

\n

If none of the items can be processed due to insufficient\n provisioned throughput on all of the tables in the request, then\n BatchGetItem returns a\n ProvisionedThroughputExceededException. If at least\n one of the items is successfully processed, then\n BatchGetItem completes successfully, while returning the keys of the\n unread items in UnprocessedKeys.

\n \n

If DynamoDB returns any unprocessed items, you should retry the batch operation on\n those items. However, we strongly recommend that you use an exponential\n backoff algorithm. If you retry the batch operation immediately, the\n underlying read or write requests can still fail due to throttling on the individual\n tables. If you delay the batch operation using exponential backoff, the individual\n requests in the batch are much more likely to succeed.

\n

For more information, see Batch Operations and Error Handling in the Amazon DynamoDB\n Developer Guide.

\n
\n

By default, BatchGetItem performs eventually consistent reads on every\n table in the request. If you want strongly consistent reads instead, you can set\n ConsistentRead to true for any or all tables.

\n

In order to minimize response latency, BatchGetItem may retrieve items in\n parallel.

\n

When designing your application, keep in mind that DynamoDB does not return items in\n any particular order. To help parse the response by item, include the primary key values\n for the items in your request in the ProjectionExpression parameter.

\n

If a requested item does not exist, it is not returned in the result. Requests for\n nonexistent items consume the minimum read capacity units according to the type of read.\n For more information, see Working with Tables in the Amazon DynamoDB Developer\n Guide.

" } }, "com.amazonaws.dynamodb#BatchGetItemInput": { @@ -3924,8 +3924,8 @@ }, "params": { "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3937,8 +3937,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3950,8 +3950,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3963,8 +3963,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3976,8 +3976,8 @@ }, "params": { "Region": "ap-northeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3989,8 +3989,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4002,8 +4002,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4015,8 +4015,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4028,8 +4028,8 @@ }, "params": { "Region": "ap-southeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4041,8 +4041,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4054,8 +4054,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4067,8 +4067,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4080,8 +4080,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4093,8 +4093,8 @@ }, "params": { "Region": "eu-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4106,8 +4106,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4119,8 +4119,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4132,8 +4132,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4154,8 +4154,8 @@ }, "params": { "Region": "local", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4167,8 +4167,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4180,8 +4180,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4193,8 +4193,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4206,8 +4206,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4219,8 +4219,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4232,8 +4232,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4245,8 +4245,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4258,8 +4258,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4271,8 +4271,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4284,8 +4284,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4297,8 +4297,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4310,8 +4310,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4323,8 +4323,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4336,8 +4336,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4349,8 +4349,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4362,8 +4362,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4375,8 +4375,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4388,8 +4388,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4401,8 +4401,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4414,8 +4414,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4427,8 +4427,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4440,8 +4440,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4453,8 +4453,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4466,8 +4466,8 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4479,8 +4479,19 @@ }, "params": { "Region": "us-iso-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4492,8 +4503,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4505,8 +4527,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4518,8 +4551,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4531,8 +4575,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4544,8 +4588,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4556,8 +4600,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4568,10 +4612,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -8863,7 +8913,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

Creates a new table from an existing backup. Any number of users can execute up to 4\n concurrent restores (any type of restore) in a given account.

\n

You can call RestoreTableFromBackup at a maximum rate of 10 times per\n second.

\n

You must manually set up the following on the restored table:

\n
    \n
  • \n

    Auto scaling policies

    \n
  • \n
  • \n

    IAM policies

    \n
  • \n
  • \n

    Amazon CloudWatch metrics and alarms

    \n
  • \n
  • \n

    Tags

    \n
  • \n
  • \n

    Stream settings

    \n
  • \n
  • \n

    Time to Live (TTL) settings

    \n
  • \n
" + "smithy.api#documentation": "

Creates a new table from an existing backup. Any number of users can execute up to 50\n concurrent restores (any type of restore) in a given account.

\n

You can call RestoreTableFromBackup at a maximum rate of 10 times per\n second.

\n

You must manually set up the following on the restored table:

\n
    \n
  • \n

    Auto scaling policies

    \n
  • \n
  • \n

    IAM policies

    \n
  • \n
  • \n

    Amazon CloudWatch metrics and alarms

    \n
  • \n
  • \n

    Tags

    \n
  • \n
  • \n

    Stream settings

    \n
  • \n
  • \n

    Time to Live (TTL) settings

    \n
  • \n
" } }, "com.amazonaws.dynamodb#RestoreTableFromBackupInput": { diff --git a/aws/sdk/aws-models/ec2.json b/aws/sdk/aws-models/ec2.json index b159cff5a5..47805fb881 100644 --- a/aws/sdk/aws-models/ec2.json +++ b/aws/sdk/aws-models/ec2.json @@ -1060,7 +1060,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "AdditionalDetailType", - "smithy.api#documentation": "

The information type.

", + "smithy.api#documentation": "

The additional detail code.

", "smithy.api#xmlName": "additionalDetailType" } }, @@ -1071,10 +1071,58 @@ "smithy.api#documentation": "

The path component.

", "smithy.api#xmlName": "component" } + }, + "VpcEndpointService": { + "target": "com.amazonaws.ec2#AnalysisComponent", + "traits": { + "aws.protocols#ec2QueryName": "VpcEndpointService", + "smithy.api#documentation": "

The VPC endpoint service.

", + "smithy.api#xmlName": "vpcEndpointService" + } + }, + "RuleOptions": { + "target": "com.amazonaws.ec2#RuleOptionList", + "traits": { + "aws.protocols#ec2QueryName": "RuleOptionSet", + "smithy.api#documentation": "

The rule options.

", + "smithy.api#xmlName": "ruleOptionSet" + } + }, + "RuleGroupTypePairs": { + "target": "com.amazonaws.ec2#RuleGroupTypePairList", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupTypePairSet", + "smithy.api#documentation": "

The rule group type.

", + "smithy.api#xmlName": "ruleGroupTypePairSet" + } + }, + "RuleGroupRuleOptionsPairs": { + "target": "com.amazonaws.ec2#RuleGroupRuleOptionsPairList", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupRuleOptionsPairSet", + "smithy.api#documentation": "

The rule options.

", + "smithy.api#xmlName": "ruleGroupRuleOptionsPairSet" + } + }, + "ServiceName": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "ServiceName", + "smithy.api#documentation": "

The name of the VPC endpoint service.

", + "smithy.api#xmlName": "serviceName" + } + }, + "LoadBalancers": { + "target": "com.amazonaws.ec2#AnalysisComponentList", + "traits": { + "aws.protocols#ec2QueryName": "LoadBalancerSet", + "smithy.api#documentation": "

The load balancers.

", + "smithy.api#xmlName": "loadBalancerSet" + } } }, "traits": { - "smithy.api#documentation": "

Describes an additional detail for a path analysis.

" + "smithy.api#documentation": "

Describes an additional detail for a path analysis. For more information, see Reachability Analyzer additional detail codes.

" } }, "com.amazonaws.ec2#AdditionalDetailList": { @@ -1713,7 +1761,7 @@ "target": "com.amazonaws.ec2#AllocateIpamPoolCidrResult" }, "traits": { - "smithy.api#documentation": "

Allocate a CIDR from an IPAM pool. In IPAM, an allocation is a CIDR assignment from an IPAM pool to another IPAM pool or to a resource. For more information, see Allocate CIDRs in the Amazon VPC IPAM User Guide.\n

" + "smithy.api#documentation": "

Allocate a CIDR from an IPAM pool. In IPAM, an allocation is a CIDR assignment from an IPAM pool to another IPAM pool or to a resource. For more information, see Allocate CIDRs in the Amazon VPC IPAM User Guide.

\n \n

This action creates an allocation with strong consistency. The returned CIDR will not overlap with any other allocations from the same pool.

\n
" } }, "com.amazonaws.ec2#AllocateIpamPoolCidrRequest": { @@ -3392,6 +3440,9 @@ { "target": "com.amazonaws.ec2#GetVpnConnectionDeviceTypes" }, + { + "target": "com.amazonaws.ec2#GetVpnTunnelReplacementStatus" + }, { "target": "com.amazonaws.ec2#ImportClientVpnClientCertificateRevocationList" }, @@ -3692,6 +3743,9 @@ { "target": "com.amazonaws.ec2#ReplaceTransitGatewayRoute" }, + { + "target": "com.amazonaws.ec2#ReplaceVpnTunnel" + }, { "target": "com.amazonaws.ec2#ReportInstanceStatus" }, @@ -4175,44 +4229,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-east-1" - ] - } - ], - "endpoint": { - "url": "https://ec2.us-gov-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-west-1" - ] - } - ], - "endpoint": { - "url": "https://ec2.us-gov-west-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -4248,8 +4264,8 @@ }, "params": { "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4261,8 +4277,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4274,8 +4290,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4287,8 +4303,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4300,8 +4316,8 @@ }, "params": { "Region": "ap-northeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4313,8 +4329,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4326,8 +4342,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4339,8 +4355,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4352,8 +4368,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4365,8 +4381,8 @@ }, "params": { "Region": "ap-southeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4378,8 +4394,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4391,8 +4407,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4404,8 +4420,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4417,8 +4433,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4430,8 +4446,8 @@ }, "params": { "Region": "eu-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4443,8 +4459,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4456,8 +4472,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4469,8 +4485,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4482,8 +4498,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4495,8 +4511,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4508,8 +4524,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4521,8 +4537,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4534,8 +4550,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4547,8 +4563,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4560,8 +4576,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4573,8 +4589,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4586,8 +4602,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4599,8 +4615,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4612,8 +4628,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4625,8 +4641,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4638,8 +4654,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4651,8 +4667,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4664,8 +4680,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4677,8 +4693,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4690,8 +4706,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4703,8 +4719,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4716,8 +4732,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4729,8 +4745,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4742,8 +4758,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4755,8 +4771,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4768,8 +4784,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4781,8 +4797,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4794,8 +4810,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -4807,8 +4823,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4820,8 +4836,8 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -4833,8 +4849,19 @@ }, "params": { "Region": "us-iso-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4846,8 +4873,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4859,8 +4897,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -4872,8 +4921,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -4885,8 +4945,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4898,8 +4958,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4910,8 +4970,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -4922,10 +4982,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -5241,6 +5307,30 @@ "smithy.api#documentation": "

The state. The following are the possible values:

\n
    \n
  • \n

    active

    \n
  • \n
  • \n

    blackhole

    \n
  • \n
", "smithy.api#xmlName": "state" } + }, + "CarrierGatewayId": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "CarrierGatewayId", + "smithy.api#documentation": "

The ID of a carrier gateway.

", + "smithy.api#xmlName": "carrierGatewayId" + } + }, + "CoreNetworkArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "CoreNetworkArn", + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of a core network.

", + "smithy.api#xmlName": "coreNetworkArn" + } + }, + "LocalGatewayId": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "LocalGatewayId", + "smithy.api#documentation": "

The ID of a local gateway.

", + "smithy.api#xmlName": "localGatewayId" + } } }, "traits": { @@ -8169,6 +8259,9 @@ } } }, + "com.amazonaws.ec2#AvailabilityZoneName": { + "type": "string" + }, "com.amazonaws.ec2#AvailabilityZoneOptInStatus": { "type": "enum", "members": { @@ -15973,7 +16066,7 @@ "target": "com.amazonaws.ec2#CreateNetworkInsightsPathResult" }, "traits": { - "smithy.api#documentation": "

Creates a path to analyze for reachability.

\n

Reachability Analyzer enables you to analyze and debug network reachability between\n two resources in your virtual private cloud (VPC). For more information, see \n What is Reachability Analyzer.

" + "smithy.api#documentation": "

Creates a path to analyze for reachability.

\n

Reachability Analyzer enables you to analyze and debug network reachability between\n two resources in your virtual private cloud (VPC). For more information, see the \n Reachability Analyzer Guide.

" } }, "com.amazonaws.ec2#CreateNetworkInsightsPathRequest": { @@ -15982,29 +16075,27 @@ "SourceIp": { "target": "com.amazonaws.ec2#IpAddress", "traits": { - "smithy.api#documentation": "

The IP address of the Amazon Web Services resource that is the source of the path.

" + "smithy.api#documentation": "

The IP address of the source.

" } }, "DestinationIp": { "target": "com.amazonaws.ec2#IpAddress", "traits": { - "smithy.api#documentation": "

The IP address of the Amazon Web Services resource that is the destination of the path.

" + "smithy.api#documentation": "

The IP address of the destination.

" } }, "Source": { "target": "com.amazonaws.ec2#NetworkInsightsResourceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The Amazon Web Services resource that is the source of the path.

", + "smithy.api#documentation": "

The ID or ARN of the source. If the resource is in another account, you must specify an ARN.

", "smithy.api#required": {} } }, "Destination": { "target": "com.amazonaws.ec2#NetworkInsightsResourceId", "traits": { - "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The Amazon Web Services resource that is the destination of the path.

", - "smithy.api#required": {} + "smithy.api#documentation": "

The ID or ARN of the destination. If the resource is in another account, you must specify an ARN.

" } }, "Protocol": { @@ -16046,6 +16137,18 @@ "smithy.api#idempotencyToken": {}, "smithy.api#required": {} } + }, + "FilterAtSource": { + "target": "com.amazonaws.ec2#PathRequestFilter", + "traits": { + "smithy.api#documentation": "

Scopes the analysis to network paths that match specific filters at the source. If you specify\n this parameter, you can't specify the parameters for the source IP address or the destination port.

" + } + }, + "FilterAtDestination": { + "target": "com.amazonaws.ec2#PathRequestFilter", + "traits": { + "smithy.api#documentation": "

Scopes the analysis to network paths that match specific filters at the destination. If you specify\n this parameter, you can't specify the parameter for the destination IP address.

" + } } }, "traits": { @@ -16901,7 +17004,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

A description for the security group. This is informational only.

\n

Constraints: Up to 255 characters in length

\n

Constraints for EC2-Classic: ASCII characters

\n

Constraints for EC2-VPC: a-z, A-Z, 0-9, spaces, and ._-:/()#,@[]+=&;{}!$*

", + "smithy.api#documentation": "

A description for the security group.

\n

Constraints: Up to 255 characters in length

\n

Constraints for EC2-Classic: ASCII characters

\n

Constraints for EC2-VPC: a-z, A-Z, 0-9, spaces, and ._-:/()#,@[]+=&;{}!$*

", "smithy.api#required": {}, "smithy.api#xmlName": "GroupDescription" } @@ -16972,7 +17075,7 @@ "target": "com.amazonaws.ec2#Snapshot" }, "traits": { - "smithy.api#documentation": "

Creates a snapshot of an EBS volume and stores it in Amazon S3. You can use snapshots for\n \tbackups, to make copies of EBS volumes, and to save data before shutting down an\n \tinstance.

\n

You can create snapshots of volumes in a Region and volumes on an Outpost. If you \n \tcreate a snapshot of a volume in a Region, the snapshot must be stored in the same \n \tRegion as the volume. If you create a snapshot of a volume on an Outpost, the snapshot \n \tcan be stored on the same Outpost as the volume, or in the Region for that Outpost.

\n

When a snapshot is created, any Amazon Web Services Marketplace product codes that are associated with the\n source volume are propagated to the snapshot.

\n

You can take a snapshot of an attached volume that is in use. However, snapshots only\n capture data that has been written to your Amazon EBS volume at the time the snapshot command is\n issued; this might exclude any data that has been cached by any applications or the operating\n system. If you can pause any file systems on the volume long enough to take a snapshot, your\n snapshot should be complete. However, if you cannot pause all file writes to the volume, you\n should unmount the volume from within the instance, issue the snapshot command, and then\n remount the volume to ensure a consistent and complete snapshot. You may remount and use your\n volume while the snapshot status is pending.

\n

To create a snapshot for Amazon EBS volumes that serve as root devices, you should stop the\n instance before taking the snapshot.

\n

Snapshots that are taken from encrypted volumes are automatically encrypted. Volumes that\n are created from encrypted snapshots are also automatically encrypted. Your encrypted volumes\n and any associated snapshots always remain protected.

\n

You can tag your snapshots during creation. For more information, see Tag your Amazon EC2\n resources in the Amazon Elastic Compute Cloud User Guide.

\n

For more information, see Amazon Elastic Block Store and Amazon EBS encryption in the Amazon Elastic Compute Cloud User Guide.

" + "smithy.api#documentation": "

Creates a snapshot of an EBS volume and stores it in Amazon S3. You can use snapshots for\n \tbackups, to make copies of EBS volumes, and to save data before shutting down an\n \tinstance.

\n

You can create snapshots of volumes in a Region and volumes on an Outpost. If you \n \tcreate a snapshot of a volume in a Region, the snapshot must be stored in the same \n \tRegion as the volume. If you create a snapshot of a volume on an Outpost, the snapshot \n \tcan be stored on the same Outpost as the volume, or in the Region for that Outpost.

\n

When a snapshot is created, any Amazon Web Services Marketplace product codes that are associated with the\n source volume are propagated to the snapshot.

\n

You can take a snapshot of an attached volume that is in use. However, snapshots only\n capture data that has been written to your Amazon EBS volume at the time the snapshot command is\n issued; this might exclude any data that has been cached by any applications or the operating\n system. If you can pause any file systems on the volume long enough to take a snapshot, your\n snapshot should be complete. However, if you cannot pause all file writes to the volume, you\n should unmount the volume from within the instance, issue the snapshot command, and then\n remount the volume to ensure a consistent and complete snapshot. You may remount and use your\n volume while the snapshot status is pending.

\n

When you create a snapshot for an EBS volume that serves as a root device, we recommend \n that you stop the instance before taking the snapshot.

\n

Snapshots that are taken from encrypted volumes are automatically encrypted. Volumes that\n are created from encrypted snapshots are also automatically encrypted. Your encrypted volumes\n and any associated snapshots always remain protected.

\n

You can tag your snapshots during creation. For more information, see Tag your Amazon EC2\n resources in the Amazon Elastic Compute Cloud User Guide.

\n

For more information, see Amazon Elastic Block Store and Amazon EBS encryption in the Amazon Elastic Compute Cloud User Guide.

" } }, "com.amazonaws.ec2#CreateSnapshotRequest": { @@ -19257,7 +19360,7 @@ "type": "structure", "members": { "AvailabilityZone": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#AvailabilityZoneName", "traits": { "smithy.api#clientOptional": {}, "smithy.api#documentation": "

The Availability Zone in which to create the volume.

", @@ -31807,7 +31910,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

The filters. The following are the possible values:

\n
    \n
  • \n

    destination - The ID of the resource.

    \n
  • \n
  • \n

    destination-port - The destination port.

    \n
  • \n
  • \n

    protocol - The protocol.

    \n
  • \n
  • \n

    source - The ID of the resource.

    \n
  • \n
", + "smithy.api#documentation": "

The filters. The following are the possible values:

\n
    \n
  • \n

    destination - The ID of the resource.

    \n
  • \n
  • \n

    filter-at-source.source-address - The source IPv4 address at the source.

    \n
  • \n
  • \n

    filter-at-source.source-port-range - The source port range at the source.

    \n
  • \n
  • \n

    filter-at-source.destination-address - The destination IPv4 address at the source.

    \n
  • \n
  • \n

    filter-at-source.destination-port-range - The destination port range at the source.

    \n
  • \n
  • \n

    filter-at-destination.source-address - The source IPv4 address at the destination.

    \n
  • \n
  • \n

    filter-at-destination.source-port-range - The source port range at the destination.

    \n
  • \n
  • \n

    filter-at-destination.destination-address - The destination IPv4 address at the destination.

    \n
  • \n
  • \n

    filter-at-destination.destination-port-range - The destination port range at the destination.

    \n
  • \n
  • \n

    protocol - The protocol.

    \n
  • \n
  • \n

    source - The ID of the resource.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -40523,7 +40626,7 @@ } }, "ImportManifestUrl": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#ImportManifestUrl", "traits": { "aws.protocols#ec2QueryName": "ImportManifestUrl", "smithy.api#clientOptional": {}, @@ -43347,6 +43450,22 @@ "smithy.api#documentation": "

The Region for the component.

", "smithy.api#xmlName": "componentRegion" } + }, + "FirewallStatelessRule": { + "target": "com.amazonaws.ec2#FirewallStatelessRule", + "traits": { + "aws.protocols#ec2QueryName": "FirewallStatelessRule", + "smithy.api#documentation": "

The Network Firewall stateless rule.

", + "smithy.api#xmlName": "firewallStatelessRule" + } + }, + "FirewallStatefulRule": { + "target": "com.amazonaws.ec2#FirewallStatefulRule", + "traits": { + "aws.protocols#ec2QueryName": "FirewallStatefulRule", + "smithy.api#documentation": "

The Network Firewall stateful rule.

", + "smithy.api#xmlName": "firewallStatefulRule" + } } }, "traits": { @@ -44384,6 +44503,34 @@ } } }, + "com.amazonaws.ec2#FilterPortRange": { + "type": "structure", + "members": { + "FromPort": { + "target": "com.amazonaws.ec2#Port", + "traits": { + "aws.protocols#ec2QueryName": "FromPort", + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The first port in the range.

", + "smithy.api#xmlName": "fromPort" + } + }, + "ToPort": { + "target": "com.amazonaws.ec2#Port", + "traits": { + "aws.protocols#ec2QueryName": "ToPort", + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The last port in the range.

", + "smithy.api#xmlName": "toPort" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a port range.

" + } + }, "com.amazonaws.ec2#FindingsFound": { "type": "enum", "members": { @@ -44407,6 +44554,152 @@ } } }, + "com.amazonaws.ec2#FirewallStatefulRule": { + "type": "structure", + "members": { + "RuleGroupArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupArn", + "smithy.api#documentation": "

The ARN of the stateful rule group.

", + "smithy.api#xmlName": "ruleGroupArn" + } + }, + "Sources": { + "target": "com.amazonaws.ec2#ValueStringList", + "traits": { + "aws.protocols#ec2QueryName": "SourceSet", + "smithy.api#documentation": "

The source IP addresses, in CIDR notation.

", + "smithy.api#xmlName": "sourceSet" + } + }, + "Destinations": { + "target": "com.amazonaws.ec2#ValueStringList", + "traits": { + "aws.protocols#ec2QueryName": "DestinationSet", + "smithy.api#documentation": "

The destination IP addresses, in CIDR notation.

", + "smithy.api#xmlName": "destinationSet" + } + }, + "SourcePorts": { + "target": "com.amazonaws.ec2#PortRangeList", + "traits": { + "aws.protocols#ec2QueryName": "SourcePortSet", + "smithy.api#documentation": "

The source ports.

", + "smithy.api#xmlName": "sourcePortSet" + } + }, + "DestinationPorts": { + "target": "com.amazonaws.ec2#PortRangeList", + "traits": { + "aws.protocols#ec2QueryName": "DestinationPortSet", + "smithy.api#documentation": "

The destination ports.

", + "smithy.api#xmlName": "destinationPortSet" + } + }, + "Protocol": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "Protocol", + "smithy.api#documentation": "

The protocol.

", + "smithy.api#xmlName": "protocol" + } + }, + "RuleAction": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "RuleAction", + "smithy.api#documentation": "

The rule action. The possible values are pass, drop, and \n alert.

", + "smithy.api#xmlName": "ruleAction" + } + }, + "Direction": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "Direction", + "smithy.api#documentation": "

The direction. The possible values are FORWARD and ANY.

", + "smithy.api#xmlName": "direction" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a stateful rule.

" + } + }, + "com.amazonaws.ec2#FirewallStatelessRule": { + "type": "structure", + "members": { + "RuleGroupArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupArn", + "smithy.api#documentation": "

The ARN of the stateless rule group.

", + "smithy.api#xmlName": "ruleGroupArn" + } + }, + "Sources": { + "target": "com.amazonaws.ec2#ValueStringList", + "traits": { + "aws.protocols#ec2QueryName": "SourceSet", + "smithy.api#documentation": "

The source IP addresses, in CIDR notation.

", + "smithy.api#xmlName": "sourceSet" + } + }, + "Destinations": { + "target": "com.amazonaws.ec2#ValueStringList", + "traits": { + "aws.protocols#ec2QueryName": "DestinationSet", + "smithy.api#documentation": "

The destination IP addresses, in CIDR notation.

", + "smithy.api#xmlName": "destinationSet" + } + }, + "SourcePorts": { + "target": "com.amazonaws.ec2#PortRangeList", + "traits": { + "aws.protocols#ec2QueryName": "SourcePortSet", + "smithy.api#documentation": "

The source ports.

", + "smithy.api#xmlName": "sourcePortSet" + } + }, + "DestinationPorts": { + "target": "com.amazonaws.ec2#PortRangeList", + "traits": { + "aws.protocols#ec2QueryName": "DestinationPortSet", + "smithy.api#documentation": "

The destination ports.

", + "smithy.api#xmlName": "destinationPortSet" + } + }, + "Protocols": { + "target": "com.amazonaws.ec2#ProtocolIntList", + "traits": { + "aws.protocols#ec2QueryName": "ProtocolSet", + "smithy.api#documentation": "

The protocols.

", + "smithy.api#xmlName": "protocolSet" + } + }, + "RuleAction": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "RuleAction", + "smithy.api#documentation": "

The rule action. The possible values are pass, drop, and \n forward_to_site.

", + "smithy.api#xmlName": "ruleAction" + } + }, + "Priority": { + "target": "com.amazonaws.ec2#Priority", + "traits": { + "aws.protocols#ec2QueryName": "Priority", + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The rule priority.

", + "smithy.api#xmlName": "priority" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a stateless rule.

" + } + }, "com.amazonaws.ec2#FleetActivityStatus": { "type": "enum", "members": { @@ -46740,7 +47033,7 @@ "target": "com.amazonaws.ec2#CapacityReservationId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Capacity Reservation.

", + "smithy.api#documentation": "

The ID of the Capacity Reservation. If you specify a Capacity Reservation that is shared \n\t\t\twith you, the operation returns only Capacity Reservation groups that you own.

", "smithy.api#required": {} } }, @@ -47324,7 +47617,7 @@ "target": "com.amazonaws.ec2#GetIpamPoolAllocationsResult" }, "traits": { - "smithy.api#documentation": "

Get a list of all the CIDR allocations in an IPAM pool.

", + "smithy.api#documentation": "

Get a list of all the CIDR allocations in an IPAM pool.

\n \n

If you use this action after AllocateIpamPoolCidr or ReleaseIpamPoolAllocation, note that all EC2 API actions follow an eventual consistency model.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -49254,6 +49547,103 @@ } } }, + "com.amazonaws.ec2#GetVpnTunnelReplacementStatus": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#GetVpnTunnelReplacementStatusRequest" + }, + "output": { + "target": "com.amazonaws.ec2#GetVpnTunnelReplacementStatusResult" + }, + "traits": { + "smithy.api#documentation": "

Get details of available tunnel endpoint maintenance.

" + } + }, + "com.amazonaws.ec2#GetVpnTunnelReplacementStatusRequest": { + "type": "structure", + "members": { + "VpnConnectionId": { + "target": "com.amazonaws.ec2#VpnConnectionId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The ID of the Site-to-Site VPN connection.

", + "smithy.api#required": {} + } + }, + "VpnTunnelOutsideIpAddress": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The external IP address of the VPN tunnel.

", + "smithy.api#required": {} + } + }, + "DryRun": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually making the request, and provides an error response. If you have the required permissions, the error response is DryRunOperation. Otherwise, it is UnauthorizedOperation.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#GetVpnTunnelReplacementStatusResult": { + "type": "structure", + "members": { + "VpnConnectionId": { + "target": "com.amazonaws.ec2#VpnConnectionId", + "traits": { + "aws.protocols#ec2QueryName": "VpnConnectionId", + "smithy.api#documentation": "

The ID of the Site-to-Site VPN connection.

", + "smithy.api#xmlName": "vpnConnectionId" + } + }, + "TransitGatewayId": { + "target": "com.amazonaws.ec2#TransitGatewayId", + "traits": { + "aws.protocols#ec2QueryName": "TransitGatewayId", + "smithy.api#documentation": "

The ID of the transit gateway associated with the VPN connection.

", + "smithy.api#xmlName": "transitGatewayId" + } + }, + "CustomerGatewayId": { + "target": "com.amazonaws.ec2#CustomerGatewayId", + "traits": { + "aws.protocols#ec2QueryName": "CustomerGatewayId", + "smithy.api#documentation": "

The ID of the customer gateway.

", + "smithy.api#xmlName": "customerGatewayId" + } + }, + "VpnGatewayId": { + "target": "com.amazonaws.ec2#VpnGatewayId", + "traits": { + "aws.protocols#ec2QueryName": "VpnGatewayId", + "smithy.api#documentation": "

The ID of the virtual private gateway.

", + "smithy.api#xmlName": "vpnGatewayId" + } + }, + "VpnTunnelOutsideIpAddress": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "VpnTunnelOutsideIpAddress", + "smithy.api#documentation": "

The external IP address of the VPN tunnel.

", + "smithy.api#xmlName": "vpnTunnelOutsideIpAddress" + } + }, + "MaintenanceDetails": { + "target": "com.amazonaws.ec2#MaintenanceDetails", + "traits": { + "aws.protocols#ec2QueryName": "MaintenanceDetails", + "smithy.api#documentation": "

Get details of pending tunnel endpoint maintenance.

", + "smithy.api#xmlName": "maintenanceDetails" + } + } + } + }, "com.amazonaws.ec2#GpuDeviceCount": { "type": "integer" }, @@ -51980,6 +52370,12 @@ } } }, + "com.amazonaws.ec2#ImportManifestUrl": { + "type": "string", + "traits": { + "smithy.api#sensitive": {} + } + }, "com.amazonaws.ec2#ImportSnapshot": { "type": "operation", "input": { @@ -52617,7 +53013,7 @@ "target": "com.amazonaws.ec2#ElasticInferenceAcceleratorAssociationList", "traits": { "aws.protocols#ec2QueryName": "ElasticInferenceAcceleratorAssociationSet", - "smithy.api#documentation": "

The elastic inference accelerator associated with the instance.

", + "smithy.api#documentation": "

The elastic inference accelerator associated with the instance.

", "smithy.api#xmlName": "elasticInferenceAcceleratorAssociationSet" } }, @@ -59390,6 +59786,36 @@ "traits": { "smithy.api#enumValue": "r7g.metal" } + }, + "c6in_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "c6in.metal" + } + }, + "m6in_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m6in.metal" + } + }, + "m6idn_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "m6idn.metal" + } + }, + "r6in_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r6in.metal" + } + }, + "r6idn_metal": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "r6idn.metal" + } } } }, @@ -66086,6 +66512,38 @@ "smithy.api#default": 0 } }, + "com.amazonaws.ec2#MaintenanceDetails": { + "type": "structure", + "members": { + "PendingMaintenance": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "PendingMaintenance", + "smithy.api#documentation": "

Verify existence of a pending maintenance.

", + "smithy.api#xmlName": "pendingMaintenance" + } + }, + "MaintenanceAutoAppliedAfter": { + "target": "com.amazonaws.ec2#MillisecondDateTime", + "traits": { + "aws.protocols#ec2QueryName": "MaintenanceAutoAppliedAfter", + "smithy.api#documentation": "

The timestamp after which Amazon Web Services will automatically apply maintenance.

", + "smithy.api#xmlName": "maintenanceAutoAppliedAfter" + } + }, + "LastMaintenanceApplied": { + "target": "com.amazonaws.ec2#MillisecondDateTime", + "traits": { + "aws.protocols#ec2QueryName": "LastMaintenanceApplied", + "smithy.api#documentation": "

Timestamp of last applied maintenance.

", + "smithy.api#xmlName": "lastMaintenanceApplied" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details for Site-to-Site VPN tunnel endpoint maintenance events.

" + } + }, "com.amazonaws.ec2#ManagedPrefixList": { "type": "structure", "members": { @@ -71415,6 +71873,14 @@ "smithy.api#default": false, "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually\n making the request, and provides an error response. If you have the required\n permissions, the error response is DryRunOperation. Otherwise, it is\n UnauthorizedOperation.

" } + }, + "SkipTunnelReplacement": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Choose whether or not to trigger immediate tunnel replacement.

\n

Valid values: True | False\n

" + } } }, "traits": { @@ -71569,6 +72035,14 @@ "traits": { "smithy.api#documentation": "

Options for logging VPN tunnel activity.

" } + }, + "EnableTunnelLifecycleControl": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Turn on or off tunnel endpoint lifecycle control feature.

" + } } }, "traits": { @@ -72835,7 +73309,7 @@ "target": "com.amazonaws.ec2#ArnList", "traits": { "aws.protocols#ec2QueryName": "FilterInArnSet", - "smithy.api#documentation": "

The Amazon Resource Names (ARN) of the Amazon Web Services resources that the path must traverse.

", + "smithy.api#documentation": "

The Amazon Resource Names (ARN) of the resources that the path must traverse.

", "smithy.api#xmlName": "filterInArnSet" } }, @@ -72996,7 +73470,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "Source", - "smithy.api#documentation": "

The Amazon Web Services resource that is the source of the path.

", + "smithy.api#documentation": "

The ID of the source.

", "smithy.api#xmlName": "source" } }, @@ -73004,7 +73478,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "Destination", - "smithy.api#documentation": "

The Amazon Web Services resource that is the destination of the path.

", + "smithy.api#documentation": "

The ID of the destination.

", "smithy.api#xmlName": "destination" } }, @@ -73028,7 +73502,7 @@ "target": "com.amazonaws.ec2#IpAddress", "traits": { "aws.protocols#ec2QueryName": "SourceIp", - "smithy.api#documentation": "

The IP address of the Amazon Web Services resource that is the source of the path.

", + "smithy.api#documentation": "

The IP address of the source.

", "smithy.api#xmlName": "sourceIp" } }, @@ -73036,7 +73510,7 @@ "target": "com.amazonaws.ec2#IpAddress", "traits": { "aws.protocols#ec2QueryName": "DestinationIp", - "smithy.api#documentation": "

The IP address of the Amazon Web Services resource that is the destination of the path.

", + "smithy.api#documentation": "

The IP address of the destination.

", "smithy.api#xmlName": "destinationIp" } }, @@ -73065,6 +73539,22 @@ "smithy.api#documentation": "

The tags associated with the path.

", "smithy.api#xmlName": "tagSet" } + }, + "FilterAtSource": { + "target": "com.amazonaws.ec2#PathFilter", + "traits": { + "aws.protocols#ec2QueryName": "FilterAtSource", + "smithy.api#documentation": "

Scopes the analysis to network paths that match specific filters at the source.

", + "smithy.api#xmlName": "filterAtSource" + } + }, + "FilterAtDestination": { + "target": "com.amazonaws.ec2#PathFilter", + "traits": { + "aws.protocols#ec2QueryName": "FilterAtDestination", + "smithy.api#documentation": "

Scopes the analysis to network paths that match specific filters at the destination.

", + "smithy.api#xmlName": "filterAtDestination" + } } }, "traits": { @@ -74618,6 +75108,30 @@ "smithy.api#documentation": "

The load balancer listener.

", "smithy.api#xmlName": "elasticLoadBalancerListener" } + }, + "FirewallStatelessRule": { + "target": "com.amazonaws.ec2#FirewallStatelessRule", + "traits": { + "aws.protocols#ec2QueryName": "FirewallStatelessRule", + "smithy.api#documentation": "

The Network Firewall stateless rule.

", + "smithy.api#xmlName": "firewallStatelessRule" + } + }, + "FirewallStatefulRule": { + "target": "com.amazonaws.ec2#FirewallStatefulRule", + "traits": { + "aws.protocols#ec2QueryName": "FirewallStatefulRule", + "smithy.api#documentation": "

The Network Firewall stateful rule.

", + "smithy.api#xmlName": "firewallStatefulRule" + } + }, + "ServiceName": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "ServiceName", + "smithy.api#documentation": "

The name of the VPC endpoint service.

", + "smithy.api#xmlName": "serviceName" + } } }, "traits": { @@ -74633,6 +75147,78 @@ } } }, + "com.amazonaws.ec2#PathFilter": { + "type": "structure", + "members": { + "SourceAddress": { + "target": "com.amazonaws.ec2#IpAddress", + "traits": { + "aws.protocols#ec2QueryName": "SourceAddress", + "smithy.api#documentation": "

The source IPv4 address.

", + "smithy.api#xmlName": "sourceAddress" + } + }, + "SourcePortRange": { + "target": "com.amazonaws.ec2#FilterPortRange", + "traits": { + "aws.protocols#ec2QueryName": "SourcePortRange", + "smithy.api#documentation": "

The source port range.

", + "smithy.api#xmlName": "sourcePortRange" + } + }, + "DestinationAddress": { + "target": "com.amazonaws.ec2#IpAddress", + "traits": { + "aws.protocols#ec2QueryName": "DestinationAddress", + "smithy.api#documentation": "

The destination IPv4 address.

", + "smithy.api#xmlName": "destinationAddress" + } + }, + "DestinationPortRange": { + "target": "com.amazonaws.ec2#FilterPortRange", + "traits": { + "aws.protocols#ec2QueryName": "DestinationPortRange", + "smithy.api#documentation": "

The destination port range.

", + "smithy.api#xmlName": "destinationPortRange" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a set of filters for a path analysis. Use path filters to scope the analysis when\n there can be multiple resulting paths.

" + } + }, + "com.amazonaws.ec2#PathRequestFilter": { + "type": "structure", + "members": { + "SourceAddress": { + "target": "com.amazonaws.ec2#IpAddress", + "traits": { + "smithy.api#documentation": "

The source IPv4 address.

" + } + }, + "SourcePortRange": { + "target": "com.amazonaws.ec2#RequestFilterPortRange", + "traits": { + "smithy.api#documentation": "

The source port range.

" + } + }, + "DestinationAddress": { + "target": "com.amazonaws.ec2#IpAddress", + "traits": { + "smithy.api#documentation": "

The destination IPv4 address.

" + } + }, + "DestinationPortRange": { + "target": "com.amazonaws.ec2#RequestFilterPortRange", + "traits": { + "smithy.api#documentation": "

The destination port range.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a set of filters for a path analysis. Use path filters to scope the analysis when\n there can be multiple resulting paths.

" + } + }, "com.amazonaws.ec2#PathStatement": { "type": "structure", "members": { @@ -76085,6 +76671,16 @@ } } }, + "com.amazonaws.ec2#Priority": { + "type": "integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": -1, + "max": 65535 + } + } + }, "com.amazonaws.ec2#PrivateDnsDetails": { "type": "structure", "members": { @@ -76449,6 +77045,25 @@ } } }, + "com.amazonaws.ec2#ProtocolInt": { + "type": "integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": 0, + "max": 255 + } + } + }, + "com.amazonaws.ec2#ProtocolIntList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#ProtocolInt", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#ProtocolList": { "type": "list", "member": { @@ -78287,7 +78902,7 @@ "target": "com.amazonaws.ec2#ReleaseIpamPoolAllocationResult" }, "traits": { - "smithy.api#documentation": "

Release an allocation within an IPAM pool. You can only use this action to release manual allocations. To remove an allocation for a resource without deleting the resource, set its monitored state to false using ModifyIpamResourceCidr. For more information, see Release an allocation in the Amazon VPC IPAM User Guide.\n

" + "smithy.api#documentation": "

Release an allocation within an IPAM pool. You can only use this action to release manual allocations. To remove an allocation for a resource without deleting the resource, set its monitored state to false using ModifyIpamResourceCidr. For more information, see Release an allocation in the Amazon VPC IPAM User Guide.\n

\n \n

All EC2 API actions follow an eventual consistency model.

\n
" } }, "com.amazonaws.ec2#ReleaseIpamPoolAllocationRequest": { @@ -79058,6 +79673,73 @@ } } }, + "com.amazonaws.ec2#ReplaceVpnTunnel": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#ReplaceVpnTunnelRequest" + }, + "output": { + "target": "com.amazonaws.ec2#ReplaceVpnTunnelResult" + }, + "traits": { + "smithy.api#documentation": "

Trigger replacement of specified VPN tunnel.

" + } + }, + "com.amazonaws.ec2#ReplaceVpnTunnelRequest": { + "type": "structure", + "members": { + "VpnConnectionId": { + "target": "com.amazonaws.ec2#VpnConnectionId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The ID of the Site-to-Site VPN connection.

", + "smithy.api#required": {} + } + }, + "VpnTunnelOutsideIpAddress": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The external IP address of the VPN tunnel.

", + "smithy.api#required": {} + } + }, + "ApplyPendingMaintenance": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Trigger pending tunnel endpoint maintenance.

" + } + }, + "DryRun": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually making the request, and provides an error response. If you have the required permissions, the error response is DryRunOperation. Otherwise, it is UnauthorizedOperation.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#ReplaceVpnTunnelResult": { + "type": "structure", + "members": { + "Return": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "aws.protocols#ec2QueryName": "Return", + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Confirmation of replace tunnel operation.

", + "smithy.api#xmlName": "return" + } + } + } + }, "com.amazonaws.ec2#ReplacementStrategy": { "type": "enum", "members": { @@ -79235,6 +79917,30 @@ } } }, + "com.amazonaws.ec2#RequestFilterPortRange": { + "type": "structure", + "members": { + "FromPort": { + "target": "com.amazonaws.ec2#Port", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The first port in the range.

" + } + }, + "ToPort": { + "target": "com.amazonaws.ec2#Port", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The last port in the range.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes a port range.

" + } + }, "com.amazonaws.ec2#RequestHostIdList": { "type": "list", "member": { @@ -83233,6 +83939,105 @@ } } }, + "com.amazonaws.ec2#RuleGroupRuleOptionsPair": { + "type": "structure", + "members": { + "RuleGroupArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupArn", + "smithy.api#documentation": "

The ARN of the rule group.

", + "smithy.api#xmlName": "ruleGroupArn" + } + }, + "RuleOptions": { + "target": "com.amazonaws.ec2#RuleOptionList", + "traits": { + "aws.protocols#ec2QueryName": "RuleOptionSet", + "smithy.api#documentation": "

The rule options.

", + "smithy.api#xmlName": "ruleOptionSet" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes the rule options for a stateful rule group.

" + } + }, + "com.amazonaws.ec2#RuleGroupRuleOptionsPairList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#RuleGroupRuleOptionsPair", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, + "com.amazonaws.ec2#RuleGroupTypePair": { + "type": "structure", + "members": { + "RuleGroupArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupArn", + "smithy.api#documentation": "

The ARN of the rule group.

", + "smithy.api#xmlName": "ruleGroupArn" + } + }, + "RuleGroupType": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "RuleGroupType", + "smithy.api#documentation": "

The rule group type. The possible values are Domain List and Suricata.

", + "smithy.api#xmlName": "ruleGroupType" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes the type of a stateful rule group.

" + } + }, + "com.amazonaws.ec2#RuleGroupTypePairList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#RuleGroupTypePair", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, + "com.amazonaws.ec2#RuleOption": { + "type": "structure", + "members": { + "Keyword": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "Keyword", + "smithy.api#documentation": "

The Suricata keyword.

", + "smithy.api#xmlName": "keyword" + } + }, + "Settings": { + "target": "com.amazonaws.ec2#StringList", + "traits": { + "aws.protocols#ec2QueryName": "SettingSet", + "smithy.api#documentation": "

The settings for the keyword.

", + "smithy.api#xmlName": "settingSet" + } + } + }, + "traits": { + "smithy.api#documentation": "

Describes additional settings for a stateful rule.

" + } + }, + "com.amazonaws.ec2#RuleOptionList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#RuleOption", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#RunInstances": { "type": "operation", "input": { @@ -83463,7 +84268,7 @@ "ElasticInferenceAccelerators": { "target": "com.amazonaws.ec2#ElasticInferenceAccelerators", "traits": { - "smithy.api#documentation": "

An elastic inference accelerator to associate with the instance. Elastic inference\n accelerators are a resource you can attach to your Amazon EC2 instances to accelerate\n your Deep Learning (DL) inference workloads.

\n

You cannot specify accelerators from different generations in the same request.

", + "smithy.api#documentation": "

An elastic inference accelerator to associate with the instance. Elastic inference\n accelerators are a resource you can attach to your Amazon EC2 instances to accelerate\n your Deep Learning (DL) inference workloads.

\n

You cannot specify accelerators from different generations in the same request.

\n \n

Starting April 15, 2023, Amazon Web Services will not onboard new customers to Amazon\n Elastic Inference (EI), and will help current customers migrate their workloads to\n options that offer better price and performance. After April 15, 2023, new customers\n will not be able to launch instances with Amazon EI accelerators in Amazon SageMaker,\n Amazon ECS, or Amazon EC2. However, customers who have used Amazon EI at least once during\n the past 30-day period are considered current customers and will be able to continue\n using the service.

\n
", "smithy.api#xmlName": "ElasticInferenceAccelerator" } }, @@ -93788,6 +94593,16 @@ "smithy.api#documentation": "

Options for logging VPN tunnel activity.

", "smithy.api#xmlName": "logOptions" } + }, + "EnableTunnelLifecycleControl": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "aws.protocols#ec2QueryName": "EnableTunnelLifecycleControl", + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Status of tunnel endpoint lifecycle control feature.

", + "smithy.api#xmlName": "enableTunnelLifecycleControl" + } } }, "traits": { @@ -98578,6 +99393,14 @@ "traits": { "smithy.api#documentation": "

Options for logging VPN tunnel activity.

" } + }, + "EnableTunnelLifecycleControl": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Turn on or off tunnel endpoint lifecycle control feature.

" + } } }, "traits": { diff --git a/aws/sdk/aws-models/ecs.json b/aws/sdk/aws-models/ecs.json index b408252d21..76f7118492 100644 --- a/aws/sdk/aws-models/ecs.json +++ b/aws/sdk/aws-models/ecs.json @@ -632,9 +632,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "af-south-1", "UseFIPS": false, - "Region": "af-south-1" + "UseDualStack": false } }, { @@ -645,9 +645,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-east-1", "UseFIPS": false, - "Region": "ap-east-1" + "UseDualStack": false } }, { @@ -658,9 +658,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-1", "UseFIPS": false, - "Region": "ap-northeast-1" + "UseDualStack": false } }, { @@ -671,9 +671,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-2", "UseFIPS": false, - "Region": "ap-northeast-2" + "UseDualStack": false } }, { @@ -684,9 +684,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-3", "UseFIPS": false, - "Region": "ap-northeast-3" + "UseDualStack": false } }, { @@ -697,9 +697,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-south-1", "UseFIPS": false, - "Region": "ap-south-1" + "UseDualStack": false } }, { @@ -710,9 +710,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-1", "UseFIPS": false, - "Region": "ap-southeast-1" + "UseDualStack": false } }, { @@ -723,9 +723,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-2", "UseFIPS": false, - "Region": "ap-southeast-2" + "UseDualStack": false } }, { @@ -736,9 +736,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-3", "UseFIPS": false, - "Region": "ap-southeast-3" + "UseDualStack": false } }, { @@ -749,9 +749,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ca-central-1", "UseFIPS": false, - "Region": "ca-central-1" + "UseDualStack": false } }, { @@ -762,9 +762,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-central-1", "UseFIPS": false, - "Region": "eu-central-1" + "UseDualStack": false } }, { @@ -775,9 +775,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-north-1", "UseFIPS": false, - "Region": "eu-north-1" + "UseDualStack": false } }, { @@ -788,9 +788,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-south-1", "UseFIPS": false, - "Region": "eu-south-1" + "UseDualStack": false } }, { @@ -801,9 +801,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-1", "UseFIPS": false, - "Region": "eu-west-1" + "UseDualStack": false } }, { @@ -814,9 +814,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-2", "UseFIPS": false, - "Region": "eu-west-2" + "UseDualStack": false } }, { @@ -827,9 +827,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-3", "UseFIPS": false, - "Region": "eu-west-3" + "UseDualStack": false } }, { @@ -840,9 +840,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "me-south-1", "UseFIPS": false, - "Region": "me-south-1" + "UseDualStack": false } }, { @@ -853,9 +853,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "sa-east-1", "UseFIPS": false, - "Region": "sa-east-1" + "UseDualStack": false } }, { @@ -866,9 +866,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -879,9 +879,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -892,9 +892,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": false, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -905,9 +905,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": true, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -918,9 +918,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": false, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -931,9 +931,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": true, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -944,9 +944,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": false, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -957,9 +957,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": true, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -970,9 +970,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -983,9 +983,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -996,9 +996,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -1009,9 +1009,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-northwest-1", "UseFIPS": false, - "Region": "cn-northwest-1" + "UseDualStack": false } }, { @@ -1022,9 +1022,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -1035,9 +1035,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -1048,9 +1048,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -1061,9 +1061,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -1074,9 +1074,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -1087,9 +1087,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": false, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -1100,9 +1100,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": true, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -1113,9 +1113,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -1126,9 +1126,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -1139,9 +1139,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": false, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -1152,9 +1152,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-west-1", "UseFIPS": false, - "Region": "us-iso-west-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1165,9 +1176,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1178,9 +1200,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": false, - "Region": "us-isob-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1191,9 +1224,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": true, - "Region": "us-isob-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1204,9 +1248,9 @@ } }, "params": { - "UseDualStack": false, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1218,8 +1262,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1229,9 +1273,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseDualStack": false, - "UseFIPS": true, "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1241,11 +1285,17 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseDualStack": true, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -1434,7 +1484,7 @@ "managedTerminationProtection": { "target": "com.amazonaws.ecs#ManagedTerminationProtection", "traits": { - "smithy.api#documentation": "

The managed termination protection setting to use for the Auto Scaling group capacity\n\t\t\tprovider. This determines whether the Auto Scaling group has managed termination\n\t\t\tprotection. The default is off.

\n \n

When using managed termination protection, managed scaling must also be used\n\t\t\t\totherwise managed termination protection doesn't work.

\n
\n

When managed termination protection is on, Amazon ECS prevents the Amazon EC2 instances in an Auto\n\t\t\tScaling group that contain tasks from being terminated during a scale-in action. The\n\t\t\tAuto Scaling group and each instance in the Auto Scaling group must have instance\n\t\t\tprotection from scale-in actions enabled as well. For more information, see Instance Protection in the Auto Scaling User Guide.

\n

When managed termination protection is off, your Amazon EC2 instances aren't protected from\n\t\t\ttermination when the Auto Scaling group scales in.

" + "smithy.api#documentation": "

The managed termination protection setting to use for the Auto Scaling group capacity\n\t\t\tprovider. This determines whether the Auto Scaling group has managed termination\n\t\t\tprotection. The default is off.

\n \n

When using managed termination protection, managed scaling must also be used\n\t\t\t\totherwise managed termination protection doesn't work.

\n
\n

When managed termination protection is on, Amazon ECS prevents the Amazon EC2 instances in an Auto\n\t\t\tScaling group that contain tasks from being terminated during a scale-in action. The\n\t\t\tAuto Scaling group and each instance in the Auto Scaling group must have instance\n\t\t\tprotection from scale-in actions on as well. For more information, see Instance Protection in the Auto Scaling User Guide.

\n

When managed termination protection is off, your Amazon EC2 instances aren't protected from\n\t\t\ttermination when the Auto Scaling group scales in.

" } } }, @@ -1959,7 +2009,7 @@ "namespace": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The namespace name or full Amazon Resource Name (ARN) of the Cloud Map namespace that's used when you create a service and don't specify\n\t\t\ta Service Connect configuration. The namespace name can include up to 1024 characters.\n\t\t\tThe name is case-sensitive. The name can't include hyphens (-), tilde (~), greater than\n\t\t\t(>), less than (<), or slash (/).

\n

If you enter an existing namespace name or ARN, then that namespace will be used.\n\t\t\tAny namespace type is supported. The namespace must be in this account and this Amazon Web Services\n\t\t\tRegion.

\n

If you enter a new name, a Cloud Map namespace will be created. Amazon ECS creates a\n\t\t\tCloud Map namespace with the \"API calls\" method of instance discovery only. This instance\n\t\t\tdiscovery method is the \"HTTP\" namespace type in the Command Line Interface. Other types of instance\n\t\t\tdiscovery aren't used by Service Connect.

\n

If you update the service with an empty string \"\" for the namespace name,\n\t\t\tthe cluster configuration for Service Connect is removed. Note that the namespace will\n\t\t\tremain in Cloud Map and must be deleted separately.

\n

For more information about Cloud Map, see Working\n\t\t\t\twith Services in the Cloud Map Developer Guide.

", + "smithy.api#documentation": "

The namespace name or full Amazon Resource Name (ARN) of the Cloud Map namespace that's used when you create a service and don't specify\n\t\t\ta Service Connect configuration. The namespace name can include up to 1024 characters.\n\t\t\tThe name is case-sensitive. The name can't include hyphens (-), tilde (~), greater than\n\t\t\t(>), less than (<), or slash (/).

\n

If you enter an existing namespace name or ARN, then that namespace will be used.\n\t\t\tAny namespace type is supported. The namespace must be in this account and this Amazon Web Services\n\t\t\tRegion.

\n

If you enter a new name, a Cloud Map namespace will be created. Amazon ECS creates a\n\t\t\tCloud Map namespace with the \"API calls\" method of instance discovery only. This instance\n\t\t\tdiscovery method is the \"HTTP\" namespace type in the Command Line Interface. Other types of instance\n\t\t\tdiscovery aren't used by Service Connect.

\n

If you update the service with an empty string \"\" for the namespace name,\n\t\t\tthe cluster configuration for Service Connect is removed. Note that the namespace will\n\t\t\tremain in Cloud Map and must be deleted separately.

\n

For more information about Cloud Map, see Working\n\t\t\t\twith Services in the Cloud Map Developer Guide.

", "smithy.api#required": {} } } @@ -1974,13 +2024,13 @@ "name": { "target": "com.amazonaws.ecs#ClusterSettingName", "traits": { - "smithy.api#documentation": "

The name of the cluster setting. The only supported value is\n\t\t\t\tcontainerInsights.

" + "smithy.api#documentation": "

The name of the cluster setting. The value is containerInsights .

" } }, "value": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The value to set for the cluster setting. The supported values are enabled and\n\t\t\t\tdisabled. If enabled is specified, CloudWatch Container Insights\n\t\t\twill be enabled for the cluster, otherwise it will be off unless the\n\t\t\t\tcontainerInsights account setting is turned on. If a cluster value is\n\t\t\tspecified, it will override the containerInsights value set with PutAccountSetting or PutAccountSettingDefault.

" + "smithy.api#documentation": "

The value to set for the cluster setting. The supported values are enabled and\n\t\t\t\tdisabled.

\n

If you set name to containerInsights and value\n\t\t\tto enabled, CloudWatch Container Insights will be on for the cluster, otherwise\n\t\t\tit will be off unless the containerInsights account setting is turned on.\n\t\t\tIf a cluster value is specified, it will override the containerInsights\n\t\t\tvalue set with PutAccountSetting or PutAccountSettingDefault.

" } } }, @@ -2377,7 +2427,7 @@ "dockerSecurityOptions": { "target": "com.amazonaws.ecs#StringList", "traits": { - "smithy.api#documentation": "

A list of strings to provide custom labels for SELinux and AppArmor multi-level\n\t\t\tsecurity systems. This field isn't valid for containers in tasks using the\n\t\t\tFargate launch type.

\n

With Windows containers, this parameter can be used to reference a credential spec\n\t\t\tfile when configuring a container for Active Directory authentication. For more\n\t\t\tinformation, see Using gMSAs for Windows\n\t\t\t\tContainers in the Amazon Elastic Container Service Developer Guide.

\n

This parameter maps to SecurityOpt in the\n\t\t\tCreate a container section of the Docker Remote API and the\n\t\t\t\t--security-opt option to docker\n\t\t\t\trun.

\n \n

The Amazon ECS container agent running on a container instance must register with the\n\t\t\t\t\tECS_SELINUX_CAPABLE=true or ECS_APPARMOR_CAPABLE=true\n\t\t\t\tenvironment variables before containers placed on that instance can use these\n\t\t\t\tsecurity options. For more information, see Amazon ECS Container\n\t\t\t\t\tAgent Configuration in the Amazon Elastic Container Service Developer Guide.

\n
\n

For more information about valid values, see Docker\n\t\t\t\tRun Security Configuration.

\n

Valid values: \"no-new-privileges\" | \"apparmor:PROFILE\" | \"label:value\" |\n\t\t\t\"credentialspec:CredentialSpecFilePath\"

" + "smithy.api#documentation": "

A list of strings to provide custom configuration for multiple\n\t\t\tsecurity systems. For more information about valid values, see Docker Run Security Configuration. This field isn't valid\n\t\t\tfor containers in tasks using the Fargate launch\n\t\t\ttype.

\n

For Linux tasks on EC2, this parameter can be used to reference custom\n\t\t\tlabels for SELinux and AppArmor multi-level security systems.

\n

For any tasks on EC2, this parameter can be used to reference a\n\t\t\tcredential spec file that configures a container for Active Directory\n\t\t\tauthentication. For more\n\t\t\tinformation, see Using gMSAs for Windows\n\t\t\t\tContainers and Using gMSAs for Linux\n\t\t\t\t\tContainers in the Amazon Elastic Container Service Developer Guide.

\n

This parameter maps to SecurityOpt in the\n\t\t\tCreate a container section of the Docker Remote API and the\n\t\t\t\t--security-opt option to docker\n\t\t\t\trun.

\n \n

The Amazon ECS container agent running on a container instance must register with the\n\t\t\t\t\tECS_SELINUX_CAPABLE=true or ECS_APPARMOR_CAPABLE=true\n\t\t\t\tenvironment variables before containers placed on that instance can use these\n\t\t\t\tsecurity options. For more information, see Amazon ECS Container\n\t\t\t\t\tAgent Configuration in the Amazon Elastic Container Service Developer Guide.

\n
\n

For more information about valid values, see Docker\n\t\t\t\tRun Security Configuration.

\n

Valid values: \"no-new-privileges\" | \"apparmor:PROFILE\" | \"label:value\" |\n\t\t\t\"credentialspec:CredentialSpecFilePath\"

" } }, "interactive": { @@ -2997,7 +3047,7 @@ } ], "traits": { - "smithy.api#documentation": "

Runs and maintains your desired number of tasks from a specified task definition. If\n\t\t\tthe number of tasks running in a service drops below the desiredCount,\n\t\t\tAmazon ECS runs another copy of the task in the specified cluster. To update an existing\n\t\t\tservice, see the UpdateService action.

\n

In addition to maintaining the desired count of tasks in your service, you can\n\t\t\toptionally run your service behind one or more load balancers. The load balancers\n\t\t\tdistribute traffic across the tasks that are associated with the service. For more\n\t\t\tinformation, see Service load balancing in the Amazon Elastic Container Service Developer Guide.

\n

Tasks for services that don't use a load balancer are considered healthy if they're in\n\t\t\tthe RUNNING state. Tasks for services that use a load balancer are\n\t\t\tconsidered healthy if they're in the RUNNING state and are reported as\n\t\t\thealthy by the load balancer.

\n

There are two service scheduler strategies available:

\n
    \n
  • \n

    \n REPLICA - The replica scheduling strategy places and\n\t\t\t\t\tmaintains your desired number of tasks across your cluster. By default, the\n\t\t\t\t\tservice scheduler spreads tasks across Availability Zones. You can use task\n\t\t\t\t\tplacement strategies and constraints to customize task placement decisions. For\n\t\t\t\t\tmore information, see Service scheduler concepts in the Amazon Elastic Container Service Developer Guide.

    \n
  • \n
  • \n

    \n DAEMON - The daemon scheduling strategy deploys exactly one\n\t\t\t\t\ttask on each active container instance that meets all of the task placement\n\t\t\t\t\tconstraints that you specify in your cluster. The service scheduler also\n\t\t\t\t\tevaluates the task placement constraints for running tasks. It also stops tasks\n\t\t\t\t\tthat don't meet the placement constraints. When using this strategy, you don't\n\t\t\t\t\tneed to specify a desired number of tasks, a task placement strategy, or use\n\t\t\t\t\tService Auto Scaling policies. For more information, see Service scheduler concepts in the Amazon Elastic Container Service Developer Guide.

    \n
  • \n
\n

You can optionally specify a deployment configuration for your service. The deployment\n\t\t\tis initiated by changing properties. For example, the deployment might be initiated by\n\t\t\tthe task definition or by your desired count of a service. This is done with an UpdateService operation. The default value for a replica service for\n\t\t\t\tminimumHealthyPercent is 100%. The default value for a daemon service\n\t\t\tfor minimumHealthyPercent is 0%.

\n

If a service uses the ECS deployment controller, the minimum healthy\n\t\t\tpercent represents a lower limit on the number of tasks in a service that must remain in\n\t\t\tthe RUNNING state during a deployment. Specifically, it represents it as a\n\t\t\tpercentage of your desired number of tasks (rounded up to the nearest integer). This\n\t\t\thappens when any of your container instances are in the DRAINING state if\n\t\t\tthe service contains tasks using the EC2 launch type. Using this\n\t\t\tparameter, you can deploy without using additional cluster capacity. For example, if you\n\t\t\tset your service to have desired number of four tasks and a minimum healthy percent of\n\t\t\t50%, the scheduler might stop two existing tasks to free up cluster capacity before\n\t\t\tstarting two new tasks. If they're in the RUNNING state, tasks for services\n\t\t\tthat don't use a load balancer are considered healthy . If they're in the\n\t\t\t\tRUNNING state and reported as healthy by the load balancer, tasks for\n\t\t\tservices that do use a load balancer are considered healthy . The\n\t\t\tdefault value for minimum healthy percent is 100%.

\n

If a service uses the ECS deployment controller, the maximum percent parameter represents an upper limit on the\n\t\t\tnumber of tasks in a service that are allowed in the RUNNING or\n\t\t\t\tPENDING state during a deployment. Specifically, it represents it as a\n\t\t\tpercentage of the desired number of tasks (rounded down to the nearest integer). This\n\t\t\thappens when any of your container instances are in the DRAINING state if\n\t\t\tthe service contains tasks using the EC2 launch type. Using this\n\t\t\tparameter, you can define the deployment batch size. For example, if your service has a\n\t\t\tdesired number of four tasks and a maximum percent value of 200%, the scheduler may\n\t\t\tstart four new tasks before stopping the four older tasks (provided that the cluster\n\t\t\tresources required to do this are available). The default value for maximum percent is\n\t\t\t200%.

\n

If a service uses either the CODE_DEPLOY or EXTERNAL\n\t\t\tdeployment controller types and tasks that use the EC2 launch type, the\n\t\t\t\tminimum healthy percent and maximum percent values are used only to define the lower and upper limit\n\t\t\ton the number of the tasks in the service that remain in the RUNNING state.\n\t\t\tThis is while the container instances are in the DRAINING state. If the\n\t\t\ttasks in the service use the Fargate launch type, the minimum healthy\n\t\t\tpercent and maximum percent values aren't used. This is the case even if they're\n\t\t\tcurrently visible when describing your service.

\n

When creating a service that uses the EXTERNAL deployment controller, you\n\t\t\tcan specify only parameters that aren't controlled at the task set level. The only\n\t\t\trequired parameter is the service name. You control your services using the CreateTaskSet operation. For more information, see Amazon ECS deployment types in the Amazon Elastic Container Service Developer Guide.

\n

When the service scheduler launches new tasks, it determines task placement. For\n\t\t\tinformation about task placement and task placement strategies, see Amazon ECS\n\t\t\t\ttask placement in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

Runs and maintains your desired number of tasks from a specified task definition. If\n\t\t\tthe number of tasks running in a service drops below the desiredCount,\n\t\t\tAmazon ECS runs another copy of the task in the specified cluster. To update an existing\n\t\t\tservice, see the UpdateService action.

\n \n

Starting April 15, 2023, Amazon Web Services will not onboard new customers to Amazon Elastic Inference (EI), and will help current customers migrate their workloads to options that offer better price and performance. After April 15, 2023, new customers will not be able to launch instances with Amazon EI accelerators in Amazon SageMaker, Amazon ECS, or Amazon EC2. However, customers who have used Amazon EI at least once during the past 30-day period are considered current customers and will be able to continue using the service.

\n
\n

In addition to maintaining the desired count of tasks in your service, you can\n\t\t\toptionally run your service behind one or more load balancers. The load balancers\n\t\t\tdistribute traffic across the tasks that are associated with the service. For more\n\t\t\tinformation, see Service load balancing in the Amazon Elastic Container Service Developer Guide.

\n

Tasks for services that don't use a load balancer are considered healthy if they're in\n\t\t\tthe RUNNING state. Tasks for services that use a load balancer are\n\t\t\tconsidered healthy if they're in the RUNNING state and are reported as\n\t\t\thealthy by the load balancer.

\n

There are two service scheduler strategies available:

\n
    \n
  • \n

    \n REPLICA - The replica scheduling strategy places and\n\t\t\t\t\tmaintains your desired number of tasks across your cluster. By default, the\n\t\t\t\t\tservice scheduler spreads tasks across Availability Zones. You can use task\n\t\t\t\t\tplacement strategies and constraints to customize task placement decisions. For\n\t\t\t\t\tmore information, see Service scheduler concepts in the Amazon Elastic Container Service Developer Guide.

    \n
  • \n
  • \n

    \n DAEMON - The daemon scheduling strategy deploys exactly one\n\t\t\t\t\ttask on each active container instance that meets all of the task placement\n\t\t\t\t\tconstraints that you specify in your cluster. The service scheduler also\n\t\t\t\t\tevaluates the task placement constraints for running tasks. It also stops tasks\n\t\t\t\t\tthat don't meet the placement constraints. When using this strategy, you don't\n\t\t\t\t\tneed to specify a desired number of tasks, a task placement strategy, or use\n\t\t\t\t\tService Auto Scaling policies. For more information, see Service scheduler concepts in the Amazon Elastic Container Service Developer Guide.

    \n
  • \n
\n

You can optionally specify a deployment configuration for your service. The deployment\n\t\t\tis initiated by changing properties. For example, the deployment might be initiated by\n\t\t\tthe task definition or by your desired count of a service. This is done with an UpdateService operation. The default value for a replica service for\n\t\t\t\tminimumHealthyPercent is 100%. The default value for a daemon service\n\t\t\tfor minimumHealthyPercent is 0%.

\n

If a service uses the ECS deployment controller, the minimum healthy\n\t\t\tpercent represents a lower limit on the number of tasks in a service that must remain in\n\t\t\tthe RUNNING state during a deployment. Specifically, it represents it as a\n\t\t\tpercentage of your desired number of tasks (rounded up to the nearest integer). This\n\t\t\thappens when any of your container instances are in the DRAINING state if\n\t\t\tthe service contains tasks using the EC2 launch type. Using this\n\t\t\tparameter, you can deploy without using additional cluster capacity. For example, if you\n\t\t\tset your service to have desired number of four tasks and a minimum healthy percent of\n\t\t\t50%, the scheduler might stop two existing tasks to free up cluster capacity before\n\t\t\tstarting two new tasks. If they're in the RUNNING state, tasks for services\n\t\t\tthat don't use a load balancer are considered healthy . If they're in the\n\t\t\t\tRUNNING state and reported as healthy by the load balancer, tasks for\n\t\t\tservices that do use a load balancer are considered healthy . The\n\t\t\tdefault value for minimum healthy percent is 100%.

\n

If a service uses the ECS deployment controller, the maximum percent parameter represents an upper limit on the\n\t\t\tnumber of tasks in a service that are allowed in the RUNNING or\n\t\t\t\tPENDING state during a deployment. Specifically, it represents it as a\n\t\t\tpercentage of the desired number of tasks (rounded down to the nearest integer). This\n\t\t\thappens when any of your container instances are in the DRAINING state if\n\t\t\tthe service contains tasks using the EC2 launch type. Using this\n\t\t\tparameter, you can define the deployment batch size. For example, if your service has a\n\t\t\tdesired number of four tasks and a maximum percent value of 200%, the scheduler may\n\t\t\tstart four new tasks before stopping the four older tasks (provided that the cluster\n\t\t\tresources required to do this are available). The default value for maximum percent is\n\t\t\t200%.

\n

If a service uses either the CODE_DEPLOY or EXTERNAL\n\t\t\tdeployment controller types and tasks that use the EC2 launch type, the\n\t\t\t\tminimum healthy percent and maximum percent values are used only to define the lower and upper limit\n\t\t\ton the number of the tasks in the service that remain in the RUNNING state.\n\t\t\tThis is while the container instances are in the DRAINING state. If the\n\t\t\ttasks in the service use the Fargate launch type, the minimum healthy\n\t\t\tpercent and maximum percent values aren't used. This is the case even if they're\n\t\t\tcurrently visible when describing your service.

\n

When creating a service that uses the EXTERNAL deployment controller, you\n\t\t\tcan specify only parameters that aren't controlled at the task set level. The only\n\t\t\trequired parameter is the service name. You control your services using the CreateTaskSet operation. For more information, see Amazon ECS deployment types in the Amazon Elastic Container Service Developer Guide.

\n

When the service scheduler launches new tasks, it determines task placement. For\n\t\t\tinformation about task placement and task placement strategies, see Amazon ECS\n\t\t\t\ttask placement in the Amazon Elastic Container Service Developer Guide.

" } }, "com.amazonaws.ecs#CreateServiceRequest": { @@ -3019,7 +3069,7 @@ "taskDefinition": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The family and revision (family:revision) or\n\t\t\tfull ARN of the task definition to run in your service. If a revision\n\t\t\tisn't specified, the latest ACTIVE revision is used.

\n

A task definition must be specified if the service uses either the ECS or\n\t\t\t\tCODE_DEPLOY deployment controllers.

" + "smithy.api#documentation": "

The family and revision (family:revision) or\n\t\t\tfull ARN of the task definition to run in your service. If a revision\n\t\t\tisn't specified, the latest ACTIVE revision is used.

\n

A task definition must be specified if the service uses either the ECS or\n\t\t\t\tCODE_DEPLOY deployment controllers.

\n

For more information about deployment types, see Amazon ECS deployment types.

" } }, "loadBalancers": { @@ -3128,14 +3178,14 @@ "propagateTags": { "target": "com.amazonaws.ecs#PropagateTags", "traits": { - "smithy.api#documentation": "

Specifies whether to propagate the tags from the task definition to the task. If no\n\t\t\tvalue is specified, the tags aren't propagated. Tags can only be propagated to the task\n\t\t\tduring task creation. To add tags to a task after task creation, use the TagResource API action.

" + "smithy.api#documentation": "

Specifies whether to propagate the tags from the task definition to the task. If no\n\t\t\tvalue is specified, the tags aren't propagated. Tags can only be propagated to the task\n\t\t\tduring task creation. To add tags to a task after task creation, use the TagResource API action.

" } }, "enableExecuteCommand": { "target": "com.amazonaws.ecs#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Determines whether the execute command functionality is enabled for the service. If\n\t\t\t\ttrue, this enables execute command functionality on all containers in\n\t\t\tthe service tasks.

" + "smithy.api#documentation": "

Determines whether the execute command functionality is turned on for the service. If\n\t\t\t\ttrue, this enables execute command functionality on all containers in\n\t\t\tthe service tasks.

" } }, "serviceConnectConfiguration": { @@ -3863,7 +3913,7 @@ "rolloutState": { "target": "com.amazonaws.ecs#DeploymentRolloutState", "traits": { - "smithy.api#documentation": "\n

The rolloutState of a service is only returned for services that use\n\t\t\t\tthe rolling update (ECS) deployment type that aren't behind a\n\t\t\t\tClassic Load Balancer.

\n
\n

The rollout state of the deployment. When a service deployment is started, it begins\n\t\t\tin an IN_PROGRESS state. When the service reaches a steady state, the\n\t\t\tdeployment transitions to a COMPLETED state. If the service fails to reach\n\t\t\ta steady state and circuit breaker is enabled, the deployment transitions to a\n\t\t\t\tFAILED state. A deployment in FAILED state doesn't launch\n\t\t\tany new tasks. For more information, see DeploymentCircuitBreaker.

" + "smithy.api#documentation": "\n

The rolloutState of a service is only returned for services that use\n\t\t\t\tthe rolling update (ECS) deployment type that aren't behind a\n\t\t\t\tClassic Load Balancer.

\n
\n

The rollout state of the deployment. When a service deployment is started, it begins\n\t\t\tin an IN_PROGRESS state. When the service reaches a steady state, the\n\t\t\tdeployment transitions to a COMPLETED state. If the service fails to reach\n\t\t\ta steady state and circuit breaker is turned on, the deployment transitions to a\n\t\t\t\tFAILED state. A deployment in FAILED state doesn't launch\n\t\t\tany new tasks. For more information, see DeploymentCircuitBreaker.

" } }, "rolloutStateReason": { @@ -3941,7 +3991,7 @@ } }, "traits": { - "smithy.api#documentation": "\n

The deployment circuit breaker can only be used for services using the rolling\n\t\t\t\tupdate (ECS) deployment type.

\n
\n

The deployment circuit breaker determines whether a\n\t\t\tservice deployment will fail if the service can't reach a steady state. If enabled, a\n\t\t\tservice deployment will transition to a failed state and stop launching new tasks. You\n\t\t\tcan also configure Amazon ECS to roll back your service to the last completed deployment\n\t\t\tafter a failure. For more information, see Rolling\n\t\t\t\tupdate in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "\n

The deployment circuit breaker can only be used for services using the rolling\n\t\t\t\tupdate (ECS) deployment type.

\n
\n

The deployment circuit breaker determines whether a\n\t\t\tservice deployment will fail if the service can't reach a steady state. If it is turned on, a\n\t\t\tservice deployment will transition to a failed state and stop launching new tasks. You\n\t\t\tcan also configure Amazon ECS to roll back your service to the last completed deployment\n\t\t\tafter a failure. For more information, see Rolling\n\t\t\t\tupdate in the Amazon Elastic Container Service Developer Guide.

" } }, "com.amazonaws.ecs#DeploymentConfiguration": { @@ -5015,13 +5065,13 @@ "accessPointId": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The Amazon EFS access point ID to use. If an access point is specified, the root directory\n\t\t\tvalue specified in the EFSVolumeConfiguration must either be omitted or set\n\t\t\tto / which will enforce the path set on the EFS access point. If an access\n\t\t\tpoint is used, transit encryption must be enabled in the\n\t\t\t\tEFSVolumeConfiguration. For more information, see Working with Amazon\n\t\t\t\tEFS access points in the Amazon Elastic File System User Guide.

" + "smithy.api#documentation": "

The Amazon EFS access point ID to use. If an access point is specified, the root directory\n\t\t\tvalue specified in the EFSVolumeConfiguration must either be omitted or set\n\t\t\tto / which will enforce the path set on the EFS access point. If an access\n\t\t\tpoint is used, transit encryption must be on in the\n\t\t\t\tEFSVolumeConfiguration. For more information, see Working with Amazon\n\t\t\t\tEFS access points in the Amazon Elastic File System User Guide.

" } }, "iam": { "target": "com.amazonaws.ecs#EFSAuthorizationConfigIAM", "traits": { - "smithy.api#documentation": "

Determines whether to use the Amazon ECS task role defined in a task definition when\n\t\t\tmounting the Amazon EFS file system. If enabled, transit encryption must be enabled in the\n\t\t\t\tEFSVolumeConfiguration. If this parameter is omitted, the default value\n\t\t\tof DISABLED is used. For more information, see Using\n\t\t\t\tAmazon EFS access points in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

Determines whether to use the Amazon ECS task role defined in a task definition when\n\t\t\tmounting the Amazon EFS file system. If it is turned on, transit encryption must be turned on in the\n\t\t\t\tEFSVolumeConfiguration. If this parameter is omitted, the default value\n\t\t\tof DISABLED is used. For more information, see Using\n\t\t\t\tAmazon EFS access points in the Amazon Elastic Container Service Developer Guide.

" } } }, @@ -5082,7 +5132,7 @@ "transitEncryption": { "target": "com.amazonaws.ecs#EFSTransitEncryption", "traits": { - "smithy.api#documentation": "

Determines whether to use encryption for Amazon EFS data in transit between the Amazon ECS host\n\t\t\tand the Amazon EFS server. Transit encryption must be enabled if Amazon EFS IAM authorization is\n\t\t\tused. If this parameter is omitted, the default value of DISABLED is used.\n\t\t\tFor more information, see Encrypting data in transit in\n\t\t\tthe Amazon Elastic File System User Guide.

" + "smithy.api#documentation": "

Determines whether to use encryption for Amazon EFS data in transit between the Amazon ECS host\n\t\t\tand the Amazon EFS server. Transit encryption must be turned on if Amazon EFS IAM authorization is\n\t\t\tused. If this parameter is omitted, the default value of DISABLED is used.\n\t\t\tFor more information, see Encrypting data in transit in\n\t\t\tthe Amazon Elastic File System User Guide.

" } }, "transitEncryptionPort": { @@ -5160,7 +5210,7 @@ } }, "traits": { - "smithy.api#documentation": "

The amount of ephemeral storage to allocate for the task. This parameter is used to\n\t\t\texpand the total amount of ephemeral storage available, beyond the default amount, for\n\t\t\ttasks hosted on Fargate. For more information, see Fargate task\n\t\t\t\tstorage in the Amazon ECS User Guide for Fargate.

\n \n

This parameter is only supported for tasks hosted on Fargate using\n\t\t\t\tLinux platform version 1.4.0 or later. This parameter is not supported\n\t\t\t\tfor Windows containers on Fargate.

\n
" + "smithy.api#documentation": "

The amount of ephemeral storage to allocate for the task. This parameter is used to\n\t\t\texpand the total amount of ephemeral storage available, beyond the default amount, for\n\t\t\ttasks hosted on Fargate. For more information, see Fargate task\n\t\t\t\tstorage in the Amazon ECS User Guide for Fargate.

\n \n

For tasks using the Fargate launch type, the task requires\n\t\t\t\tthe following platforms:

\n
    \n
  • \n

    Linux platform version 1.4.0 or later.

    \n
  • \n
  • \n

    Windows platform version 1.0.0 or later.

    \n
  • \n
\n
" } }, "com.amazonaws.ecs#ExecuteCommand": { @@ -5192,7 +5242,7 @@ } ], "traits": { - "smithy.api#documentation": "

Runs a command remotely on a container within a task.

\n

If you use a condition key in your IAM policy to refine the conditions for the policy\n\t\t\tstatement, for example limit the actions to a specific cluster, you receive an\n\t\t\t\tAccessDeniedException when there is a mismatch between the condition\n\t\t\tkey value and the corresponding parameter value.

\n

For information about required permissions and considerations, see Using Amazon ECS Exec for\n\t\t\tdebugging in the Amazon ECS Developer Guide.

" + "smithy.api#documentation": "

Runs a command remotely on a container within a task.

\n

If you use a condition key in your IAM policy to refine the conditions for the policy\n\t\t\tstatement, for example limit the actions to a specific cluster, you receive an\n\t\t\t\tAccessDeniedException when there is a mismatch between the condition\n\t\t\tkey value and the corresponding parameter value.

\n

For information about required permissions and considerations, see Using Amazon ECS Exec for\n\t\t\tdebugging in the Amazon ECS Developer Guide.

" } }, "com.amazonaws.ecs#ExecuteCommandConfiguration": { @@ -5563,7 +5613,7 @@ "protectedTasks": { "target": "com.amazonaws.ecs#ProtectedTasks", "traits": { - "smithy.api#documentation": "

A list of tasks with the following information.

\n
    \n
  • \n

    \n taskArn: The task ARN.

    \n
  • \n
  • \n

    \n protectionEnabled: The protection status of the task. If scale-in\n\t\t\t\t\tprotection is enabled for a task, the value is true. Otherwise, it\n\t\t\t\t\tis false.

    \n
  • \n
  • \n

    \n expirationDate: The epoch time when protection for the task will\n\t\t\t\t\texpire.

    \n
  • \n
" + "smithy.api#documentation": "

A list of tasks with the following information.

\n
    \n
  • \n

    \n taskArn: The task ARN.

    \n
  • \n
  • \n

    \n protectionEnabled: The protection status of the task. If scale-in\n\t\t\t\t\tprotection is turned on for a task, the value is true. Otherwise, it\n\t\t\t\t\tis false.

    \n
  • \n
  • \n

    \n expirationDate: The epoch time when protection for the task will\n\t\t\t\t\texpire.

    \n
  • \n
" } }, "failures": { @@ -5619,7 +5669,7 @@ } }, "traits": { - "smithy.api#documentation": "

An object representing a container health check. Health check parameters that are\n\t\t\tspecified in a container definition override any Docker health checks that exist in the\n\t\t\tcontainer image (such as those specified in a parent image or from the image's\n\t\t\tDockerfile).

\n \n

The Amazon ECS container agent only monitors and reports on the health checks specified\n\t\t\t\tin the task definition. Amazon ECS does not monitor Docker health checks that are\n\t\t\t\tembedded in a container image and not specified in the container definition. Health\n\t\t\t\tcheck parameters that are specified in a container definition override any Docker\n\t\t\t\thealth checks that exist in the container image.

\n
\n

You can view the health status of both individual containers and a task with the\n\t\t\tDescribeTasks API operation or when viewing the task details in the console.

\n

The following describes the possible healthStatus values for a\n\t\t\tcontainer:

\n
    \n
  • \n

    \n HEALTHY-The container health check has passed\n\t\t\t\t\tsuccessfully.

    \n
  • \n
  • \n

    \n UNHEALTHY-The container health check has failed.

    \n
  • \n
  • \n

    \n UNKNOWN-The container health check is being evaluated or\n\t\t\t\t\tthere's no container health check defined.

    \n
  • \n
\n

The following describes the possible healthStatus values for a task. The\n\t\t\tcontainer health check status of nonessential containers only affects the health status\n\t\t\tof a task if no essential containers have health checks defined.

\n
    \n
  • \n

    \n HEALTHY-All essential containers within the task have\n\t\t\t\t\tpassed their health checks.

    \n
  • \n
  • \n

    \n UNHEALTHY-One or more essential containers have failed\n\t\t\t\t\ttheir health check.

    \n
  • \n
  • \n

    \n UNKNOWN-The essential containers within the task are still\n\t\t\t\t\thaving their health checks evaluated or there are only nonessential containers\n\t\t\t\t\twith health checks defined.

    \n
  • \n
\n

If a task is run manually, and not as part of a service, the task will continue its\n\t\t\tlifecycle regardless of its health status. For tasks that are part of a service, if the\n\t\t\ttask reports as unhealthy then the task will be stopped and the service scheduler will\n\t\t\treplace it.

\n \n

For tasks that are a part of a service and the service uses the ECS\n\t\t\t\trolling deployment type, the deployment is paused while the new tasks have the\n\t\t\t\t\tUNKNOWN task health check status. For example, tasks that define\n\t\t\t\thealth checks for nonessential containers when no essential containers have health\n\t\t\t\tchecks will have the UNKNOWN health check status indefinitely which\n\t\t\t\tprevents the deployment from completing.

\n
\n

The following are notes about container health check support:

\n
    \n
  • \n

    Container health checks require version 1.17.0 or greater of the Amazon ECS\n\t\t\t\t\tcontainer agent. For more information, see Updating the\n\t\t\t\t\t\tAmazon ECS container agent.

    \n
  • \n
  • \n

    Container health checks are supported for Fargate tasks if\n\t\t\t\t\tyou're using platform version 1.1.0 or greater. For more\n\t\t\t\t\tinformation, see Fargate\n\t\t\t\t\t\tplatform versions.

    \n
  • \n
  • \n

    Container health checks aren't supported for tasks that are part of a service\n\t\t\t\t\tthat's configured to use a Classic Load Balancer.

    \n
  • \n
" + "smithy.api#documentation": "

An object representing a container health check. Health check parameters that are\n\t\t\tspecified in a container definition override any Docker health checks that exist in the\n\t\t\tcontainer image (such as those specified in a parent image or from the image's\n\t\t\tDockerfile). This configuration maps to the HEALTHCHECK parameter of docker run.

\n \n

The Amazon ECS container agent only monitors and reports on the health checks specified\n\t\t\t\tin the task definition. Amazon ECS does not monitor Docker health checks that are\n\t\t\t\tembedded in a container image and not specified in the container definition. Health\n\t\t\t\tcheck parameters that are specified in a container definition override any Docker\n\t\t\t\thealth checks that exist in the container image.

\n
\n

You can view the health status of both individual containers and a task with the\n\t\t\tDescribeTasks API operation or when viewing the task details in the console.

\n

The following describes the possible healthStatus values for a\n\t\t\tcontainer:

\n
    \n
  • \n

    \n HEALTHY-The container health check has passed\n\t\t\t\t\tsuccessfully.

    \n
  • \n
  • \n

    \n UNHEALTHY-The container health check has failed.

    \n
  • \n
  • \n

    \n UNKNOWN-The container health check is being evaluated or\n\t\t\t\t\tthere's no container health check defined.

    \n
  • \n
\n

The following describes the possible healthStatus values for a task. The\n\t\t\tcontainer health check status of\n\t\t\tnon-essential containers don't have an effect on the health status of a task.

\n
    \n
  • \n

    \n HEALTHY-All essential containers within the task have\n\t\t\t\t\tpassed their health checks.

    \n
  • \n
  • \n

    \n UNHEALTHY-One or more essential containers have failed\n\t\t\t\t\ttheir health check.

    \n
  • \n
  • \n

    \n UNKNOWN-The essential containers within the task are still\n\t\t\t\t\thaving their health checks evaluated, there are only nonessential containers\n\t\t\t\t\twith health checks defined, or there are no container health checks\n\t\t\t\t\tdefined.

    \n
  • \n
\n

If a task is run manually, and not as part of a service, the task will continue its\n\t\t\tlifecycle regardless of its health status. For tasks that are part of a service, if the\n\t\t\ttask reports as unhealthy then the task will be stopped and the service scheduler will\n\t\t\treplace it.

\n

The following are notes about container health check support:

\n
    \n
  • \n

    Container health checks require version 1.17.0 or greater of the Amazon ECS\n\t\t\t\t\tcontainer agent. For more information, see Updating the\n\t\t\t\t\t\tAmazon ECS container agent.

    \n
  • \n
  • \n

    Container health checks are supported for Fargate tasks if\n\t\t\t\t\tyou're using platform version 1.1.0 or greater. For more\n\t\t\t\t\tinformation, see Fargate\n\t\t\t\t\t\tplatform versions.

    \n
  • \n
  • \n

    Container health checks aren't supported for tasks that are part of a service\n\t\t\t\t\tthat's configured to use a Classic Load Balancer.

    \n
  • \n
" } }, "com.amazonaws.ecs#HealthStatus": { @@ -5693,7 +5743,7 @@ "deviceName": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The Elastic Inference accelerator device name. The deviceName must also\n\t\t\tbe referenced in a container definition as a ResourceRequirement.

", + "smithy.api#documentation": "

The Elastic Inference accelerator device name. The deviceName must also\n\t\t\tbe referenced in a container definition as a ResourceRequirement.

", "smithy.api#required": {} } }, @@ -5971,18 +6021,18 @@ "maxSwap": { "target": "com.amazonaws.ecs#BoxedInteger", "traits": { - "smithy.api#documentation": "

The total amount of swap memory (in MiB) a container can use. This parameter will be\n\t\t\ttranslated to the --memory-swap option to docker run where the value would be the sum of\n\t\t\tthe container memory plus the maxSwap value.

\n

If a maxSwap value of 0 is specified, the container will not\n\t\t\tuse swap. Accepted values are 0 or any positive integer. If the\n\t\t\t\tmaxSwap parameter is omitted, the container will use the swap\n\t\t\tconfiguration for the container instance it is running on. A maxSwap value\n\t\t\tmust be set for the swappiness parameter to be used.

\n \n

If you're using tasks that use the Fargate launch type, the\n\t\t\t\t\tmaxSwap parameter isn't supported.

\n
" + "smithy.api#documentation": "

The total amount of swap memory (in MiB) a container can use. This parameter will be\n\t\t\ttranslated to the --memory-swap option to docker run where the value would be the sum of\n\t\t\tthe container memory plus the maxSwap value.

\n

If a maxSwap value of 0 is specified, the container will not\n\t\t\tuse swap. Accepted values are 0 or any positive integer. If the\n\t\t\t\tmaxSwap parameter is omitted, the container will use the swap\n\t\t\tconfiguration for the container instance it is running on. A maxSwap value\n\t\t\tmust be set for the swappiness parameter to be used.

\n \n

If you're using tasks that use the Fargate launch type, the\n\t\t\t\t\tmaxSwap parameter isn't supported.

\n

If you're using tasks on Amazon Linux 2023 the swappiness parameter isn't supported.

\n
" } }, "swappiness": { "target": "com.amazonaws.ecs#BoxedInteger", "traits": { - "smithy.api#documentation": "

This allows you to tune a container's memory swappiness behavior. A\n\t\t\t\tswappiness value of 0 will cause swapping to not happen\n\t\t\tunless absolutely necessary. A swappiness value of 100 will\n\t\t\tcause pages to be swapped very aggressively. Accepted values are whole numbers between\n\t\t\t\t0 and 100. If the swappiness parameter is not\n\t\t\tspecified, a default value of 60 is used. If a value is not specified for\n\t\t\t\tmaxSwap then this parameter is ignored. This parameter maps to the\n\t\t\t\t--memory-swappiness option to docker run.

\n \n

If you're using tasks that use the Fargate launch type, the\n\t\t\t\t\tswappiness parameter isn't supported.

\n
" + "smithy.api#documentation": "

This allows you to tune a container's memory swappiness behavior. A\n\t\t\t\tswappiness value of 0 will cause swapping to not happen\n\t\t\tunless absolutely necessary. A swappiness value of 100 will\n\t\t\tcause pages to be swapped very aggressively. Accepted values are whole numbers between\n\t\t\t\t0 and 100. If the swappiness parameter is not\n\t\t\tspecified, a default value of 60 is used. If a value is not specified for\n\t\t\t\tmaxSwap then this parameter is ignored. This parameter maps to the\n\t\t\t\t--memory-swappiness option to docker run.

\n \n

If you're using tasks that use the Fargate launch type, the\n\t\t\t\t\tswappiness parameter isn't supported.

\n

If you're using tasks on Amazon Linux 2023 the swappiness parameter isn't supported.

\n
" } } }, "traits": { - "smithy.api#documentation": "

Linux-specific options that are applied to the container, such as Linux KernelCapabilities.

" + "smithy.api#documentation": "

The Linux-specific options that are applied to the container, such as Linux KernelCapabilities.

" } }, "com.amazonaws.ecs#ListAccountSettings": { @@ -6868,7 +6918,7 @@ } }, "traits": { - "smithy.api#documentation": "

The load balancer configuration to use with a service or task set.

\n

For specific notes and restrictions regarding the use of load balancers with services\n\t\t\tand task sets, see the CreateService and CreateTaskSet actions.

\n

When you add, update, or remove a load balancer configuration, Amazon ECS starts a new\n\t\t\tdeployment with the updated Elastic Load Balancing configuration. This causes tasks to register to and\n\t\t\tderegister from load balancers.

\n

We recommend that you verify this on a test environment before you update the Elastic Load Balancing\n\t\t\tconfiguration.

\n

A service-linked role is required for services that use multiple target groups. For\n\t\t\tmore information, see Using\n\t\t\t\tservice-linked roles in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

The load balancer configuration to use with a service or task set.

\n

When you add, update, or remove a load balancer configuration, Amazon ECS starts a new\n\t\t\tdeployment with the updated Elastic Load Balancing configuration. This causes tasks to register to and\n\t\t\tderegister from load balancers.

\n

We recommend that you verify this on a test environment before you update the Elastic Load Balancing\n\t\t\tconfiguration.

\n

A service-linked role is required for services that use multiple target groups. For\n\t\t\tmore information, see Using\n\t\t\t\tservice-linked roles in the Amazon Elastic Container Service Developer Guide.

" } }, "com.amazonaws.ecs#LoadBalancers": { @@ -6984,7 +7034,7 @@ "name": { "target": "com.amazonaws.ecs#ManagedAgentName", "traits": { - "smithy.api#documentation": "

The name of the managed agent. When the execute command feature is enabled, the\n\t\t\tmanaged agent name is ExecuteCommandAgent.

" + "smithy.api#documentation": "

The name of the managed agent. When the execute command feature is turned on, the\n\t\t\tmanaged agent name is ExecuteCommandAgent.

" } }, "reason": { @@ -7074,7 +7124,7 @@ "targetCapacity": { "target": "com.amazonaws.ecs#ManagedScalingTargetCapacity", "traits": { - "smithy.api#documentation": "

The target capacity value for the capacity provider. The specified value must be\n\t\t\tgreater than 0 and less than or equal to 100. A value of\n\t\t\t\t100 results in the Amazon EC2 instances in your Auto Scaling group being\n\t\t\tcompletely used.

" + "smithy.api#documentation": "

The target capacity utilization as a percentage for the capacity provider. The\n\t\t\tspecified value must be greater than 0 and less than or equal to\n\t\t\t\t100. For example, if you want the capacity provider to maintain 10%\n\t\t\tspare capacity, then that means the utilization is 90%, so use a\n\t\t\t\ttargetCapacity of 90. The default value of\n\t\t\t\t100 percent results in the Amazon EC2 instances in your Auto Scaling group being\n\t\t\tcompletely used.

" } }, "minimumScalingStepSize": { @@ -7097,7 +7147,7 @@ } }, "traits": { - "smithy.api#documentation": "

The managed scaling settings for the Auto Scaling group capacity provider.

\n

When managed scaling is enabled, Amazon ECS manages the scale-in and scale-out actions of\n\t\t\tthe Auto Scaling group. Amazon ECS manages a target tracking scaling policy using an Amazon ECS\n\t\t\tmanaged CloudWatch metric with the specified targetCapacity value as the target\n\t\t\tvalue for the metric. For more information, see Using managed scaling in the Amazon Elastic Container Service Developer Guide.

\n

If managed scaling is off, the user must manage the scaling of the Auto Scaling\n\t\t\tgroup.

" + "smithy.api#documentation": "

The managed scaling settings for the Auto Scaling group capacity provider.

\n

When managed scaling is turned on, Amazon ECS manages the scale-in and scale-out actions of\n\t\t\tthe Auto Scaling group. Amazon ECS manages a target tracking scaling policy using an Amazon ECS\n\t\t\tmanaged CloudWatch metric with the specified targetCapacity value as the target\n\t\t\tvalue for the metric. For more information, see Using managed scaling in the Amazon Elastic Container Service Developer Guide.

\n

If managed scaling is off, the user must manage the scaling of the Auto Scaling\n\t\t\tgroup.

" } }, "com.amazonaws.ecs#ManagedScalingInstanceWarmupPeriod": { @@ -7196,7 +7246,7 @@ } }, "traits": { - "smithy.api#documentation": "

Details for a volume mount point that's used in a container definition.

" + "smithy.api#documentation": "

The details for a volume mount point that's used in a container definition.

" } }, "com.amazonaws.ecs#MountPointList": { @@ -7591,7 +7641,7 @@ "hostPort": { "target": "com.amazonaws.ecs#BoxedInteger", "traits": { - "smithy.api#documentation": "

The port number on the container instance to reserve for your container.

\n

If you specify a containerPortRange, leave this field empty and the value of\n\t\t\tthe hostPort is set as follows:

\n
    \n
  • \n

    For containers in a task with the awsvpc network mode, the\n\t\t\t\t\t\thostPort is set to the same value as the\n\t\t\t\t\t\tcontainerPort. This is a static mapping strategy.

    \n
  • \n
  • \n

    For containers in a task with the bridge network mode, the Amazon ECS\n\t\t\t\t\tagent finds open ports on the host and automaticaly binds them to the container\n\t\t\t\t\tports. This is a dynamic mapping strategy.

    \n
  • \n
\n

If you use containers in a task with the awsvpc or host\n\t\t\tnetwork mode, the hostPort can either be left blank or set to the same\n\t\t\tvalue as the containerPort.

\n

If you use containers in a task with the bridge network mode, you can\n\t\t\tspecify a non-reserved host port for your container port mapping, or you can omit the\n\t\t\t\thostPort (or set it to 0) while specifying a\n\t\t\t\tcontainerPort and your container automatically receives a port in the\n\t\t\tephemeral port range for your container instance operating system and Docker\n\t\t\tversion.

\n

The default ephemeral port range for Docker version 1.6.0 and later is listed on the\n\t\t\tinstance under /proc/sys/net/ipv4/ip_local_port_range. If this kernel\n\t\t\tparameter is unavailable, the default ephemeral port range from 49153 through 65535 is\n\t\t\tused. Do not attempt to specify a host port in the ephemeral port range as these are\n\t\t\treserved for automatic assignment. In general, ports below 32768 are outside of the\n\t\t\tephemeral port range.

\n

The default reserved ports are 22 for SSH, the Docker ports 2375 and 2376, and the\n\t\t\tAmazon ECS container agent ports 51678-51680. Any host port that was previously specified in\n\t\t\ta running task is also reserved while the task is running. That is, after a task stops,\n\t\t\tthe host port is released. The current reserved ports are displayed in the\n\t\t\t\tremainingResources of DescribeContainerInstances\n\t\t\toutput. A container instance can have up to 100 reserved ports at a time. This number\n\t\t\tincludes the default reserved ports. Automatically assigned ports aren't included in the\n\t\t\t100 reserved ports quota.

" + "smithy.api#documentation": "

The port number on the container instance to reserve for your container.

\n

If you specify a containerPortRange, leave this field empty and the value of\n\t\t\tthe hostPort is set as follows:

\n
    \n
  • \n

    For containers in a task with the awsvpc network mode, the\n\t\t\t\t\t\thostPort is set to the same value as the\n\t\t\t\t\t\tcontainerPort. This is a static mapping strategy.

    \n
  • \n
  • \n

    For containers in a task with the bridge network mode, the Amazon ECS agent finds\n\t\t\t\t\topen ports on the host and automatically binds them to the container ports. This\n\t\t\t\t\tis a dynamic mapping strategy.

    \n
  • \n
\n

If you use containers in a task with the awsvpc or host\n\t\t\tnetwork mode, the hostPort can either be left blank or set to the same\n\t\t\tvalue as the containerPort.

\n

If you use containers in a task with the bridge network mode, you can\n\t\t\tspecify a non-reserved host port for your container port mapping, or you can omit the\n\t\t\t\thostPort (or set it to 0) while specifying a\n\t\t\t\tcontainerPort and your container automatically receives a port in the\n\t\t\tephemeral port range for your container instance operating system and Docker\n\t\t\tversion.

\n

The default ephemeral port range for Docker version 1.6.0 and later is listed on the\n\t\t\tinstance under /proc/sys/net/ipv4/ip_local_port_range. If this kernel\n\t\t\tparameter is unavailable, the default ephemeral port range from 49153 through 65535 is\n\t\t\tused. Do not attempt to specify a host port in the ephemeral port range as these are\n\t\t\treserved for automatic assignment. In general, ports below 32768 are outside of the\n\t\t\tephemeral port range.

\n

The default reserved ports are 22 for SSH, the Docker ports 2375 and 2376, and the\n\t\t\tAmazon ECS container agent ports 51678-51680. Any host port that was previously specified in\n\t\t\ta running task is also reserved while the task is running. That is, after a task stops,\n\t\t\tthe host port is released. The current reserved ports are displayed in the\n\t\t\tremainingResources of DescribeContainerInstances\n\t\t\toutput. A container instance can have up to 100 reserved ports at a time. This number\n\t\t\tincludes the default reserved ports. Automatically assigned ports aren't included in the\n\t\t\t100 reserved ports quota.

" } }, "protocol": { @@ -7620,7 +7670,7 @@ } }, "traits": { - "smithy.api#documentation": "

Port mappings allow containers to access ports on the host container instance to send\n\t\t\tor receive traffic. Port mappings are specified as part of the container\n\t\t\tdefinition.

\n

If you use containers in a task with the awsvpc or host\n\t\t\tnetwork mode, specify the exposed ports using containerPort. The\n\t\t\t\thostPort can be left blank or it must be the same value as the\n\t\t\t\tcontainerPort.

\n \n

You can't expose the same container port for multiple protocols. If you attempt\n\t\t\t\tthis, an error is returned.

\n
\n

After a task reaches the RUNNING status, manual and automatic host and\n\t\t\tcontainer port assignments are visible in the networkBindings section of\n\t\t\t\tDescribeTasks API responses.

" + "smithy.api#documentation": "

Port mappings allow containers to access ports on the host container instance to send\n\t\t\tor receive traffic. Port mappings are specified as part of the container\n\t\t\tdefinition.

\n

If you use containers in a task with the awsvpc or host\n\t\t\tnetwork mode, specify the exposed ports using containerPort. The\n\t\t\t\thostPort can be left blank or it must be the same value as the\n\t\t\t\tcontainerPort.

\n

Most fields of this parameter (containerPort, hostPort,\n\t\t\t\tprotocol) maps to PortBindings in the\n\t\t\tCreate a container section of the Docker Remote API and the\n\t\t\t\t--publish option to \n docker\n\t\t\t\t\trun\n . If the network mode of a task definition is set to\n\t\t\t\thost, host ports must either be undefined or match the container port\n\t\t\tin the port mapping.

\n \n

You can't expose the same container port for multiple protocols. If you attempt\n\t\t\t\tthis, an error is returned.

\n
\n

After a task reaches the RUNNING status, manual and automatic host and\n\t\t\tcontainer port assignments are visible in the networkBindings section of\n\t\t\t\tDescribeTasks API responses.

" } }, "com.amazonaws.ecs#PortMappingList": { @@ -7758,7 +7808,7 @@ } ], "traits": { - "smithy.api#documentation": "

Modifies an account setting. Account settings are set on a per-Region basis.

\n

If you change the account setting for the root user, the default settings for all of\n\t\t\tthe users and roles that no individual account setting was specified are reset for.\n\t\t\tFor more information, see Account\n\t\t\t\tSettings in the Amazon Elastic Container Service Developer Guide.

\n

When serviceLongArnFormat, taskLongArnFormat, or\n\t\t\t\tcontainerInstanceLongArnFormat are specified, the Amazon Resource Name\n\t\t\t(ARN) and resource ID format of the resource type for a specified user, role, or\n\t\t\tthe root user for an account is affected. The opt-in and opt-out account setting must be\n\t\t\tset for each Amazon ECS resource separately. The ARN and resource ID format of a resource\n\t\t\tis defined by the opt-in status of the user or role that created the resource. You\n\t\t\tmust turn on this setting to use Amazon ECS features such as resource tagging.

\n

When awsvpcTrunking is specified, the elastic network interface (ENI)\n\t\t\tlimit for any new container instances that support the feature is changed. If\n\t\t\t\tawsvpcTrunking is enabled, any new container instances that support the\n\t\t\tfeature are launched have the increased ENI limits available to them. For more\n\t\t\tinformation, see Elastic Network\n\t\t\t\tInterface Trunking in the Amazon Elastic Container Service Developer Guide.

\n

When containerInsights is specified, the default setting indicating\n\t\t\twhether CloudWatch Container Insights is enabled for your clusters is changed. If\n\t\t\t\tcontainerInsights is enabled, any new clusters that are created will\n\t\t\thave Container Insights enabled unless you disable it during cluster creation. For more\n\t\t\tinformation, see CloudWatch\n\t\t\t\tContainer Insights in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

Modifies an account setting. Account settings are set on a per-Region basis.

\n

If you change the root user account setting, the default settings are reset for users\n\t\t\tand roles that do not have specified individual account settings. For more information,\n\t\t\tsee Account\n\t\t\t\tSettings in the Amazon Elastic Container Service Developer Guide.

\n

When serviceLongArnFormat, taskLongArnFormat, or\n\t\t\t\tcontainerInstanceLongArnFormat are specified, the Amazon Resource Name\n\t\t\t(ARN) and resource ID format of the resource type for a specified user, role, or\n\t\t\tthe root user for an account is affected. The opt-in and opt-out account setting must be\n\t\t\tset for each Amazon ECS resource separately. The ARN and resource ID format of a resource\n\t\t\tis defined by the opt-in status of the user or role that created the resource. You\n\t\t\tmust turn on this setting to use Amazon ECS features such as resource tagging.

\n

When awsvpcTrunking is specified, the elastic network interface (ENI)\n\t\t\tlimit for any new container instances that support the feature is changed. If\n\t\t\t\tawsvpcTrunking is turned on, any new container instances that support the\n\t\t\tfeature are launched have the increased ENI limits available to them. For more\n\t\t\tinformation, see Elastic Network\n\t\t\t\tInterface Trunking in the Amazon Elastic Container Service Developer Guide.

\n

When containerInsights is specified, the default setting indicating whether\n\t\t\tAmazon Web Services CloudWatch Container Insights is turned on for your clusters is changed. If\n\t\t\t\tcontainerInsights is turned on, any new clusters that are created will\n\t\t\thave Container Insights turned on unless you disable it during cluster creation. For\n\t\t\tmore information, see CloudWatch\n\t\t\t\tContainer Insights in the Amazon Elastic Container Service Developer Guide.

\n

Amazon ECS is introducing tagging authorization for resource creation. Users must have\n\t\t\tpermissions for actions that create the resource, such as ecsCreateCluster.\n\t\t\tIf tags are specified when you create a resource, Amazon Web Services performs additional\n\t\t\tauthorization to verify if users or roles have permissions to create tags. Therefore,\n\t\t\tyou must grant explicit permissions to use the ecs:TagResource action. For\n\t\t\tmore information, see Grant\n\t\t\t\tpermission to tag resources on creation in the Amazon ECS Developer\n\t\t\t\t\tGuide.

" } }, "com.amazonaws.ecs#PutAccountSettingDefault": { @@ -7790,7 +7840,7 @@ "name": { "target": "com.amazonaws.ecs#SettingName", "traits": { - "smithy.api#documentation": "

The resource name for which to modify the account setting. If\n\t\t\t\tserviceLongArnFormat is specified, the ARN for your Amazon ECS services is\n\t\t\taffected. If taskLongArnFormat is specified, the ARN and resource ID for\n\t\t\tyour Amazon ECS tasks is affected. If containerInstanceLongArnFormat is\n\t\t\tspecified, the ARN and resource ID for your Amazon ECS container instances is affected. If\n\t\t\t\tawsvpcTrunking is specified, the ENI limit for your Amazon ECS container\n\t\t\tinstances is affected. If containerInsights is specified, the default\n\t\t\tsetting for CloudWatch Container Insights for your clusters is affected.

\n

Fargate is transitioning from task count-based quotas to vCPU-based quotas. You can\n\t\t\tset the name to fargateVCPULimit to opt in or opt out of the vCPU-based\n\t\t\tquotas. For information about the opt in timeline, see Fargate vCPU-based quotas timeline in the\n\t\t\t\tAmazon ECS Developer Guide.

", + "smithy.api#documentation": "

The resource name for which to modify the account setting. If\n\t\t\t\tserviceLongArnFormat is specified, the ARN for your Amazon ECS services is\n\t\t\taffected. If taskLongArnFormat is specified, the ARN and resource ID for\n\t\t\tyour Amazon ECS tasks is affected. If containerInstanceLongArnFormat is\n\t\t\tspecified, the ARN and resource ID for your Amazon ECS container instances is affected. If\n\t\t\t\tawsvpcTrunking is specified, the ENI limit for your Amazon ECS container\n\t\t\tinstances is affected. If containerInsights is specified, the default\n\t\t\tsetting for Amazon Web Services CloudWatch Container Insights for your clusters is affected. If\n\t\t\t\ttagResourceAuthorization is specified, the opt-in option for tagging\n\t\t\tresources on creation is affected. For information about the opt-in timeline, see Tagging authorization timeline in the Amazon ECS Developer\n\t\t\t\tGuide.

\n

When you specify fargateFIPSMode for the name and\n\t\t\tenabled for the value, Fargate uses FIPS-140 compliant\n\t\t\tcryptographic algorithms on your tasks. For more information about FIPS-140 compliance\n\t\t\twith Fargate, see Amazon Web Services Fargate Federal Information Processing Standard (FIPS) 140-2\n\t\t\t\tcompliance in the Amazon Elastic Container Service Developer Guide.

", "smithy.api#required": {} } }, @@ -7826,7 +7876,7 @@ "name": { "target": "com.amazonaws.ecs#SettingName", "traits": { - "smithy.api#documentation": "

The Amazon ECS resource name for which to modify the account setting. If\n\t\t\t\tserviceLongArnFormat is specified, the ARN for your Amazon ECS services is\n\t\t\taffected. If taskLongArnFormat is specified, the ARN and resource ID for\n\t\t\tyour Amazon ECS tasks is affected. If containerInstanceLongArnFormat is\n\t\t\tspecified, the ARN and resource ID for your Amazon ECS container instances is affected. If\n\t\t\t\tawsvpcTrunking is specified, the elastic network interface (ENI) limit\n\t\t\tfor your Amazon ECS container instances is affected. If containerInsights is\n\t\t\tspecified, the default setting for CloudWatch Container Insights for your clusters is\n\t\t\taffected.

", + "smithy.api#documentation": "

The Amazon ECS resource name for which to modify the account setting. If\n\t\t\t\tserviceLongArnFormat is specified, the ARN for your Amazon ECS services is\n\t\t\taffected. If taskLongArnFormat is specified, the ARN and resource ID for\n\t\t\tyour Amazon ECS tasks is affected. If containerInstanceLongArnFormat is\n\t\t\tspecified, the ARN and resource ID for your Amazon ECS container instances is affected. If\n\t\t\t\tawsvpcTrunking is specified, the elastic network interface (ENI) limit\n\t\t\tfor your Amazon ECS container instances is affected. If containerInsights is\n\t\t\tspecified, the default setting for Amazon Web Services CloudWatch Container Insights for your clusters is\n\t\t\taffected. If fargateFIPSMode is specified, Fargate FIPS 140 compliance is\n\t\t\taffected. If tagResourceAuthorization is specified, the opt-in option for\n\t\t\ttagging resources on creation is affected. For information about the opt-in timeline,\n\t\t\tsee Tagging authorization timeline in the Amazon ECS Developer\n\t\t\t\t\tGuide.

", "smithy.api#required": {} } }, @@ -8218,7 +8268,7 @@ "ephemeralStorage": { "target": "com.amazonaws.ecs#EphemeralStorage", "traits": { - "smithy.api#documentation": "

The amount of ephemeral storage to allocate for the task. This parameter is used to\n\t\t\texpand the total amount of ephemeral storage available, beyond the default amount, for\n\t\t\ttasks hosted on Fargate. For more information, see Fargate task\n\t\t\t\tstorage in the Amazon ECS User Guide for Fargate.

\n \n

This parameter is only supported for tasks hosted on Fargate using\n\t\t\t\tthe following platform versions:

\n
    \n
  • \n

    Linux platform version 1.4.0 or later.

    \n
  • \n
\n
" + "smithy.api#documentation": "

The amount of ephemeral storage to allocate for the task. This parameter is used to\n\t\t\texpand the total amount of ephemeral storage available, beyond the default amount, for\n\t\t\ttasks hosted on Fargate. For more information, see Fargate task\n\t\t\t\tstorage in the Amazon ECS User Guide for Fargate.

\n \n

For tasks using the Fargate launch type, the task requires\n\t\t\t\tthe following platforms:

\n
    \n
  • \n

    Linux platform version 1.4.0 or later.

    \n
  • \n
  • \n

    Windows platform version 1.0.0 or later.

    \n
  • \n
\n
" } }, "runtimePlatform": { @@ -8350,7 +8400,7 @@ "value": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The value for the specified resource type.

\n

If the GPU type is used, the value is the number of physical\n\t\t\t\tGPUs the Amazon ECS container agent reserves for the container. The number\n\t\t\tof GPUs that's reserved for all containers in a task can't exceed the number of\n\t\t\tavailable GPUs on the container instance that the task is launched on.

\n

If the InferenceAccelerator type is used, the value matches\n\t\t\tthe deviceName for an InferenceAccelerator specified in a\n\t\t\ttask definition.

", + "smithy.api#documentation": "

The value for the specified resource type.

\n

If the GPU type is used, the value is the number of physical\n\t\t\t\tGPUs the Amazon ECS container agent reserves for the container. The number\n\t\t\tof GPUs that's reserved for all containers in a task can't exceed the number of\n\t\t\tavailable GPUs on the container instance that the task is launched on.

\n

If the InferenceAccelerator type is used, the value matches\n\t\t\tthe deviceName for an InferenceAccelerator specified in a\n\t\t\ttask definition.

", "smithy.api#required": {} } }, @@ -8363,7 +8413,7 @@ } }, "traits": { - "smithy.api#documentation": "

The type and amount of a resource to assign to a container. The supported resource\n\t\t\ttypes are GPUs and Elastic Inference accelerators. For more information, see Working with\n\t\t\t\tGPUs on Amazon ECS or Working with\n\t\t\t\tAmazon Elastic Inference on Amazon ECS in the Amazon Elastic Container Service Developer Guide\n

" + "smithy.api#documentation": "

The type and amount of a resource to assign to a container. The supported resource types are\n\t\t\tGPUs and Elastic Inference accelerators. For more information, see Working with\n\t\t\t\tGPUs on Amazon ECS or Working with\n\t\t\t\tAmazon Elastic Inference on Amazon ECS in the Amazon Elastic Container Service Developer Guide\n

" } }, "com.amazonaws.ecs#ResourceRequirements": { @@ -8433,7 +8483,7 @@ } ], "traits": { - "smithy.api#documentation": "

Starts a new task using the specified task definition.

\n

You can allow Amazon ECS to place tasks for you, or you can customize how Amazon ECS places\n\t\t\ttasks using placement constraints and placement strategies. For more information, see\n\t\t\t\tScheduling Tasks in the Amazon Elastic Container Service Developer Guide.

\n

Alternatively, you can use StartTask to use your own scheduler or\n\t\t\tplace tasks manually on specific container instances.

\n

The Amazon ECS API follows an eventual consistency model. This is because of the\n\t\t\tdistributed nature of the system supporting the API. This means that the result of an\n\t\t\tAPI command you run that affects your Amazon ECS resources might not be immediately visible\n\t\t\tto all subsequent commands you run. Keep this in mind when you carry out an API command\n\t\t\tthat immediately follows a previous API command.

\n

To manage eventual consistency, you can do the following:

\n
    \n
  • \n

    Confirm the state of the resource before you run a command to modify it. Run\n\t\t\t\t\tthe DescribeTasks command using an exponential backoff algorithm to ensure that\n\t\t\t\t\tyou allow enough time for the previous command to propagate through the system.\n\t\t\t\t\tTo do this, run the DescribeTasks command repeatedly, starting with a couple of\n\t\t\t\t\tseconds of wait time and increasing gradually up to five minutes of wait\n\t\t\t\t\ttime.

    \n
  • \n
  • \n

    Add wait time between subsequent commands, even if the DescribeTasks command\n\t\t\t\t\treturns an accurate response. Apply an exponential backoff algorithm starting\n\t\t\t\t\twith a couple of seconds of wait time, and increase gradually up to about five\n\t\t\t\t\tminutes of wait time.

    \n
  • \n
" + "smithy.api#documentation": "

Starts a new task using the specified task definition.

\n

You can allow Amazon ECS to place tasks for you, or you can customize how Amazon ECS places\n\t\t\ttasks using placement constraints and placement strategies. For more information, see\n\t\t\t\tScheduling Tasks in the Amazon Elastic Container Service Developer Guide.

\n

Alternatively, you can use StartTask to use your own scheduler or\n\t\t\tplace tasks manually on specific container instances.

\n \n

Starting April 15, 2023, Amazon Web Services will not onboard new customers to Amazon Elastic Inference (EI), and will help current customers migrate their workloads to options that offer better price and performance. After April 15, 2023, new customers will not be able to launch instances with Amazon EI accelerators in Amazon SageMaker, Amazon ECS, or Amazon EC2. However, customers who have used Amazon EI at least once during the past 30-day period are considered current customers and will be able to continue using the service.

\n
\n

The Amazon ECS API follows an eventual consistency model. This is because of the\n\t\t\tdistributed nature of the system supporting the API. This means that the result of an\n\t\t\tAPI command you run that affects your Amazon ECS resources might not be immediately visible\n\t\t\tto all subsequent commands you run. Keep this in mind when you carry out an API command\n\t\t\tthat immediately follows a previous API command.

\n

To manage eventual consistency, you can do the following:

\n
    \n
  • \n

    Confirm the state of the resource before you run a command to modify it. Run\n\t\t\t\t\tthe DescribeTasks command using an exponential backoff algorithm to ensure that\n\t\t\t\t\tyou allow enough time for the previous command to propagate through the system.\n\t\t\t\t\tTo do this, run the DescribeTasks command repeatedly, starting with a couple of\n\t\t\t\t\tseconds of wait time and increasing gradually up to five minutes of wait\n\t\t\t\t\ttime.

    \n
  • \n
  • \n

    Add wait time between subsequent commands, even if the DescribeTasks command\n\t\t\t\t\treturns an accurate response. Apply an exponential backoff algorithm starting\n\t\t\t\t\twith a couple of seconds of wait time, and increase gradually up to about five\n\t\t\t\t\tminutes of wait time.

    \n
  • \n
" } }, "com.amazonaws.ecs#RunTaskRequest": { @@ -8892,12 +8942,12 @@ "target": "com.amazonaws.ecs#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Determines whether the execute command functionality is enabled for the service. If\n\t\t\t\ttrue, the execute command functionality is enabled for all containers\n\t\t\tin tasks as part of the service.

" + "smithy.api#documentation": "

Determines whether the execute command functionality is turned on for the service. If\n\t\t\t\ttrue, the execute command functionality is turned on for all containers\n\t\t\tin tasks as part of the service.

" } } }, "traits": { - "smithy.api#documentation": "

Details on a service within a cluster

" + "smithy.api#documentation": "

Details on a service within a cluster.

" } }, "com.amazonaws.ecs#ServiceConnectClientAlias": { @@ -8941,7 +8991,7 @@ "namespace": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The namespace name or full Amazon Resource Name (ARN) of the Cloud Map namespace for use with Service Connect. The namespace must be in\n\t\t\tthe same Amazon Web Services Region as the Amazon ECS service and cluster. The type of namespace doesn't\n\t\t\taffect Service Connect. For more information about Cloud Map, see Working with Services in the\n\t\t\tCloud Map Developer Guide.

" + "smithy.api#documentation": "

The namespace name or full Amazon Resource Name (ARN) of the Cloud Map namespace for use with Service Connect. The namespace must be in\n\t\t\tthe same Amazon Web Services Region as the Amazon ECS service and cluster. The type of namespace doesn't\n\t\t\taffect Service Connect. For more information about Cloud Map, see Working with Services in the\n\t\t\tCloud Map Developer Guide.

" } }, "services": { @@ -8971,7 +9021,7 @@ "discoveryName": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The discoveryName is the name of the new Cloud Map service that Amazon ECS creates\n\t\t\tfor this Amazon ECS service. This must be unique within the Cloud Map namespace. The name can contain up to 64 characters. The name can include lowercase letters,\n\t\t\tnumbers, underscores (_), and hyphens (-). The name can't start with a hyphen.

\n

If this parameter isn't specified, the default value of discoveryName.namespace is used. If the discoveryName isn't specified, the port mapping name from the task definition is used in portName.namespace.

" + "smithy.api#documentation": "

The discoveryName is the name of the new Cloud Map service that Amazon ECS creates\n\t\t\tfor this Amazon ECS service. This must be unique within the Cloud Map namespace. The name can contain up to 64 characters. The name can include lowercase letters,\n\t\t\tnumbers, underscores (_), and hyphens (-). The name can't start with a hyphen.

\n

If the discoveryName isn't specified, the port mapping name from the task definition is used in portName.namespace.

" } }, "clientAliases": { @@ -9003,7 +9053,7 @@ "discoveryName": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The discovery name of this Service Connect resource.

\n

The discoveryName is the name of the new Cloud Map service that Amazon ECS creates\n\t\t\tfor this Amazon ECS service. This must be unique within the Cloud Map namespace. The name can contain up to 64 characters. The name can include lowercase letters,\n\t\t\tnumbers, underscores (_), and hyphens (-). The name can't start with a hyphen.

\n

If this parameter isn't specified, the default value of discoveryName.namespace is used. If the discoveryName isn't specified, the port mapping name from the task definition is used in portName.namespace.

" + "smithy.api#documentation": "

The discovery name of this Service Connect resource.

\n

The discoveryName is the name of the new Cloud Map service that Amazon ECS creates\n\t\t\tfor this Amazon ECS service. This must be unique within the Cloud Map namespace. The name can contain up to 64 characters. The name can include lowercase letters,\n\t\t\tnumbers, underscores (_), and hyphens (-). The name can't start with a hyphen.

\n

If the discoveryName isn't specified, the port mapping name from the task definition is used in portName.namespace.

" } }, "discoveryArn": { @@ -9224,6 +9274,18 @@ "traits": { "smithy.api#enumValue": "containerInsights" } + }, + "FARGATE_FIPS_MODE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "fargateFIPSMode" + } + }, + "TAG_RESOURCE_AUTHORIZATION": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "tagResourceAuthorization" + } } } }, @@ -9290,7 +9352,7 @@ } ], "traits": { - "smithy.api#documentation": "

Starts a new task from the specified task definition on the specified container\n\t\t\tinstance or instances.

\n

Alternatively, you can use RunTask to place tasks for you. For more\n\t\t\tinformation, see Scheduling Tasks in the Amazon Elastic Container Service Developer Guide.

" + "smithy.api#documentation": "

Starts a new task from the specified task definition on the specified container\n\t\t\tinstance or instances.

\n \n

Starting April 15, 2023, Amazon Web Services will not onboard new customers to Amazon Elastic Inference (EI), and will help current customers migrate their workloads to options that offer better price and performance. After April 15, 2023, new customers will not be able to launch instances with Amazon EI accelerators in Amazon SageMaker, Amazon ECS, or Amazon EC2. However, customers who have used Amazon EI at least once during the past 30-day period are considered current customers and will be able to continue using the service.

\n
\n

Alternatively, you can use RunTask to place tasks for you. For more\n\t\t\tinformation, see Scheduling Tasks in the Amazon Elastic Container Service Developer Guide.

" } }, "com.amazonaws.ecs#StartTaskRequest": { @@ -9320,7 +9382,7 @@ "target": "com.amazonaws.ecs#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Whether or not the execute command functionality is enabled for the task. If\n\t\t\t\ttrue, this enables execute command functionality on all containers in\n\t\t\tthe task.

" + "smithy.api#documentation": "

Whether or not the execute command functionality is turned on for the task. If\n\t\t\t\ttrue, this turns on the execute command functionality on all containers in\n\t\t\tthe task.

" } }, "group": { @@ -9900,7 +9962,7 @@ } }, "traits": { - "smithy.api#documentation": "

The execute command cannot run. This error can be caused by any of the following\n\t\t\tconfiguration issues:

\n
    \n
  • \n

    Incorrect IAM permissions

    \n
  • \n
  • \n

    The SSM agent is not installed or is not running

    \n
  • \n
  • \n

    There is an interface Amazon VPC endpoint for Amazon ECS, but there is not one for\n\t\t\t\t\tfor Systems Manager Session Manager

    \n
  • \n
\n

For information about how to troubleshoot the issues, see Troubleshooting issues with ECS\n\t\t\t\tExec in the Amazon Elastic Container Service Developer Guide.

", + "smithy.api#documentation": "

The execute command cannot run. This error can be caused by any of the following\n\t\t\tconfiguration issues:

\n
    \n
  • \n

    Incorrect IAM permissions

    \n
  • \n
  • \n

    The SSM agent is not installed or is not running

    \n
  • \n
  • \n

    There is an interface Amazon VPC endpoint for Amazon ECS, but there is not one for Systems\n\t\t\t\t\tManager Session Manager

    \n
  • \n
\n

For information about how to troubleshoot the issues, see Troubleshooting issues with ECS\n\t\t\t\tExec in the Amazon Elastic Container Service Developer Guide.

", "smithy.api#error": "client" } }, @@ -10006,7 +10068,7 @@ "target": "com.amazonaws.ecs#Boolean", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Determines whether execute command functionality is enabled for this task. If\n\t\t\t\ttrue, execute command functionality is enabled on all the containers in\n\t\t\tthe task.

" + "smithy.api#documentation": "

Determines whether execute command functionality is turned on for this task. If\n\t\t\t\ttrue, execute command functionality is turned on all the containers in\n\t\t\tthe task.

" } }, "executionStoppedAt": { @@ -10238,7 +10300,7 @@ "requiresCompatibilities": { "target": "com.amazonaws.ecs#CompatibilityList", "traits": { - "smithy.api#documentation": "

The task launch types the task definition was validated against. To determine which\n\t\t\ttask launch types the task definition is validated for, see the TaskDefinition$compatibilities parameter.

" + "smithy.api#documentation": "

The task launch types the task definition was validated against. For more information, see Amazon ECS launch types\n\t\t\tin the Amazon Elastic Container Service Developer Guide.

" } }, "cpu": { @@ -10369,7 +10431,7 @@ } }, "traits": { - "smithy.api#documentation": "

An object representing a constraint on task placement in the task definition. For more\n\t\t\tinformation, see Task placement constraints in the\n\t\t\tAmazon Elastic Container Service Developer Guide.

\n \n

Task placement constraints aren't supported for tasks run on Fargate.

\n
" + "smithy.api#documentation": "

The constraint on task placement in the task definition. For more\n\t\t\tinformation, see Task placement constraints in the\n\t\t\tAmazon Elastic Container Service Developer Guide.

\n \n

Task placement constraints aren't supported for tasks run on Fargate.

\n
" } }, "com.amazonaws.ecs#TaskDefinitionPlacementConstraintType": { @@ -11615,7 +11677,7 @@ } ], "traits": { - "smithy.api#documentation": "

Updates the protection status of a task. You can set protectionEnabled to\n\t\t\t\ttrue to protect your task from termination during scale-in events from\n\t\t\t\tService\n\t\t\t\tAutoscaling or deployments.

\n

Task-protection, by default, expires after 2 hours at which point Amazon ECS unsets the\n\t\t\t\tprotectionEnabled property making the task eligible for termination by\n\t\t\ta subsequent scale-in event.

\n

You can specify a custom expiration period for task protection from 1 minute to up to\n\t\t\t2,880 minutes (48 hours). To specify the custom expiration period, set the\n\t\t\t\texpiresInMinutes property. The expiresInMinutes property\n\t\t\tis always reset when you invoke this operation for a task that already has\n\t\t\t\tprotectionEnabled set to true. You can keep extending the\n\t\t\tprotection expiration period of a task by invoking this operation repeatedly.

\n

To learn more about Amazon ECS task protection, see Task scale-in\n\t\t\t\tprotection in the \n Amazon Elastic Container Service Developer Guide\n .

\n \n

This operation is only supported for tasks belonging to an Amazon ECS service. Invoking\n\t\t\t\tthis operation for a standalone task will result in an TASK_NOT_VALID\n\t\t\t\tfailure. For more information, see API failure\n\t\t\t\t\treasons.

\n
\n \n

If you prefer to set task protection from within the container, we recommend using\n\t\t\t\tthe Task scale-in protection endpoint.

\n
" + "smithy.api#documentation": "

Updates the protection status of a task. You can set protectionEnabled to\n\t\t\t\ttrue to protect your task from termination during scale-in events from\n\t\t\t\tService\n\t\t\t\tAutoscaling or deployments.

\n

Task-protection, by default, expires after 2 hours at which point Amazon ECS clears the\n\t\t\t\tprotectionEnabled property making the task eligible for termination by\n\t\t\ta subsequent scale-in event.

\n

You can specify a custom expiration period for task protection from 1 minute to up to\n\t\t\t2,880 minutes (48 hours). To specify the custom expiration period, set the\n\t\t\t\texpiresInMinutes property. The expiresInMinutes property\n\t\t\tis always reset when you invoke this operation for a task that already has\n\t\t\t\tprotectionEnabled set to true. You can keep extending the\n\t\t\tprotection expiration period of a task by invoking this operation repeatedly.

\n

To learn more about Amazon ECS task protection, see Task scale-in\n\t\t\t\tprotection in the \n Amazon Elastic Container Service Developer Guide\n .

\n \n

This operation is only supported for tasks belonging to an Amazon ECS service. Invoking\n\t\t\t\tthis operation for a standalone task will result in an TASK_NOT_VALID\n\t\t\t\tfailure. For more information, see API failure\n\t\t\t\t\treasons.

\n
\n \n

If you prefer to set task protection from within the container, we recommend using\n\t\t\t\tthe Task scale-in protection endpoint.

\n
" } }, "com.amazonaws.ecs#UpdateTaskProtectionRequest": { @@ -11660,7 +11722,7 @@ "protectedTasks": { "target": "com.amazonaws.ecs#ProtectedTasks", "traits": { - "smithy.api#documentation": "

A list of tasks with the following information.

\n
    \n
  • \n

    \n taskArn: The task ARN.

    \n
  • \n
  • \n

    \n protectionEnabled: The protection status of the task. If scale-in\n\t\t\t\t\tprotection is enabled for a task, the value is true. Otherwise, it\n\t\t\t\t\tis false.

    \n
  • \n
  • \n

    \n expirationDate: The epoch time when protection for the task will\n\t\t\t\t\texpire.

    \n
  • \n
" + "smithy.api#documentation": "

A list of tasks with the following information.

\n
    \n
  • \n

    \n taskArn: The task ARN.

    \n
  • \n
  • \n

    \n protectionEnabled: The protection status of the task. If scale-in\n\t\t\t\t\tprotection is turned on for a task, the value is true. Otherwise, it\n\t\t\t\t\tis false.

    \n
  • \n
  • \n

    \n expirationDate: The epoch time when protection for the task will\n\t\t\t\t\texpire.

    \n
  • \n
" } }, "failures": { diff --git a/aws/sdk/aws-models/glacier.json b/aws/sdk/aws-models/glacier.json index 6fc9d21fe1..32cb84212f 100644 --- a/aws/sdk/aws-models/glacier.json +++ b/aws/sdk/aws-models/glacier.json @@ -2065,44 +2065,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-east-1" - ] - } - ], - "endpoint": { - "url": "https://glacier.us-gov-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-west-1" - ] - } - ], - "endpoint": { - "url": "https://glacier.us-gov-west-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -2138,8 +2100,8 @@ }, "params": { "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2151,8 +2113,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2164,8 +2126,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2177,8 +2139,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2190,8 +2152,8 @@ }, "params": { "Region": "ap-northeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2203,8 +2165,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2216,8 +2178,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2229,8 +2191,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2242,8 +2204,8 @@ }, "params": { "Region": "ap-southeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2255,8 +2217,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2268,8 +2230,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2281,8 +2243,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2294,8 +2256,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2307,8 +2269,8 @@ }, "params": { "Region": "eu-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2320,8 +2282,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2333,8 +2295,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2346,8 +2308,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2359,8 +2321,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2372,8 +2334,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2385,8 +2347,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2398,8 +2360,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2411,8 +2373,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2424,8 +2386,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2437,8 +2399,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2450,8 +2412,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2463,8 +2425,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2476,8 +2438,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2489,8 +2451,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2502,8 +2464,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2515,8 +2477,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2528,8 +2490,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2541,8 +2503,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2554,8 +2516,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2567,8 +2529,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2580,8 +2542,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2593,8 +2555,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2606,8 +2568,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2619,8 +2581,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2632,8 +2594,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2645,8 +2607,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2658,8 +2620,8 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2671,8 +2633,19 @@ }, "params": { "Region": "us-iso-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2684,8 +2657,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2697,8 +2681,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2710,8 +2705,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2723,8 +2729,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2736,8 +2742,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2748,8 +2754,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2760,10 +2766,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/aws-models/iam.json b/aws/sdk/aws-models/iam.json index bd2c1ac793..8453b370fc 100644 --- a/aws/sdk/aws-models/iam.json +++ b/aws/sdk/aws-models/iam.json @@ -665,216 +665,91 @@ }, "aws" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] + "ref": "UseFIPS" }, + false + ] + }, + { + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://iam.amazonaws.com", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-east-1" } ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "booleanEquals", + "fn": "getAttr", "argv": [ { - "ref": "UseFIPS" + "ref": "PartitionResult" }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } + "name" ] }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + "aws" ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } + true ] }, { - "conditions": [], - "endpoint": { - "url": "https://iam.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://iam-fips.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -892,208 +767,40 @@ }, "aws-cn" ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.api.amazonwebservices.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" - } - ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.amazonaws.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + false ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam.{Region}.api.amazonwebservices.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://iam.cn-north-1.amazonaws.com.cn", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" + "name": "sigv4", + "signingName": "iam", + "signingRegion": "cn-north-1" } ] }, - { - "conditions": [], - "endpoint": { - "url": "https://iam.cn-north-1.amazonaws.com.cn", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "cn-north-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1111,216 +818,91 @@ }, "aws-us-gov" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] + "ref": "UseFIPS" }, + false + ] + }, + { + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://iam.us-gov.amazonaws.com", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-gov-west-1" } ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "booleanEquals", + "fn": "getAttr", "argv": [ { - "ref": "UseFIPS" + "ref": "PartitionResult" }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } + "name" ] }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + "aws-us-gov" ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } + true ] }, { - "conditions": [], - "endpoint": { - "url": "https://iam.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-gov-west-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://iam.us-gov.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-gov-west-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1338,80 +920,40 @@ }, "aws-iso" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.c2s.ic.gov", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - }, + "fn": "booleanEquals", + "argv": [ { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + "ref": "UseFIPS" + }, + false ] }, { - "conditions": [], - "endpoint": { - "url": "https://iam.us-iso-east-1.c2s.ic.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-iso-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://iam.us-iso-east-1.c2s.ic.gov", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-iso-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1429,80 +971,40 @@ }, "aws-iso-b" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://iam-fips.{Region}.sc2s.sgov.gov", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + false ] }, { - "conditions": [], - "endpoint": { - "url": "https://iam.us-isob-east-1.sc2s.sgov.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-isob-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://iam.us-isob-east-1.sc2s.sgov.gov", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "iam", + "signingRegion": "us-isob-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1624,60 +1126,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://iam-fips.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-us-gov-global" - ] - } - ], - "endpoint": { - "url": "https://iam.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -1760,141 +1208,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://iam.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-cn-global" - ] - } - ], - "endpoint": { - "url": "https://iam.cn-north-1.amazonaws.com.cn", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "cn-north-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-us-gov-global" - ] - } - ], - "endpoint": { - "url": "https://iam.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-iso-global" - ] - } - ], - "endpoint": { - "url": "https://iam.us-iso-east-1.c2s.ic.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-iso-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-iso-b-global" - ] - } - ], - "endpoint": { - "url": "https://iam.us-isob-east-1.sc2s.sgov.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "iam", - "signingRegion": "us-isob-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -1938,9 +1251,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-global", "UseFIPS": false, - "Region": "aws-global" + "UseDualStack": false } }, { @@ -1960,9 +1273,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-global", "UseFIPS": true, - "Region": "aws-global" + "UseDualStack": false } }, { @@ -1973,9 +1286,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -1995,9 +1308,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -2008,9 +1321,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -2030,9 +1343,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -2052,9 +1365,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-cn-global", "UseFIPS": false, - "Region": "aws-cn-global" + "UseDualStack": false } }, { @@ -2065,9 +1378,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -2078,9 +1391,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -2091,9 +1404,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -2113,9 +1426,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -2135,9 +1448,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-us-gov-global", "UseFIPS": false, - "Region": "aws-us-gov-global" + "UseDualStack": false } }, { @@ -2157,9 +1470,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-us-gov-global", "UseFIPS": true, - "Region": "aws-us-gov-global" + "UseDualStack": false } }, { @@ -2170,9 +1483,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -2192,9 +1505,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -2205,9 +1518,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -2227,9 +1540,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -2249,9 +1562,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-iso-global", "UseFIPS": false, - "Region": "aws-iso-global" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2262,9 +1586,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2284,9 +1619,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": false, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -2306,9 +1641,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "aws-iso-b-global", "UseFIPS": false, - "Region": "aws-iso-b-global" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2319,9 +1665,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": true, - "Region": "us-isob-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2341,9 +1698,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": false, - "Region": "us-isob-east-1" + "UseDualStack": false } }, { @@ -2354,9 +1711,9 @@ } }, "params": { - "UseDualStack": false, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2368,8 +1725,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2379,9 +1736,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseDualStack": false, - "UseFIPS": true, "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2391,11 +1748,17 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseDualStack": true, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -4120,7 +3483,7 @@ "VirtualMFADeviceName": { "target": "com.amazonaws.iam#virtualMFADeviceName", "traits": { - "smithy.api#documentation": "

The name of the virtual MFA device, which must be unique. Use with path to uniquely identify a virtual MFA\n device.

\n

This parameter allows (through its regex pattern) a string of characters consisting of upper and lowercase alphanumeric \n characters with no spaces. You can also include any of the following characters: _+=,.@-

", + "smithy.api#documentation": "

The name of the virtual MFA device, which must be unique. Use with path to uniquely\n identify a virtual MFA device.

\n

This parameter allows (through its regex pattern) a string of characters consisting of upper and lowercase alphanumeric \n characters with no spaces. You can also include any of the following characters: _+=,.@-

", "smithy.api#required": {} } }, @@ -4725,7 +4088,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the permissions boundary for the specified IAM role.

\n \n

Deleting the permissions boundary for a role might increase its permissions. For\n example, it might allow anyone who assumes the role to perform all the actions\n granted in its permissions policies.

\n
" + "smithy.api#documentation": "

Deletes the permissions boundary for the specified IAM role.

\n

You cannot set the boundary for a service-linked role.

\n \n

Deleting the permissions boundary for a role might increase its permissions. For\n example, it might allow anyone who assumes the role to perform all the actions\n granted in its permissions policies.

\n
" } }, "com.amazonaws.iam#DeleteRolePermissionsBoundaryRequest": { @@ -11411,7 +10774,7 @@ } ], "traits": { - "smithy.api#documentation": "

Adds or updates the policy that is specified as the IAM role's permissions boundary.\n You can use an Amazon Web Services managed policy or a customer managed policy to set the boundary for\n a role. Use the boundary to control the maximum permissions that the role can have.\n Setting a permissions boundary is an advanced feature that can affect the permissions\n for the role.

\n

You cannot set the boundary for a service-linked role.

\n \n

Policies used as permissions boundaries do not provide permissions. You must also\n attach a permissions policy to the role. To learn how the effective permissions for\n a role are evaluated, see IAM JSON policy\n evaluation logic in the IAM User Guide.

\n
" + "smithy.api#documentation": "

Adds or updates the policy that is specified as the IAM role's permissions boundary.\n You can use an Amazon Web Services managed policy or a customer managed policy to set the boundary for\n a role. Use the boundary to control the maximum permissions that the role can have.\n Setting a permissions boundary is an advanced feature that can affect the permissions\n for the role.

\n

You cannot set the boundary for a service-linked role.

\n \n

Policies used as permissions boundaries do not provide permissions. You must also\n attach a permissions policy to the role. To learn how the effective permissions for\n a role are evaluated, see IAM JSON policy\n evaluation logic in the IAM User Guide.

\n
" } }, "com.amazonaws.iam#PutRolePermissionsBoundaryRequest": { @@ -13514,7 +12877,7 @@ "code": "UnmodifiableEntity", "httpResponseCode": 400 }, - "smithy.api#documentation": "

The request was rejected because only the service that depends on the service-linked role\n can modify or delete the role on your behalf. The error message includes the name of the\n service that depends on this service-linked role. You must request the change through that\n service.

", + "smithy.api#documentation": "

The request was rejected because service-linked roles are protected Amazon Web Services resources. Only\n the service that depends on the service-linked role can modify or delete the role on your\n behalf. The error message includes the name of the service that depends on this service-linked\n role. You must request the change through that service.

", "smithy.api#error": "client", "smithy.api#httpError": 400 } diff --git a/aws/sdk/aws-models/kms.json b/aws/sdk/aws-models/kms.json index cd4fd4edd6..7a9449eb24 100644 --- a/aws/sdk/aws-models/kms.json +++ b/aws/sdk/aws-models/kms.json @@ -6222,9 +6222,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "af-south-1", "UseFIPS": false, - "Region": "af-south-1" + "UseDualStack": false } }, { @@ -6235,9 +6235,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "af-south-1", "UseFIPS": true, - "Region": "af-south-1" + "UseDualStack": false } }, { @@ -6248,9 +6248,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-east-1", "UseFIPS": false, - "Region": "ap-east-1" + "UseDualStack": false } }, { @@ -6261,9 +6261,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-east-1", "UseFIPS": true, - "Region": "ap-east-1" + "UseDualStack": false } }, { @@ -6274,9 +6274,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-1", "UseFIPS": false, - "Region": "ap-northeast-1" + "UseDualStack": false } }, { @@ -6287,9 +6287,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-1", "UseFIPS": true, - "Region": "ap-northeast-1" + "UseDualStack": false } }, { @@ -6300,9 +6300,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-2", "UseFIPS": false, - "Region": "ap-northeast-2" + "UseDualStack": false } }, { @@ -6313,9 +6313,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-2", "UseFIPS": true, - "Region": "ap-northeast-2" + "UseDualStack": false } }, { @@ -6326,9 +6326,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-3", "UseFIPS": false, - "Region": "ap-northeast-3" + "UseDualStack": false } }, { @@ -6339,9 +6339,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-3", "UseFIPS": true, - "Region": "ap-northeast-3" + "UseDualStack": false } }, { @@ -6352,9 +6352,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-south-1", "UseFIPS": false, - "Region": "ap-south-1" + "UseDualStack": false } }, { @@ -6365,9 +6365,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-south-1", "UseFIPS": true, - "Region": "ap-south-1" + "UseDualStack": false } }, { @@ -6378,9 +6378,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-1", "UseFIPS": false, - "Region": "ap-southeast-1" + "UseDualStack": false } }, { @@ -6391,9 +6391,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-1", "UseFIPS": true, - "Region": "ap-southeast-1" + "UseDualStack": false } }, { @@ -6404,9 +6404,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-2", "UseFIPS": false, - "Region": "ap-southeast-2" + "UseDualStack": false } }, { @@ -6417,9 +6417,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-2", "UseFIPS": true, - "Region": "ap-southeast-2" + "UseDualStack": false } }, { @@ -6430,9 +6430,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-3", "UseFIPS": false, - "Region": "ap-southeast-3" + "UseDualStack": false } }, { @@ -6443,9 +6443,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-3", "UseFIPS": true, - "Region": "ap-southeast-3" + "UseDualStack": false } }, { @@ -6456,9 +6456,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ca-central-1", "UseFIPS": false, - "Region": "ca-central-1" + "UseDualStack": false } }, { @@ -6469,9 +6469,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ca-central-1", "UseFIPS": true, - "Region": "ca-central-1" + "UseDualStack": false } }, { @@ -6482,9 +6482,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-central-1", "UseFIPS": false, - "Region": "eu-central-1" + "UseDualStack": false } }, { @@ -6495,9 +6495,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-central-1", "UseFIPS": true, - "Region": "eu-central-1" + "UseDualStack": false } }, { @@ -6508,9 +6508,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-north-1", "UseFIPS": false, - "Region": "eu-north-1" + "UseDualStack": false } }, { @@ -6521,9 +6521,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-north-1", "UseFIPS": true, - "Region": "eu-north-1" + "UseDualStack": false } }, { @@ -6534,9 +6534,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-south-1", "UseFIPS": false, - "Region": "eu-south-1" + "UseDualStack": false } }, { @@ -6547,9 +6547,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-south-1", "UseFIPS": true, - "Region": "eu-south-1" + "UseDualStack": false } }, { @@ -6560,9 +6560,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-1", "UseFIPS": false, - "Region": "eu-west-1" + "UseDualStack": false } }, { @@ -6573,9 +6573,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-1", "UseFIPS": true, - "Region": "eu-west-1" + "UseDualStack": false } }, { @@ -6586,9 +6586,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-2", "UseFIPS": false, - "Region": "eu-west-2" + "UseDualStack": false } }, { @@ -6599,9 +6599,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-2", "UseFIPS": true, - "Region": "eu-west-2" + "UseDualStack": false } }, { @@ -6612,9 +6612,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-3", "UseFIPS": false, - "Region": "eu-west-3" + "UseDualStack": false } }, { @@ -6625,9 +6625,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-3", "UseFIPS": true, - "Region": "eu-west-3" + "UseDualStack": false } }, { @@ -6638,9 +6638,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "me-south-1", "UseFIPS": false, - "Region": "me-south-1" + "UseDualStack": false } }, { @@ -6651,9 +6651,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "me-south-1", "UseFIPS": true, - "Region": "me-south-1" + "UseDualStack": false } }, { @@ -6664,9 +6664,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "sa-east-1", "UseFIPS": false, - "Region": "sa-east-1" + "UseDualStack": false } }, { @@ -6677,9 +6677,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "sa-east-1", "UseFIPS": true, - "Region": "sa-east-1" + "UseDualStack": false } }, { @@ -6690,9 +6690,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -6703,9 +6703,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -6716,9 +6716,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": false, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -6729,9 +6729,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": true, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -6742,9 +6742,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": false, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -6755,9 +6755,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": true, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -6768,9 +6768,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": false, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -6781,9 +6781,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": true, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -6794,9 +6794,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -6807,9 +6807,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -6820,9 +6820,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -6833,9 +6833,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-northwest-1", "UseFIPS": false, - "Region": "cn-northwest-1" + "UseDualStack": false } }, { @@ -6846,9 +6846,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -6859,9 +6859,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -6872,9 +6872,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -6885,9 +6885,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -6898,9 +6898,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -6911,9 +6911,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": false, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -6924,9 +6924,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": true, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -6937,9 +6937,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -6950,9 +6950,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -6963,9 +6963,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": false, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -6976,9 +6976,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -6989,9 +6989,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-west-1", "UseFIPS": false, - "Region": "us-iso-west-1" + "UseDualStack": false } }, { @@ -7002,9 +7002,31 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-west-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-west-1" + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -7015,9 +7037,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": false, - "Region": "us-isob-east-1" + "UseDualStack": false } }, { @@ -7028,9 +7050,31 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", "UseFIPS": true, - "Region": "us-isob-east-1" + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -7041,9 +7085,9 @@ } }, "params": { - "UseDualStack": false, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -7055,8 +7099,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -7066,9 +7110,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseDualStack": false, - "UseFIPS": true, "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -7078,11 +7122,17 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseDualStack": true, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/aws-models/lambda.json b/aws/sdk/aws-models/lambda.json index 18759e228b..8ed02f91b7 100644 --- a/aws/sdk/aws-models/lambda.json +++ b/aws/sdk/aws-models/lambda.json @@ -138,6 +138,9 @@ { "target": "com.amazonaws.lambda#InvokeAsync" }, + { + "target": "com.amazonaws.lambda#InvokeWithResponseStream" + }, { "target": "com.amazonaws.lambda#ListAliases" }, @@ -603,9 +606,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "af-south-1", "UseFIPS": false, - "Region": "af-south-1" + "UseDualStack": false } }, { @@ -616,9 +619,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "af-south-1", "UseFIPS": false, - "Region": "af-south-1" + "UseDualStack": true } }, { @@ -629,9 +632,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-east-1", "UseFIPS": false, - "Region": "ap-east-1" + "UseDualStack": false } }, { @@ -642,9 +645,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-east-1", "UseFIPS": false, - "Region": "ap-east-1" + "UseDualStack": true } }, { @@ -655,9 +658,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-1", "UseFIPS": false, - "Region": "ap-northeast-1" + "UseDualStack": false } }, { @@ -668,9 +671,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-northeast-1", "UseFIPS": false, - "Region": "ap-northeast-1" + "UseDualStack": true } }, { @@ -681,9 +684,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-2", "UseFIPS": false, - "Region": "ap-northeast-2" + "UseDualStack": false } }, { @@ -694,9 +697,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-northeast-2", "UseFIPS": false, - "Region": "ap-northeast-2" + "UseDualStack": true } }, { @@ -707,9 +710,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-3", "UseFIPS": false, - "Region": "ap-northeast-3" + "UseDualStack": false } }, { @@ -720,9 +723,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-northeast-3", "UseFIPS": false, - "Region": "ap-northeast-3" + "UseDualStack": true } }, { @@ -733,9 +736,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-south-1", "UseFIPS": false, - "Region": "ap-south-1" + "UseDualStack": false } }, { @@ -746,9 +749,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-south-1", "UseFIPS": false, - "Region": "ap-south-1" + "UseDualStack": true } }, { @@ -759,9 +762,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-1", "UseFIPS": false, - "Region": "ap-southeast-1" + "UseDualStack": false } }, { @@ -772,9 +775,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-southeast-1", "UseFIPS": false, - "Region": "ap-southeast-1" + "UseDualStack": true } }, { @@ -785,9 +788,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-2", "UseFIPS": false, - "Region": "ap-southeast-2" + "UseDualStack": false } }, { @@ -798,9 +801,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-southeast-2", "UseFIPS": false, - "Region": "ap-southeast-2" + "UseDualStack": true } }, { @@ -811,9 +814,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-3", "UseFIPS": false, - "Region": "ap-southeast-3" + "UseDualStack": false } }, { @@ -824,9 +827,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ap-southeast-3", "UseFIPS": false, - "Region": "ap-southeast-3" + "UseDualStack": true } }, { @@ -837,9 +840,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ca-central-1", "UseFIPS": false, - "Region": "ca-central-1" + "UseDualStack": false } }, { @@ -850,9 +853,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "ca-central-1", "UseFIPS": false, - "Region": "ca-central-1" + "UseDualStack": true } }, { @@ -863,9 +866,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-central-1", "UseFIPS": false, - "Region": "eu-central-1" + "UseDualStack": false } }, { @@ -876,9 +879,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-central-1", "UseFIPS": false, - "Region": "eu-central-1" + "UseDualStack": true } }, { @@ -889,9 +892,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-north-1", "UseFIPS": false, - "Region": "eu-north-1" + "UseDualStack": false } }, { @@ -902,9 +905,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-north-1", "UseFIPS": false, - "Region": "eu-north-1" + "UseDualStack": true } }, { @@ -915,9 +918,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-south-1", "UseFIPS": false, - "Region": "eu-south-1" + "UseDualStack": false } }, { @@ -928,9 +931,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-south-1", "UseFIPS": false, - "Region": "eu-south-1" + "UseDualStack": true } }, { @@ -941,9 +944,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-1", "UseFIPS": false, - "Region": "eu-west-1" + "UseDualStack": false } }, { @@ -954,9 +957,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-west-1", "UseFIPS": false, - "Region": "eu-west-1" + "UseDualStack": true } }, { @@ -967,9 +970,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-2", "UseFIPS": false, - "Region": "eu-west-2" + "UseDualStack": false } }, { @@ -980,9 +983,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-west-2", "UseFIPS": false, - "Region": "eu-west-2" + "UseDualStack": true } }, { @@ -993,9 +996,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-3", "UseFIPS": false, - "Region": "eu-west-3" + "UseDualStack": false } }, { @@ -1006,9 +1009,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "eu-west-3", "UseFIPS": false, - "Region": "eu-west-3" + "UseDualStack": true } }, { @@ -1019,9 +1022,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "me-south-1", "UseFIPS": false, - "Region": "me-south-1" + "UseDualStack": false } }, { @@ -1032,9 +1035,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "me-south-1", "UseFIPS": false, - "Region": "me-south-1" + "UseDualStack": true } }, { @@ -1045,9 +1048,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "sa-east-1", "UseFIPS": false, - "Region": "sa-east-1" + "UseDualStack": false } }, { @@ -1058,9 +1061,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "sa-east-1", "UseFIPS": false, - "Region": "sa-east-1" + "UseDualStack": true } }, { @@ -1071,9 +1074,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -1084,9 +1087,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -1097,9 +1100,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -1110,9 +1113,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": false, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -1123,9 +1126,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": true, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -1136,9 +1139,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-2", "UseFIPS": false, - "Region": "us-east-2" + "UseDualStack": true } }, { @@ -1149,9 +1152,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": false, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -1162,9 +1165,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": true, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -1175,9 +1178,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-west-1", "UseFIPS": false, - "Region": "us-west-1" + "UseDualStack": true } }, { @@ -1188,9 +1191,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": false, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -1201,9 +1204,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": true, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -1214,9 +1217,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-west-2", "UseFIPS": false, - "Region": "us-west-2" + "UseDualStack": true } }, { @@ -1227,9 +1230,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -1240,9 +1243,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -1253,9 +1256,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -1266,9 +1269,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-northwest-1", "UseFIPS": false, - "Region": "cn-northwest-1" + "UseDualStack": false } }, { @@ -1279,9 +1282,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-northwest-1", "UseFIPS": false, - "Region": "cn-northwest-1" + "UseDualStack": true } }, { @@ -1292,9 +1295,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -1305,9 +1308,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -1318,9 +1321,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -1331,9 +1334,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -1344,9 +1347,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": false, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -1357,9 +1360,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": true, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -1370,9 +1373,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -1383,9 +1386,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -1396,9 +1399,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": false, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -1409,9 +1412,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-west-1", "UseFIPS": false, - "Region": "us-iso-west-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1422,9 +1436,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1435,9 +1460,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": false, - "Region": "us-isob-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1448,9 +1484,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": true, - "Region": "us-isob-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1461,9 +1508,9 @@ } }, "params": { - "UseDualStack": false, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1475,8 +1522,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1486,9 +1533,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseDualStack": false, - "UseFIPS": true, "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1498,11 +1545,17 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseDualStack": true, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -2457,7 +2510,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a mapping between an event source and an Lambda function. Lambda reads items from the event source and invokes the function.

\n

For details about how to configure different event sources, see the following topics.

\n \n

The following error handling options are available only for stream sources (DynamoDB and Kinesis):

\n
    \n
  • \n

    \n BisectBatchOnFunctionError – If the function returns an error, split the batch in two and retry.

    \n
  • \n
  • \n

    \n DestinationConfig – Send discarded records to an Amazon SQS queue or Amazon SNS topic.

    \n
  • \n
  • \n

    \n MaximumRecordAgeInSeconds – Discard records older than the specified age. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires

    \n
  • \n
  • \n

    \n MaximumRetryAttempts – Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

    \n
  • \n
  • \n

    \n ParallelizationFactor – Process multiple batches from each shard concurrently.

    \n
  • \n
\n

For information about which configuration parameters apply to each event source, see the following topics.

\n ", + "smithy.api#documentation": "

Creates a mapping between an event source and an Lambda function. Lambda reads items from the event source and invokes the function.

\n

For details about how to configure different event sources, see the following topics.

\n \n

The following error handling options are available only for stream sources (DynamoDB and Kinesis):

\n
    \n
  • \n

    \n BisectBatchOnFunctionError – If the function returns an error, split the batch in two and retry.

    \n
  • \n
  • \n

    \n DestinationConfig – Send discarded records to an Amazon SQS queue or Amazon SNS topic.

    \n
  • \n
  • \n

    \n MaximumRecordAgeInSeconds – Discard records older than the specified age. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires

    \n
  • \n
  • \n

    \n MaximumRetryAttempts – Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

    \n
  • \n
  • \n

    \n ParallelizationFactor – Process multiple batches from each shard concurrently.

    \n
  • \n
\n

For information about which configuration parameters apply to each event source, see the following topics.

\n ", "smithy.api#http": { "method": "POST", "uri": "/2015-03-31/event-source-mappings", @@ -2471,7 +2524,7 @@ "EventSourceArn": { "target": "com.amazonaws.lambda#Arn", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the event source.

\n
    \n
  • \n

    \n Amazon Kinesis – The ARN of the data stream or a stream consumer.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – The ARN of the stream.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – The ARN of the queue.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – The ARN of the cluster.

    \n
  • \n
  • \n

    \n Amazon MQ – The ARN of the broker.

    \n
  • \n
" + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the event source.

\n
    \n
  • \n

    \n Amazon Kinesis – The ARN of the data stream or a stream consumer.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – The ARN of the stream.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – The ARN of the queue.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – The ARN of the cluster.

    \n
  • \n
  • \n

    \n Amazon MQ – The ARN of the broker.

    \n
  • \n
  • \n

    \n Amazon DocumentDB – The ARN of the DocumentDB change stream.

    \n
  • \n
" } }, "FunctionName": { @@ -2490,7 +2543,7 @@ "BatchSize": { "target": "com.amazonaws.lambda#BatchSize", "traits": { - "smithy.api#documentation": "

The maximum number of records in each batch that Lambda pulls from your stream or queue and sends to your function. Lambda passes all of the records in the batch to the function in a single call, up to the payload limit for synchronous invocation\n (6 MB).

\n
    \n
  • \n

    \n Amazon Kinesis – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – Default 10. For standard queues the max is 10,000. For FIFO queues the max is 10.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Self-managed Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon MQ (ActiveMQ and RabbitMQ) – Default 100. Max 10,000.

    \n
  • \n
" + "smithy.api#documentation": "

The maximum number of records in each batch that Lambda pulls from your stream or queue and sends to your function. Lambda passes all of the records in the batch to the function in a single call, up to the payload limit for synchronous invocation\n (6 MB).

\n
    \n
  • \n

    \n Amazon Kinesis – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – Default 10. For standard queues the max is 10,000. For FIFO queues the max is 10.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Self-managed Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon MQ (ActiveMQ and RabbitMQ) – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n DocumentDB – Default 100. Max 10,000.

    \n
  • \n
" } }, "FilterCriteria": { @@ -2502,19 +2555,19 @@ "MaximumBatchingWindowInSeconds": { "target": "com.amazonaws.lambda#MaximumBatchingWindowInSeconds", "traits": { - "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, and Amazon MQ event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" + "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, Amazon MQ, and DocumentDB event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" } }, "ParallelizationFactor": { "target": "com.amazonaws.lambda#ParallelizationFactor", "traits": { - "smithy.api#documentation": "

(Streams only) The number of batches to process from each shard concurrently.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The number of batches to process from each shard concurrently.

" } }, "StartingPosition": { "target": "com.amazonaws.lambda#EventSourcePosition", "traits": { - "smithy.api#documentation": "

The position in a stream from which to start reading. Required for Amazon Kinesis, Amazon\n DynamoDB, and Amazon MSK Streams sources. AT_TIMESTAMP is supported only for\n Amazon Kinesis streams.

" + "smithy.api#documentation": "

The position in a stream from which to start reading. Required for Amazon Kinesis, Amazon\n DynamoDB, and Amazon MSK Streams sources. AT_TIMESTAMP is supported only for\n Amazon Kinesis streams and Amazon DocumentDB.

" } }, "StartingPositionTimestamp": { @@ -2526,31 +2579,31 @@ "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

(Streams only) An Amazon SQS queue or Amazon SNS topic destination for discarded records.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) A standard Amazon SQS queue or standard Amazon SNS topic destination for discarded records.

" } }, "MaximumRecordAgeInSeconds": { "target": "com.amazonaws.lambda#MaximumRecordAgeInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records older than the specified age. The default value is infinite (-1).

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records older than the specified age. The default value is infinite (-1).

" } }, "BisectBatchOnFunctionError": { "target": "com.amazonaws.lambda#BisectBatchOnFunctionError", "traits": { - "smithy.api#documentation": "

(Streams only) If the function returns an error, split the batch in two and retry.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) If the function returns an error, split the batch in two and retry.

" } }, "MaximumRetryAttempts": { "target": "com.amazonaws.lambda#MaximumRetryAttemptsEventSourceMapping", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

" } }, "TumblingWindowInSeconds": { "target": "com.amazonaws.lambda#TumblingWindowInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) The duration in seconds of a processing window. The range is between 1 second and 900 seconds.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The duration in seconds of a processing window for DynamoDB and Kinesis Streams event sources. A value of 0 seconds indicates no tumbling window.

" } }, "Topics": { @@ -2580,7 +2633,7 @@ "FunctionResponseTypes": { "target": "com.amazonaws.lambda#FunctionResponseTypeList", "traits": { - "smithy.api#documentation": "

(Streams and Amazon SQS) A list of current response type enums applied to the event source mapping.

" + "smithy.api#documentation": "

(Kinesis, DynamoDB Streams, and Amazon SQS) A list of current response type enums applied to the event source mapping.

" } }, "AmazonManagedKafkaEventSourceConfig": { @@ -2872,6 +2925,12 @@ "traits": { "smithy.api#documentation": "

The cross-origin resource sharing (CORS) settings\n for your function URL.

" } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function using the Invoke API operation. Invocation results \n are available when the payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available. Lambda invokes your function using \n the InvokeWithResponseStream API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { @@ -2914,6 +2973,12 @@ "smithy.api#documentation": "

When the function URL was created, in ISO-8601 format (YYYY-MM-DDThh:mm:ss.sTZD).

", "smithy.api#required": {} } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function using the Invoke API operation. Invocation results \n are available when the payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available. Lambda invokes your function using \n the InvokeWithResponseStream API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { @@ -3847,7 +3912,7 @@ "StartingPosition": { "target": "com.amazonaws.lambda#EventSourcePosition", "traits": { - "smithy.api#documentation": "

The position in a stream from which to start reading. Required for Amazon Kinesis, Amazon DynamoDB, and Amazon MSK stream sources. AT_TIMESTAMP is supported only for Amazon Kinesis\n streams.

" + "smithy.api#documentation": "

The position in a stream from which to start reading. Required for Amazon Kinesis, Amazon DynamoDB, and Amazon MSK stream sources. AT_TIMESTAMP is supported only for Amazon Kinesis\n streams and Amazon DocumentDB.

" } }, "StartingPositionTimestamp": { @@ -3865,13 +3930,13 @@ "MaximumBatchingWindowInSeconds": { "target": "com.amazonaws.lambda#MaximumBatchingWindowInSeconds", "traits": { - "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, and Amazon MQ event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" + "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, Amazon MQ, and DocumentDB event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" } }, "ParallelizationFactor": { "target": "com.amazonaws.lambda#ParallelizationFactor", "traits": { - "smithy.api#documentation": "

(Streams only) The number of batches to process concurrently from each shard. The default value is 1.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The number of batches to process concurrently from each shard. The default value is 1.

" } }, "EventSourceArn": { @@ -3919,7 +3984,7 @@ "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

(Streams only) An Amazon SQS queue or Amazon SNS topic destination for discarded records.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) An Amazon SQS queue or Amazon SNS topic destination for discarded records.

" } }, "Topics": { @@ -3949,31 +4014,31 @@ "MaximumRecordAgeInSeconds": { "target": "com.amazonaws.lambda#MaximumRecordAgeInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records older than the specified age. The default value is -1,\nwhich sets the maximum age to infinite. When the value is set to infinite, Lambda never discards old records.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records older than the specified age. The default value is -1,\nwhich sets the maximum age to infinite. When the value is set to infinite, Lambda never discards old records.

\n \n

The minimum value that can be set is 60 seconds.

\n
" } }, "BisectBatchOnFunctionError": { "target": "com.amazonaws.lambda#BisectBatchOnFunctionError", "traits": { - "smithy.api#documentation": "

(Streams only) If the function returns an error, split the batch in two and retry. The default value is false.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) If the function returns an error, split the batch in two and retry. The default value is false.

" } }, "MaximumRetryAttempts": { "target": "com.amazonaws.lambda#MaximumRetryAttemptsEventSourceMapping", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records after the specified number of retries. The default value is -1,\nwhich sets the maximum number of retries to infinite. When MaximumRetryAttempts is infinite, Lambda retries failed records until the record expires in the event source.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records after the specified number of retries. The default value is -1,\nwhich sets the maximum number of retries to infinite. When MaximumRetryAttempts is infinite, Lambda retries failed records until the record expires in the event source.

" } }, "TumblingWindowInSeconds": { "target": "com.amazonaws.lambda#TumblingWindowInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) The duration in seconds of a processing window. The range is 1–900 seconds.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The duration in seconds of a processing window for DynamoDB and Kinesis Streams event sources. A value of 0 seconds indicates no tumbling window.

" } }, "FunctionResponseTypes": { "target": "com.amazonaws.lambda#FunctionResponseTypeList", "traits": { - "smithy.api#documentation": "

(Streams and Amazon SQS) A list of current response type enums applied to the event source mapping.

" + "smithy.api#documentation": "

(Kinesis, DynamoDB Streams, and Amazon SQS) A list of current response type enums applied to the event source mapping.

" } }, "AmazonManagedKafkaEventSourceConfig": { @@ -4239,7 +4304,7 @@ "Runtime": { "target": "com.amazonaws.lambda#Runtime", "traits": { - "smithy.api#documentation": "

The runtime environment for the Lambda function.

" + "smithy.api#documentation": "

The identifier of the function's runtime. Runtime is required if the deployment package is a .zip file archive.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

" } }, "Role": { @@ -4470,7 +4535,7 @@ "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of an SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of an SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" + "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of a standard SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of a standard SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" } } } @@ -4589,6 +4654,12 @@ "smithy.api#documentation": "

The type of authentication that your function URL uses. Set to AWS_IAM if you want to restrict access to authenticated\n users only. Set to NONE if you want to bypass IAM authentication to create a public endpoint. For more information,\n see Security and auth model for Lambda function URLs.

", "smithy.api#required": {} } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function\n using the Invoke API operation. Invocation results are available when the\n payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available.\n Lambda invokes your function using the InvokeWithResponseStream\n API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { @@ -5457,6 +5528,12 @@ "smithy.api#documentation": "

When the function URL configuration was last updated, in ISO-8601 format (YYYY-MM-DDThh:mm:ss.sTZD).

", "smithy.api#required": {} } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function using the Invoke API operation. Invocation results \n are available when the payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available. Lambda invokes your function using \n the InvokeWithResponseStream API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { @@ -6494,6 +6571,279 @@ "smithy.api#output": {} } }, + "com.amazonaws.lambda#InvokeMode": { + "type": "enum", + "members": { + "BUFFERED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BUFFERED" + } + }, + "RESPONSE_STREAM": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RESPONSE_STREAM" + } + } + } + }, + "com.amazonaws.lambda#InvokeResponseStreamUpdate": { + "type": "structure", + "members": { + "Payload": { + "target": "com.amazonaws.lambda#Blob", + "traits": { + "smithy.api#documentation": "

Data returned by your Lambda function.

", + "smithy.api#eventPayload": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A chunk of the streamed response payload.

" + } + }, + "com.amazonaws.lambda#InvokeWithResponseStream": { + "type": "operation", + "input": { + "target": "com.amazonaws.lambda#InvokeWithResponseStreamRequest" + }, + "output": { + "target": "com.amazonaws.lambda#InvokeWithResponseStreamResponse" + }, + "errors": [ + { + "target": "com.amazonaws.lambda#EC2AccessDeniedException" + }, + { + "target": "com.amazonaws.lambda#EC2ThrottledException" + }, + { + "target": "com.amazonaws.lambda#EC2UnexpectedException" + }, + { + "target": "com.amazonaws.lambda#EFSIOException" + }, + { + "target": "com.amazonaws.lambda#EFSMountConnectivityException" + }, + { + "target": "com.amazonaws.lambda#EFSMountFailureException" + }, + { + "target": "com.amazonaws.lambda#EFSMountTimeoutException" + }, + { + "target": "com.amazonaws.lambda#ENILimitReachedException" + }, + { + "target": "com.amazonaws.lambda#InvalidParameterValueException" + }, + { + "target": "com.amazonaws.lambda#InvalidRequestContentException" + }, + { + "target": "com.amazonaws.lambda#InvalidRuntimeException" + }, + { + "target": "com.amazonaws.lambda#InvalidSecurityGroupIDException" + }, + { + "target": "com.amazonaws.lambda#InvalidSubnetIDException" + }, + { + "target": "com.amazonaws.lambda#InvalidZipFileException" + }, + { + "target": "com.amazonaws.lambda#KMSAccessDeniedException" + }, + { + "target": "com.amazonaws.lambda#KMSDisabledException" + }, + { + "target": "com.amazonaws.lambda#KMSInvalidStateException" + }, + { + "target": "com.amazonaws.lambda#KMSNotFoundException" + }, + { + "target": "com.amazonaws.lambda#RequestTooLargeException" + }, + { + "target": "com.amazonaws.lambda#ResourceConflictException" + }, + { + "target": "com.amazonaws.lambda#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.lambda#ResourceNotReadyException" + }, + { + "target": "com.amazonaws.lambda#ServiceException" + }, + { + "target": "com.amazonaws.lambda#SnapStartException" + }, + { + "target": "com.amazonaws.lambda#SnapStartNotReadyException" + }, + { + "target": "com.amazonaws.lambda#SnapStartTimeoutException" + }, + { + "target": "com.amazonaws.lambda#SubnetIPAddressLimitReachedException" + }, + { + "target": "com.amazonaws.lambda#TooManyRequestsException" + }, + { + "target": "com.amazonaws.lambda#UnsupportedMediaTypeException" + } + ], + "traits": { + "smithy.api#documentation": "

Configure your Lambda functions to stream response payloads back to clients. For more information, see Configuring a Lambda function to stream responses.

\n

This operation requires permission for the lambda:InvokeFunction action. For details on how to set up\n permissions for cross-account invocations, see Granting function\n access to other accounts.

", + "smithy.api#http": { + "method": "POST", + "uri": "/2021-11-15/functions/{FunctionName}/response-streaming-invocations", + "code": 200 + } + } + }, + "com.amazonaws.lambda#InvokeWithResponseStreamCompleteEvent": { + "type": "structure", + "members": { + "ErrorCode": { + "target": "com.amazonaws.lambda#String", + "traits": { + "smithy.api#documentation": "

An error code.

" + } + }, + "ErrorDetails": { + "target": "com.amazonaws.lambda#String", + "traits": { + "smithy.api#documentation": "

The details of any returned error.

" + } + }, + "LogResult": { + "target": "com.amazonaws.lambda#String", + "traits": { + "smithy.api#documentation": "

The last 4 KB of the execution log, which is base64-encoded.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A response confirming that the event stream is complete.

" + } + }, + "com.amazonaws.lambda#InvokeWithResponseStreamRequest": { + "type": "structure", + "members": { + "FunctionName": { + "target": "com.amazonaws.lambda#NamespacedFunctionName", + "traits": { + "smithy.api#documentation": "

The name of the Lambda function.

\n

\n Name formats\n

\n
    \n
  • \n

    \n Function namemy-function.

    \n
  • \n
  • \n

    \n Function ARNarn:aws:lambda:us-west-2:123456789012:function:my-function.

    \n
  • \n
  • \n

    \n Partial ARN123456789012:function:my-function.

    \n
  • \n
\n

The length constraint applies only to the full ARN. If you specify only the function name, it is limited to 64\n characters in length.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "InvocationType": { + "target": "com.amazonaws.lambda#ResponseStreamingInvocationType", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n RequestResponse (default) – Invoke the function synchronously. Keep the\n connection open until the function returns a response or times out. The API operation\n response includes the function response and additional data.

    \n
  • \n
  • \n

    \n DryRun – Validate parameter values and verify that the IAM user or role has permission to invoke\n the function.

    \n
  • \n
", + "smithy.api#httpHeader": "X-Amz-Invocation-Type" + } + }, + "LogType": { + "target": "com.amazonaws.lambda#LogType", + "traits": { + "smithy.api#documentation": "

Set to Tail to include the execution log in the response. Applies to synchronously invoked functions only.

", + "smithy.api#httpHeader": "X-Amz-Log-Type" + } + }, + "ClientContext": { + "target": "com.amazonaws.lambda#String", + "traits": { + "smithy.api#documentation": "

Up to 3,583 bytes of base64-encoded data about the invoking client to pass to the function in the context\n object.

", + "smithy.api#httpHeader": "X-Amz-Client-Context" + } + }, + "Qualifier": { + "target": "com.amazonaws.lambda#Qualifier", + "traits": { + "smithy.api#documentation": "

The alias name.

", + "smithy.api#httpQuery": "Qualifier" + } + }, + "Payload": { + "target": "com.amazonaws.lambda#Blob", + "traits": { + "smithy.api#documentation": "

The JSON that you want to provide to your Lambda function as input.

\n

You can enter the JSON directly. For example, --payload '{ \"key\": \"value\" }'. You can also\n specify a file path. For example, --payload file://payload.json.

", + "smithy.api#httpPayload": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.lambda#InvokeWithResponseStreamResponse": { + "type": "structure", + "members": { + "StatusCode": { + "target": "com.amazonaws.lambda#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

For a successful request, the HTTP status code is in the 200 range. For the\n RequestResponse invocation type, this status code is 200. For the DryRun\n invocation type, this status code is 204.

", + "smithy.api#httpResponseCode": {} + } + }, + "ExecutedVersion": { + "target": "com.amazonaws.lambda#Version", + "traits": { + "smithy.api#documentation": "

The version of the function that executed. When you invoke a function with an alias, this\n indicates which version the alias resolved to.

", + "smithy.api#httpHeader": "X-Amz-Executed-Version" + } + }, + "EventStream": { + "target": "com.amazonaws.lambda#InvokeWithResponseStreamResponseEvent", + "traits": { + "smithy.api#documentation": "

The stream of response payloads.

", + "smithy.api#httpPayload": {} + } + }, + "ResponseStreamContentType": { + "target": "com.amazonaws.lambda#String", + "traits": { + "smithy.api#documentation": "

The type of data the stream is returning.

", + "smithy.api#httpHeader": "Content-Type" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.lambda#InvokeWithResponseStreamResponseEvent": { + "type": "union", + "members": { + "PayloadChunk": { + "target": "com.amazonaws.lambda#InvokeResponseStreamUpdate", + "traits": { + "smithy.api#documentation": "

A chunk of the streamed response payload.

" + } + }, + "InvokeComplete": { + "target": "com.amazonaws.lambda#InvokeWithResponseStreamCompleteEvent", + "traits": { + "smithy.api#documentation": "

An object that's returned when the stream has ended and all the payload chunks have been\n returned.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

An object that includes a chunk of the response payload. When the stream has ended, Lambda includes a InvokeComplete object.

", + "smithy.api#streaming": {} + } + }, "com.amazonaws.lambda#KMSAccessDeniedException": { "type": "structure", "members": { @@ -7197,7 +7547,7 @@ "EventSourceArn": { "target": "com.amazonaws.lambda#Arn", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the event source.

\n
    \n
  • \n

    \n Amazon Kinesis – The ARN of the data stream or a stream consumer.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – The ARN of the stream.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – The ARN of the queue.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – The ARN of the cluster.

    \n
  • \n
  • \n

    \n Amazon MQ – The ARN of the broker.

    \n
  • \n
", + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the event source.

\n
    \n
  • \n

    \n Amazon Kinesis – The ARN of the data stream or a stream consumer.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – The ARN of the stream.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – The ARN of the queue.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – The ARN of the cluster.

    \n
  • \n
  • \n

    \n Amazon MQ – The ARN of the broker.

    \n
  • \n
  • \n

    \n Amazon DocumentDB – The ARN of the DocumentDB change stream.

    \n
  • \n
", "smithy.api#httpQuery": "EventSourceArn" } }, @@ -8888,7 +9238,7 @@ "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of an SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of an SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" + "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of a standard SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of a standard SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" } } }, @@ -9387,6 +9737,23 @@ "smithy.api#httpError": 502 } }, + "com.amazonaws.lambda#ResponseStreamingInvocationType": { + "type": "enum", + "members": { + "RequestResponse": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RequestResponse" + } + }, + "DryRun": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DryRun" + } + } + } + }, "com.amazonaws.lambda#RoleArn": { "type": "string", "traits": { @@ -9563,6 +9930,12 @@ "traits": { "smithy.api#enumValue": "nodejs18.x" } + }, + "python310": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "python3.10" + } } } }, @@ -10698,7 +11071,7 @@ } ], "traits": { - "smithy.api#documentation": "

Updates an event source mapping. You can change the function that Lambda invokes, or pause\n invocation and resume later from the same location.

\n

For details about how to configure different event sources, see the following topics.

\n \n

The following error handling options are available only for stream sources (DynamoDB and Kinesis):

\n
    \n
  • \n

    \n BisectBatchOnFunctionError – If the function returns an error, split the batch in two and retry.

    \n
  • \n
  • \n

    \n DestinationConfig – Send discarded records to an Amazon SQS queue or Amazon SNS topic.

    \n
  • \n
  • \n

    \n MaximumRecordAgeInSeconds – Discard records older than the specified age. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires

    \n
  • \n
  • \n

    \n MaximumRetryAttempts – Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

    \n
  • \n
  • \n

    \n ParallelizationFactor – Process multiple batches from each shard concurrently.

    \n
  • \n
\n

For information about which configuration parameters apply to each event source, see the following topics.

\n ", + "smithy.api#documentation": "

Updates an event source mapping. You can change the function that Lambda invokes, or pause\n invocation and resume later from the same location.

\n

For details about how to configure different event sources, see the following topics.

\n \n

The following error handling options are available only for stream sources (DynamoDB and Kinesis):

\n
    \n
  • \n

    \n BisectBatchOnFunctionError – If the function returns an error, split the batch in two and retry.

    \n
  • \n
  • \n

    \n DestinationConfig – Send discarded records to an Amazon SQS queue or Amazon SNS topic.

    \n
  • \n
  • \n

    \n MaximumRecordAgeInSeconds – Discard records older than the specified age. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires

    \n
  • \n
  • \n

    \n MaximumRetryAttempts – Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

    \n
  • \n
  • \n

    \n ParallelizationFactor – Process multiple batches from each shard concurrently.

    \n
  • \n
\n

For information about which configuration parameters apply to each event source, see the following topics.

\n ", "smithy.api#http": { "method": "PUT", "uri": "/2015-03-31/event-source-mappings/{UUID}", @@ -10732,7 +11105,7 @@ "BatchSize": { "target": "com.amazonaws.lambda#BatchSize", "traits": { - "smithy.api#documentation": "

The maximum number of records in each batch that Lambda pulls from your stream or queue and sends to your function. Lambda passes all of the records in the batch to the function in a single call, up to the payload limit for synchronous invocation\n (6 MB).

\n
    \n
  • \n

    \n Amazon Kinesis – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – Default 10. For standard queues the max is 10,000. For FIFO queues the max is 10.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Self-managed Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon MQ (ActiveMQ and RabbitMQ) – Default 100. Max 10,000.

    \n
  • \n
" + "smithy.api#documentation": "

The maximum number of records in each batch that Lambda pulls from your stream or queue and sends to your function. Lambda passes all of the records in the batch to the function in a single call, up to the payload limit for synchronous invocation\n (6 MB).

\n
    \n
  • \n

    \n Amazon Kinesis – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon DynamoDB Streams – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon Simple Queue Service – Default 10. For standard queues the max is 10,000. For FIFO queues the max is 10.

    \n
  • \n
  • \n

    \n Amazon Managed Streaming for Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Self-managed Apache Kafka – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n Amazon MQ (ActiveMQ and RabbitMQ) – Default 100. Max 10,000.

    \n
  • \n
  • \n

    \n DocumentDB – Default 100. Max 10,000.

    \n
  • \n
" } }, "FilterCriteria": { @@ -10744,37 +11117,37 @@ "MaximumBatchingWindowInSeconds": { "target": "com.amazonaws.lambda#MaximumBatchingWindowInSeconds", "traits": { - "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, and Amazon MQ event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" + "smithy.api#documentation": "

The maximum amount of time, in seconds, that Lambda spends gathering records before invoking the function.\n You can configure MaximumBatchingWindowInSeconds to any value from 0 seconds to 300 seconds in increments of seconds.

\n

For streams and Amazon SQS event sources, the default batching window is 0 seconds. For Amazon MSK, Self-managed Apache Kafka, Amazon MQ, and DocumentDB event sources, the default\n batching window is 500 ms. Note that because you can only change MaximumBatchingWindowInSeconds in increments of seconds, you cannot revert back to the 500 ms default batching window after you have changed it.\n To restore the default batching window, you must create a new event source mapping.

\n

Related setting: For streams and Amazon SQS event sources, when you set BatchSize to a value greater than 10, you must set MaximumBatchingWindowInSeconds to at least 1.

" } }, "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

(Streams only) An Amazon SQS queue or Amazon SNS topic destination for discarded records.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) A standard Amazon SQS queue or standard Amazon SNS topic destination for discarded records.

" } }, "MaximumRecordAgeInSeconds": { "target": "com.amazonaws.lambda#MaximumRecordAgeInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records older than the specified age. The default value is infinite (-1).

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records older than the specified age. The default value is infinite (-1).

" } }, "BisectBatchOnFunctionError": { "target": "com.amazonaws.lambda#BisectBatchOnFunctionError", "traits": { - "smithy.api#documentation": "

(Streams only) If the function returns an error, split the batch in two and retry.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) If the function returns an error, split the batch in two and retry.

" } }, "MaximumRetryAttempts": { "target": "com.amazonaws.lambda#MaximumRetryAttemptsEventSourceMapping", "traits": { - "smithy.api#documentation": "

(Streams only) Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records after the specified number of retries. The default value is infinite (-1). When set to infinite (-1), failed records are retried until the record expires.

" } }, "ParallelizationFactor": { "target": "com.amazonaws.lambda#ParallelizationFactor", "traits": { - "smithy.api#documentation": "

(Streams only) The number of batches to process from each shard concurrently.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The number of batches to process from each shard concurrently.

" } }, "SourceAccessConfigurations": { @@ -10786,13 +11159,13 @@ "TumblingWindowInSeconds": { "target": "com.amazonaws.lambda#TumblingWindowInSeconds", "traits": { - "smithy.api#documentation": "

(Streams only) The duration in seconds of a processing window. The range is between 1 second and 900 seconds.

" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) The duration in seconds of a processing window for DynamoDB and Kinesis Streams event sources. A value of 0 seconds indicates no tumbling window.

" } }, "FunctionResponseTypes": { "target": "com.amazonaws.lambda#FunctionResponseTypeList", "traits": { - "smithy.api#documentation": "

(Streams and Amazon SQS) A list of current response type enums applied to the event source mapping.

" + "smithy.api#documentation": "

(Kinesis, DynamoDB Streams, and Amazon SQS) A list of current response type enums applied to the event source mapping.

" } }, "ScalingConfig": { @@ -11164,7 +11537,7 @@ "DestinationConfig": { "target": "com.amazonaws.lambda#DestinationConfig", "traits": { - "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of an SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of an SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" + "smithy.api#documentation": "

A destination for events after they have been sent to a function for processing.

\n

\n Destinations\n

\n
    \n
  • \n

    \n Function - The Amazon Resource Name (ARN) of a Lambda function.

    \n
  • \n
  • \n

    \n Queue - The ARN of a standard SQS queue.

    \n
  • \n
  • \n

    \n Topic - The ARN of a standard SNS topic.

    \n
  • \n
  • \n

    \n Event Bus - The ARN of an Amazon EventBridge event bus.

    \n
  • \n
" } } }, @@ -11235,6 +11608,12 @@ "traits": { "smithy.api#documentation": "

The cross-origin resource sharing (CORS) settings\n for your function URL.

" } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function using the Invoke API operation. Invocation results \n are available when the payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available. Lambda invokes your function using \n the InvokeWithResponseStream API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { @@ -11284,6 +11663,12 @@ "smithy.api#documentation": "

When the function URL configuration was last updated, in ISO-8601 format (YYYY-MM-DDThh:mm:ss.sTZD).

", "smithy.api#required": {} } + }, + "InvokeMode": { + "target": "com.amazonaws.lambda#InvokeMode", + "traits": { + "smithy.api#documentation": "

Use one of the following options:

\n
    \n
  • \n

    \n BUFFERED – This is the default option. Lambda invokes your function using the Invoke API operation. Invocation results \n are available when the payload is complete. The maximum payload size is 6 MB.

    \n
  • \n
  • \n

    \n RESPONSE_STREAM – Your function streams payload results as they become available. Lambda invokes your function using \n the InvokeWithResponseStream API operation. The maximum response payload size is 20 MB, however, you can request a quota increase.

    \n
  • \n
" + } } }, "traits": { diff --git a/aws/sdk/aws-models/polly.json b/aws/sdk/aws-models/polly.json index 70db8b9661..70ddbca064 100644 --- a/aws/sdk/aws-models/polly.json +++ b/aws/sdk/aws-models/polly.json @@ -1506,8 +1506,8 @@ }, "params": { "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1519,8 +1519,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1532,8 +1532,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1545,8 +1545,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1558,8 +1558,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1571,8 +1571,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1584,8 +1584,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1597,8 +1597,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1610,8 +1610,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1623,8 +1623,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1636,8 +1636,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1649,8 +1649,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1662,8 +1662,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1675,8 +1675,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1688,8 +1688,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1701,8 +1701,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1714,8 +1714,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1727,8 +1727,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1740,8 +1740,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1753,8 +1753,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1766,8 +1766,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1779,8 +1779,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1792,8 +1792,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1805,8 +1805,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1818,8 +1818,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1831,8 +1831,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1844,8 +1844,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1857,8 +1857,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1870,8 +1870,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1883,8 +1883,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1896,8 +1896,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1909,8 +1909,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1922,8 +1922,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1935,8 +1935,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1948,8 +1948,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1961,8 +1961,19 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1974,8 +1985,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1987,8 +2009,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2000,8 +2033,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2013,8 +2057,8 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2026,8 +2070,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2039,8 +2083,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2051,8 +2095,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2063,10 +2107,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/aws-models/qldb-session.json b/aws/sdk/aws-models/qldb-session.json index 37782b8b96..b12ca95320 100644 --- a/aws/sdk/aws-models/qldb-session.json +++ b/aws/sdk/aws-models/qldb-session.json @@ -772,8 +772,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -785,8 +785,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -798,8 +798,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -811,8 +811,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -824,8 +824,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -837,8 +837,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -850,8 +850,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -863,8 +863,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -876,8 +876,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -889,8 +889,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -902,8 +902,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -915,8 +915,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -928,8 +928,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -941,8 +941,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -954,8 +954,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -967,8 +967,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -980,8 +980,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -993,8 +993,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1006,8 +1006,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1019,8 +1019,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1032,8 +1032,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1045,8 +1045,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1058,8 +1058,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1071,8 +1071,19 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1084,8 +1095,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1097,8 +1119,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1110,8 +1143,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1123,8 +1167,8 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1136,8 +1180,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1149,8 +1193,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1161,8 +1205,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1173,10 +1217,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/aws-models/route53.json b/aws/sdk/aws-models/route53.json index 8c5011b2f3..72d97074e3 100644 --- a/aws/sdk/aws-models/route53.json +++ b/aws/sdk/aws-models/route53.json @@ -414,216 +414,91 @@ }, "aws" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] + "ref": "UseFIPS" }, + false + ] + }, + { + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://route53.amazonaws.com", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-east-1" } ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "booleanEquals", + "fn": "getAttr", "argv": [ { - "ref": "UseFIPS" + "ref": "PartitionResult" }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } + "name" ] }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + "aws" ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } + true ] }, { - "conditions": [], - "endpoint": { - "url": "https://route53.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://route53-fips.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -641,208 +516,40 @@ }, "aws-cn" ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.api.amazonwebservices.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - }, - { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" - } - ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.amazonaws.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + false ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53.{Region}.api.amazonwebservices.com.cn", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://route53.amazonaws.com.cn", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" + "name": "sigv4", + "signingName": "route53", + "signingRegion": "cn-northwest-1" } ] }, - { - "conditions": [], - "endpoint": { - "url": "https://route53.amazonaws.com.cn", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "cn-northwest-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } - ] + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -860,216 +567,91 @@ }, "aws-us-gov" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] + "ref": "UseFIPS" }, + false + ] + }, + { + "fn": "booleanEquals", + "argv": [ { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseDualStack" }, + false + ] + } + ], + "endpoint": { + "url": "https://route53.us-gov.amazonaws.com", + "properties": { + "authSchemes": [ { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-gov-west-1" } ] }, + "headers": {} + }, + "type": "endpoint" + }, + { + "conditions": [ { - "conditions": [ + "fn": "stringEquals", + "argv": [ { - "fn": "booleanEquals", + "fn": "getAttr", "argv": [ { - "ref": "UseFIPS" + "ref": "PartitionResult" }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - } + "name" ] }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + "aws-us-gov" ] }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53.{Region}.api.aws", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } + true ] }, { - "conditions": [], - "endpoint": { - "url": "https://route53.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-gov-west-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://route53.us-gov.amazonaws.com", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-gov-west-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1087,80 +669,40 @@ }, "aws-iso" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.c2s.ic.gov", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + false ] }, { - "conditions": [], - "endpoint": { - "url": "https://route53.c2s.ic.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-iso-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://route53.c2s.ic.gov", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-iso-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1178,80 +720,40 @@ }, "aws-iso-b" ] - } - ], - "type": "tree", - "rules": [ + }, { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ + "fn": "booleanEquals", + "argv": [ { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://route53-fips.{Region}.sc2s.sgov.gov", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] + "ref": "UseFIPS" }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } + false ] }, { - "conditions": [], - "endpoint": { - "url": "https://route53.sc2s.sgov.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-isob-east-1" - } - ] + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" }, - "headers": {} - }, - "type": "endpoint" + false + ] } - ] + ], + "endpoint": { + "url": "https://route53.sc2s.sgov.gov", + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "route53", + "signingRegion": "us-isob-east-1" + } + ] + }, + "headers": {} + }, + "type": "endpoint" }, { "conditions": [ @@ -1373,60 +875,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://route53-fips.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-us-gov-global" - ] - } - ], - "endpoint": { - "url": "https://route53.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -1509,141 +957,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-global" - ] - } - ], - "endpoint": { - "url": "https://route53.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-cn-global" - ] - } - ], - "endpoint": { - "url": "https://route53.amazonaws.com.cn", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "cn-northwest-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-us-gov-global" - ] - } - ], - "endpoint": { - "url": "https://route53.us-gov.amazonaws.com", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-gov-west-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-iso-global" - ] - } - ], - "endpoint": { - "url": "https://route53.c2s.ic.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-iso-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "aws-iso-b-global" - ] - } - ], - "endpoint": { - "url": "https://route53.sc2s.sgov.gov", - "properties": { - "authSchemes": [ - { - "name": "sigv4", - "signingName": "route53", - "signingRegion": "us-isob-east-1" - } - ] - }, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -1688,8 +1001,8 @@ }, "params": { "Region": "aws-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1710,8 +1023,8 @@ }, "params": { "Region": "aws-global", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1723,8 +1036,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1745,8 +1058,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1758,8 +1071,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1780,8 +1093,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1802,8 +1115,8 @@ }, "params": { "Region": "aws-cn-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1815,8 +1128,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1828,8 +1141,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1841,8 +1154,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1863,8 +1176,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1885,8 +1198,8 @@ }, "params": { "Region": "aws-us-gov-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1907,8 +1220,8 @@ }, "params": { "Region": "aws-us-gov-global", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1920,8 +1233,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1942,8 +1255,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1955,8 +1268,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1977,8 +1290,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1999,8 +1312,19 @@ }, "params": { "Region": "aws-iso-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2012,8 +1336,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2034,8 +1369,8 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2056,8 +1391,19 @@ }, "params": { "Region": "aws-iso-b-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2069,8 +1415,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2091,8 +1448,8 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2104,8 +1461,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2117,8 +1474,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2129,8 +1486,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2141,10 +1498,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/aws-models/s3.json b/aws/sdk/aws-models/s3.json index 57c5645bde..f6a09bc273 100644 --- a/aws/sdk/aws-models/s3.json +++ b/aws/sdk/aws-models/s3.json @@ -22652,92 +22652,173 @@ } }, "com.amazonaws.s3#Event": { - "type": "string", - "traits": { - "smithy.api#documentation": "

The bucket event for which to send notifications.

", - "smithy.api#enum": [ - { - "value": "s3:ReducedRedundancyLostObject" - }, - { - "value": "s3:ObjectCreated:*" - }, - { - "value": "s3:ObjectCreated:Put" - }, - { - "value": "s3:ObjectCreated:Post" - }, - { - "value": "s3:ObjectCreated:Copy" - }, - { - "value": "s3:ObjectCreated:CompleteMultipartUpload" - }, - { - "value": "s3:ObjectRemoved:*" - }, - { - "value": "s3:ObjectRemoved:Delete" - }, - { - "value": "s3:ObjectRemoved:DeleteMarkerCreated" - }, - { - "value": "s3:ObjectRestore:*" - }, - { - "value": "s3:ObjectRestore:Post" - }, - { - "value": "s3:ObjectRestore:Completed" - }, - { - "value": "s3:Replication:*" - }, - { - "value": "s3:Replication:OperationFailedReplication" - }, - { - "value": "s3:Replication:OperationNotTracked" - }, - { - "value": "s3:Replication:OperationMissedThreshold" - }, - { - "value": "s3:Replication:OperationReplicatedAfterThreshold" - }, - { - "value": "s3:ObjectRestore:Delete" - }, - { - "value": "s3:LifecycleTransition" - }, - { - "value": "s3:IntelligentTiering" - }, - { - "value": "s3:ObjectAcl:Put" - }, - { - "value": "s3:LifecycleExpiration:*" - }, - { - "value": "s3:LifecycleExpiration:Delete" - }, - { - "value": "s3:LifecycleExpiration:DeleteMarkerCreated" - }, - { - "value": "s3:ObjectTagging:*" - }, - { - "value": "s3:ObjectTagging:Put" - }, - { - "value": "s3:ObjectTagging:Delete" + "type": "enum", + "members": { + "s3_ReducedRedundancyLostObject": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ReducedRedundancyLostObject" + } + }, + "s3_ObjectCreated_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectCreated:*" + } + }, + "s3_ObjectCreated_Put": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectCreated:Put" + } + }, + "s3_ObjectCreated_Post": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectCreated:Post" + } + }, + "s3_ObjectCreated_Copy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectCreated:Copy" + } + }, + "s3_ObjectCreated_CompleteMultipartUpload": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectCreated:CompleteMultipartUpload" + } + }, + "s3_ObjectRemoved_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRemoved:*" } - ] + }, + "s3_ObjectRemoved_Delete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRemoved:Delete" + } + }, + "s3_ObjectRemoved_DeleteMarkerCreated": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRemoved:DeleteMarkerCreated" + } + }, + "s3_ObjectRestore_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRestore:*" + } + }, + "s3_ObjectRestore_Post": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRestore:Post" + } + }, + "s3_ObjectRestore_Completed": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRestore:Completed" + } + }, + "s3_Replication_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:Replication:*" + } + }, + "s3_Replication_OperationFailedReplication": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:Replication:OperationFailedReplication" + } + }, + "s3_Replication_OperationNotTracked": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:Replication:OperationNotTracked" + } + }, + "s3_Replication_OperationMissedThreshold": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:Replication:OperationMissedThreshold" + } + }, + "s3_Replication_OperationReplicatedAfterThreshold": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:Replication:OperationReplicatedAfterThreshold" + } + }, + "s3_ObjectRestore_Delete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectRestore:Delete" + } + }, + "s3_LifecycleTransition": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:LifecycleTransition" + } + }, + "s3_IntelligentTiering": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:IntelligentTiering" + } + }, + "s3_ObjectAcl_Put": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectAcl:Put" + } + }, + "s3_LifecycleExpiration_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:LifecycleExpiration:*" + } + }, + "s3_LifecycleExpiration_Delete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:LifecycleExpiration:Delete" + } + }, + "s3_LifecycleExpiration_DeleteMarkerCreated": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:LifecycleExpiration:DeleteMarkerCreated" + } + }, + "s3_ObjectTagging_": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectTagging:*" + } + }, + "s3_ObjectTagging_Put": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectTagging:Put" + } + }, + "s3_ObjectTagging_Delete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "s3:ObjectTagging:Delete" + } + } + }, + "traits": { + "smithy.api#documentation": "

The bucket event for which to send notifications.

" } }, "com.amazonaws.s3#EventBridgeConfiguration": { @@ -29111,6 +29192,12 @@ "traits": { "smithy.api#enumValue": "GLACIER_IR" } + }, + "SNOW": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SNOW" + } } } }, @@ -33176,6 +33263,12 @@ "traits": { "smithy.api#enumValue": "GLACIER_IR" } + }, + "SNOW": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SNOW" + } } } }, diff --git a/aws/sdk/aws-models/s3control.json b/aws/sdk/aws-models/s3control.json index c47218c753..2944356a96 100644 --- a/aws/sdk/aws-models/s3control.json +++ b/aws/sdk/aws-models/s3control.json @@ -332,6 +332,120 @@ "conditions": [], "type": "tree", "rules": [ + { + "conditions": [ + { + "fn": "stringEquals", + "argv": [ + { + "ref": "Region" + }, + "snow" + ] + }, + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + }, + { + "fn": "parseURL", + "argv": [ + { + "ref": "Endpoint" + } + ], + "assign": "url" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "Region" + } + ], + "assign": "partitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "error": "S3 Snow does not support Dual-stack", + "type": "error" + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "error": "S3 Snow does not support FIPS", + "type": "error" + }, + { + "conditions": [], + "endpoint": { + "url": "{url#scheme}://{url#authority}", + "properties": { + "authSchemes": [ + { + "disableDoubleEncoding": true, + "name": "sigv4", + "signingName": "s3", + "signingRegion": "{Region}" + } + ] + }, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + } + ] + }, + { + "conditions": [], + "error": "A valid partition could not be determined", + "type": "error" + } + ] + }, { "conditions": [ { @@ -6060,6 +6174,133 @@ "UseDualStack": false, "UseFIPS": false } + }, + { + "documentation": "S3 Snow Control with bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://10.0.1.12:433" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "https://10.0.1.12:433", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "S3 Snow Control without bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://10.0.1.12:433" + } + }, + "params": { + "Region": "snow", + "Endpoint": "https://10.0.1.12:433", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "S3 Snow Control with bucket and without port", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://10.0.1.12" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "https://10.0.1.12", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "S3 Snow Control with bucket and with DNS", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://s3snow.com" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "http://s3snow.com", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "S3 Snow Control with FIPS enabled", + "expect": { + "error": "S3 Snow does not support FIPS" + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "https://10.0.1.12:433", + "UseFIPS": true, + "UseDualStack": false, + "Accelerate": false + } + }, + { + "documentation": "S3 Snow Control with Dual-stack enabled", + "expect": { + "error": "S3 Snow does not support Dual-stack" + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "https://10.0.1.12:433", + "UseFIPS": false, + "UseDualStack": true, + "Accelerate": false + } } ], "version": "1.0" @@ -6810,6 +7051,12 @@ "traits": { "smithy.api#documentation": "

Specifies the ARN for the Object Lambda Access Point.

" } + }, + "Alias": { + "target": "com.amazonaws.s3control#ObjectLambdaAccessPointAlias", + "traits": { + "smithy.api#documentation": "

The alias of the Object Lambda Access Point.

" + } } } }, @@ -7651,7 +7898,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "\n

This operation deletes an Amazon S3 on Outposts bucket's replication configuration. To\n delete an S3 bucket's replication configuration, see DeleteBucketReplication in the Amazon S3 API Reference.

\n
\n

Deletes the replication configuration from the specified S3 on Outposts bucket.

\n

To use this operation, you must have permissions to perform the\n s3-outposts:PutReplicationConfiguration action. The Outposts bucket owner\n has this permission by default and can grant it to others. For more information about\n permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts buckets in the Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

For information about S3 replication on Outposts configuration, see Replicating objects for Amazon Web Services Outposts in the\n Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketReplication:

\n ", + "smithy.api#documentation": "\n

This operation deletes an Amazon S3 on Outposts bucket's replication configuration. To\n delete an S3 bucket's replication configuration, see DeleteBucketReplication in the Amazon S3 API Reference.

\n
\n

Deletes the replication configuration from the specified S3 on Outposts bucket.

\n

To use this operation, you must have permissions to perform the\n s3-outposts:PutReplicationConfiguration action. The Outposts bucket owner\n has this permission by default and can grant it to others. For more information about\n permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts buckets in the Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

For information about S3 replication on Outposts configuration, see Replicating objects for S3 on Outposts in the\n Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketReplication:

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -8679,6 +8926,12 @@ "traits": { "smithy.api#documentation": "

The date and time when the specified Object Lambda Access Point was created.

" } + }, + "Alias": { + "target": "com.amazonaws.s3control#ObjectLambdaAccessPointAlias", + "traits": { + "smithy.api#documentation": "

The alias of the Object Lambda Access Point.

" + } } } }, @@ -9202,7 +9455,7 @@ "target": "com.amazonaws.s3control#GetBucketReplicationResult" }, "traits": { - "smithy.api#documentation": "\n

This operation gets an Amazon S3 on Outposts bucket's replication configuration. To get an\n S3 bucket's replication configuration, see GetBucketReplication\n in the Amazon S3 API Reference.

\n
\n

Returns the replication configuration of an S3 on Outposts bucket. For more information\n about S3 on Outposts, see Using Amazon S3 on Outposts in the\n Amazon S3 User Guide. For information about S3 replication on Outposts\n configuration, see Replicating objects for Amazon Web Services\n Outposts in the Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

This action requires permissions for the\n s3-outposts:GetReplicationConfiguration action. The Outposts bucket owner\n has this permission by default and can grant it to others. For more information about\n permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts bucket in the Amazon S3 User Guide.

\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

If you include the Filter element in a replication configuration, you must\n also include the DeleteMarkerReplication, Status, and\n Priority elements. The response also returns those elements.

\n

For information about S3 on Outposts replication failure reasons, see Replication failure reasons in the Amazon S3 User Guide.

\n

The following operations are related to GetBucketReplication:

\n ", + "smithy.api#documentation": "\n

This operation gets an Amazon S3 on Outposts bucket's replication configuration. To get an\n S3 bucket's replication configuration, see GetBucketReplication\n in the Amazon S3 API Reference.

\n
\n

Returns the replication configuration of an S3 on Outposts bucket. For more information\n about S3 on Outposts, see Using Amazon S3 on Outposts in the\n Amazon S3 User Guide. For information about S3 replication on Outposts\n configuration, see Replicating objects for S3 on Outposts in the Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

This action requires permissions for the\n s3-outposts:GetReplicationConfiguration action. The Outposts bucket owner\n has this permission by default and can grant it to others. For more information about\n permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts bucket in the Amazon S3 User Guide.

\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

If you include the Filter element in a replication configuration, you must\n also include the DeleteMarkerReplication, Status, and\n Priority elements. The response also returns those elements.

\n

For information about S3 on Outposts replication failure reasons, see Replication failure reasons in the Amazon S3 User Guide.

\n

The following operations are related to GetBucketReplication:

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -10968,7 +11221,7 @@ "AbortIncompleteMultipartUpload": { "target": "com.amazonaws.s3control#AbortIncompleteMultipartUpload", "traits": { - "smithy.api#documentation": "

Specifies the days since the initiation of an incomplete multipart upload that Amazon S3\n waits before permanently removing all parts of the upload. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Policy in the\n Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies the days since the initiation of an incomplete multipart upload that Amazon S3\n waits before permanently removing all parts of the upload. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Configuration in the\n Amazon S3 User Guide.

" } } }, @@ -12116,12 +12369,71 @@ "traits": { "smithy.api#documentation": "

Specifies the ARN for the Object Lambda Access Point.

" } + }, + "Alias": { + "target": "com.amazonaws.s3control#ObjectLambdaAccessPointAlias", + "traits": { + "smithy.api#documentation": "

The alias of the Object Lambda Access Point.

" + } } }, "traits": { "smithy.api#documentation": "

An access point with an attached Lambda function used to access transformed data from an Amazon S3\n bucket.

" } }, + "com.amazonaws.s3control#ObjectLambdaAccessPointAlias": { + "type": "structure", + "members": { + "Value": { + "target": "com.amazonaws.s3control#ObjectLambdaAccessPointAliasValue", + "traits": { + "smithy.api#documentation": "

The alias value of the Object Lambda Access Point.

" + } + }, + "Status": { + "target": "com.amazonaws.s3control#ObjectLambdaAccessPointAliasStatus", + "traits": { + "smithy.api#documentation": "

The status of the Object Lambda Access Point alias. If the status is PROVISIONING, the Object Lambda Access Point is provisioning the alias and the alias is not ready for use yet. If \n the status is READY, the Object Lambda Access Point alias is successfully provisioned and ready for use.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The alias of an Object Lambda Access Point. For more information, see How to use a bucket-style alias for your S3 bucket\n Object Lambda Access Point.

" + } + }, + "com.amazonaws.s3control#ObjectLambdaAccessPointAliasStatus": { + "type": "enum", + "members": { + "PROVISIONING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PROVISIONING" + } + }, + "READY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "READY" + } + } + }, + "traits": { + "smithy.api#length": { + "min": 2, + "max": 16 + } + } + }, + "com.amazonaws.s3control#ObjectLambdaAccessPointAliasValue": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 3, + "max": 63 + }, + "smithy.api#pattern": "^[0-9a-z\\\\-]{3,63}$" + } + }, "com.amazonaws.s3control#ObjectLambdaAccessPointArn": { "type": "string", "traits": { @@ -12869,7 +13181,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "\n

This action creates an Amazon S3 on Outposts bucket's replication configuration. To create\n an S3 bucket's replication configuration, see PutBucketReplication\n in the Amazon S3 API Reference.

\n
\n

Creates a replication configuration or replaces an existing one. For information about\n S3 replication on Outposts configuration, see Replicating objects for Amazon Web Services Outposts in the\n Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

Specify the replication configuration in the request body. In the replication\n configuration, you provide the following information:

\n
    \n
  • \n

    The name of the destination bucket or buckets where you want S3 on Outposts to\n replicate objects

    \n
  • \n
  • \n

    The Identity and Access Management (IAM) role that S3 on Outposts can assume to replicate objects on\n your behalf

    \n
  • \n
  • \n

    Other relevant information, such as replication rules

    \n
  • \n
\n

A replication configuration must include at least one rule and can contain a maximum of\n 100. Each rule identifies a subset of objects to replicate by filtering the objects in\n the source Outposts bucket. To choose additional subsets of objects to replicate, add a\n rule for each subset.

\n

To specify a subset of the objects in the source Outposts bucket to apply a replication\n rule to, add the Filter element as a child of the Rule element.\n You can filter objects based on an object key prefix, one or more object tags, or both.\n When you add the Filter element in the configuration, you must also add the\n following elements: DeleteMarkerReplication, Status, and\n Priority.

\n

Using PutBucketReplication on Outposts requires that both the source and\n destination buckets must have versioning enabled. For information about enabling versioning\n on a bucket, see Managing S3 Versioning\n for your S3 on Outposts bucket.

\n

For information about S3 on Outposts replication failure reasons, see Replication failure reasons in the Amazon S3 User Guide.

\n

\n Handling Replication of Encrypted Objects\n

\n

Outposts buckets are encrypted at all times. All the objects in the source Outposts\n bucket are encrypted and can be replicated. Also, all the replicas in the destination\n Outposts bucket are encrypted with the same encryption key as the objects in the source\n Outposts bucket.

\n

\n Permissions\n

\n

To create a PutBucketReplication request, you must have\n s3-outposts:PutReplicationConfiguration permissions for the bucket. The\n Outposts bucket owner has this permission by default and can grant it to others. For more\n information about permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts buckets.

\n \n

To perform this operation, the user or role must also have the iam:PassRole permission.

\n
\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

The following operations are related to PutBucketReplication:

\n ", + "smithy.api#documentation": "\n

This action creates an Amazon S3 on Outposts bucket's replication configuration. To create\n an S3 bucket's replication configuration, see PutBucketReplication\n in the Amazon S3 API Reference.

\n
\n

Creates a replication configuration or replaces an existing one. For information about\n S3 replication on Outposts configuration, see Replicating objects for S3 on Outposts in the\n Amazon S3 User Guide.

\n \n

It can take a while to propagate PUT or DELETE requests for\n a replication configuration to all S3 on Outposts systems. Therefore, the replication\n configuration that's returned by a GET request soon after a\n PUT or DELETE request might return a more recent result\n than what's on the Outpost. If an Outpost is offline, the delay in updating the\n replication configuration on that Outpost can be significant.

\n
\n

Specify the replication configuration in the request body. In the replication\n configuration, you provide the following information:

\n
    \n
  • \n

    The name of the destination bucket or buckets where you want S3 on Outposts to\n replicate objects

    \n
  • \n
  • \n

    The Identity and Access Management (IAM) role that S3 on Outposts can assume to replicate objects on\n your behalf

    \n
  • \n
  • \n

    Other relevant information, such as replication rules

    \n
  • \n
\n

A replication configuration must include at least one rule and can contain a maximum of\n 100. Each rule identifies a subset of objects to replicate by filtering the objects in\n the source Outposts bucket. To choose additional subsets of objects to replicate, add a\n rule for each subset.

\n

To specify a subset of the objects in the source Outposts bucket to apply a replication\n rule to, add the Filter element as a child of the Rule element.\n You can filter objects based on an object key prefix, one or more object tags, or both.\n When you add the Filter element in the configuration, you must also add the\n following elements: DeleteMarkerReplication, Status, and\n Priority.

\n

Using PutBucketReplication on Outposts requires that both the source and\n destination buckets must have versioning enabled. For information about enabling versioning\n on a bucket, see Managing S3 Versioning\n for your S3 on Outposts bucket.

\n

For information about S3 on Outposts replication failure reasons, see Replication failure reasons in the Amazon S3 User Guide.

\n

\n Handling Replication of Encrypted Objects\n

\n

Outposts buckets are encrypted at all times. All the objects in the source Outposts\n bucket are encrypted and can be replicated. Also, all the replicas in the destination\n Outposts bucket are encrypted with the same encryption key as the objects in the source\n Outposts bucket.

\n

\n Permissions\n

\n

To create a PutBucketReplication request, you must have\n s3-outposts:PutReplicationConfiguration permissions for the bucket. The\n Outposts bucket owner has this permission by default and can grant it to others. For more\n information about permissions, see Setting up IAM with\n S3 on Outposts and Managing access to\n S3 on Outposts buckets.

\n \n

To perform this operation, the user or role must also have the iam:CreateRole and iam:PassRole permissions. \n For more information, see Granting a user\n permissions to pass a role to an Amazon Web Services service.

\n
\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

The following operations are related to PutBucketReplication:

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -13001,7 +13313,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "\n

This operation sets the versioning state\n for\n S3 on Outposts\n buckets\n only. To set the versioning state for an S3 bucket, see PutBucketVersioning in the Amazon S3 API Reference.

\n
\n

Sets the versioning state for an S3 on Outposts bucket. With\n S3\n Versioning,\n you can save multiple distinct copies of your\n objects\n and recover from unintended user actions and application failures.

\n

You can set the versioning state to one of the following:

\n
    \n
  • \n

    \n Enabled - Enables versioning for the objects in\n the bucket. All objects added to the bucket receive a unique version ID.

    \n
  • \n
  • \n

    \n Suspended - Suspends versioning for the objects\n in the bucket. All objects added to the bucket receive the version ID\n null.

    \n
  • \n
\n

If you've never set versioning on your bucket, it has no versioning state. In that case,\n a \n GetBucketVersioning request does not return a versioning state value.

\n

When you enable S3 Versioning, for each object in your bucket, you have a current\n version and zero or more noncurrent versions. You can configure your bucket S3 Lifecycle\n rules to expire noncurrent versions after a specified time period. For more information,\n see Creating and managing\n a lifecycle configuration for your S3 on Outposts bucket in the Amazon S3\n User Guide.

\n

If you have an object expiration lifecycle policy in your non-versioned bucket and you\n want to maintain the same permanent delete behavior when you enable versioning, you must\n add a noncurrent expiration policy. The noncurrent expiration lifecycle policy will manage\n the\n deletions\n of the noncurrent object versions in the version-enabled bucket. For more information, see\n Versioning in the Amazon S3 User Guide.

\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

The following operations are related to PutBucketVersioning for\n S3 on Outposts.

\n ", + "smithy.api#documentation": "\n

This operation sets the versioning state\n for\n S3 on Outposts\n buckets\n only. To set the versioning state for an S3 bucket, see PutBucketVersioning in the Amazon S3 API Reference.

\n
\n

Sets the versioning state for an S3 on Outposts bucket. With\n S3\n Versioning,\n you can save multiple distinct copies of your\n objects\n and recover from unintended user actions and application failures.

\n

You can set the versioning state to one of the following:

\n
    \n
  • \n

    \n Enabled - Enables versioning for the objects in\n the bucket. All objects added to the bucket receive a unique version ID.

    \n
  • \n
  • \n

    \n Suspended - Suspends versioning for the objects\n in the bucket. All objects added to the bucket receive the version ID\n null.

    \n
  • \n
\n

If you've never set versioning on your bucket, it has no versioning state. In that case,\n a \n GetBucketVersioning request does not return a versioning state value.

\n

When you enable S3 Versioning, for each object in your bucket, you have a current\n version and zero or more noncurrent versions. You can configure your bucket S3 Lifecycle\n rules to expire noncurrent versions after a specified time period. For more information,\n see Creating and managing\n a lifecycle configuration for your S3 on Outposts bucket in the Amazon S3\n User Guide.

\n

If you have an object expiration lifecycle configuration in your non-versioned bucket and you\n want to maintain the same permanent delete behavior when you enable versioning, you must\n add a noncurrent expiration policy. The noncurrent expiration lifecycle configuration will manage\n the deletes of the noncurrent object versions in the version-enabled bucket. For more\n information, see Versioning in the Amazon S3\n User Guide.

\n

All Amazon S3 on Outposts REST API requests for this action require an additional parameter of x-amz-outpost-id to be passed with the request. In addition, you must use an S3 on Outposts endpoint hostname prefix instead of s3-control. For an example of the request syntax for Amazon S3 on Outposts that uses the S3 on Outposts endpoint hostname prefix and the x-amz-outpost-id derived by using the access point ARN, see the Examples section.

\n

The following operations are related to PutBucketVersioning for\n S3 on Outposts.

\n ", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, @@ -13625,7 +13937,7 @@ "target": "com.amazonaws.s3control#Priority", "traits": { "smithy.api#default": null, - "smithy.api#documentation": "

The priority indicates which rule has precedence whenever two or more replication rules\n conflict. S3 on Outposts attempts to replicate objects according to all replication rules.\n However, if there are two or more rules with the same destination Outposts bucket, then objects will\n be replicated according to the rule with the highest priority. The higher the number, the\n higher the priority.

\n

For more information, see Creating replication rules between Outposts in the\n Amazon S3 User Guide.

" + "smithy.api#documentation": "

The priority indicates which rule has precedence whenever two or more replication rules\n conflict. S3 on Outposts attempts to replicate objects according to all replication rules.\n However, if there are two or more rules with the same destination Outposts bucket, then objects will\n be replicated according to the rule with the highest priority. The higher the number, the\n higher the priority.

\n

For more information, see Creating replication rules on Outposts in the\n Amazon S3 User Guide.

" } }, "Prefix": { @@ -15273,7 +15585,7 @@ "target": "com.amazonaws.s3control#SubmitMultiRegionAccessPointRoutesResult" }, "traits": { - "smithy.api#documentation": "

Submits an updated route configuration for a Multi-Region Access Point. This API operation updates the\n routing status for the specified Regions from active to passive, or from passive to active.\n A value of 0 indicates a passive status, which means that traffic won't be\n routed to the specified Region. A value of 100 indicates an active status,\n which means that traffic will be routed to the specified Region. At least one Region must be active at all times.

\n

When\n the routing configuration is changed, any in-progress operations (uploads, copies, deletes,\n and so on) to formerly active Regions will continue to run to their\n final completion state (success or failure). The routing configurations of any Regions that\n aren’t specified remain unchanged.

\n \n

Updated routing configurations might not be immediately applied.\n It\n can take up to 2 minutes for your changes to take effect.

\n
\n

To submit routing control changes and failover requests, use the Amazon S3 failover control\n infrastructure endpoints in these five Amazon Web Services Regions:

\n
    \n
  • \n

    \n us-east-1\n

    \n
  • \n
  • \n

    \n us-west-2\n

    \n
  • \n
  • \n

    \n ap-southeast-2\n

    \n
  • \n
  • \n

    \n ap-northeast-1\n

    \n
  • \n
  • \n

    \n eu-west-1\n

    \n
  • \n
\n \n

Your Amazon S3 bucket does not need to be in these five Regions.

\n
", + "smithy.api#documentation": "

Submits an updated route configuration for a Multi-Region Access Point. This API operation updates the\n routing status for the specified Regions from active to passive, or from passive to active.\n A value of 0 indicates a passive status, which means that traffic won't be\n routed to the specified Region. A value of 100 indicates an active status,\n which means that traffic will be routed to the specified Region. At least one Region must be active at all times.

\n

When the routing configuration is changed, any in-progress operations (uploads, copies,\n deletes, and so on) to formerly active Regions will continue to run to their final\n completion state (success or failure). The routing configurations of any Regions that\n aren’t specified remain unchanged.

\n \n

Updated routing configurations might not be immediately applied. It can take up to 2\n minutes for your changes to take effect.

\n
\n

To submit routing control changes and failover requests, use the Amazon S3 failover control\n infrastructure endpoints in these five Amazon Web Services Regions:

\n
    \n
  • \n

    \n us-east-1\n

    \n
  • \n
  • \n

    \n us-west-2\n

    \n
  • \n
  • \n

    \n ap-southeast-2\n

    \n
  • \n
  • \n

    \n ap-northeast-1\n

    \n
  • \n
  • \n

    \n eu-west-1\n

    \n
  • \n
\n \n

Your Amazon S3 bucket does not need to be in these five Regions.

\n
", "smithy.api#endpoint": { "hostPrefix": "{AccountId}." }, diff --git a/aws/sdk/aws-models/sso.json b/aws/sdk/aws-models/sso.json index 474fc29342..4f48553e76 100644 --- a/aws/sdk/aws-models/sso.json +++ b/aws/sdk/aws-models/sso.json @@ -841,405 +841,6 @@ "conditions": [], "type": "tree", "rules": [ - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-east-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-northeast-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-northeast-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-northeast-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-northeast-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-northeast-3" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-northeast-3.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-south-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-south-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-southeast-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-southeast-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ap-southeast-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ap-southeast-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "ca-central-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.ca-central-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-central-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-central-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-north-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-north-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-south-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-south-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-west-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-west-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-west-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-west-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "eu-west-3" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.eu-west-3.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "me-south-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.me-south-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "sa-east-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.sa-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-east-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-east-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-east-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-west-2" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-west-2.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-east-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-gov-east-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, - { - "conditions": [ - { - "fn": "stringEquals", - "argv": [ - { - "ref": "Region" - }, - "us-gov-west-1" - ] - } - ], - "endpoint": { - "url": "https://portal.sso.us-gov-west-1.amazonaws.com", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - }, { "conditions": [], "endpoint": { @@ -1275,8 +876,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1288,8 +889,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1301,8 +902,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1314,8 +915,8 @@ }, "params": { "Region": "ap-northeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1327,8 +928,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1340,8 +941,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1353,8 +954,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1366,8 +967,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1379,8 +980,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1392,8 +993,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1405,8 +1006,8 @@ }, "params": { "Region": "eu-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1418,8 +1019,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1431,8 +1032,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1444,8 +1045,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1457,8 +1058,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1470,8 +1071,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1483,8 +1084,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1496,8 +1097,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1509,8 +1110,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1522,8 +1123,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1535,8 +1136,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1548,8 +1149,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1561,8 +1162,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1574,8 +1175,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1587,8 +1188,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1600,8 +1201,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1613,8 +1214,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1626,8 +1227,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1639,8 +1240,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1652,8 +1253,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1665,8 +1266,19 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1678,8 +1290,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1691,8 +1314,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1704,8 +1338,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1717,8 +1362,8 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1730,8 +1375,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1743,8 +1388,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1755,8 +1400,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1767,10 +1412,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/aws-models/sts.json b/aws/sdk/aws-models/sts.json index 915bf2d076..2543c7a989 100644 --- a/aws/sdk/aws-models/sts.json +++ b/aws/sdk/aws-models/sts.json @@ -1008,8 +1008,8 @@ }, "params": { "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1021,8 +1021,8 @@ }, "params": { "Region": "ap-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1034,8 +1034,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1047,8 +1047,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1060,8 +1060,8 @@ }, "params": { "Region": "ap-northeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1073,8 +1073,8 @@ }, "params": { "Region": "ap-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1086,8 +1086,8 @@ }, "params": { "Region": "ap-southeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1099,8 +1099,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1112,8 +1112,8 @@ }, "params": { "Region": "ap-southeast-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1134,8 +1134,8 @@ }, "params": { "Region": "aws-global", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1147,8 +1147,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1160,8 +1160,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1173,8 +1173,8 @@ }, "params": { "Region": "eu-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1186,8 +1186,8 @@ }, "params": { "Region": "eu-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1199,8 +1199,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1212,8 +1212,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1225,8 +1225,8 @@ }, "params": { "Region": "eu-west-3", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1238,8 +1238,8 @@ }, "params": { "Region": "me-south-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1251,8 +1251,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1264,8 +1264,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1277,8 +1277,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1290,8 +1290,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1303,8 +1303,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1316,8 +1316,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1329,8 +1329,8 @@ }, "params": { "Region": "us-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1342,8 +1342,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1355,8 +1355,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1368,8 +1368,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1381,8 +1381,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1394,8 +1394,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1407,8 +1407,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1420,8 +1420,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1433,8 +1433,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1446,8 +1446,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1459,8 +1459,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1472,8 +1472,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1485,8 +1485,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1498,8 +1498,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -1511,8 +1511,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1524,8 +1524,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1537,8 +1537,8 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -1550,8 +1550,19 @@ }, "params": { "Region": "us-iso-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1563,8 +1574,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1576,8 +1598,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -1589,8 +1622,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -1602,8 +1646,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1615,8 +1659,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1627,8 +1671,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -1639,11 +1683,17 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } + }, { "documentation": "UseGlobalEndpoint with legacy region `ap-northeast-1`", "expect": { @@ -1651,8 +1701,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -1670,9 +1720,9 @@ } ], "params": { - "Region": "ap-northeast-1", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "ap-northeast-1", "UseGlobalEndpoint": true } }, @@ -1683,8 +1733,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -1702,9 +1752,9 @@ } ], "params": { - "Region": "ap-south-1", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "ap-south-1", "UseGlobalEndpoint": true } }, @@ -1715,8 +1765,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -1734,9 +1784,9 @@ } ], "params": { - "Region": "ap-southeast-1", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "ap-southeast-1", "UseGlobalEndpoint": true } }, @@ -1747,8 +1797,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -1766,9 +1816,9 @@ } ], "params": { - "Region": "ap-southeast-2", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "ap-southeast-2", "UseGlobalEndpoint": true } }, @@ -1779,8 +1829,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -1798,9 +1848,9 @@ } ], "params": { - "Region": "aws-global", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "aws-global", "UseGlobalEndpoint": true } }, @@ -1811,8 +1861,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -1830,9 +1880,9 @@ } ], "params": { - "Region": "ca-central-1", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "ca-central-1", "UseGlobalEndpoint": true } }, @@ -1843,8 +1893,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -1862,9 +1912,9 @@ } ], "params": { - "Region": "eu-central-1", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "eu-central-1", "UseGlobalEndpoint": true } }, @@ -1875,8 +1925,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -1894,9 +1944,9 @@ } ], "params": { - "Region": "eu-north-1", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "eu-north-1", "UseGlobalEndpoint": true } }, @@ -1907,8 +1957,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -1926,9 +1976,9 @@ } ], "params": { - "Region": "eu-west-1", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "eu-west-1", "UseGlobalEndpoint": true } }, @@ -1939,8 +1989,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -1958,9 +2008,9 @@ } ], "params": { - "Region": "eu-west-2", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "eu-west-2", "UseGlobalEndpoint": true } }, @@ -1971,8 +2021,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -1990,9 +2040,9 @@ } ], "params": { - "Region": "eu-west-3", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "eu-west-3", "UseGlobalEndpoint": true } }, @@ -2003,8 +2053,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -2022,9 +2072,9 @@ } ], "params": { - "Region": "sa-east-1", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "sa-east-1", "UseGlobalEndpoint": true } }, @@ -2035,8 +2085,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -2054,9 +2104,9 @@ } ], "params": { - "Region": "us-east-1", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "us-east-1", "UseGlobalEndpoint": true } }, @@ -2067,8 +2117,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -2086,9 +2136,9 @@ } ], "params": { - "Region": "us-east-2", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "us-east-2", "UseGlobalEndpoint": true } }, @@ -2099,8 +2149,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -2118,9 +2168,9 @@ } ], "params": { - "Region": "us-west-1", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "us-west-1", "UseGlobalEndpoint": true } }, @@ -2131,8 +2181,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-1" } ] @@ -2150,9 +2200,9 @@ } ], "params": { - "Region": "us-west-2", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "us-west-2", "UseGlobalEndpoint": true } }, @@ -2163,8 +2213,8 @@ "properties": { "authSchemes": [ { - "name": "sigv4", "signingName": "sts", + "name": "sigv4", "signingRegion": "us-east-3" } ] @@ -2182,9 +2232,9 @@ } ], "params": { - "Region": "us-east-3", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "us-east-3", "UseGlobalEndpoint": true } }, @@ -2206,9 +2256,9 @@ } ], "params": { - "Region": "us-west-1", - "UseFIPS": false, "UseDualStack": false, + "UseFIPS": false, + "Region": "us-west-1", "UseGlobalEndpoint": true, "Endpoint": "https://example.com" } @@ -2221,10 +2271,10 @@ } }, "params": { - "UseFIPS": false, "UseDualStack": false, - "UseGlobalEndpoint": false, - "Endpoint": "https://example.com" + "UseFIPS": false, + "Endpoint": "https://example.com", + "UseGlobalEndpoint": false } } ], diff --git a/aws/sdk/aws-models/transcribe-streaming.json b/aws/sdk/aws-models/transcribe-streaming.json index 93e63cde42..a1464b0462 100644 --- a/aws/sdk/aws-models/transcribe-streaming.json +++ b/aws/sdk/aws-models/transcribe-streaming.json @@ -2654,8 +2654,8 @@ }, "params": { "Region": "ap-northeast-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2667,8 +2667,8 @@ }, "params": { "Region": "ap-northeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2680,8 +2680,8 @@ }, "params": { "Region": "ap-southeast-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2693,8 +2693,8 @@ }, "params": { "Region": "ca-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2706,8 +2706,8 @@ }, "params": { "Region": "eu-central-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2719,8 +2719,8 @@ }, "params": { "Region": "eu-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2732,8 +2732,8 @@ }, "params": { "Region": "eu-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2745,8 +2745,8 @@ }, "params": { "Region": "sa-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2758,8 +2758,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2771,8 +2771,8 @@ }, "params": { "Region": "us-east-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2784,8 +2784,8 @@ }, "params": { "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2797,8 +2797,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2810,8 +2810,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2823,8 +2823,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2836,8 +2836,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2849,8 +2849,8 @@ }, "params": { "Region": "cn-northwest-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2862,8 +2862,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2875,8 +2875,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2888,8 +2888,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2901,8 +2901,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2914,8 +2914,8 @@ }, "params": { "Region": "us-gov-west-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2927,8 +2927,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2940,8 +2940,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2953,8 +2953,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2966,8 +2966,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2979,8 +2990,30 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2992,8 +3025,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -3005,8 +3049,8 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -3018,8 +3062,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -3031,8 +3075,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -3043,8 +3087,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -3055,10 +3099,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" From 168184e5e3b6ceb2331313bc3e3cf51f707bf330 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Tue, 25 Apr 2023 17:27:11 -0500 Subject: [PATCH 057/253] Address vulnerabilities reported within the tools directory (#2633) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation and Context Addresses vulnerabilities reported by `cargo audit` within the repository. ## Description This commit addresses vulnerabilities within the tools directory reported by `cargo audit`. Mostly they have been fixed by regenerating `Cargo.lock` files. Two exceptions: - `crate-hasher` needs to drop the `temp_dir` crate and switch over to the `tempfile` crate - `canary-runner` needs to upgrade the `octorust` crate ## Testing After the PR, no vulnerabilities reported from the crates that have been patched. Ran `cargo t` on the updated crates. Furthermore, no vulnerabilities reported currently within `rust-runtime` and `aws/rust-runtime`: ``` ➜ rust-runtime git:(ysaito/fix-cargo-audit) pwd smithy-rs/rust-runtime ➜ rust-runtime git:(ysaito/fix-cargo-audit) rm Cargo.lock && cargo generate-lockfile && cargo audit Updating crates.io index Fetching advisory database from `https://github.com/RustSec/advisory-db.git` Loaded 543 security advisories Updating crates.io index Scanning Cargo.lock for vulnerabilities (314 crate dependencies) ➜ rust-runtime git:(ysaito/fix-cargo-audit) pwd smithy-rs/aws/rust-runtime ➜ rust-runtime git:(ysaito/fix-cargo-audit) rm Cargo.lock && cargo generate-lockfile && cargo audit Updating crates.io index Fetching advisory database from `https://github.com/RustSec/advisory-db.git` Loaded 543 security advisories Updating crates.io index Scanning Cargo.lock for vulnerabilities (249 crate dependencies) ``` ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ Co-authored-by: Yuki Saito --- tools/ci-build/changelogger/Cargo.lock | 559 +++++++++++------ tools/ci-build/crate-hasher/Cargo.lock | 438 +++++++++----- tools/ci-build/crate-hasher/Cargo.toml | 2 +- tools/ci-build/crate-hasher/tests/test.rs | 20 +- tools/ci-build/publisher/Cargo.lock | 573 ++++++++++-------- tools/ci-build/sdk-lints/Cargo.lock | 533 ++++++++++------ tools/ci-build/sdk-versioner/Cargo.lock | 547 +++++++++++------ tools/ci-cdk/canary-runner/Cargo.lock | 701 +++++++++++++--------- tools/ci-cdk/canary-runner/Cargo.toml | 2 +- tools/echo-server/Cargo.lock | 288 +++++---- 10 files changed, 2273 insertions(+), 1390 deletions(-) diff --git a/tools/ci-build/changelogger/Cargo.lock b/tools/ci-build/changelogger/Cargo.lock index 4ee3df2db4..91da13c161 100644 --- a/tools/ci-build/changelogger/Cargo.lock +++ b/tools/ci-build/changelogger/Cargo.lock @@ -4,28 +4,28 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.19" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -34,7 +34,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -47,9 +47,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.13.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bitflags" @@ -59,21 +59,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -100,9 +100,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.22" +version = "3.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" +checksum = "eef2b3ded6a26dfaec672a742c93c8cf6b689220324da509ec5caa20de55dc83" dependencies = [ "atty", "bitflags", @@ -117,15 +117,15 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.18" +version = "3.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "d756c5824fc5c0c1ee8e36000f576968dbcb2081def956c83fad6f40acd46f96" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -149,18 +149,18 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "ctor" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -171,18 +171,39 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -219,36 +240,36 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-task", @@ -258,9 +279,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -283,9 +304,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -296,11 +317,17 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -332,9 +359,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -379,9 +406,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -396,23 +423,34 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "ipnet" -version = "2.5.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -425,9 +463,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "linux-raw-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" [[package]] name = "log" @@ -446,27 +490,27 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -510,15 +554,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" dependencies = [ "bitflags", "cfg-if", @@ -531,13 +575,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -548,11 +592,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.76" +version = "0.9.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -570,9 +613,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "output_vt100" @@ -603,9 +646,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "pretty_assertions" @@ -628,7 +671,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -645,36 +688,36 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", @@ -683,24 +726,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64", "bytes", @@ -733,27 +767,40 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustix" +version = "0.37.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -764,9 +811,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -774,35 +821,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -823,9 +870,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -848,9 +895,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -864,9 +911,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -875,43 +933,50 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys 0.45.0", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "time" -version = "0.3.15" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ "libc", "num_threads", + "serde", + "time-core", ] +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + [[package]] name = "tinyvec" version = "1.6.0" @@ -923,31 +988,30 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.21.2" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "pin-project-lite", "socket2", - "winapi", + "windows-sys 0.45.0", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -955,9 +1019,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -969,9 +1033,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "indexmap", "serde", @@ -985,11 +1049,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -997,41 +1060,41 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -1083,9 +1146,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1093,24 +1156,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -1120,9 +1183,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1130,28 +1193,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -1190,46 +1253,150 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winreg" diff --git a/tools/ci-build/crate-hasher/Cargo.lock b/tools/ci-build/crate-hasher/Cargo.lock index c6f1912dd2..6c034cc675 100644 --- a/tools/ci-build/crate-hasher/Cargo.lock +++ b/tools/ci-build/crate-hasher/Cargo.lock @@ -10,18 +10,27 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "0.7.19" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "atty" @@ -29,7 +38,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -48,22 +57,29 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.9.0" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bstr" -version = "0.2.17" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" dependencies = [ "memchr", + "serde", ] +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + [[package]] name = "cfg-if" version = "1.0.0" @@ -111,9 +127,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -129,7 +145,7 @@ dependencies = [ "pretty_assertions", "sha256", "tar", - "tempdir", + "tempfile", ] [[package]] @@ -143,18 +159,28 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.12" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "ctor" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", "syn", @@ -168,30 +194,61 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" -version = "0.9.0" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "generic-array", + "block-buffer", + "crypto-common", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", ] [[package]] name = "filetime" -version = "0.2.17" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys", + "redox_syscall 0.2.16", + "windows-sys 0.48.0", ] [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", "miniz_oxide", @@ -203,17 +260,11 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "fuchsia-cprng" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" - [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -221,11 +272,11 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" +checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.20", "bstr", "fnv", "log", @@ -240,9 +291,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -253,6 +304,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -279,14 +336,34 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -295,9 +372,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "linux-raw-sys" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" [[package]] name = "log" @@ -316,30 +399,24 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "miniz_oxide" -version = "0.5.4" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ "adler", ] [[package]] name = "once_cell" -version = "1.15.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "output_vt100" @@ -388,92 +465,69 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] [[package]] -name = "rand" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" -dependencies = [ - "fuchsia-cprng", - "libc", - "rand_core 0.3.1", - "rdrand", - "winapi", -] - -[[package]] -name = "rand_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" -dependencies = [ - "rand_core 0.4.2", -] - -[[package]] -name = "rand_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" - -[[package]] -name = "rdrand" -version = "0.4.0" +name = "redox_syscall" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "rand_core 0.3.1", + "bitflags", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ - "aho-corasick", + "aho-corasick 1.0.1", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "rustix" +version = "0.37.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" dependencies = [ - "winapi", + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", ] [[package]] @@ -485,24 +539,28 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" + [[package]] name = "sha2" -version = "0.9.9" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ - "block-buffer", "cfg-if", "cpufeatures", "digest", - "opaque-debug", ] [[package]] name = "sha256" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e334db67871c14c18fc066ad14af13f9fdf5f9a91c61af432d1e3a39c8c6a141" +checksum = "5f9f8b5de2bac3a4ae28e9b611072a8e326d9b26c8189c0972d4c321fa684f1f" dependencies = [ "hex", "sha2", @@ -516,9 +574,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -537,50 +595,54 @@ dependencies = [ ] [[package]] -name = "tempdir" -version = "0.3.7" +name = "tempfile" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ - "rand", - "remove_dir_all", + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "version_check" @@ -590,12 +652,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -632,46 +693,135 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "xattr" diff --git a/tools/ci-build/crate-hasher/Cargo.toml b/tools/ci-build/crate-hasher/Cargo.toml index 3f9376443a..2643b9804d 100644 --- a/tools/ci-build/crate-hasher/Cargo.toml +++ b/tools/ci-build/crate-hasher/Cargo.toml @@ -24,4 +24,4 @@ sha256 = "1.1" flate2 = "1.0" pretty_assertions = "1.3" tar = "0.4" -tempdir = "0.3" +tempfile = "3.5.0" diff --git a/tools/ci-build/crate-hasher/tests/test.rs b/tools/ci-build/crate-hasher/tests/test.rs index cc46d03dab..98bd360bb4 100644 --- a/tools/ci-build/crate-hasher/tests/test.rs +++ b/tools/ci-build/crate-hasher/tests/test.rs @@ -8,7 +8,7 @@ use flate2::read::GzDecoder; use std::fs::File; use std::process::Command; use tar::Archive; -use tempdir::TempDir; +use tempfile::TempDir; use crate_hasher::file_list::FileList; @@ -25,32 +25,32 @@ fn assert_correct_aws_smithy_async_hash(file_list: &FileList) { #[test] fn test_against_aws_smithy_async() -> Result<()> { - let dir = TempDir::new("test_against_aws_smithy_async")?; + let dir = TempDir::new()?.path().join("test_against_aws_smithy_async"); let tar = GzDecoder::new(File::open("tests/aws-smithy-async-2022-04-08.tar.gz")?); let mut archive = Archive::new(tar); archive.unpack(&dir)?; - let file_list = FileList::discover(&dir.as_ref().join("aws-smithy-async"))?; + let file_list = FileList::discover(&dir.as_path().join("aws-smithy-async"))?; assert_correct_aws_smithy_async_hash(&file_list); Ok(()) } #[test] fn test_against_aws_smithy_async_with_ignored_files() -> Result<()> { - let dir = TempDir::new("test_against_aws_smithy_async")?; + let dir = TempDir::new()?.path().join("test_against_aws_smithy_async"); let tar = GzDecoder::new(File::open("tests/aws-smithy-async-2022-04-08.tar.gz")?); let mut archive = Archive::new(tar); archive.unpack(&dir)?; - std::fs::create_dir(dir.as_ref().join("target"))?; + std::fs::create_dir(&dir.as_path().join("target"))?; std::fs::write( - dir.as_ref().join("target/something"), + &dir.as_path().join("target/something"), b"some data that should be excluded", )?; - let file_list = FileList::discover(&dir.as_ref().join("aws-smithy-async"))?; + let file_list = FileList::discover(&dir.as_path().join("aws-smithy-async"))?; assert_correct_aws_smithy_async_hash(&file_list); Ok(()) @@ -58,7 +58,7 @@ fn test_against_aws_smithy_async_with_ignored_files() -> Result<()> { #[test] fn test_against_aws_smithy_async_with_git_repo() -> Result<()> { - let dir = TempDir::new("test_against_aws_smithy_async")?; + let dir = TempDir::new()?.path().join("test_against_aws_smithy_async"); let tar = GzDecoder::new(File::open("tests/aws-smithy-async-2022-04-08.tar.gz")?); let mut archive = Archive::new(tar); @@ -68,10 +68,10 @@ fn test_against_aws_smithy_async_with_git_repo() -> Result<()> { Command::new("git") .arg("init") .arg(".") - .current_dir(dir.as_ref().join("aws-smithy-async")) + .current_dir(&dir.as_path().join("aws-smithy-async")) .output()?; - let file_list = FileList::discover(&dir.as_ref().join("aws-smithy-async"))?; + let file_list = FileList::discover(&dir.as_path().join("aws-smithy-async"))?; assert_correct_aws_smithy_async_hash(&file_list); Ok(()) diff --git a/tools/ci-build/publisher/Cargo.lock b/tools/ci-build/publisher/Cargo.lock index 450e0b81b5..9f4f267aed 100644 --- a/tools/ci-build/publisher/Cargo.lock +++ b/tools/ci-build/publisher/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "async-recursion" @@ -25,18 +25,18 @@ checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "async-trait" -version = "0.1.64" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -70,27 +70,18 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.9.0" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "bytes" @@ -123,9 +114,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "num-integer", "num-traits", @@ -159,7 +150,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -198,15 +189,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -245,7 +236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -266,22 +257,13 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - [[package]] name = "digest" version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.3", + "block-buffer", "crypto-common", ] @@ -300,11 +282,32 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -347,9 +350,9 @@ checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541" [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -362,9 +365,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -372,15 +375,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -389,38 +392,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -436,9 +439,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -446,9 +449,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -507,6 +510,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -515,9 +524,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -549,9 +558,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -596,9 +605,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -613,17 +622,28 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "ipnet" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" @@ -642,9 +662,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "linux-raw-sys" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" [[package]] name = "lock_api" @@ -682,20 +708,20 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -757,21 +783,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl" -version = "0.10.45" +version = "0.10.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" dependencies = [ "bitflags", "cfg-if", @@ -784,13 +804,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -801,11 +821,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.80" +version = "0.9.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -814,9 +833,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "output_vt100" @@ -851,7 +870,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -864,9 +883,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pest" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab62d2fa33726dbe6321cc97ef96d8cde531e3eeaf858a058de53a8a6d40d8f" +checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" dependencies = [ "thiserror", "ucd-trie", @@ -874,9 +893,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf026e2d0581559db66d837fe5242320f525d85c76283c61f4d51a1238d65ea" +checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15" dependencies = [ "pest", "pest_generator", @@ -884,26 +903,26 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b27bd18aa01d91c8ed2b61ea23406a676b42d82609c6e2581fba42f0c15f17f" +checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "pest_meta" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02b677c1859756359fc9983c2e56a0237f18624a3789528804406b7e915e5d" +checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e" dependencies = [ "once_cell", "pest", - "sha2 0.10.6", + "sha2", ] [[package]] @@ -945,7 +964,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -962,9 +981,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] @@ -1000,9 +1019,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -1016,15 +1035,24 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" -version = "1.7.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -1033,29 +1061,26 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-syntax" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64", "bytes", @@ -1088,11 +1113,25 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustix" +version = "0.37.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "schannel" @@ -1134,35 +1173,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.92" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7434af0dc1cbd59268aa98b4c22c131c0584d2232f6fb166efb993e2832e896a" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -1181,19 +1220,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.6" @@ -1202,17 +1228,17 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest", ] [[package]] name = "sha256" -version = "1.1.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e334db67871c14c18fc066ad14af13f9fdf5f9a91c61af432d1e3a39c8c6a141" +checksum = "5f9f8b5de2bac3a4ae28e9b611072a8e326d9b26c8189c0972d4c321fa684f1f" dependencies = [ "hex", - "sha2 0.9.9", + "sha2", ] [[package]] @@ -1226,18 +1252,18 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -1267,9 +1293,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -1283,9 +1309,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -1294,16 +1331,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", ] [[package]] @@ -1333,30 +1369,31 @@ checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] @@ -1377,14 +1414,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot", @@ -1392,25 +1428,25 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -1418,9 +1454,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -1448,11 +1484,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1460,13 +1495,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -1492,9 +1527,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", @@ -1528,15 +1563,15 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -1619,7 +1654,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -1653,7 +1688,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1711,13 +1746,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -1726,65 +1761,131 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winreg" @@ -1803,6 +1904,6 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/tools/ci-build/sdk-lints/Cargo.lock b/tools/ci-build/sdk-lints/Cargo.lock index 4d1a68488a..a3b2511898 100644 --- a/tools/ci-build/sdk-lints/Cargo.lock +++ b/tools/ci-build/sdk-lints/Cargo.lock @@ -4,28 +4,28 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.19" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -34,7 +34,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -47,9 +47,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.13.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bitflags" @@ -59,15 +59,15 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cargo_toml" @@ -82,9 +82,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -119,7 +119,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -143,24 +143,45 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -197,36 +218,36 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-task", @@ -236,9 +257,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -261,9 +282,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -274,11 +295,17 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -310,9 +337,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -357,9 +384,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -374,23 +401,34 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "ipnet" -version = "2.5.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -403,9 +441,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "linux-raw-sys" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" [[package]] name = "log" @@ -424,27 +468,27 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -460,15 +504,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" dependencies = [ "bitflags", "cfg-if", @@ -481,13 +525,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -498,11 +542,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.76" +version = "0.9.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -511,9 +554,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "percent-encoding" @@ -535,9 +578,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "proc-macro-error" @@ -548,7 +591,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -565,36 +608,36 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", @@ -603,24 +646,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64", "bytes", @@ -653,20 +687,33 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustix" +version = "0.37.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -684,9 +731,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -697,9 +744,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -707,35 +754,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -756,9 +803,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -781,9 +828,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -797,9 +844,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -808,32 +866,31 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys 0.45.0", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" [[package]] name = "tinyvec" @@ -846,31 +903,30 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.21.2" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "pin-project-lite", "socket2", - "winapi", + "windows-sys 0.45.0", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -878,9 +934,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -892,9 +948,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "indexmap", "serde", @@ -908,11 +964,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -920,41 +975,41 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -1006,9 +1061,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1016,24 +1071,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -1043,9 +1098,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1053,28 +1108,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -1113,46 +1168,150 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winreg" diff --git a/tools/ci-build/sdk-versioner/Cargo.lock b/tools/ci-build/sdk-versioner/Cargo.lock index 2862d9eeb4..4da329eedd 100644 --- a/tools/ci-build/sdk-versioner/Cargo.lock +++ b/tools/ci-build/sdk-versioner/Cargo.lock @@ -4,28 +4,28 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.19" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -34,7 +34,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -47,9 +47,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.13.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "bitflags" @@ -59,21 +59,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bumpalo" -version = "3.11.0" +version = "3.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" [[package]] name = "cfg-if" @@ -108,7 +108,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -132,18 +132,18 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "ctor" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -154,18 +154,39 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "encoding_rs" -version = "0.8.31" +version = "0.8.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" dependencies = [ "cfg-if", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fastrand" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" dependencies = [ "instant", ] @@ -202,36 +223,36 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-task", @@ -241,9 +262,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -266,9 +287,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -279,11 +300,17 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -315,9 +342,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -362,9 +389,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -379,23 +406,34 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "ipnet" -version = "2.5.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" dependencies = [ "wasm-bindgen", ] @@ -408,9 +446,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "linux-raw-sys" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" [[package]] name = "log" @@ -429,27 +473,27 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -465,15 +509,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl" -version = "0.10.42" +version = "0.10.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12fc0523e3bd51a692c8850d075d74dc062ccf251c0110668cbd921917118a13" +checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" dependencies = [ "bitflags", "cfg-if", @@ -486,13 +530,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -503,11 +547,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.76" +version = "0.9.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5230151e44c0f05157effb743e8d517472843121cf9243e8b81393edb5acd9ce" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -516,9 +559,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "output_vt100" @@ -549,9 +592,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "pretty_assertions" @@ -574,7 +617,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -591,36 +634,36 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", @@ -629,24 +672,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "reqwest" -version = "0.11.12" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc" +checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64", "bytes", @@ -679,20 +713,33 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustix" +version = "0.37.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] @@ -709,9 +756,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ "bitflags", "core-foundation", @@ -722,9 +769,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -732,35 +779,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -781,9 +828,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -806,9 +853,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -822,9 +869,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.101" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -833,32 +891,31 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys 0.45.0", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.15.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" [[package]] name = "tinyvec" @@ -871,31 +928,30 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.21.2" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "pin-project-lite", "socket2", - "winapi", + "windows-sys 0.45.0", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -903,9 +959,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -917,9 +973,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "indexmap", "serde", @@ -933,9 +989,9 @@ checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" [[package]] name = "toml_edit" -version = "0.19.6" +version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08de71aa0d6e348f070457f85af8bd566e2bc452156a423ddf22861b3a953fae" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" dependencies = [ "indexmap", "toml_datetime", @@ -950,11 +1006,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -962,41 +1017,41 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", ] [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -1048,9 +1103,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1058,24 +1113,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" dependencies = [ "cfg-if", "js-sys", @@ -1085,9 +1140,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1095,28 +1150,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "web-sys" -version = "0.3.60" +version = "0.3.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" dependencies = [ "js-sys", "wasm-bindgen", @@ -1155,52 +1210,156 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" dependencies = [ "memchr", ] diff --git a/tools/ci-cdk/canary-runner/Cargo.lock b/tools/ci-cdk/canary-runner/Cargo.lock index adccd1cddc..c4a5f3fe86 100644 --- a/tools/ci-cdk/canary-runner/Cargo.lock +++ b/tools/ci-cdk/canary-runner/Cargo.lock @@ -10,9 +10,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -28,30 +28,30 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "async-recursion" -version = "0.3.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" +checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "async-trait" -version = "0.1.64" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -92,7 +92,7 @@ dependencies = [ "http", "hyper", "ring", - "time 0.3.19", + "time", "tokio", "tower", "tracing", @@ -263,9 +263,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "0.47.0" +version = "0.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6351c3ba468b04bd819f64ea53538f5f53e3d6b366b27deabee41e73c9edb3af" +checksum = "0973cb4a7b44ca2b54b3c7c2d01217fae5dbf9ebb96c2098c5c332829e5d352c" dependencies = [ "aws-smithy-eventstream", "aws-smithy-http", @@ -277,7 +277,7 @@ dependencies = [ "percent-encoding", "regex", "ring", - "time 0.3.19", + "time", "tracing", ] @@ -413,7 +413,7 @@ dependencies = [ "itoa", "num-integer", "ryu", - "time 0.3.19", + "time", ] [[package]] @@ -441,12 +441,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - [[package]] name = "base64" version = "0.13.1" @@ -467,18 +461,18 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" [[package]] name = "byteorder" @@ -488,9 +482,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.1.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "bytes-utils" @@ -544,25 +538,22 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" dependencies = [ "iana-time-zone", - "js-sys", "num-integer", "num-traits", "serde", - "time 0.1.45", - "wasm-bindgen", "winapi", ] [[package]] name = "clap" -version = "3.2.23" +version = "3.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "eef2b3ded6a26dfaec672a742c93c8cf6b689220324da509ec5caa20de55dc83" dependencies = [ "atty", "bitflags", @@ -577,15 +568,15 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.18" +version = "3.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "d756c5824fc5c0c1ee8e36000f576968dbcb2081def956c83fad6f40acd46f96" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -619,15 +610,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -652,9 +643,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if", "crossbeam-utils", @@ -662,9 +653,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" dependencies = [ "cfg-if", ] @@ -695,14 +686,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" dependencies = [ "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "cxx" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -712,9 +703,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", @@ -722,24 +713,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn", + "syn 2.0.15", ] [[package]] name = "cxxbridge-flags" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.91" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -760,9 +751,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" [[package]] name = "either" @@ -779,6 +770,27 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "fastrand" version = "1.9.0" @@ -821,19 +833,18 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", "percent-encoding", ] [[package]] name = "futures" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -846,9 +857,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -856,15 +867,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -873,38 +884,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "futures-sink" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.26" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -920,9 +931,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -930,20 +941,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "h2" -version = "0.3.15" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -988,6 +999,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + [[package]] name = "hex" version = "0.4.3" @@ -1030,9 +1047,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.24" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -1095,34 +1112,18 @@ dependencies = [ "tokio-native-tls", ] -[[package]] -name = "hyperx" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5617e92fc2f2501c3e2bc6ce547cad841adba2bae5b921c7e52510beca6d084c" -dependencies = [ - "base64 0.13.1", - "bytes", - "http", - "httpdate", - "language-tags", - "mime", - "percent-encoding", - "unicase", -] - [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "winapi", + "windows", ] [[package]] @@ -1137,20 +1138,19 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -1165,17 +1165,28 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-lifetimes" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "ipnet" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "js-sys" @@ -1188,11 +1199,11 @@ dependencies = [ [[package]] name = "jsonwebtoken" -version = "7.2.0" +version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afabcc15e437a6484fc4f12d0fd63068fe457bf93f1c148d3d9649c60b103f32" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.12.3", + "base64 0.21.0", "pem", "ring", "serde", @@ -1200,12 +1211,6 @@ dependencies = [ "simple_asn1", ] -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - [[package]] name = "lazy_static" version = "1.4.0" @@ -1214,9 +1219,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "link-cplusplus" @@ -1227,6 +1232,12 @@ dependencies = [ "cc", ] +[[package]] +name = "linux-raw-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" + [[package]] name = "lock_api" version = "0.4.9" @@ -1256,12 +1267,6 @@ dependencies = [ "regex-automata", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "md-5" version = "0.10.5" @@ -1279,9 +1284,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" @@ -1310,7 +1315,7 @@ checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.45.0", ] @@ -1344,9 +1349,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.2.6" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg", "num-integer", @@ -1384,18 +1389,18 @@ dependencies = [ [[package]] name = "octorust" -version = "0.1.37" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9541376e7f3dd40c4efdf2a7342d961ce506f33cf0cb828b7ca5b7b1d83dce87" +checksum = "b8fac860084f1858d4207a3242df22e2a60dbae86d3df6878ce8eac2a51eb5c9" dependencies = [ "anyhow", "async-recursion", "chrono", "http", - "hyperx", "jsonwebtoken", "log", "mime", + "parse_link_header", "pem", "percent-encoding", "reqwest", @@ -1420,9 +1425,9 @@ checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" [[package]] name = "openssl" -version = "0.10.45" +version = "0.10.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" dependencies = [ "bitflags", "cfg-if", @@ -1435,13 +1440,13 @@ dependencies = [ [[package]] name = "openssl-macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -1452,11 +1457,10 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.80" +version = "0.9.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" dependencies = [ - "autocfg", "cc", "libc", "pkg-config", @@ -1484,9 +1488,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" [[package]] name = "output_vt100" @@ -1521,27 +1525,36 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] +[[package]] +name = "parse_link_header" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3687fe9debbbf2a019f381a8bc6b42049b22647449b39af54b3013985c0cf6de" +dependencies = [ + "http", + "lazy_static", + "regex", +] + [[package]] name = "pem" -version = "0.8.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ "base64 0.13.1", - "once_cell", - "regex", ] [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pin-project" @@ -1560,7 +1573,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1608,7 +1621,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -1625,18 +1638,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.51" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -1680,15 +1693,24 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" -version = "1.7.1" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -1697,29 +1719,26 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "regex-syntax" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "reqwest" -version = "0.11.14" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" dependencies = [ "base64 0.21.0", "bytes", @@ -1808,9 +1827,9 @@ dependencies = [ [[package]] name = "reqwest-tracing" -version = "0.2.3" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c2af6c0571bef48275098bebe17863764989276c49433dab1d52328f744f1d" +checksum = "64977f9a47fa7768cc88751e29026e569730ac1667c2eaeaac04b32624849fbe" dependencies = [ "async-trait", "opentelemetry", @@ -1857,6 +1876,20 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.37.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + [[package]] name = "rustls" version = "0.19.1" @@ -1905,9 +1938,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "schannel" @@ -1920,9 +1953,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5fb6c61f29e723026dc8e923d94c694313212abbecbbe5f55a7748eec5b307" +checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" dependencies = [ "bytes", "chrono", @@ -1936,14 +1969,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f188d036977451159430f3b8dc82ec76364a42b7e289c2b18a9a18f4470058e9" +checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 1.0.109", ] [[package]] @@ -1954,9 +1987,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "scratch" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "sct" @@ -2003,28 +2036,28 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" [[package]] name = "serde" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.152" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -2035,14 +2068,14 @@ checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "serde_json" -version = "1.0.93" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -2103,13 +2136,14 @@ dependencies = [ [[package]] name = "simple_asn1" -version = "0.4.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692ca13de57ce0613a363c8c2f1de925adebc81b04c923ac60c5488bb44abe4b" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ - "chrono", "num-bigint", "num-traits", + "thiserror", + "time", ] [[package]] @@ -2146,9 +2180,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -2168,9 +2202,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -2179,25 +2224,24 @@ dependencies = [ [[package]] name = "task-local-extensions" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4167afbec18ae012de40f8cf1b9bf48420abb390678c34821caa07d924941cc4" +checksum = "ba323866e5d033818e3240feeb9f7db2c4296674e4d9e16b97b7bf8f490434e8" dependencies = [ - "tokio", + "pin-utils", ] [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", ] [[package]] @@ -2217,22 +2261,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -2247,21 +2291,11 @@ dependencies = [ [[package]] name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53250a3b3fed8ff8fd988587d8925d26a83ac3845d9e03b220b37f34c2b8d6c2" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" dependencies = [ + "itoa", "serde", "time-core", "time-macros", @@ -2275,9 +2309,9 @@ checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" [[package]] name = "time-macros" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a460aeb8de6dcb0f381e1ee05f1cd56fcf5a5f6eb8187ff3d8f0b11078d38b7c" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" dependencies = [ "time-core", ] @@ -2299,14 +2333,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.25.0" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot", @@ -2314,18 +2347,18 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -2425,11 +2458,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -2438,13 +2470,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -2484,9 +2516,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", @@ -2523,15 +2555,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.10" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "unicode-normalization" @@ -2556,9 +2588,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22fe195a4f217c25b25cb5058ced57059824a678474874038dc88d211bf508d3" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", "idna", @@ -2574,9 +2606,9 @@ checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" [[package]] name = "uuid" -version = "0.8.2" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "5b55a3fef2a1e3b3a00ce878640918820d3c51081576ac657d23af9fc7928fdb" [[package]] name = "valuable" @@ -2606,12 +2638,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2639,7 +2665,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -2673,7 +2699,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2754,19 +2780,28 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + [[package]] name = "windows-sys" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -2775,65 +2810,131 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] name = "windows-targets" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.1" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.42.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winreg" @@ -2858,9 +2959,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zeroize" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" [[package]] name = "zip" diff --git a/tools/ci-cdk/canary-runner/Cargo.toml b/tools/ci-cdk/canary-runner/Cargo.toml index d2325d59f9..b1de3e0aff 100644 --- a/tools/ci-cdk/canary-runner/Cargo.toml +++ b/tools/ci-cdk/canary-runner/Cargo.toml @@ -20,7 +20,7 @@ base64 = "0.13" clap = { version = "3.2.17", features = ["derive"] } hex = "0.4.3" lazy_static = "1" -octorust = "0.1.37" +octorust = "0.3.0" regex = "1.6.0" semver = "1" serde = { version = "1", features = ["derive"] } diff --git a/tools/echo-server/Cargo.lock b/tools/echo-server/Cargo.lock index 07ba693807..bebe33f6fd 100644 --- a/tools/echo-server/Cargo.lock +++ b/tools/echo-server/Cargo.lock @@ -2,24 +2,15 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] @@ -30,9 +21,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.5.16" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e3356844c4d6a6d6467b8da2cffb4a2820be256f50a3a386c9d152bab31043" +checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43" dependencies = [ "async-trait", "axum-core", @@ -61,9 +52,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f0c0a60006f2a293d82d571f635042a72edf927539b7685bd62d361963839b" +checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc" dependencies = [ "async-trait", "bytes", @@ -83,9 +74,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cfg-if" @@ -122,36 +113,36 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-sink" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.24" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-task", @@ -161,9 +152,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.14" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca32592cf21ac7ccab1825cd87f6c9b3d9022c44d086172ed0966bec8af30be" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -186,18 +177,18 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hermit-abi" -version = "0.1.19" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" dependencies = [ "libc", ] [[package]] name = "http" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ "bytes", "fnv", @@ -235,9 +226,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -259,9 +250,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -269,9 +260,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" [[package]] name = "lazy_static" @@ -281,9 +272,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" [[package]] name = "lock_api" @@ -327,15 +318,15 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mio" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", "log", @@ -343,11 +334,21 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ "hermit-abi", "libc", @@ -355,9 +356,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "overload" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking_lot" @@ -371,9 +378,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", @@ -405,7 +412,7 @@ checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -422,18 +429,18 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" -version = "1.0.46" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] @@ -449,11 +456,11 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" dependencies = [ - "regex-syntax", + "regex-syntax 0.7.1", ] [[package]] @@ -462,20 +469,26 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" [[package]] name = "scopeguard" @@ -485,15 +498,15 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" [[package]] name = "serde_json" -version = "1.0.85" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -523,18 +536,18 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" dependencies = [ "autocfg", ] @@ -547,9 +560,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -557,9 +570,20 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.101" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e90cde112c4b9690b8cbe810cba9ddd8bc1d7472e2cae317b69e9438c1cba7d2" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -568,29 +592,29 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "thread_local" -version = "1.1.4" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" dependencies = [ + "cfg-if", "once_cell", ] [[package]] name = "tokio" -version = "1.21.2" +version = "1.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", "parking_lot", @@ -598,25 +622,25 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "tokio-util" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" dependencies = [ "bytes", "futures-core", @@ -644,9 +668,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c530c8675c1dbf98facee631536fa116b5fb6382d7dd6dc1b118d970eafe3ba" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" dependencies = [ "bitflags", "bytes", @@ -663,9 +687,9 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-service" @@ -675,11 +699,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -688,20 +711,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.15", ] [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", "valuable", @@ -720,12 +743,12 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ - "ansi_term", "matchers", + "nu-ansi-term", "once_cell", "regex", "sharded-slab", @@ -738,15 +761,15 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-ident" -version = "1.0.4" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" [[package]] name = "valuable" @@ -794,43 +817,66 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" From bc4c1862ae23526e119ad34f44baa7c253b817c4 Mon Sep 17 00:00:00 2001 From: 82marbag <69267416+82marbag@users.noreply.github.com> Date: Wed, 26 Apr 2023 09:52:47 +0100 Subject: [PATCH 058/253] Lazy initialize mime accept header (#2629) ## Description Do not parse and initialize the mime, known at compile time, on every request. See https://github.com/awslabs/smithy-rs/pull/2607#discussion_r1172607704 ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --------- Signed-off-by: Daniele Ahmed --- .../ServerHttpBoundProtocolGenerator.kt | 22 +++++++++- .../aws-smithy-http-server/src/protocols.rs | 41 +++++++++++++------ 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 85b624b567..c61947a397 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -178,11 +178,13 @@ class ServerHttpBoundProtocolTraitImplGenerator( outputSymbol: Symbol, operationShape: OperationShape, ) { + val operationName = symbolProvider.toSymbol(operationShape).name + val staticContentType = "CONTENT_TYPE_${operationName.uppercase()}" val verifyAcceptHeader = writable { httpBindingResolver.responseContentType(operationShape)?.also { contentType -> rustTemplate( """ - if !#{SmithyHttpServer}::protocols::accept_header_classifier(request.headers(), ${contentType.dq()}) { + if !#{SmithyHttpServer}::protocols::accept_header_classifier(request.headers(), &$staticContentType) { return Err(#{RequestRejection}::NotAcceptable); } """, @@ -190,6 +192,22 @@ class ServerHttpBoundProtocolTraitImplGenerator( ) } } + val verifyAcceptHeaderStaticContentTypeInit = writable { + httpBindingResolver.responseContentType(operationShape)?.also { contentType -> + val init = when (contentType) { + "application/json" -> "const $staticContentType: #{Mime}::Mime = #{Mime}::APPLICATION_JSON;" + "application/octet-stream" -> "const $staticContentType: #{Mime}::Mime = #{Mime}::APPLICATION_OCTET_STREAM;" + "application/x-www-form-urlencoded" -> "const $staticContentType: #{Mime}::Mime = #{Mime}::APPLICATION_WWW_FORM_URLENCODED;" + else -> + """ + static $staticContentType: #{OnceCell}::sync::Lazy<#{Mime}::Mime> = #{OnceCell}::sync::Lazy::new(|| { + ${contentType.dq()}.parse::<#{Mime}::Mime>().expect("BUG: MIME parsing failed, content_type is not valid") + }); + """ + } + rustTemplate(init, *codegenScope) + } + } val verifyRequestContentTypeHeader = writable { operationShape .inputShape(model) @@ -215,6 +233,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( // TODO(https://github.com/awslabs/smithy-rs/issues/2238): Remove the `Pin>` and replace with thin wrapper around `Collect`. rustTemplate( """ + #{verifyAcceptHeaderStaticContentTypeInit:W} #{PinProjectLite}::pin_project! { /// A [`Future`](std::future::Future) aggregating the body bytes of a [`Request`] and constructing the /// [`${inputSymbol.name}`](#{I}) using modelled bindings. @@ -267,6 +286,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( "Marker" to protocol.markerStruct(), "parse_request" to serverParseRequest(operationShape), "verifyAcceptHeader" to verifyAcceptHeader, + "verifyAcceptHeaderStaticContentTypeInit" to verifyAcceptHeaderStaticContentTypeInit, "verifyRequestContentTypeHeader" to verifyRequestContentTypeHeader, ) diff --git a/rust-runtime/aws-smithy-http-server/src/protocols.rs b/rust-runtime/aws-smithy-http-server/src/protocols.rs index 2db5e7163d..75d723f3fc 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocols.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocols.rs @@ -66,14 +66,10 @@ pub fn content_type_header_classifier( Ok(()) } -pub fn accept_header_classifier(headers: &HeaderMap, content_type: &'static str) -> bool { +pub fn accept_header_classifier(headers: &HeaderMap, content_type: &mime::Mime) -> bool { if !headers.contains_key(http::header::ACCEPT) { return true; } - // Must be of the form: type/subtype - let content_type = content_type - .parse::() - .expect("BUG: MIME parsing failed, content_type is not valid"); headers .get_all(http::header::ACCEPT) .into_iter() @@ -195,41 +191,62 @@ mod tests { #[test] fn valid_accept_header_classifier_multiple_values() { let valid_request = req_accept("text/strings, application/json, invalid"); - assert!(accept_header_classifier(&valid_request, "application/json")); + assert!(accept_header_classifier( + &valid_request, + &"application/json".parse().unwrap() + )); } #[test] fn invalid_accept_header_classifier() { let invalid_request = req_accept("text/invalid, invalid, invalid/invalid"); - assert!(!accept_header_classifier(&invalid_request, "application/json")); + assert!(!accept_header_classifier( + &invalid_request, + &"application/json".parse().unwrap() + )); } #[test] fn valid_accept_header_classifier_star() { let valid_request = req_accept("application/*"); - assert!(accept_header_classifier(&valid_request, "application/json")); + assert!(accept_header_classifier( + &valid_request, + &"application/json".parse().unwrap() + )); } #[test] fn valid_accept_header_classifier_star_star() { let valid_request = req_accept("*/*"); - assert!(accept_header_classifier(&valid_request, "application/json")); + assert!(accept_header_classifier( + &valid_request, + &"application/json".parse().unwrap() + )); } #[test] fn valid_empty_accept_header_classifier() { - assert!(accept_header_classifier(&HeaderMap::new(), "application/json")); + assert!(accept_header_classifier( + &HeaderMap::new(), + &"application/json".parse().unwrap() + )); } #[test] fn valid_accept_header_classifier_with_params() { let valid_request = req_accept("application/json; q=30, */*"); - assert!(accept_header_classifier(&valid_request, "application/json")); + assert!(accept_header_classifier( + &valid_request, + &"application/json".parse().unwrap() + )); } #[test] fn valid_accept_header_classifier() { let valid_request = req_accept("application/json"); - assert!(accept_header_classifier(&valid_request, "application/json")); + assert!(accept_header_classifier( + &valid_request, + &"application/json".parse().unwrap() + )); } } From 9f10be04b4c268905533a5a5ac2775dc9631b45a Mon Sep 17 00:00:00 2001 From: Thomas Cameron <68596478+thomas-k-cameron@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:54:48 +0900 Subject: [PATCH 059/253] Add `RUSTFLAGS = aws_sdk_unstable` as environment variable when running Cargo from kotlin (#2614) ## Motivation and Context This PR set `aws_sdk_unstable` to RUSTFALGS when running cargo from kotlin. It is required to enable test gated features introduced on RFC30. No breaking changers are introduced. ## Description add `RUSTFLAGS = aws_sdk_unstable` when running cargo from kotlin. ## Parent PR This PR is listed as one of prerequisite PRs on this PR. - https://github.com/awslabs/smithy-rs/pull/2615 ## Testing NA. ---- *By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.* --- buildSrc/src/main/kotlin/CodegenTestCommon.kt | 4 ++++ buildSrc/src/main/kotlin/RustBuildTool.kt | 1 + 2 files changed, 5 insertions(+) diff --git a/buildSrc/src/main/kotlin/CodegenTestCommon.kt b/buildSrc/src/main/kotlin/CodegenTestCommon.kt index 2173c237a6..e42d7e5ed2 100644 --- a/buildSrc/src/main/kotlin/CodegenTestCommon.kt +++ b/buildSrc/src/main/kotlin/CodegenTestCommon.kt @@ -244,12 +244,14 @@ fun Project.registerCargoCommandsTasks( this.tasks.register(Cargo.CHECK.toString) { dependsOn(dependentTasks) workingDir(outputDir) + environment("RUSTFLAGS", "--cfg aws_sdk_unstable") commandLine("cargo", "check", "--lib", "--tests", "--benches", "--all-features") } this.tasks.register(Cargo.TEST.toString) { dependsOn(dependentTasks) workingDir(outputDir) + environment("RUSTFLAGS", "--cfg aws_sdk_unstable") commandLine("cargo", "test", "--all-features") } @@ -257,12 +259,14 @@ fun Project.registerCargoCommandsTasks( dependsOn(dependentTasks) workingDir(outputDir) environment("RUSTDOCFLAGS", defaultRustDocFlags) + environment("RUSTFLAGS", "--cfg aws_sdk_unstable") commandLine("cargo", "doc", "--no-deps", "--document-private-items") } this.tasks.register(Cargo.CLIPPY.toString) { dependsOn(dependentTasks) workingDir(outputDir) + environment("RUSTFLAGS", "--cfg aws_sdk_unstable") commandLine("cargo", "clippy") } } diff --git a/buildSrc/src/main/kotlin/RustBuildTool.kt b/buildSrc/src/main/kotlin/RustBuildTool.kt index 1e67995648..b8a1a15fb1 100644 --- a/buildSrc/src/main/kotlin/RustBuildTool.kt +++ b/buildSrc/src/main/kotlin/RustBuildTool.kt @@ -28,6 +28,7 @@ private fun runCli( } } .copyTo(action) + action.environment("RUSTFLAGS", "--cfg aws_sdk_unstable") action.execute() } } From d729759940de5b095a73be2afae9b9cc36d1bfe9 Mon Sep 17 00:00:00 2001 From: Thomas Cameron <68596478+thomas-k-cameron@users.noreply.github.com> Date: Wed, 26 Apr 2023 23:55:39 +0900 Subject: [PATCH 060/253] Refactor aws smithy types (#2638) ## Motivation and Context This PR refactors `aws-smithy-types` crate. `Blob`, `Datetime`, `Number` and `Document` structs now goes to it's own files. No changes on feature is introduced. This is a child-PR of https://github.com/awslabs/smithy-rs/pull/2616 . However, it is completely independent. PR that introduces same changes were previously merged to `unstable-serde` branch, however, it has not been merged to main branch. ## Testing NA ## Checklist This PR does not introduce API changes. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- rust-runtime/aws-smithy-types/src/blob.rs | 32 ++ rust-runtime/aws-smithy-types/src/document.rs | 73 +++ rust-runtime/aws-smithy-types/src/lib.rs | 544 +----------------- rust-runtime/aws-smithy-types/src/number.rs | 444 ++++++++++++++ 4 files changed, 556 insertions(+), 537 deletions(-) create mode 100644 rust-runtime/aws-smithy-types/src/blob.rs create mode 100644 rust-runtime/aws-smithy-types/src/document.rs create mode 100644 rust-runtime/aws-smithy-types/src/number.rs diff --git a/rust-runtime/aws-smithy-types/src/blob.rs b/rust-runtime/aws-smithy-types/src/blob.rs new file mode 100644 index 0000000000..bdd335c492 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/blob.rs @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Binary Blob Type +/// +/// Blobs represent protocol-agnostic binary content. +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct Blob { + inner: Vec, +} + +impl Blob { + /// Creates a new blob from the given `input`. + pub fn new>>(input: T) -> Self { + Blob { + inner: input.into(), + } + } + + /// Consumes the `Blob` and returns a `Vec` with its contents. + pub fn into_inner(self) -> Vec { + self.inner + } +} + +impl AsRef<[u8]> for Blob { + fn as_ref(&self) -> &[u8] { + &self.inner + } +} diff --git a/rust-runtime/aws-smithy-types/src/document.rs b/rust-runtime/aws-smithy-types/src/document.rs new file mode 100644 index 0000000000..4bfb641fe3 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/document.rs @@ -0,0 +1,73 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::Number; +use std::collections::HashMap; + +/* ANCHOR: document */ + +/// Document Type +/// +/// Document types represents protocol-agnostic open content that is accessed like JSON data. +/// Open content is useful for modeling unstructured data that has no schema, data that can't be +/// modeled using rigid types, or data that has a schema that evolves outside of the purview of a model. +/// The serialization format of a document is an implementation detail of a protocol. +#[derive(Debug, Clone, PartialEq)] +pub enum Document { + /// JSON object + Object(HashMap), + /// JSON array + Array(Vec), + /// JSON number + Number(Number), + /// JSON string + String(String), + /// JSON boolean + Bool(bool), + /// JSON null + Null, +} + +impl From for Document { + fn from(value: bool) -> Self { + Document::Bool(value) + } +} + +impl From for Document { + fn from(value: String) -> Self { + Document::String(value) + } +} + +impl From> for Document { + fn from(values: Vec) -> Self { + Document::Array(values) + } +} + +impl From> for Document { + fn from(values: HashMap) -> Self { + Document::Object(values) + } +} + +impl From for Document { + fn from(value: u64) -> Self { + Document::Number(Number::PosInt(value)) + } +} + +impl From for Document { + fn from(value: i64) -> Self { + Document::Number(Number::NegInt(value)) + } +} + +impl From for Document { + fn from(value: i32) -> Self { + Document::Number(Number::NegInt(value as i64)) + } +} diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index 2634ff7f39..ffa1c86ea5 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -13,10 +13,6 @@ rust_2018_idioms, unreachable_pub )] - -use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; -use std::collections::HashMap; - pub mod base64; pub mod date_time; pub mod endpoint; @@ -25,543 +21,17 @@ pub mod primitive; pub mod retry; pub mod timeout; -pub use crate::date_time::DateTime; +mod blob; +mod document; +mod number; +pub use blob::Blob; +pub use date_time::DateTime; +pub use document::Document; // TODO(deprecated): Remove deprecated re-export /// Use [error::ErrorMetadata] instead. #[deprecated( note = "`aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`" )] pub use error::ErrorMetadata as Error; - -/// Binary Blob Type -/// -/// Blobs represent protocol-agnostic binary content. -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub struct Blob { - inner: Vec, -} - -impl Blob { - /// Creates a new blob from the given `input`. - pub fn new>>(input: T) -> Self { - Blob { - inner: input.into(), - } - } - - /// Consumes the `Blob` and returns a `Vec` with its contents. - pub fn into_inner(self) -> Vec { - self.inner - } -} - -impl AsRef<[u8]> for Blob { - fn as_ref(&self) -> &[u8] { - &self.inner - } -} - -/* ANCHOR: document */ - -/// Document Type -/// -/// Document types represents protocol-agnostic open content that is accessed like JSON data. -/// Open content is useful for modeling unstructured data that has no schema, data that can't be -/// modeled using rigid types, or data that has a schema that evolves outside of the purview of a model. -/// The serialization format of a document is an implementation detail of a protocol. -#[derive(Debug, Clone, PartialEq)] -pub enum Document { - /// JSON object - Object(HashMap), - /// JSON array - Array(Vec), - /// JSON number - Number(Number), - /// JSON string - String(String), - /// JSON boolean - Bool(bool), - /// JSON null - Null, -} - -impl From for Document { - fn from(value: bool) -> Self { - Document::Bool(value) - } -} - -impl From for Document { - fn from(value: String) -> Self { - Document::String(value) - } -} - -impl From> for Document { - fn from(values: Vec) -> Self { - Document::Array(values) - } -} - -impl From> for Document { - fn from(values: HashMap) -> Self { - Document::Object(values) - } -} - -impl From for Document { - fn from(value: u64) -> Self { - Document::Number(Number::PosInt(value)) - } -} - -impl From for Document { - fn from(value: i64) -> Self { - Document::Number(Number::NegInt(value)) - } -} - -impl From for Document { - fn from(value: i32) -> Self { - Document::Number(Number::NegInt(value as i64)) - } -} - -/// A number type that implements Javascript / JSON semantics, modeled on serde_json: -/// -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum Number { - /// Unsigned 64-bit integer value. - PosInt(u64), - /// Signed 64-bit integer value. The wrapped value is _always_ negative. - NegInt(i64), - /// 64-bit floating-point value. - Float(f64), -} - -/* ANCHOR_END: document */ - -impl Number { - /// Converts to an `f64` lossily. - /// Use `Number::try_from` to make the conversion only if it is not lossy. - pub fn to_f64_lossy(self) -> f64 { - match self { - Number::PosInt(v) => v as f64, - Number::NegInt(v) => v as f64, - Number::Float(v) => v, - } - } - - /// Converts to an `f32` lossily. - /// Use `Number::try_from` to make the conversion only if it is not lossy. - pub fn to_f32_lossy(self) -> f32 { - match self { - Number::PosInt(v) => v as f32, - Number::NegInt(v) => v as f32, - Number::Float(v) => v as f32, - } - } -} - -macro_rules! to_unsigned_integer_converter { - ($typ:ident, $styp:expr) => { - #[doc = "Converts to a `"] - #[doc = $styp] - #[doc = "`. This conversion fails if it is lossy."] - impl TryFrom for $typ { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => Ok(Self::try_from(v)?), - Number::NegInt(v) => { - Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) - } - Number::Float(v) => { - Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) - } - } - } - } - }; - - ($typ:ident) => { - to_unsigned_integer_converter!($typ, stringify!($typ)); - }; -} - -macro_rules! to_signed_integer_converter { - ($typ:ident, $styp:expr) => { - #[doc = "Converts to a `"] - #[doc = $styp] - #[doc = "`. This conversion fails if it is lossy."] - impl TryFrom for $typ { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => Ok(Self::try_from(v)?), - Number::NegInt(v) => Ok(Self::try_from(v)?), - Number::Float(v) => { - Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) - } - } - } - } - }; - - ($typ:ident) => { - to_signed_integer_converter!($typ, stringify!($typ)); - }; -} - -/// Converts to a `u64`. The conversion fails if it is lossy. -impl TryFrom for u64 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => Ok(v), - Number::NegInt(v) => { - Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) - } - Number::Float(v) => { - Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) - } - } - } -} -to_unsigned_integer_converter!(u32); -to_unsigned_integer_converter!(u16); -to_unsigned_integer_converter!(u8); - -impl TryFrom for i64 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => Ok(Self::try_from(v)?), - Number::NegInt(v) => Ok(v), - Number::Float(v) => { - Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) - } - } - } -} -to_signed_integer_converter!(i32); -to_signed_integer_converter!(i16); -to_signed_integer_converter!(i8); - -/// Converts to an `f64`. The conversion fails if it is lossy. -impl TryFrom for f64 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - // Integers can only be represented with full precision in a float if they fit in the - // significand, which is 24 bits in `f32` and 53 bits in `f64`. - // https://github.com/rust-lang/rust/blob/58f11791af4f97572e7afd83f11cffe04bbbd12f/library/core/src/convert/num.rs#L151-L153 - Number::PosInt(v) => { - if v <= (1 << 53) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) - } - } - Number::NegInt(v) => { - if (-(1 << 53)..=(1 << 53)).contains(&v) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) - } - } - Number::Float(v) => Ok(v), - } - } -} - -/// Converts to an `f64`. The conversion fails if it is lossy. -impl TryFrom for f32 { - type Error = TryFromNumberError; - - fn try_from(value: Number) -> Result { - match value { - Number::PosInt(v) => { - if v <= (1 << 24) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) - } - } - Number::NegInt(v) => { - if (-(1 << 24)..=(1 << 24)).contains(&v) { - Ok(v as Self) - } else { - Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) - } - } - Number::Float(v) => Err(TryFromNumberErrorKind::F64ToF32LossyConversion(v).into()), - } - } -} - -#[cfg(test)] -mod number { - use super::*; - use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; - - macro_rules! to_unsigned_converter_tests { - ($typ:ident) => { - assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); - - assert!(matches!( - $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::OutsideIntegerRange(..) - } - )); - - assert!(matches!( - $typ::try_from(Number::NegInt(-1i64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) - } - )); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - $typ::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - }; - } - - #[test] - fn to_u64() { - assert_eq!(u64::try_from(Number::PosInt(69u64)).unwrap(), 69u64); - - assert!(matches!( - u64::try_from(Number::NegInt(-1i64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) - } - )); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - u64::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - } - - #[test] - fn to_u32() { - to_unsigned_converter_tests!(u32); - } - - #[test] - fn to_u16() { - to_unsigned_converter_tests!(u16); - } - - #[test] - fn to_u8() { - to_unsigned_converter_tests!(u8); - } - - macro_rules! to_signed_converter_tests { - ($typ:ident) => { - assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); - assert_eq!($typ::try_from(Number::NegInt(-69i64)).unwrap(), -69); - - assert!(matches!( - $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::OutsideIntegerRange(..) - } - )); - - assert!(matches!( - $typ::try_from(Number::NegInt(($typ::MIN as i64) - 1i64)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::OutsideIntegerRange(..) - } - )); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - u64::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - }; - } - - #[test] - fn to_i64() { - assert_eq!(i64::try_from(Number::PosInt(69u64)).unwrap(), 69); - assert_eq!(i64::try_from(Number::NegInt(-69i64)).unwrap(), -69); - - for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - u64::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) - } - )); - } - } - - #[test] - fn to_i32() { - to_signed_converter_tests!(i32); - } - - #[test] - fn to_i16() { - to_signed_converter_tests!(i16); - } - - #[test] - fn to_i8() { - to_signed_converter_tests!(i8); - } - - #[test] - fn to_f64() { - assert_eq!(f64::try_from(Number::PosInt(69u64)).unwrap(), 69f64); - assert_eq!(f64::try_from(Number::NegInt(-69i64)).unwrap(), -69f64); - assert_eq!(f64::try_from(Number::Float(-69f64)).unwrap(), -69f64); - assert!(f64::try_from(Number::Float(f64::NAN)).unwrap().is_nan()); - assert_eq!( - f64::try_from(Number::Float(f64::INFINITY)).unwrap(), - f64::INFINITY - ); - assert_eq!( - f64::try_from(Number::Float(f64::NEG_INFINITY)).unwrap(), - f64::NEG_INFINITY - ); - - let significand_max_u64: u64 = 1 << 53; - let significand_max_i64: i64 = 1 << 53; - - assert_eq!( - f64::try_from(Number::PosInt(significand_max_u64)).unwrap(), - 9007199254740992f64 - ); - - assert_eq!( - f64::try_from(Number::NegInt(significand_max_i64)).unwrap(), - 9007199254740992f64 - ); - assert_eq!( - f64::try_from(Number::NegInt(-significand_max_i64)).unwrap(), - -9007199254740992f64 - ); - - assert!(matches!( - f64::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) - } - )); - - assert!(matches!( - f64::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - assert!(matches!( - f64::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - } - - #[test] - fn to_f32() { - assert_eq!(f32::try_from(Number::PosInt(69u64)).unwrap(), 69f32); - assert_eq!(f32::try_from(Number::NegInt(-69i64)).unwrap(), -69f32); - - let significand_max_u64: u64 = 1 << 24; - let significand_max_i64: i64 = 1 << 24; - - assert_eq!( - f32::try_from(Number::PosInt(significand_max_u64)).unwrap(), - 16777216f32 - ); - - assert_eq!( - f32::try_from(Number::NegInt(significand_max_i64)).unwrap(), - 16777216f32 - ); - assert_eq!( - f32::try_from(Number::NegInt(-significand_max_i64)).unwrap(), - -16777216f32 - ); - - assert!(matches!( - f32::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) - } - )); - - assert!(matches!( - f32::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - assert!(matches!( - f32::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) - } - )); - - for val in [69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { - assert!(matches!( - f32::try_from(Number::Float(val)).unwrap_err(), - TryFromNumberError { - kind: TryFromNumberErrorKind::F64ToF32LossyConversion(..) - } - )); - } - } - - #[test] - fn to_f64_lossy() { - assert_eq!(Number::PosInt(69u64).to_f64_lossy(), 69f64); - assert_eq!( - Number::PosInt((1 << 53) + 1).to_f64_lossy(), - 9007199254740992f64 - ); - assert_eq!( - Number::NegInt(-(1 << 53) - 1).to_f64_lossy(), - -9007199254740992f64 - ); - } - - #[test] - fn to_f32_lossy() { - assert_eq!(Number::PosInt(69u64).to_f32_lossy(), 69f32); - assert_eq!(Number::PosInt((1 << 24) + 1).to_f32_lossy(), 16777216f32); - assert_eq!(Number::NegInt(-(1 << 24) - 1).to_f32_lossy(), -16777216f32); - assert_eq!( - Number::Float(1452089033.7674935).to_f32_lossy(), - 1452089100f32 - ); - } -} +pub use number::Number; diff --git a/rust-runtime/aws-smithy-types/src/number.rs b/rust-runtime/aws-smithy-types/src/number.rs new file mode 100644 index 0000000000..76fc08a218 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/number.rs @@ -0,0 +1,444 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; + +/// A number type that implements Javascript / JSON semantics, modeled on serde_json: +/// +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum Number { + /// Unsigned 64-bit integer value. + PosInt(u64), + /// Signed 64-bit integer value. The wrapped value is _always_ negative. + NegInt(i64), + /// 64-bit floating-point value. + Float(f64), +} + +/* ANCHOR_END: document */ + +impl Number { + /// Converts to an `f64` lossily. + /// Use `Number::try_from` to make the conversion only if it is not lossy. + pub fn to_f64_lossy(self) -> f64 { + match self { + Number::PosInt(v) => v as f64, + Number::NegInt(v) => v as f64, + Number::Float(v) => v, + } + } + + /// Converts to an `f32` lossily. + /// Use `Number::try_from` to make the conversion only if it is not lossy. + pub fn to_f32_lossy(self) -> f32 { + match self { + Number::PosInt(v) => v as f32, + Number::NegInt(v) => v as f32, + Number::Float(v) => v as f32, + } + } +} + +macro_rules! to_unsigned_integer_converter { + ($typ:ident, $styp:expr) => { + #[doc = "Converts to a `"] + #[doc = $styp] + #[doc = "`. This conversion fails if it is lossy."] + impl TryFrom for $typ { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => Ok(Self::try_from(v)?), + Number::NegInt(v) => { + Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) + } + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } + } + } + } + }; + + ($typ:ident) => { + to_unsigned_integer_converter!($typ, stringify!($typ)); + }; +} + +macro_rules! to_signed_integer_converter { + ($typ:ident, $styp:expr) => { + #[doc = "Converts to a `"] + #[doc = $styp] + #[doc = "`. This conversion fails if it is lossy."] + impl TryFrom for $typ { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => Ok(Self::try_from(v)?), + Number::NegInt(v) => Ok(Self::try_from(v)?), + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } + } + } + } + }; + + ($typ:ident) => { + to_signed_integer_converter!($typ, stringify!($typ)); + }; +} + +/// Converts to a `u64`. The conversion fails if it is lossy. +impl TryFrom for u64 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => Ok(v), + Number::NegInt(v) => { + Err(TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(v).into()) + } + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } + } + } +} +to_unsigned_integer_converter!(u32); +to_unsigned_integer_converter!(u16); +to_unsigned_integer_converter!(u8); + +impl TryFrom for i64 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => Ok(Self::try_from(v)?), + Number::NegInt(v) => Ok(v), + Number::Float(v) => { + Err(TryFromNumberErrorKind::FloatToIntegerLossyConversion(v).into()) + } + } + } +} +to_signed_integer_converter!(i32); +to_signed_integer_converter!(i16); +to_signed_integer_converter!(i8); + +/// Converts to an `f64`. The conversion fails if it is lossy. +impl TryFrom for f64 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + // Integers can only be represented with full precision in a float if they fit in the + // significand, which is 24 bits in `f32` and 53 bits in `f64`. + // https://github.com/rust-lang/rust/blob/58f11791af4f97572e7afd83f11cffe04bbbd12f/library/core/src/convert/num.rs#L151-L153 + Number::PosInt(v) => { + if v <= (1 << 53) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) + } + } + Number::NegInt(v) => { + if (-(1 << 53)..=(1 << 53)).contains(&v) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) + } + } + Number::Float(v) => Ok(v), + } + } +} + +/// Converts to an `f64`. The conversion fails if it is lossy. +impl TryFrom for f32 { + type Error = TryFromNumberError; + + fn try_from(value: Number) -> Result { + match value { + Number::PosInt(v) => { + if v <= (1 << 24) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::U64ToFloatLossyConversion(v).into()) + } + } + Number::NegInt(v) => { + if (-(1 << 24)..=(1 << 24)).contains(&v) { + Ok(v as Self) + } else { + Err(TryFromNumberErrorKind::I64ToFloatLossyConversion(v).into()) + } + } + Number::Float(v) => Err(TryFromNumberErrorKind::F64ToF32LossyConversion(v).into()), + } + } +} + +#[cfg(test)] +mod number { + use super::*; + use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; + + macro_rules! to_unsigned_converter_tests { + ($typ:ident) => { + assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); + + assert!(matches!( + $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::OutsideIntegerRange(..) + } + )); + + assert!(matches!( + $typ::try_from(Number::NegInt(-1i64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) + } + )); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + $typ::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + }; + } + + #[test] + fn to_u64() { + assert_eq!(u64::try_from(Number::PosInt(69u64)).unwrap(), 69u64); + + assert!(matches!( + u64::try_from(Number::NegInt(-1i64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::NegativeToUnsignedLossyConversion(..) + } + )); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + u64::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + } + + #[test] + fn to_u32() { + to_unsigned_converter_tests!(u32); + } + + #[test] + fn to_u16() { + to_unsigned_converter_tests!(u16); + } + + #[test] + fn to_u8() { + to_unsigned_converter_tests!(u8); + } + + macro_rules! to_signed_converter_tests { + ($typ:ident) => { + assert_eq!($typ::try_from(Number::PosInt(69u64)).unwrap(), 69); + assert_eq!($typ::try_from(Number::NegInt(-69i64)).unwrap(), -69); + + assert!(matches!( + $typ::try_from(Number::PosInt(($typ::MAX as u64) + 1u64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::OutsideIntegerRange(..) + } + )); + + assert!(matches!( + $typ::try_from(Number::NegInt(($typ::MIN as i64) - 1i64)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::OutsideIntegerRange(..) + } + )); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + u64::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + }; + } + + #[test] + fn to_i64() { + assert_eq!(i64::try_from(Number::PosInt(69u64)).unwrap(), 69); + assert_eq!(i64::try_from(Number::NegInt(-69i64)).unwrap(), -69); + + for val in [69.69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + u64::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::FloatToIntegerLossyConversion(..) + } + )); + } + } + + #[test] + fn to_i32() { + to_signed_converter_tests!(i32); + } + + #[test] + fn to_i16() { + to_signed_converter_tests!(i16); + } + + #[test] + fn to_i8() { + to_signed_converter_tests!(i8); + } + + #[test] + fn to_f64() { + assert_eq!(f64::try_from(Number::PosInt(69u64)).unwrap(), 69f64); + assert_eq!(f64::try_from(Number::NegInt(-69i64)).unwrap(), -69f64); + assert_eq!(f64::try_from(Number::Float(-69f64)).unwrap(), -69f64); + assert!(f64::try_from(Number::Float(f64::NAN)).unwrap().is_nan()); + assert_eq!( + f64::try_from(Number::Float(f64::INFINITY)).unwrap(), + f64::INFINITY + ); + assert_eq!( + f64::try_from(Number::Float(f64::NEG_INFINITY)).unwrap(), + f64::NEG_INFINITY + ); + + let significand_max_u64: u64 = 1 << 53; + let significand_max_i64: i64 = 1 << 53; + + assert_eq!( + f64::try_from(Number::PosInt(significand_max_u64)).unwrap(), + 9007199254740992f64 + ); + + assert_eq!( + f64::try_from(Number::NegInt(significand_max_i64)).unwrap(), + 9007199254740992f64 + ); + assert_eq!( + f64::try_from(Number::NegInt(-significand_max_i64)).unwrap(), + -9007199254740992f64 + ); + + assert!(matches!( + f64::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) + } + )); + + assert!(matches!( + f64::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + assert!(matches!( + f64::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + } + + #[test] + fn to_f32() { + assert_eq!(f32::try_from(Number::PosInt(69u64)).unwrap(), 69f32); + assert_eq!(f32::try_from(Number::NegInt(-69i64)).unwrap(), -69f32); + + let significand_max_u64: u64 = 1 << 24; + let significand_max_i64: i64 = 1 << 24; + + assert_eq!( + f32::try_from(Number::PosInt(significand_max_u64)).unwrap(), + 16777216f32 + ); + + assert_eq!( + f32::try_from(Number::NegInt(significand_max_i64)).unwrap(), + 16777216f32 + ); + assert_eq!( + f32::try_from(Number::NegInt(-significand_max_i64)).unwrap(), + -16777216f32 + ); + + assert!(matches!( + f32::try_from(Number::PosInt(significand_max_u64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::U64ToFloatLossyConversion(..) + } + )); + + assert!(matches!( + f32::try_from(Number::NegInt(significand_max_i64 + 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + assert!(matches!( + f32::try_from(Number::NegInt(-significand_max_i64 - 1)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::I64ToFloatLossyConversion(..) + } + )); + + for val in [69f64, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] { + assert!(matches!( + f32::try_from(Number::Float(val)).unwrap_err(), + TryFromNumberError { + kind: TryFromNumberErrorKind::F64ToF32LossyConversion(..) + } + )); + } + } + + #[test] + fn to_f64_lossy() { + assert_eq!(Number::PosInt(69u64).to_f64_lossy(), 69f64); + assert_eq!( + Number::PosInt((1 << 53) + 1).to_f64_lossy(), + 9007199254740992f64 + ); + assert_eq!( + Number::NegInt(-(1 << 53) - 1).to_f64_lossy(), + -9007199254740992f64 + ); + } + + #[test] + fn to_f32_lossy() { + assert_eq!(Number::PosInt(69u64).to_f32_lossy(), 69f32); + assert_eq!(Number::PosInt((1 << 24) + 1).to_f32_lossy(), 16777216f32); + assert_eq!(Number::NegInt(-(1 << 24) - 1).to_f32_lossy(), -16777216f32); + assert_eq!( + Number::Float(1452089033.7674935).to_f32_lossy(), + 1452089100f32 + ); + } +} From f7265aeb492ff39bf3bb47c48c303e88f9609131 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Wed, 26 Apr 2023 12:14:16 -0500 Subject: [PATCH 061/253] Port fix for debug bound on ResolveEndpoint (#2636) ## Motivation and Context Port https://github.com/awslabs/smithy-rs/pull/2630 to the `main` branch. ## Description Adding `Debug` bound on `ResolveEndpoint` in https://github.com/awslabs/smithy-rs/pull/2577 was a breaking change. Unfortunately, the semvar check that ran in that PR barked at a different thing (i.e. barked at moving `StaticUriEndpointResolver` and `DefaultEndpointResolver` from `aws-smithy-runtime-api` to `aws-smithy-runtime`). Eventually the breaking change got merged to the `main` branch. This PR reverts the change in question and implements the `Debug` trait for `ResolveEndpoint` in a semvar non-breaking way. ## Testing - [ ] Passed tests in CI ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ Co-authored-by: Yuki Saito --- .../generators/config/ServiceConfigGenerator.kt | 15 ++++++++++++++- rust-runtime/aws-smithy-http/src/endpoint.rs | 14 ++++++++++---- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index f6972f5e6a..bb0653f327 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -228,12 +228,25 @@ class ServiceConfigGenerator(private val customizations: List) -> std::fmt::Result { + let mut config = f.debug_struct("Builder"); + config.finish() + } + """, + ) + } + writer.rustBlock("impl Builder") { writer.docs("Constructs a config builder.") writer.rustTemplate("pub fn new() -> Self { Self::default() }") diff --git a/rust-runtime/aws-smithy-http/src/endpoint.rs b/rust-runtime/aws-smithy-http/src/endpoint.rs index d9d7844746..dcc1db18d7 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint.rs @@ -9,7 +9,7 @@ use crate::endpoint::error::InvalidEndpointError; use crate::operation::error::BuildError; use http::uri::{Authority, Uri}; use std::borrow::Cow; -use std::fmt::Debug; +use std::fmt::{Debug, Formatter}; use std::result::Result as StdResult; use std::str::FromStr; use std::sync::Arc; @@ -23,7 +23,7 @@ pub use error::ResolveEndpointError; pub type Result = std::result::Result; /// Implementors of this trait can resolve an endpoint that will be applied to a request. -pub trait ResolveEndpoint: Debug + Send + Sync { +pub trait ResolveEndpoint: Send + Sync { /// Given some endpoint parameters, resolve an endpoint or return an error when resolution is /// impossible. fn resolve_endpoint(&self, params: &Params) -> Result; @@ -38,9 +38,15 @@ impl ResolveEndpoint for &'static str { } /// Endpoint Resolver wrapper that may be shared -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct SharedEndpointResolver(Arc>); +impl Debug for SharedEndpointResolver { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SharedEndpointResolver").finish() + } +} + impl SharedEndpointResolver { /// Create a new `SharedEndpointResolver` from `ResolveEndpoint` pub fn new(resolve_endpoint: impl ResolveEndpoint + 'static) -> Self { @@ -60,7 +66,7 @@ impl From>> for SharedEndpointResolver { } } -impl ResolveEndpoint for SharedEndpointResolver { +impl ResolveEndpoint for SharedEndpointResolver { fn resolve_endpoint(&self, params: &T) -> Result { self.0.resolve_endpoint(params) } From cfd22450ee41889712547c3f824af36bd64e5ca9 Mon Sep 17 00:00:00 2001 From: Burak Date: Wed, 26 Apr 2023 18:14:46 +0100 Subject: [PATCH 062/253] Remove `CHANGELOG.next.toml` entries that have been released with `v0.55.2` (#2624) ## Motivation and Context Removes changelog entries that have been released with `v0.55.2` (https://github.com/awslabs/smithy-rs/pull/2623) ## Description ## Testing ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --- CHANGELOG.next.toml | 161 -------------------------------------------- 1 file changed, 161 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index a03141576a..0afcaae4a3 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -10,164 +10,3 @@ # references = ["smithy-rs#920"] # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} # author = "rcoh" - -[[smithy-rs]] -message = """ -Implement layer for servers to handle [ALB health checks](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html). -Take a look at `aws_smithy_http_server::plugin::alb_health_check` to learn about it. -""" -references = ["smithy-rs#2540"] -meta = { "breaking" = false, "tada" = true, "bug" = false, "target" = "server" } -author = "jjant" - -[[smithy-rs]] -message = "Implement `PluginPipeline::http_layer` which allows you to apply a `tower::Layer` to all operations." -references = ["smithy-rs#2540"] -meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "server" } -author = "jjant" - -[[aws-sdk-rust]] -message = "Implement std::error::Error#source() properly for the service meta Error enum." -references = ["aws-sdk-rust#784"] -meta = { "breaking" = false, "tada" = false, "bug" = false } -author = "abusch" - -[[smithy-rs]] -message = "Implement std::error::Error#source() properly for the service meta Error enum." -references = ["aws-sdk-rust#784"] -meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } -author = "abusch" - -[[aws-sdk-rust]] -message = "The outputs for event stream operations (for example, S3's SelectObjectContent) now implement the `Sync` auto-trait." -references = ["smithy-rs#2496"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "jdisanti" - -[[smithy-rs]] -message = "The outputs for event stream operations now implement the `Sync` auto-trait." -references = ["smithy-rs#2496"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "all" } -author = "jdisanti" - -[[aws-sdk-rust]] -message = "The AWS SDK now compiles for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it!" -references = ["smithy-rs#2254"] -meta = { "breaking" = false, "tada" = true, "bug" = false } -author = "eduardomourar" - -[[smithy-rs]] -message = "Clients now compile for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it!" -references = ["smithy-rs#2254"] -meta = { "breaking" = false, "tada" = true, "bug" = false, "target" = "client" } -author = "eduardomourar" - -[[smithy-rs]] -message = "Streaming operations now emit the request ID at the `debug` log level like their non-streaming counterparts." -references = ["smithy-rs#2495"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "jdisanti" - -[[smithy-rs]] -message = "Streaming operations now emit the request ID at the `debug` log level like their non-streaming counterparts." -references = ["smithy-rs#2495"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client" } -author = "jdisanti" - -[[smithy-rs]] -message = "The `enableNewCrateOrganizationScheme` codegen flag has been removed. If you opted out of the new crate organization scheme, it must be adopted now in order to upgrade (see [the upgrade guidance](https://github.com/awslabs/smithy-rs/discussions/2449) from March 23rd's release)." -references = ["smithy-rs#2507"] -meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } -author = "jdisanti" - -[[aws-sdk-rust]] -message = """ -S3's `GetObject` will no longer panic when checksum validation is enabled and the target object was uploaded as a multi-part upload. -However, these objects cannot be checksum validated by the SDK due to the way checksums are calculated for multipart uploads. -For more information, see [this page](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#large-object-checksums). -""" -references = ["aws-sdk-rust#764"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "Velfi" - -[[aws-sdk-rust]] -message = "`AppName` is now configurable from within `ConfigLoader`." -references = ["smithy-rs#2513"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "ysaito1001" - -[[aws-sdk-rust]] -message = "Add support for omitting session token in canonical requests for SigV4 signing." -references = ["smithy-rs#2473"] -meta = { "breaking" = false, "tada" = false, "bug" = false } -author = "martinjlowm" - -[[aws-sdk-rust]] -message = "Add `into_segments` method to `AggregatedBytes`, for zero-copy conversions." -references = ["smithy-rs#2525"] -meta = { "breaking" = false, "tada" = false, "bug" = false } -author = "parker-timmerman" - -[[aws-sdk-rust]] -message = "Fix bug where an incorrect endpoint was produced for `WriteGetObjectResponse`" -references = ["smithy-rs#781", "aws-sdk-rust#781"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "rcoh" - -[[aws-sdk-rust]] -message = "Update the `std::fmt::Debug` implementation for `aws-sigv4::SigningParams` so that it will no longer print sensitive information." -references = ["smithy-rs#2562"] -meta = { "breaking" = false, "tada" = true, "bug" = true } -author = "Velfi" - -[[aws-sdk-rust]] -message = "`aws_smithy_types::date_time::Format` has been re-exported in SDK crates." -references = ["smithy-rs#2534"] -meta = { "breaking" = false, "tada" = false, "bug" = false } -author = "ysaito1001" - -[[smithy-rs]] -message = "`aws_smithy_types::date_time::Format` has been re-exported in service client crates." -references = ["smithy-rs#2534"] -meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } -author = "ysaito1001" - -[[smithy-rs]] -message = "Fix generation of constrained shapes reaching `@sensitive` shapes" -references = ["smithy-rs#2582", "smithy-rs#2585"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" } -author = "david-perez" - -[[smithy-rs]] -message = "Fix server code generation bug affecting constrained shapes bound with `@httpPayload`" -references = ["smithy-rs#2583", "smithy-rs#2584"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" } -author = "david-perez" - -[[aws-sdk-rust]] -message = """Reduce several instances of credential exposure in the SDK logs: -- IMDS now suppresses the body of the response from logs -- `aws-sigv4` marks the `x-amz-session-token` header as sensitive -- STS & SSO credentials have been manually marked as sensitive which suppresses logging of response bodies for relevant operations -""" -author = "rcoh" -references = ["smithy-rs#2603"] -meta = { "breaking" = false, "tada" = false, "bug" = false } - -[[smithy-rs]] -message = "Add a sensitive method to `ParseHttpResponse`. When this returns true, logging of the HTTP response body will be suppressed." -author = "rcoh" -references = ["smithy-rs#2603"] -meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } - -[[aws-sdk-rust]] -message = "Update MSRV to Rust 1.67.1" -references = ["smithy-rs#2611"] -meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "jdisanti" - -[[smithy-rs]] -message = "Update MSRV to Rust 1.67.1" -references = ["smithy-rs#2611"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all"} -author = "jdisanti" From 6af5d40d3bc942d840bec58fa99f19bd8f25a5d2 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 26 Apr 2023 16:19:47 -0400 Subject: [PATCH 063/253] Add useful Debug impl to PropertyBag and ConfigBag (#2612) ## Motivation and Context - it's hard to debug what properties are set, especially for layered configuration ## Description Add Debug Info for PropertyBag and Config bag: ``` PropertyBag { contents: ["aws_smithy_http::property_bag::test::test_extensions::MyType"] } ``` ``` ConfigBag { layers: [ Layer { name: "c", properties: [ "aws_smithy_runtime_api::config_bag::Value", "aws_smithy_runtime_api::config_bag::Value", ], }, Layer { name: "b", properties: [ "aws_smithy_runtime_api::config_bag::Value", "aws_smithy_runtime_api::config_bag::Value", ], }, Layer { name: "a", properties: [ "aws_smithy_runtime_api::config_bag::Value", ], }, Layer { name: "base", properties: [], }, ], } ``` There is still some work to do, but this is a start ## Testing - [x] UT ## Checklist - [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._ --------- Co-authored-by: John DiSanti --- CHANGELOG.next.toml | 7 ++ .../aws-smithy-http/src/property_bag.rs | 101 +++++++++++++----- .../aws-smithy-runtime-api/src/config_bag.rs | 41 ++++++- 3 files changed, 119 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 0afcaae4a3..f0ae279af4 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -10,3 +10,10 @@ # references = ["smithy-rs#920"] # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} # author = "rcoh" + +[[smithy-rs]] +message = "The `Debug` implementation for `PropertyBag` now prints a list of the types it contains. This significantly improves debuggability." +author = "rcoh" +references = ["smithy-rs#2612"] +meta = { "breaking" = false, "tada" = false, "bug" = false } + diff --git a/rust-runtime/aws-smithy-http/src/property_bag.rs b/rust-runtime/aws-smithy-http/src/property_bag.rs index 4964b7f09b..1ac6adc989 100644 --- a/rust-runtime/aws-smithy-http/src/property_bag.rs +++ b/rust-runtime/aws-smithy-http/src/property_bag.rs @@ -13,12 +13,38 @@ use std::any::{Any, TypeId}; use std::collections::HashMap; use std::fmt; -use std::fmt::Debug; +use std::fmt::{Debug, Formatter}; use std::hash::{BuildHasherDefault, Hasher}; use std::ops::{Deref, DerefMut}; use std::sync::{Arc, Mutex}; -type AnyMap = HashMap, BuildHasherDefault>; +type AnyMap = HashMap>; + +struct NamedType { + name: &'static str, + value: Box, +} + +impl NamedType { + fn as_mut(&mut self) -> Option<&mut T> { + self.value.downcast_mut() + } + + fn as_ref(&self) -> Option<&T> { + self.value.downcast_ref() + } + + fn assume(self) -> Option { + self.value.downcast().map(|t| *t).ok() + } + + fn new(value: T) -> Self { + Self { + name: std::any::type_name::(), + value: Box::new(value), + } + } +} // With TypeIds as keys, there's no need to hash them. They are already hashes // themselves, coming from the compiler. The IdHasher just holds the u64 of @@ -82,13 +108,8 @@ impl PropertyBag { /// ``` pub fn insert(&mut self, val: T) -> Option { self.map - .insert(TypeId::of::(), Box::new(val)) - .and_then(|boxed| { - (boxed as Box) - .downcast() - .ok() - .map(|boxed| *boxed) - }) + .insert(TypeId::of::(), NamedType::new(val)) + .and_then(|val| val.assume()) } /// Get a reference to a type previously inserted on this `PropertyBag`. @@ -106,7 +127,16 @@ impl PropertyBag { pub fn get(&self) -> Option<&T> { self.map .get(&TypeId::of::()) - .and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref()) + .and_then(|val| val.as_ref()) + } + + /// Returns an iterator of the types contained in this PropertyBag + /// + /// # Stability + /// This method is unstable and may be removed or changed in a future release. The exact + /// format of the returned types may also change. + pub fn contents(&self) -> impl Iterator + '_ { + self.map.values().map(|tpe| tpe.name) } /// Get a mutable reference to a type previously inserted on this `PropertyBag`. @@ -124,7 +154,7 @@ impl PropertyBag { pub fn get_mut(&mut self) -> Option<&mut T> { self.map .get_mut(&TypeId::of::()) - .and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut()) + .map(|val| val.as_mut().expect("type mismatch!")) } /// Remove a type from this `PropertyBag`. @@ -141,8 +171,8 @@ impl PropertyBag { /// assert!(props.get::().is_none()); /// ``` pub fn remove(&mut self) -> Option { - self.map.remove(&TypeId::of::()).and_then(|boxed| { - (boxed as Box) + self.map.remove(&TypeId::of::()).and_then(|tpe| { + (tpe.value as Box) .downcast() .ok() .map(|boxed| *boxed) @@ -168,7 +198,16 @@ impl PropertyBag { impl fmt::Debug for PropertyBag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PropertyBag").finish() + let mut fmt = f.debug_struct("PropertyBag"); + + struct Contents<'a>(&'a PropertyBag); + impl<'a> Debug for Contents<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_list().entries(self.0.contents()).finish() + } + } + fmt.field("contents", &Contents(self)); + fmt.finish() } } @@ -225,22 +264,30 @@ impl From for SharedPropertyBag { } #[cfg(test)] -#[test] -fn test_extensions() { - #[derive(Debug, PartialEq)] - struct MyType(i32); +mod test { + use crate::property_bag::PropertyBag; - let mut extensions = PropertyBag::new(); + #[test] + fn test_extensions() { + #[derive(Debug, PartialEq)] + struct MyType(i32); - extensions.insert(5i32); - extensions.insert(MyType(10)); + let mut property_bag = PropertyBag::new(); - assert_eq!(extensions.get(), Some(&5i32)); - assert_eq!(extensions.get_mut(), Some(&mut 5i32)); + property_bag.insert(5i32); + property_bag.insert(MyType(10)); - assert_eq!(extensions.remove::(), Some(5i32)); - assert!(extensions.get::().is_none()); + assert_eq!(property_bag.get(), Some(&5i32)); + assert_eq!(property_bag.get_mut(), Some(&mut 5i32)); - assert_eq!(extensions.get::(), None); - assert_eq!(extensions.get(), Some(&MyType(10))); + assert_eq!(property_bag.remove::(), Some(5i32)); + assert!(property_bag.get::().is_none()); + + assert_eq!(property_bag.get::(), None); + assert_eq!(property_bag.get(), Some(&MyType(10))); + assert_eq!( + format!("{:?}", property_bag), + r#"PropertyBag { contents: ["aws_smithy_http::property_bag::test::test_extensions::MyType"] }"# + ); + } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs index bf8d42e6cb..418fbd9b13 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs @@ -10,7 +10,7 @@ //! 1. A new layer of configuration may be applied onto an existing configuration structure without modifying it or taking ownership. //! 2. No lifetime shenanigans to deal with use aws_smithy_http::property_bag::PropertyBag; -use std::fmt::Debug; +use std::fmt::{Debug, Formatter}; use std::ops::Deref; use std::sync::Arc; @@ -18,12 +18,32 @@ use std::sync::Arc; /// /// [`ConfigBag`] is the "unlocked" form of the bag. Only the top layer of the bag may be unlocked. #[must_use] -#[derive(Debug)] pub struct ConfigBag { head: Layer, tail: Option, } +impl Debug for ConfigBag { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + struct Layers<'a>(&'a ConfigBag); + impl Debug for Layers<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut list = f.debug_list(); + list.entry(&self.0.head); + let mut us = self.0.tail.as_ref(); + while let Some(bag) = us { + list.entry(&bag.head); + us = bag.tail.as_ref() + } + list.finish() + } + } + f.debug_struct("ConfigBag") + .field("layers", &Layers(self)) + .finish() + } +} + /// Layered Configuration Structure /// /// [`FrozenConfigBag`] is the "locked" form of the bag. @@ -55,12 +75,26 @@ enum Value { ExplicitlyUnset, } -#[derive(Debug)] struct Layer { name: &'static str, props: PropertyBag, } +impl Debug for Layer { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + struct Contents<'a>(&'a Layer); + impl Debug for Contents<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.0.props.contents()).finish() + } + } + f.debug_struct("Layer") + .field("name", &self.name) + .field("properties", &Contents(self)) + .finish() + } +} + fn no_op(_: &mut ConfigBag) {} impl FrozenConfigBag { @@ -258,6 +292,7 @@ mod test { assert!(final_bag.get::().is_some()); // we unset prop3 assert!(final_bag.get::().is_none()); + println!("{:#?}", final_bag); } #[test] From e3ecb6ceb913aaf7469a586459db859bce58f071 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Wed, 26 Apr 2023 15:40:49 -0500 Subject: [PATCH 064/253] remove: non-existent retry strategy from SRA test (#2640) I made a mistake when I split the classifier work from the retry strategy work. Russell pointed that out to me, and this PR fixes it. _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index 28d8d9a3b7..a07665f8f6 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -106,7 +106,7 @@ async fn sra_manual_test() { cfg.put(params_builder); cfg.set_retry_strategy( - aws_smithy_runtime::client::retries::strategy::StandardRetryStrategy::default(), + aws_smithy_runtime::client::retries::strategy::NeverRetryStrategy::default(), ); let connection: Box = Box::new(DynConnectorAdapter::new( From a85cef6d1e876bc7eaa2278fa30c0f4b3ca813b0 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 26 Apr 2023 20:09:53 -0400 Subject: [PATCH 065/253] Add configurable runtime plugin to send_v2 so that it is usable (#2635) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation and Context - `send_v2()` doesn't work for a very small number of reasons—allow it one last final runtime_plugin so that we can use it to make an e2e request ## Description - update a few decorators to be deterministic ## Testing ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --------- Co-authored-by: John DiSanti Co-authored-by: Zelda Hessler --- aws/rust-runtime/aws-runtime/src/auth.rs | 1 + .../aws-runtime/src/invocation_id.rs | 39 ++- .../integration-tests/aws-sdk-s3/Cargo.toml | 2 +- .../aws-sdk-s3/test-data/list-objects-v2.json | 105 +++++++ .../aws-sdk-s3/tests/sra_test.rs | 297 +++--------------- .../customizations/HttpAuthDecorator.kt | 2 +- .../client/FluentClientGenerator.kt | 10 + .../rust/codegen/core/smithy/RuntimeType.kt | 27 +- rust-runtime/aws-smithy-client/Cargo.toml | 3 +- .../aws-smithy-client/external-types.toml | 1 + rust-runtime/aws-smithy-client/src/dvr.rs | 1 + .../aws-smithy-client/src/dvr/record.rs | 18 +- .../aws-smithy-client/src/dvr/replay.rs | 49 ++- .../aws-smithy-protocol-test/src/lib.rs | 1 + .../aws-smithy-protocol-test/src/xml.rs | 3 + .../src/client/runtime_plugin.rs | 23 +- 16 files changed, 286 insertions(+), 296 deletions(-) create mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/test-data/list-objects-v2.json diff --git a/aws/rust-runtime/aws-runtime/src/auth.rs b/aws/rust-runtime/aws-runtime/src/auth.rs index e2b355f0be..cf6f3b9ac3 100644 --- a/aws/rust-runtime/aws-runtime/src/auth.rs +++ b/aws/rust-runtime/aws-runtime/src/auth.rs @@ -196,6 +196,7 @@ pub mod sigv4 { &self, request: &mut HttpRequest, identity: &Identity, + // TODO(enableNewSmithyRuntime): should this be the config bag? signing_properties: &PropertyBag, ) -> Result<(), BoxError> { let operation_config = signing_properties diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index ecdf16ed5f..55d2e981ac 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -17,7 +17,7 @@ const AMZ_SDK_INVOCATION_ID: HeaderName = HeaderName::from_static("amz-sdk-invoc #[non_exhaustive] #[derive(Debug)] pub struct InvocationIdInterceptor { - id: HeaderValue, + id: InvocationId, } impl InvocationIdInterceptor { @@ -29,12 +29,9 @@ impl InvocationIdInterceptor { impl Default for InvocationIdInterceptor { fn default() -> Self { - let id = Uuid::new_v4(); - let id = id - .to_string() - .parse() - .expect("UUIDs always produce a valid header value"); - Self { id } + Self { + id: InvocationId::from_uuid(), + } } } @@ -45,11 +42,33 @@ impl Interceptor for InvocationIdInterceptor { _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let headers = context.request_mut()?.headers_mut(); - headers.append(AMZ_SDK_INVOCATION_ID, self.id.clone()); + let id = _cfg.get::().unwrap_or(&self.id); + headers.append(AMZ_SDK_INVOCATION_ID, id.0.clone()); Ok(()) } } +/// InvocationId provides a consistent ID across retries +#[derive(Debug)] +pub struct InvocationId(HeaderValue); +impl InvocationId { + /// A test invocation id to allow deterministic requests + pub fn for_tests() -> Self { + InvocationId(HeaderValue::from_static( + "00000000-0000-4000-8000-000000000000", + )) + } + + fn from_uuid() -> Self { + let id = Uuid::new_v4(); + let id = id + .to_string() + .parse() + .expect("UUIDs always produce a valid header value"); + Self(id) + } +} + #[cfg(test)] mod tests { use crate::invocation_id::InvocationIdInterceptor; @@ -84,8 +103,8 @@ mod tests { .unwrap(); let header = expect_header(&context, "amz-sdk-invocation-id"); - assert_eq!(&interceptor.id, header); + assert_eq!(&interceptor.id.0, header); // UUID should include 32 chars and 4 dashes - assert_eq!(interceptor.id.len(), 36); + assert_eq!(interceptor.id.0.len(), 36); } } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml index 70aaa61a75..40c9f1bdbb 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml +++ b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml @@ -13,7 +13,7 @@ aws-sdk-s3 = { path = "../../build/sdk/aws-sdk-s3", features = ["test-util"] } aws-sigv4 = { path = "../../../rust-runtime/aws-sigv4" } aws-types = { path = "../../../rust-runtime/aws-types" } aws-smithy-async = { path = "../../../../rust-runtime/aws-smithy-async", features = ["rt-tokio"] } -aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client", features = ["test-util"] } +aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-types = { path = "../../../../rust-runtime/aws-smithy-types" } aws-smithy-http = { path = "../../../../rust-runtime/aws-smithy-http" } aws-smithy-runtime = { path = "../../../../rust-runtime/aws-smithy-runtime", features = ["test-util"] } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/test-data/list-objects-v2.json b/aws/sra-test/integration-tests/aws-sdk-s3/test-data/list-objects-v2.json new file mode 100644 index 0000000000..366e1fab1b --- /dev/null +++ b/aws/sra-test/integration-tests/aws-sdk-s3/test-data/list-objects-v2.json @@ -0,0 +1,105 @@ +{ + "events": [ + { + "connection_id": 0, + "action": { + "Request": { + "request": { + "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + "headers": { + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" + ], + "x-amz-date": [ + "20210618T170728Z" + ], + "x-amz-content-sha256": [ + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "amz-sdk-invocation-id": [ + "00000000-0000-4000-8000-000000000000" + ], + "user-agent": [ + "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" + ] + }, + "method": "GET" + } + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "x-amz-request-id": [ + "9X5E7C9EAB6AQEP2" + ], + "x-amz-id-2": [ + "gZsrBxajPyo1Q0DE2plGf7T6kAnxd4Xx7/S+8lq18GegL6kFbnVXLLh1LnBzpEpFiHN9XoNHkeA=" + ], + "content-type": [ + "application/xml" + ], + "transfer-encoding": [ + "chunked" + ], + "server": [ + "AmazonS3" + ], + "date": [ + "Wed, 26 Apr 2023 14:00:24 GMT" + ], + "x-amz-bucket-region": [ + "us-east-1" + ] + } + } + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "\n\n test-bucket\n prefix~\n 1\n 1000\n false\n \n some-file.file\n 2009-10-12T17:50:30.000Z\n 434234\n STANDARD\n \n" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + } + ], + "docs": "Test sending an S3 ListObjectsV2 operation with a successful response.", + "version": "V0" +} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index a07665f8f6..b306f42753 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -3,282 +3,75 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache}; -use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; -use aws_runtime::recursion_detection::RecursionDetectionInterceptor; -use aws_runtime::user_agent::UserAgentInterceptor; +use aws_http::user_agent::AwsUserAgent; +use aws_runtime::invocation_id::InvocationId; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::endpoint::Params; -use aws_sdk_s3::operation::list_objects_v2::{ - ListObjectsV2Error, ListObjectsV2Input, ListObjectsV2Output, -}; -use aws_sdk_s3::primitives::SdkBody; + +use aws_sdk_s3::Client; + +use aws_smithy_client::dvr; +use aws_smithy_client::dvr::MediaType; use aws_smithy_client::erase::DynConnector; -use aws_smithy_client::test_connection::TestConnection; -use aws_smithy_http::endpoint::SharedEndpointResolver; -use aws_smithy_runtime::client::connections::adapter::DynConnectorAdapter; -use aws_smithy_runtime::client::orchestrator::endpoints::DefaultEndpointResolver; -use aws_smithy_runtime_api::client::interceptors::error::ContextAttachedError; -use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext, Interceptors}; -use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, ConfigBagAccessors, Connection, HttpRequest, HttpResponse, RequestTime, TraceProbe, -}; -use aws_smithy_runtime_api::client::retries::RetryClassifiers; + +use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::config_bag::ConfigBag; -use aws_smithy_runtime_api::type_erasure::TypedBox; -use aws_types::region::SigningRegion; -use aws_types::SigningService; -use http::Uri; -use std::sync::Arc; -use std::time::{Duration, UNIX_EPOCH}; -// TODO(orchestrator-test): unignore -#[ignore] +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +const LIST_BUCKETS_PATH: &str = "test-data/list-objects-v2.json"; + #[tokio::test] async fn sra_test() { tracing_subscriber::fmt::init(); - let conn = TestConnection::new(vec![( - http::Request::builder() - .header("authorization", "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=ae78f74d26b6b0c3a403d9e8cc7ec3829d6264a2b33db672bf2b151bbb901786") - .uri("https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~") - .body(SdkBody::empty()) - .unwrap(), - http::Response::builder().status(200).body("").unwrap(), - )]); + let conn = dvr::ReplayingConnection::from_file(LIST_BUCKETS_PATH).unwrap(); let config = aws_sdk_s3::Config::builder() .credentials_provider(Credentials::for_tests()) .region(Region::new("us-east-1")) - .http_connector(conn.clone()) + .http_connector(DynConnector::new(conn.clone())) .build(); - let client = aws_sdk_s3::Client::from_conf(config); + let client = Client::from_conf(config); + let fixup = FixupPlugin { + client: client.clone(), + timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), + }; - let _ = dbg!( + let resp = dbg!( client .list_objects_v2() .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) .bucket("test-bucket") .prefix("prefix~") - .send_v2() + .send_v2_with_plugin(Some(fixup)) .await ); - - conn.assert_requests_match(&[]); + // To regenerate the test: + // conn.dump_to_file("test-data/list-objects-v2.json").unwrap(); + let resp = resp.expect("valid e2e test"); + assert_eq!(resp.name(), Some("test-bucket")); + conn.full_validate(MediaType::Xml).await.expect("failed") } -// TODO(orchestrator-test): replace with the above once runtime plugin config works -#[tokio::test] -async fn sra_manual_test() { - tracing_subscriber::fmt::init(); - - struct ManualServiceRuntimePlugin { - connector: TestConnection<&'static str>, - endpoint_resolver: SharedEndpointResolver, - credentials_cache: SharedCredentialsCache, - } - - impl RuntimePlugin for ManualServiceRuntimePlugin { - fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { - let http_auth_schemes = - aws_smithy_runtime_api::client::orchestrator::HttpAuthSchemes::builder() - .auth_scheme( - aws_runtime::auth::sigv4::SCHEME_ID, - aws_runtime::auth::sigv4::SigV4HttpAuthScheme::new(), - ) - .build(); - cfg.set_http_auth_schemes(http_auth_schemes); - - // Set an empty auth option resolver to be overridden by operations that need auth. - cfg.set_auth_option_resolver( - aws_smithy_runtime_api::client::auth::option_resolver::AuthOptionListResolver::new( - Vec::new(), - ), - ); - - cfg.set_endpoint_resolver(DefaultEndpointResolver::new(self.endpoint_resolver.clone())); - - let params_builder = Params::builder() - .set_region(Some("us-east-1".to_owned())) - .set_endpoint(Some("https://s3.us-east-1.amazonaws.com/".to_owned())); - cfg.put(params_builder); - - cfg.set_retry_strategy( - aws_smithy_runtime::client::retries::strategy::NeverRetryStrategy::default(), - ); - - let connection: Box = Box::new(DynConnectorAdapter::new( - DynConnector::new(self.connector.clone()), - )); - cfg.set_connection(connection); - - cfg.set_trace_probe({ - #[derive(Debug)] - struct StubTraceProbe; - impl TraceProbe for StubTraceProbe { - fn dispatch_events(&self) { - // no-op - } - } - StubTraceProbe - }); - - cfg.put(SigningService::from_static("s3")); - cfg.put(SigningRegion::from(Region::from_static("us-east-1"))); - cfg.set_request_time(RequestTime::new( - UNIX_EPOCH + Duration::from_secs(1624036048), - )); - - cfg.put(ApiMetadata::new("unused", "unused")); - cfg.put(AwsUserAgent::for_tests()); // Override the user agent with the test UA - cfg.get::>() - .expect("interceptors set") - .register_client_interceptor(Arc::new(UserAgentInterceptor::new()) as _) - .register_client_interceptor(Arc::new(RecursionDetectionInterceptor::new()) as _); - cfg.set_identity_resolvers( - aws_smithy_runtime_api::client::identity::IdentityResolvers::builder() - .identity_resolver( - aws_runtime::auth::sigv4::SCHEME_ID, - aws_runtime::identity::credentials::CredentialsIdentityResolver::new( - self.credentials_cache.clone(), - ), - ) - .build(), - ); - Ok(()) - } - } - - // This is a temporary operation runtime plugin until EndpointParamsInterceptor and - // EndpointParamsFinalizerInterceptor have been fully implemented, in which case - // `.with_operation_plugin(ManualOperationRuntimePlugin)` can be removed. - struct ManualOperationRuntimePlugin; - - impl RuntimePlugin for ManualOperationRuntimePlugin { - fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { - #[derive(Debug)] - struct ListObjectsV2EndpointParamsInterceptor; - impl Interceptor for ListObjectsV2EndpointParamsInterceptor { - fn read_before_execution( - &self, - context: &InterceptorContext, - cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { - let input = context.input()?; - let input = input - .downcast_ref::() - .ok_or_else(|| "failed to downcast to ListObjectsV2Input")?; - let mut params_builder = cfg - .get::() - .ok_or("missing endpoint params builder")? - .clone(); - params_builder = params_builder.set_bucket(input.bucket.clone()); - cfg.put(params_builder); - - Ok(()) - } - } - - #[derive(Debug)] - struct ListObjectsV2EndpointParamsFinalizerInterceptor; - impl Interceptor for ListObjectsV2EndpointParamsFinalizerInterceptor { - fn read_before_execution( - &self, - _context: &InterceptorContext, - cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { - let params_builder = cfg - .get::() - .ok_or("missing endpoint params builder")? - .clone(); - let params = params_builder.build().map_err(|err| { - ContextAttachedError::new("endpoint params could not be built", err) - })?; - cfg.put( - aws_smithy_runtime_api::client::orchestrator::EndpointResolverParams::new( - params, - ), - ); - - Ok(()) - } - } - - cfg.get::>() - .expect("interceptors set") - .register_operation_interceptor( - Arc::new(ListObjectsV2EndpointParamsInterceptor) as _ - ) - .register_operation_interceptor(Arc::new( - ListObjectsV2EndpointParamsFinalizerInterceptor, - ) as _); - Ok(()) - } +struct FixupPlugin { + client: Client, + timestamp: SystemTime, +} +impl RuntimePlugin for FixupPlugin { + fn configure( + &self, + cfg: &mut ConfigBag, + ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { + let params_builder = Params::builder() + .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) + .bucket("test-bucket"); + + cfg.put(params_builder); + cfg.set_request_time(RequestTime::new(self.timestamp.clone())); + cfg.put(AwsUserAgent::for_tests()); + cfg.put(InvocationId::for_tests()); + Ok(()) } - - let conn = TestConnection::new(vec![( - http::Request::builder() - .header("authorization", "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=ae78f74d26b6b0c3a403d9e8cc7ec3829d6264a2b33db672bf2b151bbb901786") - .uri("https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~") - .body(SdkBody::empty()) - .unwrap(), - http::Response::builder().status(200).body(r#" - - test-bucket - prefix~ - 1 - 1000 - false - - some-file.file - 2009-10-12T17:50:30.000Z - 434234 - STANDARD - - -"#).unwrap(), - )]); - - let endpoint_resolver = - SharedEndpointResolver::new(aws_sdk_s3::endpoint::DefaultResolver::new()); - let credentials_cache = SharedCredentialsCache::new( - CredentialsCache::lazy() - .create_cache(SharedCredentialsProvider::new(Credentials::for_tests())), - ); - - let service_runtime_plugin = ManualServiceRuntimePlugin { - connector: conn.clone(), - endpoint_resolver, - credentials_cache, - }; - - let runtime_plugins = aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins::new() - .with_client_plugin(service_runtime_plugin) - .with_operation_plugin(aws_sdk_s3::operation::list_objects_v2::ListObjectsV2::new()) - .with_operation_plugin(ManualOperationRuntimePlugin); - - let input = ListObjectsV2Input::builder() - .bucket("test-bucket") - .prefix("prefix~") - .build() - .unwrap(); - let input = TypedBox::new(input).erase(); - let output = aws_smithy_runtime::client::orchestrator::invoke(input, &runtime_plugins) - .await - .map_err(|err| { - err.map_service_error(|err| { - TypedBox::::assume_from(err) - .expect("correct error type") - .unwrap() - }) - }) - .unwrap(); - let output = TypedBox::::assume_from(output) - .expect("correct output type") - .unwrap(); - dbg!(output); - - conn.assert_requests_match(&[]); } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index 7f23f7a853..f9757b4ef9 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -147,7 +147,7 @@ private class HttpAuthServiceRuntimePluginCustomization( is ServiceRuntimePluginSection.HttpAuthScheme -> { if (authSchemes.apiKey) { val trait = serviceShape.getTrait()!! - val location = when (trait.`in`) { + val location = when (trait.`in`!!) { HttpApiKeyAuthTrait.Location.HEADER -> { check(trait.scheme.isPresent) { "A scheme is required for `@httpApiKey` when `in` is set to `header`" diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 8cdc6d8562..6c085fd673 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -344,12 +344,21 @@ class FluentClientGenerator( /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be /// set when configuring the client. pub async fn send_v2(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + self.send_v2_with_plugin(Option::>::None).await + } + + // TODO(enableNewSmithyRuntime): Delete when unused + /// Equivalent to [`Self::send_v2`] but adds a final runtime plugin to shim missing behavior + pub async fn send_v2_with_plugin(self, final_plugin: Option) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { let mut runtime_plugins = #{RuntimePlugins}::new() .with_client_plugin(crate::config::ServiceRuntimePlugin::new(self.handle.clone())); if let Some(config_override) = self.config_override { runtime_plugins = runtime_plugins.with_operation_plugin(config_override); } runtime_plugins = runtime_plugins.with_operation_plugin(#{Operation}::new()); + if let Some(final_plugin) = final_plugin { + runtime_plugins = runtime_plugins.with_client_plugin(final_plugin); + } let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; let input = #{TypedBox}::new(input).erase(); let output = #{invoke}(input, &runtime_plugins) @@ -369,6 +378,7 @@ class FluentClientGenerator( "OperationError" to errorType, "Operation" to symbolProvider.toSymbol(operation), "OperationOutput" to outputType, + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::runtime_plugin::RuntimePlugins"), "SdkError" to RuntimeType.sdkError(runtimeConfig), diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 6be653bd59..f8c656c08e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -255,6 +255,7 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun smithyClient(runtimeConfig: RuntimeConfig) = CargoDependency.smithyClient(runtimeConfig).toType() fun smithyClientTestUtil(runtimeConfig: RuntimeConfig) = CargoDependency.smithyClient(runtimeConfig) .copy(features = setOf("test-util"), scope = DependencyScope.Dev).toType() + fun smithyEventStream(runtimeConfig: RuntimeConfig) = CargoDependency.smithyEventStream(runtimeConfig).toType() fun smithyHttp(runtimeConfig: RuntimeConfig) = CargoDependency.smithyHttp(runtimeConfig).toType() fun smithyHttpAuth(runtimeConfig: RuntimeConfig) = CargoDependency.smithyHttpAuth(runtimeConfig).toType() @@ -282,14 +283,24 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun document(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("Document") fun format(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("date_time::Format") fun retryErrorKind(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("retry::ErrorKind") - fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::Receiver") - fun eventStreamSender(runtimeConfig: RuntimeConfig): RuntimeType = smithyHttp(runtimeConfig).resolve("event_stream::EventStreamSender") + fun eventStreamReceiver(runtimeConfig: RuntimeConfig): RuntimeType = + smithyHttp(runtimeConfig).resolve("event_stream::Receiver") + + fun eventStreamSender(runtimeConfig: RuntimeConfig): RuntimeType = + smithyHttp(runtimeConfig).resolve("event_stream::EventStreamSender") + fun errorMetadata(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::ErrorMetadata") - fun errorMetadataBuilder(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::metadata::Builder") - fun provideErrorMetadataTrait(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::metadata::ProvideErrorMetadata") + fun errorMetadataBuilder(runtimeConfig: RuntimeConfig) = + smithyTypes(runtimeConfig).resolve("error::metadata::Builder") + + fun provideErrorMetadataTrait(runtimeConfig: RuntimeConfig) = + smithyTypes(runtimeConfig).resolve("error::metadata::ProvideErrorMetadata") + fun unhandledError(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("error::Unhandled") fun jsonErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.jsonErrors(runtimeConfig)) - fun awsQueryCompatibleErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.awsQueryCompatibleErrors(runtimeConfig)) + fun awsQueryCompatibleErrors(runtimeConfig: RuntimeConfig) = + forInlineDependency(InlineDependency.awsQueryCompatibleErrors(runtimeConfig)) + fun labelFormat(runtimeConfig: RuntimeConfig, func: String) = smithyHttp(runtimeConfig).resolve("label::$func") fun operation(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("operation::Operation") fun operationModule(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("operation") @@ -345,7 +356,8 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun captureRequest(runtimeConfig: RuntimeConfig) = CargoDependency.smithyClientTestUtil(runtimeConfig).toType().resolve("test_connection::capture_request") - fun forInlineDependency(inlineDependency: InlineDependency) = RuntimeType("crate::${inlineDependency.name}", inlineDependency) + fun forInlineDependency(inlineDependency: InlineDependency) = + RuntimeType("crate::${inlineDependency.name}", inlineDependency) fun forInlineFun(name: String, module: RustModule, func: Writable) = RuntimeType( "${module.fullyQualifiedPath()}::$name", @@ -363,5 +375,8 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) forInlineDependency(InlineDependency.unwrappedXmlErrors(runtimeConfig)) val IdempotencyToken by lazy { forInlineDependency(InlineDependency.idempotencyToken()) } + + fun runtimePlugin(runtimeConfig: RuntimeConfig) = + RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugin") } } diff --git a/rust-runtime/aws-smithy-client/Cargo.toml b/rust-runtime/aws-smithy-client/Cargo.toml index 43b40da126..e68320d7b9 100644 --- a/rust-runtime/aws-smithy-client/Cargo.toml +++ b/rust-runtime/aws-smithy-client/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/awslabs/smithy-rs" [features] rt-tokio = ["aws-smithy-async/rt-tokio"] -test-util = ["dep:aws-smithy-protocol-test", "dep:hyper", "hyper?/server", "hyper?/h2", "dep:serde", "serde?/derive", "rustls", "tokio/full"] +test-util = ["dep:aws-smithy-protocol-test", "dep:hyper", "hyper?/server", "hyper?/h2", "dep:serde", "dep:serde_json", "serde?/derive", "rustls", "tokio/full"] native-tls = ["dep:hyper-tls", "client-hyper", "rt-tokio"] rustls = ["dep:hyper-rustls", "dep:lazy_static", "dep:rustls", "client-hyper", "rt-tokio"] client-hyper = ["dep:hyper"] @@ -36,6 +36,7 @@ rustls = { version = "0.20", optional = true } lazy_static = { version = "1", optional = true } pin-project-lite = "0.2.7" serde = { version = "1", features = ["derive"], optional = true } +serde_json = { version = "1", optional = true } tokio = { version = "1.13.1" } tower = { version = "0.4.6", features = ["util", "retry"] } tracing = "0.1" diff --git a/rust-runtime/aws-smithy-client/external-types.toml b/rust-runtime/aws-smithy-client/external-types.toml index 0bab3e6536..5e4b43b8a0 100644 --- a/rust-runtime/aws-smithy-client/external-types.toml +++ b/rust-runtime/aws-smithy-client/external-types.toml @@ -3,6 +3,7 @@ allowed_external_types = [ "aws_smithy_http::*", "aws_smithy_http_tower::*", "aws_smithy_types::*", + "aws_smithy_protocol_test::MediaType", "http::header::name::HeaderName", "http::request::Request", "http::response::Response", diff --git a/rust-runtime/aws-smithy-client/src/dvr.rs b/rust-runtime/aws-smithy-client/src/dvr.rs index be48521994..f0f68a4709 100644 --- a/rust-runtime/aws-smithy-client/src/dvr.rs +++ b/rust-runtime/aws-smithy-client/src/dvr.rs @@ -14,6 +14,7 @@ use std::collections::HashMap; use bytes::Bytes; use serde::{Deserialize, Serialize}; +pub use aws_smithy_protocol_test::MediaType; use aws_smithy_types::base64; pub use record::RecordingConnection; pub use replay::ReplayingConnection; diff --git a/rust-runtime/aws-smithy-client/src/dvr/record.rs b/rust-runtime/aws-smithy-client/src/dvr/record.rs index 7d1f95a290..234bc23d64 100644 --- a/rust-runtime/aws-smithy-client/src/dvr/record.rs +++ b/rust-runtime/aws-smithy-client/src/dvr/record.rs @@ -18,7 +18,11 @@ use aws_smithy_http::body::SdkBody; use crate::dvr::{self, Action, BodyData, ConnectionId, Direction, Error, NetworkTraffic, Version}; use super::Event; +use crate::conns::Https; +use crate::hyper_ext::Adapter; use std::fmt::Display; +use std::io; +use std::path::Path; /// Recording Connection Wrapper /// @@ -30,13 +34,13 @@ pub struct RecordingConnection { pub(crate) inner: S, } -impl RecordingConnection { +impl RecordingConnection> { /// Construct a recording connection wrapping a default HTTPS implementation - #[cfg(feature = "hyper-rustls")] + #[cfg(feature = "rustls")] pub fn https() -> Self { Self { data: Default::default(), - inner: crate::conns::https(), + inner: crate::hyper_ext::Adapter::builder().build(crate::conns::https()), num_events: Arc::new(AtomicUsize::new(0)), } } @@ -66,6 +70,14 @@ impl RecordingConnection { } } + /// Dump the network traffic to a file + pub fn dump_to_file(&self, path: impl AsRef) -> Result<(), io::Error> { + std::fs::write( + path, + serde_json::to_string(&self.network_traffic()).unwrap(), + ) + } + fn next_id(&self) -> ConnectionId { ConnectionId(self.num_events.fetch_add(1, Ordering::Relaxed)) } diff --git a/rust-runtime/aws-smithy-client/src/dvr/replay.rs b/rust-runtime/aws-smithy-client/src/dvr/replay.rs index 95bb78468d..fc5d867741 100644 --- a/rust-runtime/aws-smithy-client/src/dvr/replay.rs +++ b/rust-runtime/aws-smithy-client/src/dvr/replay.rs @@ -6,6 +6,7 @@ use std::collections::{HashMap, VecDeque}; use std::error::Error; use std::ops::DerefMut; +use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; use std::task::{Context, Poll}; @@ -17,8 +18,9 @@ use tokio::task::JoinHandle; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; +use aws_smithy_protocol_test::MediaType; -use crate::dvr::{Action, ConnectionId, Direction, Event}; +use crate::dvr::{Action, ConnectionId, Direction, Event, NetworkTraffic}; /// Wrapper type to enable optionally waiting for a future to complete #[derive(Debug)] @@ -59,11 +61,33 @@ impl ReplayingConnection { ConnectionId(self.num_events.fetch_add(1, Ordering::Relaxed)) } + /// Validate all headers and bodies + pub async fn full_validate(self, media_type: MediaType) -> Result<(), Box> { + self.validate_base(None, |b1, b2| { + aws_smithy_protocol_test::validate_body( + b1, + std::str::from_utf8(b2).unwrap(), + media_type.clone(), + ) + .map_err(|e| Box::new(e) as _) + }) + .await + } + /// Validate actual requests against expected requests pub async fn validate( self, checked_headers: &[&str], body_comparer: impl Fn(&[u8], &[u8]) -> Result<(), Box>, + ) -> Result<(), Box> { + self.validate_base(Some(checked_headers), body_comparer) + .await + } + + async fn validate_base( + self, + checked_headers: Option<&[&str]>, + body_comparer: impl Fn(&[u8], &[u8]) -> Result<(), Box>, ) -> Result<(), Box> { let mut actual_requests = std::mem::take(self.recorded_requests.lock().unwrap().deref_mut()); @@ -80,15 +104,21 @@ impl ReplayingConnection { .await; aws_smithy_protocol_test::assert_uris_match(actual.uri(), expected.uri()); body_comparer(expected.body().as_ref(), actual.body().as_ref())?; - let expected_headers = checked_headers - .iter() + let expected_headers = expected + .headers() + .keys() + .map(|k| k.as_str()) + .filter(|k| match checked_headers { + Some(list) => list.contains(k), + None => true, + }) .flat_map(|key| { - let _ = expected.headers().get(*key)?; + let _ = expected.headers().get(key)?; Some(( - *key, + key, expected .headers() - .get_all(*key) + .get_all(key) .iter() .map(|h| h.to_str().unwrap()) .collect::>() @@ -118,6 +148,13 @@ impl ReplayingConnection { out } + /// Build a replay connection from a JSON file + pub fn from_file(path: impl AsRef) -> Result> { + let events: NetworkTraffic = + serde_json::from_str(&std::fs::read_to_string(path.as_ref())?)?; + Ok(Self::new(events.events)) + } + /// Build a replay connection from a sequence of events pub fn new(events: Vec) -> Self { let mut event_map: HashMap<_, VecDeque<_>> = HashMap::new(); diff --git a/rust-runtime/aws-smithy-protocol-test/src/lib.rs b/rust-runtime/aws-smithy-protocol-test/src/lib.rs index da9c213d5d..d110272cea 100644 --- a/rust-runtime/aws-smithy-protocol-test/src/lib.rs +++ b/rust-runtime/aws-smithy-protocol-test/src/lib.rs @@ -276,6 +276,7 @@ pub fn require_headers( Ok(()) } +#[derive(Clone)] pub enum MediaType { /// Json media types are deserialized and compared Json, diff --git a/rust-runtime/aws-smithy-protocol-test/src/xml.rs b/rust-runtime/aws-smithy-protocol-test/src/xml.rs index c92882c5ee..a27420e36b 100644 --- a/rust-runtime/aws-smithy-protocol-test/src/xml.rs +++ b/rust-runtime/aws-smithy-protocol-test/src/xml.rs @@ -12,6 +12,9 @@ use std::fmt::Write; /// This will normalize documents and attempts to determine if it is OK to sort members or not by /// using a heuristic to determine if the tag represents a list (which should not be reordered) pub(crate) fn try_xml_equivalent(actual: &str, expected: &str) -> Result<(), ProtocolTestFailure> { + if actual == expected { + return Ok(()); + } let norm_1 = normalize_xml(actual).map_err(|e| ProtocolTestFailure::InvalidBodyFormat { expected: "actual document to be valid XML".to_string(), found: format!("{}\n{}", e, actual), diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index 8b262a453e..835686f337 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -11,12 +11,9 @@ pub trait RuntimePlugin { fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError>; } -impl From for Box -where - T: RuntimePlugin + 'static, -{ - fn from(t: T) -> Self { - Box::new(t) as _ +impl RuntimePlugin for Box { + fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { + self.as_ref().configure(cfg) } } @@ -31,19 +28,13 @@ impl RuntimePlugins { Default::default() } - pub fn with_client_plugin( - mut self, - plugin: impl Into>, - ) -> Self { - self.client_plugins.push(plugin.into()); + pub fn with_client_plugin(mut self, plugin: impl RuntimePlugin + 'static) -> Self { + self.client_plugins.push(Box::new(plugin)); self } - pub fn with_operation_plugin( - mut self, - plugin: impl Into>, - ) -> Self { - self.operation_plugins.push(plugin.into()); + pub fn with_operation_plugin(mut self, plugin: impl RuntimePlugin + 'static) -> Self { + self.operation_plugins.push(Box::new(plugin)); self } From 5a8fff7a3222e519d977da5840a491e85ae65a10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sj=C3=B6=C3=B6h?= Date: Thu, 27 Apr 2023 18:16:01 +0200 Subject: [PATCH 066/253] implement `Ord` and `PartialOrd` for `DateTime` (#2653) ## Motivation and Context This change will allow easy sorting or comparing anything that contains a DateTime. My example is wanting to sort a list of S3 objects by last modified. This PR fixes #2406 ## Description It's a pretty small PR, it implements the `Ord` trait for `DateTime` by comparing the `seconds` property of `self` and `other`, if they are equal it compares the `subsec_nanos` properties instead. The `PartialOrd` trait is implemented by calling the `Ord` trait. ## Testing I added a unit test that compares a number of `DateTime` values with different combinations of positive/zero/negative `seconds`/`subsec_nanos`. ## Checklist - [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._ --- CHANGELOG.next.toml | 6 +++ .../aws-smithy-types/src/date_time/mod.rs | 44 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index f0ae279af4..9f69d7e109 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -17,3 +17,9 @@ author = "rcoh" references = ["smithy-rs#2612"] meta = { "breaking" = false, "tada" = false, "bug" = false } + +[[smithy-rs]] +message = "Implement `Ord` and `PartialOrd` for `DateTime`." +author = "henriiik" +references = ["smithy-rs#2653"] +meta = { "breaking" = false, "tada" = false, "bug" = false } diff --git a/rust-runtime/aws-smithy-types/src/date_time/mod.rs b/rust-runtime/aws-smithy-types/src/date_time/mod.rs index 0459619998..ea3f5baf6c 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/mod.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs @@ -9,6 +9,7 @@ use crate::date_time::format::rfc3339::AllowOffsets; use crate::date_time::format::DateTimeParseErrorKind; use num_integer::div_mod_floor; use num_integer::Integer; +use std::cmp::Ordering; use std::convert::TryFrom; use std::error::Error as StdError; use std::fmt; @@ -301,6 +302,21 @@ impl From for DateTime { } } +impl PartialOrd for DateTime { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for DateTime { + fn cmp(&self, other: &Self) -> Ordering { + match self.seconds.cmp(&other.seconds) { + Ordering::Equal => self.subsecond_nanos.cmp(&other.subsecond_nanos), + ordering => ordering, + } + } +} + /// Failure to convert a `DateTime` to or from another type. #[derive(Debug)] #[non_exhaustive] @@ -552,4 +568,32 @@ mod test { SystemTime::try_from(date_time).unwrap() ); } + + #[test] + fn ord() { + let first = DateTime::from_secs_and_nanos(-1, 0); + let second = DateTime::from_secs_and_nanos(0, 0); + let third = DateTime::from_secs_and_nanos(0, 1); + let fourth = DateTime::from_secs_and_nanos(1, 0); + + assert!(first == first); + assert!(first < second); + assert!(first < third); + assert!(first < fourth); + + assert!(second > first); + assert!(second == second); + assert!(second < third); + assert!(second < fourth); + + assert!(third > first); + assert!(third > second); + assert!(third == third); + assert!(third < fourth); + + assert!(fourth > first); + assert!(fourth > second); + assert!(fourth > third); + assert!(fourth == fourth); + } } From b1ce5e3c6c1b6f0f2538ebfc3a764d05b62529c2 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 27 Apr 2023 10:26:33 -0700 Subject: [PATCH 067/253] Recalculate signing time for every retry attempt (#2643) ## Motivation and Context When SigV4 signing was ported over to the orchestrator, the request time got calculated once before the retry loop, which was incorrect. This PR moves that request time calculation into the request signer so that it happens on every attempt (unless there is a configured request time override). This PR also refactors auth to use the `ConfigBag` instead of a separate signing properties `PropertyBag`. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-runtime/src/auth.rs | 34 +-- aws/rust-runtime/aws-runtime/src/identity.rs | 4 +- .../smithy/rustsdk/SigV4AuthDecorator.kt | 13 +- .../benches/middleware_vs_orchestrator.rs | 256 +++--------------- .../aws-sdk-s3/tests/sra_test.rs | 4 - .../customizations/HttpAuthDecorator.kt | 38 +-- .../OperationRuntimePluginGenerator.kt | 6 +- .../ServiceRuntimePluginGenerator.kt | 6 +- .../aws-smithy-runtime-api/src/client/auth.rs | 134 +++++++++ .../src/client/auth/http.rs | 10 +- .../src/client/auth/option_resolver.rs | 27 +- .../src/client/identity.rs | 20 +- .../src/client/identity/http.rs | 6 +- .../src/client/orchestrator.rs | 137 +--------- .../src/client/auth/http.rs | 39 ++- .../src/client/orchestrator/auth.rs | 45 ++- 16 files changed, 280 insertions(+), 499 deletions(-) diff --git a/aws/rust-runtime/aws-runtime/src/auth.rs b/aws/rust-runtime/aws-runtime/src/auth.rs index cf6f3b9ac3..1da617abdd 100644 --- a/aws/rust-runtime/aws-runtime/src/auth.rs +++ b/aws/rust-runtime/aws-runtime/src/auth.rs @@ -11,11 +11,10 @@ pub mod sigv4 { SignableRequest, SignatureLocation, SigningParams, SigningSettings, UriPathNormalizationMode, }; - use aws_smithy_http::property_bag::PropertyBag; + use aws_smithy_runtime_api::client::auth::{AuthSchemeId, HttpAuthScheme, HttpRequestSigner}; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; - use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, HttpAuthScheme, HttpRequest, HttpRequestSigner, - }; + use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpRequest}; + use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_types::region::SigningRegion; use aws_types::SigningService; use std::time::{Duration, SystemTime}; @@ -24,7 +23,7 @@ pub mod sigv4 { `expires_in` duration because the credentials used to sign it will expire first."; /// Auth scheme ID for SigV4. - pub const SCHEME_ID: &str = "sigv4"; + pub const SCHEME_ID: AuthSchemeId = AuthSchemeId::new("sigv4"); /// SigV4 auth scheme. #[derive(Debug, Default)] @@ -40,7 +39,7 @@ pub mod sigv4 { } impl HttpAuthScheme for SigV4HttpAuthScheme { - fn scheme_id(&self) -> &'static str { + fn scheme_id(&self) -> AuthSchemeId { SCHEME_ID } @@ -88,8 +87,6 @@ pub mod sigv4 { pub signing_optional: bool, /// Optional expiration (for presigning) pub expires_in: Option, - /// Timestamp to sign with. - pub request_timestamp: SystemTime, } impl Default for SigningOptions { @@ -103,7 +100,6 @@ pub mod sigv4 { signature_type: HttpSignatureType::HttpRequestHeaders, signing_optional: false, expires_in: None, - request_timestamp: SystemTime::now(), } } } @@ -168,11 +164,11 @@ pub mod sigv4 { settings: SigningSettings, credentials: &'a Credentials, operation_config: &'a SigV4OperationSigningConfig, + request_timestamp: SystemTime, ) -> SigningParams<'a> { if let Some(expires_in) = settings.expires_in { if let Some(creds_expires_time) = credentials.expiry() { - let presigned_expires_time = - operation_config.signing_options.request_timestamp + expires_in; + let presigned_expires_time = request_timestamp + expires_in; if presigned_expires_time > creds_expires_time { tracing::warn!(EXPIRATION_WARNING); } @@ -184,7 +180,7 @@ pub mod sigv4 { .secret_key(credentials.secret_access_key()) .region(operation_config.region.as_ref()) .service_name(operation_config.service.as_ref()) - .time(operation_config.signing_options.request_timestamp) + .time(request_timestamp) .settings(settings); builder.set_security_token(credentials.session_token()); builder.build().expect("all required fields set") @@ -196,12 +192,12 @@ pub mod sigv4 { &self, request: &mut HttpRequest, identity: &Identity, - // TODO(enableNewSmithyRuntime): should this be the config bag? - signing_properties: &PropertyBag, + config_bag: &ConfigBag, ) -> Result<(), BoxError> { - let operation_config = signing_properties + let operation_config = config_bag .get::() .ok_or("missing operation signing config for SigV4")?; + let request_time = config_bag.request_time().unwrap_or_default().system_time(); let credentials = if let Some(creds) = identity.data::() { creds @@ -213,7 +209,8 @@ pub mod sigv4 { }; let settings = Self::settings(operation_config); - let signing_params = Self::signing_params(settings, credentials, operation_config); + let signing_params = + Self::signing_params(settings, credentials, operation_config, request_time); let (signing_instructions, _signature) = { // A body that is already in memory can be signed directly. A body that is not in memory @@ -283,17 +280,16 @@ pub mod sigv4 { signature_type: HttpSignatureType::HttpRequestHeaders, signing_optional: false, expires_in: None, - request_timestamp: now, payload_override: None, }, }; - SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config); + SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now); assert!(!logs_contain(EXPIRATION_WARNING)); let mut settings = SigningSettings::default(); settings.expires_in = Some(creds_expire_in + Duration::from_secs(10)); - SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config); + SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now); assert!(logs_contain(EXPIRATION_WARNING)); } } diff --git a/aws/rust-runtime/aws-runtime/src/identity.rs b/aws/rust-runtime/aws-runtime/src/identity.rs index 2e2eff1bf2..c120e4eb7f 100644 --- a/aws/rust-runtime/aws-runtime/src/identity.rs +++ b/aws/rust-runtime/aws-runtime/src/identity.rs @@ -6,9 +6,9 @@ /// Credentials-based identity support. pub mod credentials { use aws_credential_types::cache::SharedCredentialsCache; - use aws_smithy_http::property_bag::PropertyBag; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver}; use aws_smithy_runtime_api::client::orchestrator::{BoxError, Future}; + use aws_smithy_runtime_api::config_bag::ConfigBag; /// Smithy identity resolver for AWS credentials. #[derive(Debug)] @@ -24,7 +24,7 @@ pub mod credentials { } impl IdentityResolver for CredentialsIdentityResolver { - fn resolve_identity(&self, _identity_properties: &PropertyBag) -> Future { + fn resolve_identity(&self, _config_bag: &ConfigBag) -> Future { let cache = self.credentials_cache.clone(); Future::new(Box::pin(async move { let credentials = cache.as_ref().provide_cached_credentials().await?; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 7ae7582e69..f4973330c5 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -102,10 +102,8 @@ private class AuthOperationRuntimePluginCustomization(private val codegenContext val runtimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) arrayOf( - "AuthOptionListResolver" to runtimeApi.resolve("client::auth::option_resolver::AuthOptionListResolver"), - "HttpAuthOption" to runtimeApi.resolve("client::orchestrator::HttpAuthOption"), + "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "HttpSignatureType" to awsRuntime.resolve("auth::sigv4::HttpSignatureType"), - "PropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::PropertyBag"), "SIGV4_SCHEME_ID" to awsRuntime.resolve("auth::sigv4::SCHEME_ID"), "SigV4OperationSigningConfig" to awsRuntime.resolve("auth::sigv4::SigV4OperationSigningConfig"), "SigningOptions" to awsRuntime.resolve("auth::sigv4::SigningOptions"), @@ -136,16 +134,15 @@ private class AuthOperationRuntimePluginCustomization(private val codegenContext signing_options.normalize_uri_path = $normalizeUrlPath; signing_options.signing_optional = $signingOptional; signing_options.payload_override = #{payload_override}; - signing_options.request_timestamp = cfg.request_time().unwrap_or_default().system_time(); - let mut sigv4_properties = #{PropertyBag}::new(); - sigv4_properties.insert(#{SigV4OperationSigningConfig} { + ${section.configBagName}.put(#{SigV4OperationSigningConfig} { region: signing_region, service: signing_service, signing_options, }); - let auth_option_resolver = #{AuthOptionListResolver}::new( - vec![#{HttpAuthOption}::new(#{SIGV4_SCHEME_ID}, std::sync::Arc::new(sigv4_properties))] + // TODO(enableNewSmithyRuntime): Make auth options additive in the config bag so that multiple codegen decorators can register them + let auth_option_resolver = #{StaticAuthOptionResolver}::new( + vec![#{SIGV4_SCHEME_ID}] ); ${section.configBagName}.set_auth_option_resolver(auth_option_resolver); """, diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs index d587cdb0b4..3fba943ba1 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs @@ -5,16 +5,13 @@ #[macro_use] extern crate criterion; -use aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache}; -use aws_credential_types::provider::SharedCredentialsProvider; -use aws_credential_types::Credentials; use aws_sdk_s3 as s3; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::infallible_connection_fn; -use aws_smithy_http::endpoint::SharedEndpointResolver; -use aws_smithy_runtime_api::type_erasure::TypedBox; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::config_bag::ConfigBag; use criterion::Criterion; -use s3::operation::list_objects_v2::{ListObjectsV2Error, ListObjectsV2Input, ListObjectsV2Output}; +use s3::endpoint::Params; async fn middleware(client: &s3::Client) { client @@ -26,41 +23,36 @@ async fn middleware(client: &s3::Client) { .expect("successful execution"); } -async fn orchestrator( - connector: &DynConnector, - endpoint_resolver: SharedEndpointResolver, - credentials_cache: SharedCredentialsCache, -) { - let service_runtime_plugin = orchestrator::ManualServiceRuntimePlugin { - connector: connector.clone(), - endpoint_resolver: endpoint_resolver.clone(), - credentials_cache: credentials_cache.clone(), - }; +async fn orchestrator(client: &s3::Client) { + struct FixupPlugin { + region: String, + } + impl RuntimePlugin for FixupPlugin { + fn configure( + &self, + cfg: &mut ConfigBag, + ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { + let params_builder = Params::builder() + .set_region(Some(self.region.clone())) + .bucket("test-bucket"); - // TODO(enableNewSmithyRuntime): benchmark with `send_v2` directly once it works - let runtime_plugins = aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins::new() - .with_client_plugin(service_runtime_plugin) - .with_operation_plugin(aws_sdk_s3::operation::list_objects_v2::ListObjectsV2::new()) - .with_operation_plugin(orchestrator::ManualOperationRuntimePlugin); - let input = ListObjectsV2Input::builder() + cfg.put(params_builder); + Ok(()) + } + } + let _output = client + .list_objects_v2() .bucket("test-bucket") .prefix("prefix~") - .build() - .unwrap(); - let input = TypedBox::new(input).erase(); - let output = aws_smithy_runtime::client::orchestrator::invoke(input, &runtime_plugins) + .send_v2_with_plugin(Some(FixupPlugin { + region: client + .conf() + .region() + .map(|c| c.as_ref().to_string()) + .unwrap(), + })) .await - .map_err(|err| { - err.map_service_error(|err| { - TypedBox::::assume_from(err) - .expect("correct error type") - .unwrap() - }) - }) - .unwrap(); - TypedBox::::assume_from(output) - .expect("correct output type") - .unwrap(); + .expect("successful execution"); } fn test_connection() -> DynConnector { @@ -93,14 +85,18 @@ fn test_connection() -> DynConnector { }) } -fn middleware_bench(c: &mut Criterion) { +fn client() -> s3::Client { let conn = test_connection(); let config = s3::Config::builder() .credentials_provider(s3::config::Credentials::for_tests()) .region(s3::config::Region::new("us-east-1")) .http_connector(conn.clone()) .build(); - let client = s3::Client::from_conf(config); + s3::Client::from_conf(config) +} + +fn middleware_bench(c: &mut Criterion) { + let client = client(); c.bench_function("middleware", move |b| { b.to_async(tokio::runtime::Runtime::new().unwrap()) .iter(|| async { middleware(&client).await }) @@ -108,190 +104,12 @@ fn middleware_bench(c: &mut Criterion) { } fn orchestrator_bench(c: &mut Criterion) { - let conn = test_connection(); - let endpoint_resolver = SharedEndpointResolver::new(s3::endpoint::DefaultResolver::new()); - let credentials_cache = SharedCredentialsCache::new( - CredentialsCache::lazy() - .create_cache(SharedCredentialsProvider::new(Credentials::for_tests())), - ); - + let client = client(); c.bench_function("orchestrator", move |b| { b.to_async(tokio::runtime::Runtime::new().unwrap()) - .iter(|| async { - orchestrator(&conn, endpoint_resolver.clone(), credentials_cache.clone()).await - }) + .iter(|| async { orchestrator(&client).await }) }); } -mod orchestrator { - use aws_credential_types::cache::SharedCredentialsCache; - use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; - use aws_runtime::recursion_detection::RecursionDetectionInterceptor; - use aws_runtime::user_agent::UserAgentInterceptor; - use aws_sdk_s3::config::Region; - use aws_sdk_s3::endpoint::Params; - use aws_sdk_s3::operation::list_objects_v2::ListObjectsV2Input; - use aws_smithy_client::erase::DynConnector; - use aws_smithy_http::endpoint::SharedEndpointResolver; - use aws_smithy_runtime::client::connections::adapter::DynConnectorAdapter; - use aws_smithy_runtime::client::orchestrator::endpoints::DefaultEndpointResolver; - use aws_smithy_runtime_api::client::interceptors::error::ContextAttachedError; - use aws_smithy_runtime_api::client::interceptors::{ - Interceptor, InterceptorContext, Interceptors, - }; - use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, ConfigBagAccessors, Connection, HttpRequest, HttpResponse, TraceProbe, - }; - use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; - use aws_smithy_runtime_api::config_bag::ConfigBag; - use aws_types::region::SigningRegion; - use aws_types::SigningService; - use std::sync::Arc; - - pub struct ManualServiceRuntimePlugin { - pub connector: DynConnector, - pub endpoint_resolver: SharedEndpointResolver, - pub credentials_cache: SharedCredentialsCache, - } - - impl RuntimePlugin for ManualServiceRuntimePlugin { - fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { - let identity_resolvers = - aws_smithy_runtime_api::client::identity::IdentityResolvers::builder() - .identity_resolver( - aws_runtime::auth::sigv4::SCHEME_ID, - aws_runtime::identity::credentials::CredentialsIdentityResolver::new( - self.credentials_cache.clone(), - ), - ) - .identity_resolver( - "anonymous", - aws_smithy_runtime_api::client::identity::AnonymousIdentityResolver::new(), - ) - .build(); - cfg.set_identity_resolvers(identity_resolvers); - - let http_auth_schemes = - aws_smithy_runtime_api::client::orchestrator::HttpAuthSchemes::builder() - .auth_scheme( - aws_runtime::auth::sigv4::SCHEME_ID, - aws_runtime::auth::sigv4::SigV4HttpAuthScheme::new(), - ) - .build(); - cfg.set_http_auth_schemes(http_auth_schemes); - - cfg.set_auth_option_resolver( - aws_smithy_runtime_api::client::auth::option_resolver::AuthOptionListResolver::new( - Vec::new(), - ), - ); - - cfg.set_endpoint_resolver(DefaultEndpointResolver::new(self.endpoint_resolver.clone())); - - let params_builder = aws_sdk_s3::endpoint::Params::builder() - .set_region(Some("us-east-1".to_owned())) - .set_endpoint(Some("https://s3.us-east-1.amazonaws.com/".to_owned())); - cfg.put(params_builder); - - cfg.set_retry_strategy( - aws_smithy_runtime_api::client::retries::NeverRetryStrategy::new(), - ); - - let connection: Box = - Box::new(DynConnectorAdapter::new(self.connector.clone())); - cfg.set_connection(connection); - - cfg.set_trace_probe({ - #[derive(Debug)] - struct StubTraceProbe; - impl TraceProbe for StubTraceProbe { - fn dispatch_events(&self) { - // no-op - } - } - StubTraceProbe - }); - - cfg.put(SigningService::from_static("s3")); - cfg.put(SigningRegion::from(Region::from_static("us-east-1"))); - - cfg.put(ApiMetadata::new("unused", "unused")); - cfg.put(AwsUserAgent::for_tests()); // Override the user agent with the test UA - cfg.get::>() - .expect("interceptors set") - .register_client_interceptor(Arc::new(UserAgentInterceptor::new()) as _) - .register_client_interceptor(Arc::new(RecursionDetectionInterceptor::new()) as _); - Ok(()) - } - } - - // This is a temporary operation runtime plugin until EndpointParamsInterceptor and - // EndpointParamsFinalizerInterceptor have been fully implemented, in which case - // `.with_operation_plugin(ManualOperationRuntimePlugin)` can be removed. - pub struct ManualOperationRuntimePlugin; - - impl RuntimePlugin for ManualOperationRuntimePlugin { - fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { - #[derive(Debug)] - struct ListObjectsV2EndpointParamsInterceptor; - impl Interceptor for ListObjectsV2EndpointParamsInterceptor { - fn read_before_execution( - &self, - context: &InterceptorContext, - cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { - let input = context.input()?; - let input = input - .downcast_ref::() - .ok_or_else(|| "failed to downcast to ListObjectsV2Input")?; - let mut params_builder = cfg - .get::() - .ok_or_else(|| "missing endpoint params builder")? - .clone(); - params_builder = params_builder.set_bucket(input.bucket.clone()); - cfg.put(params_builder); - - Ok(()) - } - } - - #[derive(Debug)] - struct ListObjectsV2EndpointParamsFinalizerInterceptor; - impl Interceptor for ListObjectsV2EndpointParamsFinalizerInterceptor { - fn read_before_execution( - &self, - _context: &InterceptorContext, - cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { - let params_builder = cfg - .get::() - .ok_or_else(|| "missing endpoint params builder")? - .clone(); - let params = params_builder.build().map_err(|err| { - ContextAttachedError::new("endpoint params could not be built", err) - })?; - cfg.put( - aws_smithy_runtime_api::client::orchestrator::EndpointResolverParams::new( - params, - ), - ); - - Ok(()) - } - } - - cfg.get::>() - .expect("interceptors set") - .register_operation_interceptor( - Arc::new(ListObjectsV2EndpointParamsInterceptor) as _ - ) - .register_operation_interceptor(Arc::new( - ListObjectsV2EndpointParamsFinalizerInterceptor, - ) as _); - Ok(()) - } - } -} - criterion_group!(benches, middleware_bench, orchestrator_bench); criterion_main!(benches); diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index b306f42753..464c815d68 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -7,17 +7,13 @@ use aws_http::user_agent::AwsUserAgent; use aws_runtime::invocation_id::InvocationId; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::endpoint::Params; - use aws_sdk_s3::Client; - use aws_smithy_client::dvr; use aws_smithy_client::dvr::MediaType; use aws_smithy_client::erase::DynConnector; - use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::config_bag::ConfigBag; - use std::time::{Duration, SystemTime, UNIX_EPOCH}; const LIST_BUCKETS_PATH: &str = "test-data/list-objects-v2.json"; diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index f9757b4ef9..53aa5ee2aa 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -43,7 +43,7 @@ fun codegenScope(runtimeConfig: RuntimeConfig): Array> { return arrayOf( "ApiKeyAuthScheme" to authHttp.resolve("ApiKeyAuthScheme"), "ApiKeyLocation" to authHttp.resolve("ApiKeyLocation"), - "AuthOptionListResolver" to smithyRuntimeApi.resolve("client::auth::option_resolver::AuthOptionListResolver"), + "StaticAuthOptionResolver" to smithyRuntimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "BasicAuthScheme" to authHttp.resolve("BasicAuthScheme"), "BearerAuthScheme" to authHttp.resolve("BearerAuthScheme"), "DigestAuthScheme" to authHttp.resolve("DigestAuthScheme"), @@ -51,7 +51,6 @@ fun codegenScope(runtimeConfig: RuntimeConfig): Array> { "HTTP_BASIC_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_BASIC_AUTH_SCHEME_ID"), "HTTP_BEARER_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_BEARER_AUTH_SCHEME_ID"), "HTTP_DIGEST_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_DIGEST_AUTH_SCHEME_ID"), - "HttpAuthOption" to smithyRuntimeApi.resolve("client::orchestrator::HttpAuthOption"), "IdentityResolver" to smithyRuntimeApi.resolve("client::identity::IdentityResolver"), "IdentityResolvers" to smithyRuntimeApi.resolve("client::identity::IdentityResolvers"), "Login" to smithyRuntimeApi.resolve("client::identity::http::Login"), @@ -208,46 +207,23 @@ private class HttpAuthOperationRuntimePluginCustomization( when (section) { is OperationRuntimePluginSection.AdditionalConfig -> { withBlockTemplate( - "let auth_option_resolver = #{AuthOptionListResolver}::new(vec![", + "let auth_option_resolver = #{StaticAuthOptionResolver}::new(vec![", "]);", *codegenScope, ) { val authTrait: AuthTrait? = section.operationShape.getTrait() ?: serviceShape.getTrait() for (authScheme in authTrait?.valueSet ?: emptySet()) { when (authScheme) { - HttpApiKeyAuthTrait.ID -> { - rustTemplate( - "#{HttpAuthOption}::new(#{HTTP_API_KEY_AUTH_SCHEME_ID}, std::sync::Arc::new(#{PropertyBag}::new())),", - *codegenScope, - ) - } - - HttpBasicAuthTrait.ID -> { - rustTemplate( - "#{HttpAuthOption}::new(#{HTTP_BASIC_AUTH_SCHEME_ID}, std::sync::Arc::new(#{PropertyBag}::new())),", - *codegenScope, - ) - } - - HttpBearerAuthTrait.ID -> { - rustTemplate( - "#{HttpAuthOption}::new(#{HTTP_BEARER_AUTH_SCHEME_ID}, std::sync::Arc::new(#{PropertyBag}::new())),", - *codegenScope, - ) - } - - HttpDigestAuthTrait.ID -> { - rustTemplate( - "#{HttpAuthOption}::new(#{HTTP_DIGEST_AUTH_SCHEME_ID}, std::sync::Arc::new(#{PropertyBag}::new())),", - *codegenScope, - ) - } - + HttpApiKeyAuthTrait.ID -> rustTemplate("#{HTTP_API_KEY_AUTH_SCHEME_ID},", *codegenScope) + HttpBasicAuthTrait.ID -> rustTemplate("#{HTTP_BASIC_AUTH_SCHEME_ID},", *codegenScope) + HttpBearerAuthTrait.ID -> rustTemplate("#{HTTP_BEARER_AUTH_SCHEME_ID},", *codegenScope) + HttpDigestAuthTrait.ID -> rustTemplate("#{HTTP_DIGEST_AUTH_SCHEME_ID},", *codegenScope) else -> {} } } } + // TODO(enableNewSmithyRuntime): Make auth options additive in the config bag so that multiple codegen decorators can register them rustTemplate("${section.configBagName}.set_auth_option_resolver(auth_option_resolver);", *codegenScope) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index 7c2d1e7662..a2ba50cb6f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -76,8 +76,8 @@ class OperationRuntimePluginGenerator( private val codegenScope = codegenContext.runtimeConfig.let { rc -> val runtimeApi = RuntimeType.smithyRuntimeApi(rc) arrayOf( - "AuthOptionListResolverParams" to runtimeApi.resolve("client::auth::option_resolver::AuthOptionListResolverParams"), - "AuthOptionResolverParams" to runtimeApi.resolve("client::orchestrator::AuthOptionResolverParams"), + "StaticAuthOptionResolverParams" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolverParams"), + "AuthOptionResolverParams" to runtimeApi.resolve("client::auth::AuthOptionResolverParams"), "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), @@ -101,7 +101,7 @@ class OperationRuntimePluginGenerator( cfg.set_response_deserializer(${operationStructName}ResponseDeserializer); ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} - cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{AuthOptionListResolverParams}::new())); + cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StaticAuthOptionResolverParams}::new())); // Retry classifiers are operation-specific because they need to downcast operation-specific error types. let retry_classifiers = #{RetryClassifiers}::new() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 7f5963c572..a570d4df91 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -70,7 +70,7 @@ class ServiceRuntimePluginGenerator( val runtimeApi = RuntimeType.smithyRuntimeApi(rc) arrayOf( "AnonymousIdentityResolver" to runtimeApi.resolve("client::identity::AnonymousIdentityResolver"), - "AuthOptionListResolver" to runtimeApi.resolve("client::auth::option_resolver::AuthOptionListResolver"), + "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), @@ -78,7 +78,7 @@ class ServiceRuntimePluginGenerator( "ConnectorSettings" to RuntimeType.smithyClient(rc).resolve("http_connector::ConnectorSettings"), "DefaultEndpointResolver" to runtime.resolve("client::orchestrator::endpoints::DefaultEndpointResolver"), "DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"), - "HttpAuthSchemes" to runtimeApi.resolve("client::orchestrator::HttpAuthSchemes"), + "HttpAuthSchemes" to runtimeApi.resolve("client::auth::HttpAuthSchemes"), "IdentityResolvers" to runtimeApi.resolve("client::identity::IdentityResolvers"), "NeverRetryStrategy" to runtime.resolve("client::retries::strategy::NeverRetryStrategy"), "Params" to endpointTypesGenerator.paramsStruct(), @@ -112,7 +112,7 @@ class ServiceRuntimePluginGenerator( cfg.set_http_auth_schemes(http_auth_schemes); // Set an empty auth option resolver to be overridden by operations that need auth. - cfg.set_auth_option_resolver(#{AuthOptionListResolver}::new(Vec::new())); + cfg.set_auth_option_resolver(#{StaticAuthOptionResolver}::new(Vec::new())); let endpoint_resolver = #{DefaultEndpointResolver}::<#{Params}>::new( #{SharedEndpointResolver}::from(self.handle.conf.endpoint_resolver())); diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 0438c1bdfa..3d8cccad47 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -3,7 +3,141 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::client::identity::{Identity, IdentityResolver, IdentityResolvers}; +use crate::client::orchestrator::{BoxError, HttpRequest}; +use crate::config_bag::ConfigBag; +use crate::type_erasure::{TypeErasedBox, TypedBox}; +use std::any::Any; +use std::borrow::Cow; +use std::fmt::Debug; +use std::sync::Arc; + #[cfg(feature = "http-auth")] pub mod http; pub mod option_resolver; + +/// New type around an auth scheme ID. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub struct AuthSchemeId { + scheme_id: &'static str, +} + +impl AuthSchemeId { + /// Creates a new auth scheme ID. + pub const fn new(scheme_id: &'static str) -> Self { + Self { scheme_id } + } + + /// Returns the string equivalent of this auth scheme ID. + pub const fn as_str(&self) -> &'static str { + self.scheme_id + } +} + +#[derive(Debug)] +pub struct AuthOptionResolverParams(TypeErasedBox); + +impl AuthOptionResolverParams { + pub fn new(params: T) -> Self { + Self(TypedBox::new(params).erase()) + } + + pub fn get(&self) -> Option<&T> { + self.0.downcast_ref() + } +} + +pub trait AuthOptionResolver: Send + Sync + Debug { + fn resolve_auth_options<'a>( + &'a self, + params: &AuthOptionResolverParams, + ) -> Result, BoxError>; +} + +impl AuthOptionResolver for Box { + fn resolve_auth_options<'a>( + &'a self, + params: &AuthOptionResolverParams, + ) -> Result, BoxError> { + (**self).resolve_auth_options(params) + } +} + +#[derive(Debug)] +struct HttpAuthSchemesInner { + schemes: Vec<(AuthSchemeId, Box)>, +} +#[derive(Debug)] +pub struct HttpAuthSchemes { + inner: Arc, +} + +impl HttpAuthSchemes { + pub fn builder() -> builders::HttpAuthSchemesBuilder { + Default::default() + } + + pub fn scheme(&self, scheme_id: AuthSchemeId) -> Option<&dyn HttpAuthScheme> { + self.inner + .schemes + .iter() + .find(|scheme| scheme.0 == scheme_id) + .map(|scheme| &*scheme.1) + } +} + +pub trait HttpAuthScheme: Send + Sync + Debug { + fn scheme_id(&self) -> AuthSchemeId; + + fn identity_resolver<'a>( + &self, + identity_resolvers: &'a IdentityResolvers, + ) -> Option<&'a dyn IdentityResolver>; + + fn request_signer(&self) -> &dyn HttpRequestSigner; +} + +pub trait HttpRequestSigner: Send + Sync + Debug { + /// Return a signed version of the given request using the given identity. + /// + /// If the provided identity is incompatible with this signer, an error must be returned. + fn sign_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + config_bag: &ConfigBag, + ) -> Result<(), BoxError>; +} + +pub mod builders { + use super::*; + + #[derive(Debug, Default)] + pub struct HttpAuthSchemesBuilder { + schemes: Vec<(AuthSchemeId, Box)>, + } + + impl HttpAuthSchemesBuilder { + pub fn new() -> Self { + Default::default() + } + + pub fn auth_scheme( + mut self, + scheme_id: AuthSchemeId, + auth_scheme: impl HttpAuthScheme + 'static, + ) -> Self { + self.schemes.push((scheme_id, Box::new(auth_scheme) as _)); + self + } + + pub fn build(self) -> HttpAuthSchemes { + HttpAuthSchemes { + inner: Arc::new(HttpAuthSchemesInner { + schemes: self.schemes, + }), + } + } + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs index 2f93a2eb40..a4583c6e0a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs @@ -3,7 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -pub const HTTP_API_KEY_AUTH_SCHEME_ID: &str = "http-api-key-auth"; -pub const HTTP_BASIC_AUTH_SCHEME_ID: &str = "http-basic-auth"; -pub const HTTP_BEARER_AUTH_SCHEME_ID: &str = "http-bearer-auth"; -pub const HTTP_DIGEST_AUTH_SCHEME_ID: &str = "http-digest-auth"; +use crate::client::auth::AuthSchemeId; + +pub const HTTP_API_KEY_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-api-key-auth"); +pub const HTTP_BASIC_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-basic-auth"); +pub const HTTP_BEARER_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-bearer-auth"); +pub const HTTP_DIGEST_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-digest-auth"); diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs index ca3acef5f6..851233486b 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs @@ -3,41 +3,40 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::client::orchestrator::{ - AuthOptionResolver, AuthOptionResolverParams, BoxError, HttpAuthOption, -}; +use crate::client::auth::{AuthOptionResolver, AuthOptionResolverParams, AuthSchemeId}; +use crate::client::orchestrator::BoxError; use std::borrow::Cow; /// New-type around a `Vec` that implements `AuthOptionResolver`. /// /// This is useful for clients that don't require `AuthOptionResolverParams` to resolve auth options. #[derive(Debug)] -pub struct AuthOptionListResolver { - auth_options: Vec, +pub struct StaticAuthOptionResolver { + auth_options: Vec, } -impl AuthOptionListResolver { - /// Creates a new instance of `AuthOptionListResolver`. - pub fn new(auth_options: Vec) -> Self { +impl StaticAuthOptionResolver { + /// Creates a new instance of `StaticAuthOptionResolver`. + pub fn new(auth_options: Vec) -> Self { Self { auth_options } } } -impl AuthOptionResolver for AuthOptionListResolver { +impl AuthOptionResolver for StaticAuthOptionResolver { fn resolve_auth_options<'a>( &'a self, _params: &AuthOptionResolverParams, - ) -> Result, BoxError> { + ) -> Result, BoxError> { Ok(Cow::Borrowed(&self.auth_options)) } } -/// Empty params to be used with [`AuthOptionListResolver`]. +/// Empty params to be used with [`StaticAuthOptionResolver`]. #[derive(Debug)] -pub struct AuthOptionListResolverParams; +pub struct StaticAuthOptionResolverParams; -impl AuthOptionListResolverParams { - /// Creates new `AuthOptionListResolverParams`. +impl StaticAuthOptionResolverParams { + /// Creates new `StaticAuthOptionResolverParams`. pub fn new() -> Self { Self } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index 11a54af820..6d5cf9ff82 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::client::auth::AuthSchemeId; use crate::client::orchestrator::Future; -use aws_smithy_http::property_bag::PropertyBag; +use crate::config_bag::ConfigBag; use std::any::Any; use std::fmt::Debug; use std::sync::Arc; @@ -14,12 +15,12 @@ use std::time::SystemTime; pub mod http; pub trait IdentityResolver: Send + Sync + Debug { - fn resolve_identity(&self, identity_properties: &PropertyBag) -> Future; + fn resolve_identity(&self, config_bag: &ConfigBag) -> Future; } #[derive(Clone, Debug, Default)] pub struct IdentityResolvers { - identity_resolvers: Vec<(&'static str, Arc)>, + identity_resolvers: Vec<(AuthSchemeId, Arc)>, } impl IdentityResolvers { @@ -27,10 +28,10 @@ impl IdentityResolvers { builders::IdentityResolversBuilder::new() } - pub fn identity_resolver(&self, identity_type: &'static str) -> Option<&dyn IdentityResolver> { + pub fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option<&dyn IdentityResolver> { self.identity_resolvers .iter() - .find(|resolver| resolver.0 == identity_type) + .find(|resolver| resolver.0 == scheme_id) .map(|resolver| &*resolver.1) } @@ -83,17 +84,18 @@ impl AnonymousIdentityResolver { } impl IdentityResolver for AnonymousIdentityResolver { - fn resolve_identity(&self, _: &PropertyBag) -> Future { + fn resolve_identity(&self, _: &ConfigBag) -> Future { Future::ready(Ok(Identity::new(AnonymousIdentity::new(), None))) } } pub mod builders { use super::*; + use crate::client::auth::AuthSchemeId; #[derive(Debug, Default)] pub struct IdentityResolversBuilder { - pub(super) identity_resolvers: Vec<(&'static str, Arc)>, + pub(super) identity_resolvers: Vec<(AuthSchemeId, Arc)>, } impl IdentityResolversBuilder { @@ -103,11 +105,11 @@ pub mod builders { pub fn identity_resolver( mut self, - name: &'static str, + scheme_id: AuthSchemeId, resolver: impl IdentityResolver + 'static, ) -> Self { self.identity_resolvers - .push((name, Arc::new(resolver) as _)); + .push((scheme_id, Arc::new(resolver) as _)); self } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs index 5a58588b02..5866d772f4 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs @@ -7,7 +7,7 @@ use crate::client::identity::{Identity, IdentityResolver}; use crate::client::orchestrator::Future; -use aws_smithy_http::property_bag::PropertyBag; +use crate::config_bag::ConfigBag; use std::fmt::Debug; use std::sync::Arc; use std::time::SystemTime; @@ -65,7 +65,7 @@ impl From for Token { } impl IdentityResolver for Token { - fn resolve_identity(&self, _identity_properties: &PropertyBag) -> Future { + fn resolve_identity(&self, _config_bag: &ConfigBag) -> Future { Future::ready(Ok(Identity::new(self.clone(), self.0.expiration))) } } @@ -124,7 +124,7 @@ impl Login { } impl IdentityResolver for Login { - fn resolve_identity(&self, _identity_properties: &PropertyBag) -> Future { + fn resolve_identity(&self, _config_bag: &ConfigBag) -> Future { Future::ready(Ok(Identity::new(self.clone(), self.0.expiration))) } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 85233f12ce..db36e762d9 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::identity::{IdentityResolver, IdentityResolvers}; -use crate::client::identity::Identity; +use crate::client::auth::{AuthOptionResolver, AuthOptionResolverParams, HttpAuthSchemes}; +use crate::client::identity::IdentityResolvers; use crate::client::interceptors::context::{Input, OutputOrError}; use crate::client::retries::RetryClassifiers; use crate::client::retries::RetryStrategy; @@ -13,13 +13,10 @@ use crate::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_http::body::SdkBody; use aws_smithy_http::endpoint::EndpointPrefix; -use aws_smithy_http::property_bag::PropertyBag; use std::any::Any; -use std::borrow::Cow; use std::fmt::Debug; use std::future::Future as StdFuture; use std::pin::Pin; -use std::sync::Arc; use std::time::SystemTime; pub type HttpRequest = http::Request; @@ -55,104 +52,6 @@ impl Connection for Box { } } -#[derive(Debug)] -pub struct AuthOptionResolverParams(TypeErasedBox); - -impl AuthOptionResolverParams { - pub fn new(params: T) -> Self { - Self(TypedBox::new(params).erase()) - } - - pub fn get(&self) -> Option<&T> { - self.0.downcast_ref() - } -} - -pub trait AuthOptionResolver: Send + Sync + Debug { - fn resolve_auth_options<'a>( - &'a self, - params: &AuthOptionResolverParams, - ) -> Result, BoxError>; -} - -impl AuthOptionResolver for Box { - fn resolve_auth_options<'a>( - &'a self, - params: &AuthOptionResolverParams, - ) -> Result, BoxError> { - (**self).resolve_auth_options(params) - } -} - -#[derive(Clone, Debug)] -pub struct HttpAuthOption { - scheme_id: &'static str, - properties: Arc, -} - -impl HttpAuthOption { - pub fn new(scheme_id: &'static str, properties: Arc) -> Self { - Self { - scheme_id, - properties, - } - } - - pub fn scheme_id(&self) -> &'static str { - self.scheme_id - } - - pub fn properties(&self) -> &PropertyBag { - &self.properties - } -} - -#[derive(Debug)] -struct HttpAuthSchemesInner { - schemes: Vec<(&'static str, Box)>, -} -#[derive(Debug)] -pub struct HttpAuthSchemes { - inner: Arc, -} - -impl HttpAuthSchemes { - pub fn builder() -> builders::HttpAuthSchemesBuilder { - Default::default() - } - - pub fn scheme(&self, name: &'static str) -> Option<&dyn HttpAuthScheme> { - self.inner - .schemes - .iter() - .find(|scheme| scheme.0 == name) - .map(|scheme| &*scheme.1) - } -} - -pub trait HttpAuthScheme: Send + Sync + Debug { - fn scheme_id(&self) -> &'static str; - - fn identity_resolver<'a>( - &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver>; - - fn request_signer(&self) -> &dyn HttpRequestSigner; -} - -pub trait HttpRequestSigner: Send + Sync + Debug { - /// Return a signed version of the given request using the given identity. - /// - /// If the provided identity is incompatible with this signer, an error must be returned. - fn sign_request( - &self, - request: &mut HttpRequest, - identity: &Identity, - signing_properties: &PropertyBag, - ) -> Result<(), BoxError>; -} - #[derive(Debug)] pub struct EndpointResolverParams(TypeErasedBox); @@ -378,35 +277,3 @@ impl ConfigBagAccessors for ConfigBag { self.put::(request_time); } } - -pub mod builders { - use super::*; - - #[derive(Debug, Default)] - pub struct HttpAuthSchemesBuilder { - schemes: Vec<(&'static str, Box)>, - } - - impl HttpAuthSchemesBuilder { - pub fn new() -> Self { - Default::default() - } - - pub fn auth_scheme( - mut self, - name: &'static str, - auth_scheme: impl HttpAuthScheme + 'static, - ) -> Self { - self.schemes.push((name, Box::new(auth_scheme) as _)); - self - } - - pub fn build(self) -> HttpAuthSchemes { - HttpAuthSchemes { - inner: Arc::new(HttpAuthSchemesInner { - schemes: self.schemes, - }), - } - } - } -} diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs index 8bacee21f0..a9ca17f0b3 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs @@ -3,17 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_http::property_bag::PropertyBag; use aws_smithy_http::query_writer::QueryWriter; use aws_smithy_runtime_api::client::auth::http::{ HTTP_API_KEY_AUTH_SCHEME_ID, HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID, HTTP_DIGEST_AUTH_SCHEME_ID, }; +use aws_smithy_runtime_api::client::auth::{AuthSchemeId, HttpAuthScheme, HttpRequestSigner}; use aws_smithy_runtime_api::client::identity::http::{Login, Token}; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; -use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, HttpAuthScheme, HttpRequest, HttpRequestSigner, -}; +use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpRequest}; +use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_types::base64::encode; use http::header::HeaderName; use http::HeaderValue; @@ -49,7 +48,7 @@ impl ApiKeyAuthScheme { } impl HttpAuthScheme for ApiKeyAuthScheme { - fn scheme_id(&self) -> &'static str { + fn scheme_id(&self) -> AuthSchemeId { HTTP_API_KEY_AUTH_SCHEME_ID } @@ -77,7 +76,7 @@ impl HttpRequestSigner for ApiKeySigner { &self, request: &mut HttpRequest, identity: &Identity, - _signing_properties: &PropertyBag, + _config_bag: &ConfigBag, ) -> Result<(), BoxError> { let api_key = identity .data::() @@ -118,7 +117,7 @@ impl BasicAuthScheme { } impl HttpAuthScheme for BasicAuthScheme { - fn scheme_id(&self) -> &'static str { + fn scheme_id(&self) -> AuthSchemeId { HTTP_BASIC_AUTH_SCHEME_ID } @@ -142,7 +141,7 @@ impl HttpRequestSigner for BasicAuthSigner { &self, request: &mut HttpRequest, identity: &Identity, - _signing_properties: &PropertyBag, + _config_bag: &ConfigBag, ) -> Result<(), BoxError> { let login = identity .data::() @@ -175,7 +174,7 @@ impl BearerAuthScheme { } impl HttpAuthScheme for BearerAuthScheme { - fn scheme_id(&self) -> &'static str { + fn scheme_id(&self) -> AuthSchemeId { HTTP_BEARER_AUTH_SCHEME_ID } @@ -199,7 +198,7 @@ impl HttpRequestSigner for BearerAuthSigner { &self, request: &mut HttpRequest, identity: &Identity, - _signing_properties: &PropertyBag, + _config_bag: &ConfigBag, ) -> Result<(), BoxError> { let token = identity .data::() @@ -230,7 +229,7 @@ impl DigestAuthScheme { } impl HttpAuthScheme for DigestAuthScheme { - fn scheme_id(&self) -> &'static str { + fn scheme_id(&self) -> AuthSchemeId { HTTP_DIGEST_AUTH_SCHEME_ID } @@ -254,7 +253,7 @@ impl HttpRequestSigner for DigestAuthSigner { &self, _request: &mut HttpRequest, _identity: &Identity, - _signing_properties: &PropertyBag, + _config_bag: &ConfigBag, ) -> Result<(), BoxError> { unimplemented!( "support for signing with Smithy's `@httpDigestAuth` auth scheme is not implemented yet" @@ -275,14 +274,14 @@ mod tests { location: ApiKeyLocation::Header, name: "some-header-name".into(), }; - let signing_properties = PropertyBag::new(); + let config_bag = ConfigBag::base(); let identity = Identity::new(Token::new("some-token", None), None); let mut request = http::Request::builder() .uri("http://example.com/Foobaz") .body(SdkBody::empty()) .unwrap(); signer - .sign_request(&mut request, &identity, &signing_properties) + .sign_request(&mut request, &identity, &config_bag) .expect("success"); assert_eq!( "SomeSchemeName some-token", @@ -298,14 +297,14 @@ mod tests { location: ApiKeyLocation::Query, name: "some-query-name".into(), }; - let signing_properties = PropertyBag::new(); + let config_bag = ConfigBag::base(); let identity = Identity::new(Token::new("some-token", None), None); let mut request = http::Request::builder() .uri("http://example.com/Foobaz") .body(SdkBody::empty()) .unwrap(); signer - .sign_request(&mut request, &identity, &signing_properties) + .sign_request(&mut request, &identity, &config_bag) .expect("success"); assert!(request.headers().get("some-query-name").is_none()); assert_eq!( @@ -317,12 +316,12 @@ mod tests { #[test] fn test_basic_auth() { let signer = BasicAuthSigner; - let signing_properties = PropertyBag::new(); + let config_bag = ConfigBag::base(); let identity = Identity::new(Login::new("Aladdin", "open sesame", None), None); let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); signer - .sign_request(&mut request, &identity, &signing_properties) + .sign_request(&mut request, &identity, &config_bag) .expect("success"); assert_eq!( "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", @@ -334,11 +333,11 @@ mod tests { fn test_bearer_auth() { let signer = BearerAuthSigner; - let signing_properties = PropertyBag::new(); + let config_bag = ConfigBag::base(); let identity = Identity::new(Token::new("some-token", None), None); let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); signer - .sign_request(&mut request, &identity, &signing_properties) + .sign_request(&mut request, &identity, &config_bag) .expect("success"); assert_eq!( "Bearer some-token", diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 77c6b6f21d..9818593f05 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -30,20 +30,18 @@ pub(super) async fn orchestrate_auth( identity_resolvers = ?identity_resolvers, "orchestrating auth", ); - for option in auth_options.as_ref() { - let scheme_id = option.scheme_id(); - let scheme_properties = option.properties(); + for &scheme_id in auth_options.as_ref() { if let Some(auth_scheme) = cfg.http_auth_schemes().scheme(scheme_id) { if let Some(identity_resolver) = auth_scheme.identity_resolver(identity_resolvers) { let request_signer = auth_scheme.request_signer(); let identity = identity_resolver - .resolve_identity(scheme_properties) + .resolve_identity(cfg) .await .map_err(construction_failure)?; return dispatch_phase.include_mut(|ctx| { let request = ctx.request_mut()?; - request_signer.sign_request(request, &identity, scheme_properties)?; + request_signer.sign_request(request, &identity, cfg)?; Result::<_, BoxError>::Ok(()) }); } @@ -59,23 +57,21 @@ pub(super) async fn orchestrate_auth( mod tests { use super::*; use aws_smithy_http::body::SdkBody; - use aws_smithy_http::property_bag::PropertyBag; - use aws_smithy_runtime_api::client::auth::option_resolver::AuthOptionListResolver; + use aws_smithy_runtime_api::client::auth::option_resolver::StaticAuthOptionResolver; + use aws_smithy_runtime_api::client::auth::{ + AuthOptionResolverParams, AuthSchemeId, HttpAuthScheme, HttpAuthSchemes, HttpRequestSigner, + }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; - use aws_smithy_runtime_api::client::orchestrator::{ - AuthOptionResolverParams, Future, HttpAuthOption, HttpAuthScheme, HttpAuthSchemes, - HttpRequest, HttpRequestSigner, - }; + use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; use aws_smithy_runtime_api::type_erasure::TypedBox; - use std::sync::Arc; #[tokio::test] async fn basic_case() { #[derive(Debug)] struct TestIdentityResolver; impl IdentityResolver for TestIdentityResolver { - fn resolve_identity(&self, _identity_properties: &PropertyBag) -> Future { + fn resolve_identity(&self, _config_bag: &ConfigBag) -> Future { Future::ready(Ok(Identity::new("doesntmatter", None))) } } @@ -88,7 +84,7 @@ mod tests { &self, request: &mut HttpRequest, _identity: &Identity, - _signing_properties: &PropertyBag, + _config_bag: &ConfigBag, ) -> Result<(), BoxError> { request .headers_mut() @@ -97,13 +93,15 @@ mod tests { } } + const TEST_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("test-scheme"); + #[derive(Debug)] struct TestAuthScheme { signer: TestSigner, } impl HttpAuthScheme for TestAuthScheme { - fn scheme_id(&self) -> &'static str { - "test-scheme" + fn scheme_id(&self) -> AuthSchemeId { + TEST_SCHEME_ID } fn identity_resolver<'a>( @@ -124,18 +122,15 @@ mod tests { let mut cfg = ConfigBag::base(); cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); - cfg.set_auth_option_resolver(AuthOptionListResolver::new(vec![HttpAuthOption::new( - "test-scheme", - Arc::new(PropertyBag::new()), - )])); + cfg.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![TEST_SCHEME_ID])); cfg.set_identity_resolvers( IdentityResolvers::builder() - .identity_resolver("test-scheme", TestIdentityResolver) + .identity_resolver(TEST_SCHEME_ID, TestIdentityResolver) .build(), ); cfg.set_http_auth_schemes( HttpAuthSchemes::builder() - .auth_scheme("test-scheme", TestAuthScheme { signer: TestSigner }) + .auth_scheme(TEST_SCHEME_ID, TestAuthScheme { signer: TestSigner }) .build(), ); @@ -170,9 +165,9 @@ mod tests { let mut cfg = ConfigBag::base(); cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); - cfg.set_auth_option_resolver(AuthOptionListResolver::new(vec![ - HttpAuthOption::new(HTTP_BASIC_AUTH_SCHEME_ID, Arc::new(PropertyBag::new())), - HttpAuthOption::new(HTTP_BEARER_AUTH_SCHEME_ID, Arc::new(PropertyBag::new())), + cfg.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![ + HTTP_BASIC_AUTH_SCHEME_ID, + HTTP_BEARER_AUTH_SCHEME_ID, ])); cfg.set_http_auth_schemes( HttpAuthSchemes::builder() From 65e376d8c657cd476f7af2dba7b6bf33c00d4766 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 27 Apr 2023 11:14:55 -0700 Subject: [PATCH 068/253] Add SRA benchmark to compare the last release against the current release (#2648) ## Motivation and Context This PR adds a benchmark to make sure we didn't regress the old middleware client implementation while making changes to it for the new orchestrator implementation. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../integration-tests/aws-sdk-s3/Cargo.toml | 14 +- .../benches/middleware_vs_orchestrator.rs | 167 +++++++++++------- 2 files changed, 108 insertions(+), 73 deletions(-) diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml index 40c9f1bdbb..27998067f1 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml +++ b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml @@ -6,24 +6,20 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -aws-credential-types = { path = "../../../rust-runtime/aws-credential-types", features = ["test-util"] } aws-http = { path = "../../../rust-runtime/aws-http" } aws-runtime = { path = "../../../rust-runtime/aws-runtime" } aws-sdk-s3 = { path = "../../build/sdk/aws-sdk-s3", features = ["test-util"] } -aws-sigv4 = { path = "../../../rust-runtime/aws-sigv4" } -aws-types = { path = "../../../rust-runtime/aws-types" } -aws-smithy-async = { path = "../../../../rust-runtime/aws-smithy-async", features = ["rt-tokio"] } aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client", features = ["test-util", "rustls"] } -aws-smithy-types = { path = "../../../../rust-runtime/aws-smithy-types" } -aws-smithy-http = { path = "../../../../rust-runtime/aws-smithy-http" } -aws-smithy-runtime = { path = "../../../../rust-runtime/aws-smithy-runtime", features = ["test-util"] } aws-smithy-runtime-api = { path = "../../../../rust-runtime/aws-smithy-runtime-api" } +aws-types = { path = "../../../rust-runtime/aws-types" } criterion = { version = "0.4", features = ["async_tokio"] } +http = "0.2.3" +http-body = "0.4.5" +last-release-smithy-client = { version = "0.55", package = "aws-smithy-client", features = ["test-util", "rustls"] } +last-release-s3 = { version = "0.26", package = "aws-sdk-s3", features = ["test-util"] } tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3.15", features = ["env-filter", "json"] } -http = "0.2.3" -http-body = "0.4.5" [profile.release] debug = 1 diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs index 3fba943ba1..ba9d003aa3 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs @@ -6,21 +6,84 @@ #[macro_use] extern crate criterion; use aws_sdk_s3 as s3; -use aws_smithy_client::erase::DynConnector; -use aws_smithy_client::test_connection::infallible_connection_fn; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::config_bag::ConfigBag; -use criterion::Criterion; -use s3::endpoint::Params; +use criterion::{BenchmarkId, Criterion}; -async fn middleware(client: &s3::Client) { - client - .list_objects_v2() - .bucket("test-bucket") - .prefix("prefix~") - .send() - .await - .expect("successful execution"); +macro_rules! test_connection { + (head) => { + test_connection!(aws_smithy_client) + }; + (last_release) => { + test_connection!(last_release_smithy_client) + }; + ($package:ident) => { + $package::test_connection::infallible_connection_fn(|req| { + assert_eq!( + "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + req.uri().to_string() + ); + assert!(req.headers().contains_key("authorization")); + http::Response::builder() + .status(200) + .body( + r#" + + test-bucket + prefix~ + 1 + 1000 + false + + some-file.file + 2009-10-12T17:50:30.000Z + 434234 + STANDARD + + +"#, + ) + .unwrap() + }) + }; +} + +macro_rules! create_client { + (head) => { + create_client!(head, aws_sdk_s3) + }; + (last_release) => { + create_client!(last_release, last_release_s3) + }; + ($original:ident, $package:ident) => {{ + let conn = test_connection!($original); + let config = $package::Config::builder() + .credentials_provider($package::config::Credentials::for_tests()) + .region($package::config::Region::new("us-east-1")) + .http_connector(conn.clone()) + .build(); + $package::Client::from_conf(config) + }}; +} + +macro_rules! middleware_bench_fn { + ($fn_name:ident, head) => { + middleware_bench_fn!($fn_name, aws_sdk_s3) + }; + ($fn_name:ident, last_release) => { + middleware_bench_fn!($fn_name, last_release_s3) + }; + ($fn_name:ident, $package:ident) => { + async fn $fn_name(client: &$package::Client) { + client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .send() + .await + .expect("successful execution"); + } + }; } async fn orchestrator(client: &s3::Client) { @@ -32,7 +95,7 @@ async fn orchestrator(client: &s3::Client) { &self, cfg: &mut ConfigBag, ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { - let params_builder = Params::builder() + let params_builder = s3::endpoint::Params::builder() .set_region(Some(self.region.clone())) .bucket("test-bucket"); @@ -55,61 +118,37 @@ async fn orchestrator(client: &s3::Client) { .expect("successful execution"); } -fn test_connection() -> DynConnector { - infallible_connection_fn(|req| { - assert_eq!( - "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", - req.uri().to_string() - ); - assert!(req.headers().contains_key("authorization")); - http::Response::builder() - .status(200) - .body( - r#" - - test-bucket - prefix~ - 1 - 1000 - false - - some-file.file - 2009-10-12T17:50:30.000Z - 434234 - STANDARD - - -"#, - ) - .unwrap() - }) -} - -fn client() -> s3::Client { - let conn = test_connection(); - let config = s3::Config::builder() - .credentials_provider(s3::config::Credentials::for_tests()) - .region(s3::config::Region::new("us-east-1")) - .http_connector(conn.clone()) - .build(); - s3::Client::from_conf(config) -} +fn bench(c: &mut Criterion) { + let head_client = create_client!(head); + middleware_bench_fn!(middleware_head, head); -fn middleware_bench(c: &mut Criterion) { - let client = client(); - c.bench_function("middleware", move |b| { - b.to_async(tokio::runtime::Runtime::new().unwrap()) - .iter(|| async { middleware(&client).await }) - }); -} + let last_release_client = create_client!(last_release); + middleware_bench_fn!(middleware_last_release, last_release); -fn orchestrator_bench(c: &mut Criterion) { - let client = client(); - c.bench_function("orchestrator", move |b| { + let mut group = c.benchmark_group("compare"); + let param = "S3 ListObjectsV2"; + group.bench_with_input( + BenchmarkId::new("middleware (HEAD)", param), + param, + |b, _| { + b.to_async(tokio::runtime::Runtime::new().unwrap()) + .iter(|| async { middleware_head(&head_client).await }) + }, + ); + group.bench_with_input( + BenchmarkId::new("middleware (last_release)", param), + param, + |b, _| { + b.to_async(tokio::runtime::Runtime::new().unwrap()) + .iter(|| async { middleware_last_release(&last_release_client).await }) + }, + ); + group.bench_with_input(BenchmarkId::new("orchestrator", param), param, |b, _| { b.to_async(tokio::runtime::Runtime::new().unwrap()) - .iter(|| async { orchestrator(&client).await }) + .iter(|| async { orchestrator(&head_client).await }) }); + group.finish(); } -criterion_group!(benches, middleware_bench, orchestrator_bench); +criterion_group!(benches, bench); criterion_main!(benches); From 12338ad54e99de525e2d6b979f79716d23c7100f Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 28 Apr 2023 14:31:04 -0700 Subject: [PATCH 069/253] Change the orchestrator feature gate into a quad state (#2657) ## Motivation and Context So far we have been testing with the orchestrator on or off, with "on" meaning that both the middleware and orchestrator implementations exist, and you have to opt into the orchestrator via a `send_v2` function call (instead of `send`). This approach makes it difficult to test against the orchestrator exclusively, and also test the existing integration tests with the orchestrator implementation. This PR changes `enableNewSmithyRuntime` into a quad-state with: - middleware - both middleware and orchestrator, defaulting to middleware - both middleware and orchestrator, defaulting to orchestrator - orchestrator This allows for generating a client with the orchestrator exclusively, or generating both and defaulting to the orchestrator, to reduce testing friction. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/sdk-adhoc-test/build.gradle.kts | 2 - .../smithy/rustsdk/CredentialProviders.kt | 2 +- .../HttpConnectorConfigCustomization.kt | 2 +- .../rustsdk/HttpRequestChecksumDecorator.kt | 2 +- .../rustsdk/HttpResponseChecksumDecorator.kt | 2 +- .../rustsdk/IntegrationTestDependencies.kt | 2 +- .../smithy/rustsdk/InvocationIdDecorator.kt | 2 +- .../rustsdk/RecursionDetectionDecorator.kt | 2 +- .../smithy/rustsdk/SigV4AuthDecorator.kt | 4 +- .../smithy/rustsdk/UserAgentDecorator.kt | 2 +- aws/sdk/build.gradle.kts | 3 +- aws/sra-test/build.gradle.kts | 3 +- .../benches/middleware_vs_orchestrator.rs | 2 +- .../aws-sdk-s3/tests/sra_test.rs | 2 +- .../client/smithy/ClientCodegenContext.kt | 4 +- .../client/smithy/ClientRustSettings.kt | 45 +++++- .../customizations/ApiKeyAuthDecorator.kt | 2 +- .../customizations/EndpointPrefixGenerator.kt | 2 +- .../customizations/HttpAuthDecorator.kt | 10 +- .../HttpConnectorConfigDecorator.kt | 2 +- .../endpoint/EndpointParamsDecorator.kt | 2 +- .../EndpointParamsInterceptorGenerator.kt | 2 +- .../EndpointTraitBindingGenerator.kt | 5 +- .../smithy/generators/ServiceGenerator.kt | 2 +- .../client/FluentClientDecorator.kt | 2 +- .../client/FluentClientGenerator.kt | 147 +++++++++++------- .../protocol/ClientProtocolGenerator.kt | 2 +- .../customizations/HttpAuthDecoratorTest.kt | 16 +- .../generators/EndpointTraitBindingsTest.kt | 8 +- 29 files changed, 180 insertions(+), 103 deletions(-) diff --git a/aws/sdk-adhoc-test/build.gradle.kts b/aws/sdk-adhoc-test/build.gradle.kts index 98ad953105..fbd40ae1ca 100644 --- a/aws/sdk-adhoc-test/build.gradle.kts +++ b/aws/sdk-adhoc-test/build.gradle.kts @@ -46,7 +46,6 @@ val allCodegenTests = listOf( , "codegen": { "includeFluentClient": false, - "enableNewCrateOrganizationScheme": true }, "customizationConfig": { "awsSdk": { @@ -63,7 +62,6 @@ val allCodegenTests = listOf( , "codegen": { "includeFluentClient": false, - "enableNewCrateOrganizationScheme": true }, "customizationConfig": { "awsSdk": { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index 84d644a43c..eb03212135 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -32,7 +32,7 @@ class CredentialsProviderDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + listOf(CredentialsIdentityResolverRegistration(codegenContext)) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt index 75c252d0fb..d64ddea9b3 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt @@ -26,7 +26,7 @@ class HttpConnectorDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations.letIf(!codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.exclusivelyGenerateMiddleware) { it + HttpConnectorConfigCustomization(codegenContext) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt index f2913a1aa7..b72293b1fa 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt @@ -44,7 +44,7 @@ class HttpRequestChecksumDecorator : ClientCodegenDecorator { // TODO(enableNewSmithyRuntime): Implement checksumming via interceptor and delete this decorator private fun applies(codegenContext: ClientCodegenContext): Boolean = - !codegenContext.settings.codegenConfig.enableNewSmithyRuntime + codegenContext.smithyRuntimeMode.exclusivelyGenerateMiddleware override fun operationCustomizations( codegenContext: ClientCodegenContext, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt index 832ab7e07d..cebc4e5cba 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt @@ -35,7 +35,7 @@ class HttpResponseChecksumDecorator : ClientCodegenDecorator { // TODO(enableNewSmithyRuntime): Implement checksumming via interceptor and delete this decorator private fun applies(codegenContext: ClientCodegenContext, operationShape: OperationShape): Boolean = - !codegenContext.settings.codegenConfig.enableNewSmithyRuntime && operationShape.outputShape != ShapeId.from("com.amazonaws.s3#GetObjectOutput") + codegenContext.smithyRuntimeMode.generateMiddleware && operationShape.outputShape != ShapeId.from("com.amazonaws.s3#GetObjectOutput") override fun operationCustomizations( codegenContext: ClientCodegenContext, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt index 6eedc44a89..556e71d2b0 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt @@ -126,7 +126,7 @@ class S3TestDependencies(private val codegenContext: ClientCodegenContext) : Lib // TODO(enableNewSmithyRuntime): These additional dependencies may not be needed anymore when removing this flag // depending on if the sra-test is kept around or not. - if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { addDependency(CargoDependency.smithyRuntime(codegenContext.runtimeConfig).toDevDependency()) addDependency(CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig).toDevDependency()) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt index abf317570f..c608f1c573 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt @@ -21,7 +21,7 @@ class InvocationIdDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + listOf(InvocationIdRuntimePluginCustomization(codegenContext)) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt index 09e6a30742..4685acad75 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt @@ -22,7 +22,7 @@ class RecursionDetectionDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + listOf(RecursionDetectionRuntimePluginCustomization(codegenContext)) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index f4973330c5..6131e50c79 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -33,7 +33,7 @@ class SigV4AuthDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + listOf(AuthServiceRuntimePluginCustomization(codegenContext)) } @@ -42,7 +42,7 @@ class SigV4AuthDecorator : ClientCodegenDecorator { operation: OperationShape, baseCustomizations: List, ): List = - baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + listOf(AuthOperationRuntimePluginCustomization(codegenContext)) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index 0e83d2c99d..90183fbf93 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -55,7 +55,7 @@ class UserAgentDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + listOf(AddApiMetadataIntoConfigBag(codegenContext)) } diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index 06355aaab6..1f92a77b75 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -102,8 +102,7 @@ fun generateSmithyBuild(services: AwsServices): String { "renameErrors": false, "debugMode": $debugMode, "eventStreamAllowList": [$eventStreamAllowListMembers], - "enableNewCrateOrganizationScheme": true, - "enableNewSmithyRuntime": false + "enableNewSmithyRuntime": "middleware" }, "service": "${service.service}", "module": "$moduleName", diff --git a/aws/sra-test/build.gradle.kts b/aws/sra-test/build.gradle.kts index 7344916af0..669f231741 100644 --- a/aws/sra-test/build.gradle.kts +++ b/aws/sra-test/build.gradle.kts @@ -65,7 +65,8 @@ val allCodegenTests = servicesToGenerate.map { , "codegen": { "includeFluentClient": false, - "enableNewSmithyRuntime": true + ${ ""/* "enableNewSmithyRuntime": "both_default_middleware" */ } + "enableNewSmithyRuntime": "orchestrator" }, "customizationConfig": { "awsSdk": { diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs index ba9d003aa3..622cdaca44 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs @@ -107,7 +107,7 @@ async fn orchestrator(client: &s3::Client) { .list_objects_v2() .bucket("test-bucket") .prefix("prefix~") - .send_v2_with_plugin(Some(FixupPlugin { + .send_orchestrator_with_plugin(Some(FixupPlugin { region: client .conf() .region() diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index 464c815d68..3fe7e6373e 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -41,7 +41,7 @@ async fn sra_test() { .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) .bucket("test-bucket") .prefix("prefix~") - .send_v2_with_plugin(Some(fixup)) + .send_orchestrator_with_plugin(Some(fixup)) .await ); // To regenerate the test: diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt index e0ec0afb6e..92a8a9f8ca 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt @@ -32,4 +32,6 @@ data class ClientCodegenContext( val rootDecorator: ClientCodegenDecorator, ) : CodegenContext( model, symbolProvider, moduleDocProvider, serviceShape, protocol, settings, CodegenTarget.CLIENT, -) +) { + val smithyRuntimeMode: SmithyRuntimeMode get() = settings.codegenConfig.enableNewSmithyRuntime +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt index e6680d3e0e..d7246cf986 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt @@ -72,6 +72,43 @@ data class ClientRustSettings( } } +// TODO(enableNewSmithyRuntime): Remove this mode after switching to the orchestrator +enum class SmithyRuntimeMode { + Middleware, + BothDefaultMiddleware, + BothDefaultOrchestrator, + Orchestrator, + ; + + val exclusivelyGenerateMiddleware: Boolean get() = generateMiddleware && !generateOrchestrator + + val generateMiddleware: Boolean get() = when (this) { + Middleware, BothDefaultMiddleware, BothDefaultOrchestrator -> true + else -> false + } + + val generateOrchestrator: Boolean get() = when (this) { + Orchestrator, BothDefaultMiddleware, BothDefaultOrchestrator -> true + else -> false + } + + val defaultToMiddleware: Boolean get() = when (this) { + Middleware, BothDefaultMiddleware -> true + else -> false + } + val defaultToOrchestrator: Boolean get() = !defaultToMiddleware + + companion object { + fun fromString(value: String): SmithyRuntimeMode = when (value) { + "middleware" -> Middleware + "orchestrator" -> Orchestrator + "both_default_middleware" -> BothDefaultMiddleware + "both_default_orchestrator" -> BothDefaultOrchestrator + else -> throw IllegalArgumentException("unknown runtime mode: $value") + } + } +} + /** * [renameExceptions]: Rename `Exception` to `Error` in the generated SDK * [includeFluentClient]: Generate a `client` module in the generated SDK (currently the AWS SDK sets this to `false` @@ -87,7 +124,7 @@ data class ClientCodegenConfig( // TODO(EventStream): [CLEANUP] Remove this property when turning on Event Stream for all services val eventStreamAllowList: Set = defaultEventStreamAllowList, // TODO(SmithyRuntime): Remove this once we commit to switch to aws-smithy-runtime and aws-smithy-runtime-api - val enableNewSmithyRuntime: Boolean = defaultEnableNewSmithyRuntime, + val enableNewSmithyRuntime: SmithyRuntimeMode = defaultEnableNewSmithyRuntime, ) : CoreCodegenConfig( formatTimeoutSeconds, debugMode, ) { @@ -96,7 +133,7 @@ data class ClientCodegenConfig( private const val defaultIncludeFluentClient = true private const val defaultAddMessageToErrors = true private val defaultEventStreamAllowList: Set = emptySet() - private const val defaultEnableNewSmithyRuntime = false + private val defaultEnableNewSmithyRuntime = SmithyRuntimeMode.Middleware fun fromCodegenConfigAndNode(coreCodegenConfig: CoreCodegenConfig, node: Optional) = if (node.isPresent) { @@ -109,7 +146,9 @@ data class ClientCodegenConfig( renameExceptions = node.get().getBooleanMemberOrDefault("renameErrors", defaultRenameExceptions), includeFluentClient = node.get().getBooleanMemberOrDefault("includeFluentClient", defaultIncludeFluentClient), addMessageToErrors = node.get().getBooleanMemberOrDefault("addMessageToErrors", defaultAddMessageToErrors), - enableNewSmithyRuntime = node.get().getBooleanMemberOrDefault("enableNewSmithyRuntime", defaultEnableNewSmithyRuntime), + enableNewSmithyRuntime = SmithyRuntimeMode.fromString( + node.get().getStringMemberOrDefault("enableNewSmithyRuntime", "middleware"), + ), ) } else { ClientCodegenConfig( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt index 58fd14e6a5..14fe1fd1c7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt @@ -38,7 +38,7 @@ class ApiKeyAuthDecorator : ClientCodegenDecorator { override val order: Byte = 10 private fun applies(codegenContext: ClientCodegenContext) = - !codegenContext.settings.codegenConfig.enableNewSmithyRuntime && + codegenContext.smithyRuntimeMode.generateMiddleware && isSupportedApiKeyAuth(codegenContext) override fun configCustomizations( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt index 2fe90239de..190aac3680 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt @@ -32,7 +32,7 @@ class EndpointPrefixGenerator(private val codegenContext: ClientCodegenContext, endpointTraitBindings.render( this, "self", - codegenContext.settings.codegenConfig.enableNewSmithyRuntime, + codegenContext.smithyRuntimeMode, ) } rust("request.properties_mut().insert(endpoint_prefix);") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index 53aa5ee2aa..5b0eba15da 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -68,12 +68,12 @@ private data class HttpAuthSchemes( companion object { fun from(codegenContext: ClientCodegenContext): HttpAuthSchemes { val authSchemes = ServiceIndex.of(codegenContext.model).getAuthSchemes(codegenContext.serviceShape).keys - val newRuntimeEnabled = codegenContext.settings.codegenConfig.enableNewSmithyRuntime + val generateOrchestrator = codegenContext.smithyRuntimeMode.generateOrchestrator return HttpAuthSchemes( - apiKey = newRuntimeEnabled && authSchemes.contains(HttpApiKeyAuthTrait.ID), - basic = newRuntimeEnabled && authSchemes.contains(HttpBasicAuthTrait.ID), - bearer = newRuntimeEnabled && authSchemes.contains(HttpBearerAuthTrait.ID), - digest = newRuntimeEnabled && authSchemes.contains(HttpDigestAuthTrait.ID), + apiKey = generateOrchestrator && authSchemes.contains(HttpApiKeyAuthTrait.ID), + basic = generateOrchestrator && authSchemes.contains(HttpBasicAuthTrait.ID), + bearer = generateOrchestrator && authSchemes.contains(HttpBearerAuthTrait.ID), + digest = generateOrchestrator && authSchemes.contains(HttpDigestAuthTrait.ID), ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index 102c50e718..d5ecd078b1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -25,7 +25,7 @@ class HttpConnectorConfigDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + HttpConnectorConfigCustomization(codegenContext) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt index ba728bcdf8..f35becb55a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt @@ -31,7 +31,7 @@ class EndpointParamsDecorator : ClientCodegenDecorator { operation: OperationShape, baseCustomizations: List, ): List = - baseCustomizations.letIf(codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + listOf(EndpointParametersRuntimePluginCustomization(codegenContext, operation)) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index b00a020508..2dc2f2e1e8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -118,7 +118,7 @@ class EndpointParamsInterceptorGenerator( endpointTraitBindings.render( this, "_input", - codegenContext.settings.codegenConfig.enableNewSmithyRuntime, + codegenContext.smithyRuntimeMode, ) } rust("cfg.put(endpoint_prefix);") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt index 4cc4b93347..f02cb5cde2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.EndpointTrait +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.smithy.generators.http.rustFormatString import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -44,7 +45,7 @@ class EndpointTraitBindings( * * The returned expression is a `Result` */ - fun render(writer: RustWriter, input: String, enableNewSmithyRuntime: Boolean) { + fun render(writer: RustWriter, input: String, smithyRuntimeMode: SmithyRuntimeMode) { // the Rust format pattern to make the endpoint prefix e.g. "{}.foo" val formatLiteral = endpointTrait.prefixFormatString() if (endpointTrait.hostPrefix.labels.isEmpty()) { @@ -67,7 +68,7 @@ class EndpointTraitBindings( // NOTE: this is dead code until we start respecting @required rust("let $field = &$input.$field;") } - val contents = if (enableNewSmithyRuntime) { + val contents = if (smithyRuntimeMode.generateOrchestrator) { // TODO(enableNewSmithyRuntime): Remove the allow attribute once all places need .into method """ if $field.is_empty() { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index 764efb35df..050e124376 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -47,7 +47,7 @@ class ServiceGenerator( ) serviceConfigGenerator.render(this) - if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { // Enable users to opt in to the test-utils in the runtime crate rustCrate.mergeFeature(TestUtilFeature.copy(deps = listOf("aws-smithy-runtime/test-util"))) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt index e5e2760cee..7fb225fa48 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt @@ -36,7 +36,7 @@ class FluentClientDecorator : ClientCodegenDecorator { return } - val generics = if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + val generics = if (codegenContext.smithyRuntimeMode.generateOrchestrator) { NoClientGenerics(codegenContext.runtimeConfig) } else { FlexibleClientGenerics( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 6c085fd673..f10de15ee4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -78,7 +78,7 @@ class FluentClientGenerator( private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val core = FluentClientCore(model) - private val enableNewSmithyRuntime = codegenContext.settings.codegenConfig.enableNewSmithyRuntime + private val smithyRuntimeMode = codegenContext.smithyRuntimeMode fun render(crate: RustCrate) { renderFluentClient(crate) @@ -255,7 +255,7 @@ class FluentClientGenerator( "Inner" to symbolProvider.symbolForBuilder(input), "generics" to generics.decl, ) - if (enableNewSmithyRuntime) { + if (smithyRuntimeMode.generateOrchestrator) { rust("config_override: std::option::Option,") } } @@ -279,11 +279,26 @@ class FluentClientGenerator( "}", ) { rust("handle, inner: Default::default(),") - if (enableNewSmithyRuntime) { + if (smithyRuntimeMode.generateOrchestrator) { rust("config_override: None,") } } } + val middlewareScope = arrayOf( + "CustomizableOperation" to ClientRustModule.Client.customize.toType() + .resolve("CustomizableOperation"), + "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), + "OperationError" to errorType, + "OperationOutput" to outputType, + "SdkError" to RuntimeType.sdkError(runtimeConfig), + "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), + "send_bounds" to generics.sendBounds(operationSymbol, outputType, errorType, retryClassifier), + "customizable_op_type_params" to rustTypeParameters( + symbolProvider.toSymbol(operation), + retryClassifier, + generics.toRustGenerics(), + ), + ) rustTemplate( """ /// Consume this builder, creating a customizable operation that can be modified before being @@ -300,15 +315,9 @@ class FluentClientGenerator( Ok(#{CustomizableOperation} { handle, operation }) } - /// Sends the request and returns the response. - /// - /// If an error occurs, an `SdkError` will be returned with additional details that - /// can be matched against. - /// - /// By default, any retryable failures will be retried twice. Retry behavior - /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be - /// set when configuring the client. - pub async fn send(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}>> + // This function will go away in the near future. Do not rely on it. + ##[doc(hidden)] + pub async fn send_middleware(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}>> #{send_bounds:W} { let op = self.inner.build().map_err(#{SdkError}::construction_failure)? .make_operation(&self.handle.conf) @@ -317,24 +326,11 @@ class FluentClientGenerator( self.handle.client.call(op).await } """, - "CustomizableOperation" to ClientRustModule.Client.customize.toType() - .resolve("CustomizableOperation"), - "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), - "OperationError" to errorType, - "OperationOutput" to outputType, - "SdkError" to RuntimeType.sdkError(runtimeConfig), - "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), - "send_bounds" to generics.sendBounds(operationSymbol, outputType, errorType, retryClassifier), - "customizable_op_type_params" to rustTypeParameters( - symbolProvider.toSymbol(operation), - retryClassifier, - generics.toRustGenerics(), - ), + *middlewareScope, ) - if (enableNewSmithyRuntime) { + if (smithyRuntimeMode.defaultToMiddleware) { rustTemplate( """ - // TODO(enableNewSmithyRuntime): Replace `send` with `send_v2` /// Sends the request and returns the response. /// /// If an error occurs, an `SdkError` will be returned with additional details that @@ -343,13 +339,40 @@ class FluentClientGenerator( /// By default, any retryable failures will be retried twice. Retry behavior /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be /// set when configuring the client. - pub async fn send_v2(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { - self.send_v2_with_plugin(Option::>::None).await + pub async fn send(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}>> + #{send_bounds:W} { + self.send_middleware().await } + """, + *middlewareScope, + ) + } + if (smithyRuntimeMode.generateOrchestrator) { + val orchestratorScope = arrayOf( + "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::HttpResponse"), + "OperationError" to errorType, + "Operation" to symbolProvider.toSymbol(operation), + "OperationOutput" to outputType, + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::RuntimePlugins"), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + "TypedBox" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypedBox"), + "invoke" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke"), + ) + rustTemplate( + """ + ##[doc(hidden)] + pub async fn send_orchestrator(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + self.send_orchestrator_with_plugin(Option::>::None).await + } + + ##[doc(hidden)] // TODO(enableNewSmithyRuntime): Delete when unused - /// Equivalent to [`Self::send_v2`] but adds a final runtime plugin to shim missing behavior - pub async fn send_v2_with_plugin(self, final_plugin: Option) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + /// Equivalent to [`Self::send_orchestrator`] but adds a final runtime plugin to shim missing behavior + pub async fn send_orchestrator_with_plugin(self, final_plugin: Option) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { let mut runtime_plugins = #{RuntimePlugins}::new() .with_client_plugin(crate::config::ServiceRuntimePlugin::new(self.handle.clone())); if let Some(config_override) = self.config_override { @@ -373,18 +396,26 @@ class FluentClientGenerator( Ok(#{TypedBox}::<#{OperationOutput}>::assume_from(output).expect("correct output type").unwrap()) } """, - "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::orchestrator::HttpResponse"), - "OperationError" to errorType, - "Operation" to symbolProvider.toSymbol(operation), - "OperationOutput" to outputType, - "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), - "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::runtime_plugin::RuntimePlugins"), - "SdkError" to RuntimeType.sdkError(runtimeConfig), - "TypedBox" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypedBox"), - "invoke" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke"), + *orchestratorScope, ) + if (smithyRuntimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Sends the request and returns the response. + /// + /// If an error occurs, an `SdkError` will be returned with additional details that + /// can be matched against. + /// + /// By default, any retryable failures will be retried twice. Retry behavior + /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be + /// set when configuring the client. + pub async fn send(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + self.send_orchestrator().await + } + """, + *orchestratorScope, + ) + } rustTemplate( """ @@ -426,20 +457,24 @@ class FluentClientGenerator( """, ) } - PaginatorGenerator.paginatorType(codegenContext, generics, operation, retryClassifier) - ?.also { paginatorType -> - rustTemplate( - """ - /// Create a paginator for this request - /// - /// Paginators are used by calling [`send().await`](#{Paginator}::send) which returns a `Stream`. - pub fn into_paginator(self) -> #{Paginator}${generics.inst} { - #{Paginator}::new(self.handle, self.inner) - } - """, - "Paginator" to paginatorType, - ) - } + + // TODO(enableNewSmithyRuntime): Port paginators to the orchestrator + if (smithyRuntimeMode.generateMiddleware) { + PaginatorGenerator.paginatorType(codegenContext, generics, operation, retryClassifier) + ?.also { paginatorType -> + rustTemplate( + """ + /// Create a paginator for this request + /// + /// Paginators are used by calling [`send().await`](#{Paginator}::send) which returns a `Stream`. + pub fn into_paginator(self) -> #{Paginator}${generics.inst} { + #{Paginator}::new(self.handle, self.inner) + } + """, + "Paginator" to paginatorType, + ) + } + } writeCustomizations( customizations, FluentClientSection.FluentBuilderImpl( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt index 3c8fc7c974..85dcb206db 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt @@ -97,7 +97,7 @@ open class ClientProtocolGenerator( } traitGenerator.generateTraitImpls(operationWriter, operationShape, operationCustomizations) - if (codegenContext.settings.codegenConfig.enableNewSmithyRuntime) { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { OperationRuntimePluginGenerator(codegenContext).render( operationWriter, operationShape, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt index 29c3b2e853..bcb5ec2f4e 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt @@ -6,8 +6,8 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import org.junit.jupiter.api.Test -import software.amazon.smithy.model.node.BooleanNode import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.model.node.StringNode import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency @@ -22,7 +22,7 @@ private fun additionalSettings(): ObjectNode = ObjectNode.objectNodeBuilder() .withMember( "codegen", ObjectNode.objectNodeBuilder() - .withMember("enableNewSmithyRuntime", BooleanNode.from(true)).build(), + .withMember("enableNewSmithyRuntime", StringNode.from("orchestrator")).build(), ) .build() @@ -67,7 +67,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_v2() + .send_orchestrator() .await .expect("success"); connector.assert_requests_match(&[]); @@ -101,7 +101,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_v2() + .send_orchestrator() .await .expect("success"); connector.assert_requests_match(&[]); @@ -146,7 +146,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_v2() + .send_orchestrator() .await .expect("success"); connector.assert_requests_match(&[]); @@ -192,7 +192,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_v2() + .send_orchestrator() .await .expect("success"); connector.assert_requests_match(&[]); @@ -238,7 +238,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_v2() + .send_orchestrator() .await .expect("success"); connector.assert_requests_match(&[]); @@ -284,7 +284,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_v2() + .send_orchestrator() .await .expect("success"); connector.assert_requests_match(&[]); diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt index 6fe1328bb8..34225e8dfd 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt @@ -11,6 +11,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.EndpointTrait +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.rustlang.Attribute @@ -37,8 +38,9 @@ internal class EndpointTraitBindingsTest { } @ParameterizedTest - @ValueSource(booleans = [true, false]) - fun `generate endpoint prefixes`(enableNewSmithyRuntime: Boolean) { + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generate endpoint prefixes`(smithyRuntimeModeStr: String) { + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) val model = """ namespace test @readonly @@ -76,7 +78,7 @@ internal class EndpointTraitBindingsTest { RuntimeType.smithyHttp(TestRuntimeConfig), TestRuntimeConfig.operationBuildError(), ) { - endpointBindingGenerator.render(this, "self", enableNewSmithyRuntime) + endpointBindingGenerator.render(this, "self", smithyRuntimeMode) } } unitTest( From 9361aa52345c23ae4943cb8f0e4155b801414a58 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 28 Apr 2023 14:59:30 -0700 Subject: [PATCH 070/253] Generate endpoint params in the orchestrator codegen (#2658) ## Motivation and Context This PR adds the codegen logic to generate endpoint parameters in the endpoint params interceptor. Fixes #2644. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-sdk-s3/tests/sra_test.rs | 8 -- .../endpoint/EndpointParamsDecorator.kt | 4 - .../smithy/endpoint/EndpointsDecorator.kt | 1 + .../EndpointParamsInterceptorGenerator.kt | 135 ++++++++++-------- .../ServiceRuntimePluginGenerator.kt | 8 +- 5 files changed, 83 insertions(+), 73 deletions(-) diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index 3fe7e6373e..bb7c5d4d31 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -6,7 +6,6 @@ use aws_http::user_agent::AwsUserAgent; use aws_runtime::invocation_id::InvocationId; use aws_sdk_s3::config::{Credentials, Region}; -use aws_sdk_s3::endpoint::Params; use aws_sdk_s3::Client; use aws_smithy_client::dvr; use aws_smithy_client::dvr::MediaType; @@ -31,7 +30,6 @@ async fn sra_test() { .build(); let client = Client::from_conf(config); let fixup = FixupPlugin { - client: client.clone(), timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), }; @@ -52,7 +50,6 @@ async fn sra_test() { } struct FixupPlugin { - client: Client, timestamp: SystemTime, } impl RuntimePlugin for FixupPlugin { @@ -60,11 +57,6 @@ impl RuntimePlugin for FixupPlugin { &self, cfg: &mut ConfigBag, ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { - let params_builder = Params::builder() - .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) - .bucket("test-bucket"); - - cfg.put(params_builder); cfg.set_request_time(RequestTime::new(self.timestamp.clone())); cfg.put(AwsUserAgent::for_tests()); cfg.put(InvocationId::for_tests()); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt index f35becb55a..494b6412f8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt @@ -47,10 +47,6 @@ private class EndpointParametersRuntimePluginCustomization( section.registerInterceptor(codegenContext.runtimeConfig, this) { rust("${operationName}EndpointParamsInterceptor") } - // The finalizer interceptor should be registered last - section.registerInterceptor(codegenContext.runtimeConfig, this) { - rust("${operationName}EndpointParamsFinalizerInterceptor") - } } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt index 8aa3990850..bc101412af 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt @@ -101,6 +101,7 @@ class EndpointsDecorator : ClientCodegenDecorator { override val name: String = "Endpoints" override val order: Byte = 0 + // TODO(enableNewSmithyRuntime): Remove `operationCustomizations` and `InjectEndpointInMakeOperation` override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 2dc2f2e1e8..d79ad7b600 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -5,10 +5,17 @@ package software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators +import software.amazon.smithy.model.node.BooleanNode +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.node.StringNode import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ShapeType import software.amazon.smithy.model.traits.EndpointTrait +import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters +import software.amazon.smithy.rulesengine.traits.ContextIndex import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointTypesGenerator +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName import software.amazon.smithy.rust.codegen.client.smithy.generators.EndpointTraitBindings import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -17,13 +24,17 @@ 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.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.util.PANIC +import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.inputShape +import software.amazon.smithy.rust.codegen.core.util.orNull class EndpointParamsInterceptorGenerator( private val codegenContext: ClientCodegenContext, ) { private val model = codegenContext.model private val symbolProvider = codegenContext.symbolProvider + private val endpointTypesGenerator = EndpointTypesGenerator.fromContext(codegenContext) private val codegenScope = codegenContext.runtimeConfig.let { rc -> val endpointTypesGenerator = EndpointTypesGenerator.fromContext(codegenContext) val runtimeApi = CargoDependency.smithyRuntimeApi(rc).toType() @@ -34,29 +45,19 @@ class EndpointParamsInterceptorGenerator( "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), "ContextAttachedError" to interceptors.resolve("error::ContextAttachedError"), "EndpointResolverParams" to orchestrator.resolve("EndpointResolverParams"), - "HttpResponse" to orchestrator.resolve("HttpResponse"), "HttpRequest" to orchestrator.resolve("HttpRequest"), + "HttpResponse" to orchestrator.resolve("HttpResponse"), "Interceptor" to interceptors.resolve("Interceptor"), "InterceptorContext" to interceptors.resolve("InterceptorContext"), "InterceptorError" to interceptors.resolve("error::InterceptorError"), - "ParamsBuilder" to endpointTypesGenerator.paramsBuilder(), + "Params" to endpointTypesGenerator.paramsStruct(), ) } fun render(writer: RustWriter, operationShape: OperationShape) { val operationName = symbolProvider.toSymbol(operationShape).name - renderInterceptor( - writer, - "${operationName}EndpointParamsInterceptor", - implInterceptorBodyForEndpointParams(operationShape), - ) - renderInterceptor( - writer, "${operationName}EndpointParamsFinalizerInterceptor", - implInterceptorBodyForEndpointParamsFinalizer, - ) - } - - private fun renderInterceptor(writer: RustWriter, interceptorName: String, implBody: Writable) { + val operationInput = symbolProvider.toSymbol(operationShape.inputShape(model)) + val interceptorName = "${operationName}EndpointParamsInterceptor" writer.rustTemplate( """ ##[derive(Debug)] @@ -68,37 +69,78 @@ class EndpointParamsInterceptorGenerator( context: &#{InterceptorContext}<#{HttpRequest}, #{HttpResponse}>, cfg: &mut #{ConfigBag}, ) -> Result<(), #{BoxError}> { - #{body:W} + let _input = context.input()?; + let _input = _input + .downcast_ref::<${operationInput.name}>() + .ok_or("failed to downcast to ${operationInput.name}")?; + + #{endpoint_prefix:W} + + // HACK: pull the handle out of the config bag until config is implemented right + let handle = cfg.get::>() + .expect("the handle is hacked into the config bag"); + let _config = &handle.conf; + + let params = #{Params}::builder() + #{param_setters} + .build() + .map_err(|err| #{ContextAttachedError}::new("endpoint params could not be built", err))?; + cfg.put(#{EndpointResolverParams}::new(params)); + Ok(()) } } """, *codegenScope, - "body" to implBody, + "endpoint_prefix" to endpointPrefix(operationShape), + "param_setters" to paramSetters(operationShape, endpointTypesGenerator.params), ) } - private fun implInterceptorBodyForEndpointParams(operationShape: OperationShape): Writable = writable { - val operationInput = symbolProvider.toSymbol(operationShape.inputShape(model)) - rustTemplate( - """ - let input = context.input()?; - let _input = input - .downcast_ref::<${operationInput.name}>() - .ok_or("failed to downcast to ${operationInput.name}")?; - let params_builder = cfg - .get::<#{ParamsBuilder}>() - .ok_or("missing endpoint params builder")? - .clone(); - ${"" /* TODO(EndpointResolver): Call setters on `params_builder` to update its fields by using values from `_input` */} - cfg.put(params_builder); + private fun paramSetters(operationShape: OperationShape, params: Parameters) = writable { + val idx = ContextIndex.of(codegenContext.model) + val memberParams = idx.getContextParams(operationShape).toList().sortedBy { it.first.memberName } + val builtInParams = params.toList().filter { it.isBuiltIn } + // first load builtins and their defaults + builtInParams.forEach { param -> + endpointTypesGenerator.builtInFor(param, "_config")?.also { defaultValue -> + rust(".set_${param.name.rustName()}(#W)", defaultValue) + } + } - #{endpoint_prefix:W} + idx.getClientContextParams(codegenContext.serviceShape).orNull()?.parameters?.forEach { (name, param) -> + val paramName = EndpointParamsGenerator.memberName(name) + val setterName = EndpointParamsGenerator.setterName(name) + if (param.type == ShapeType.BOOLEAN) { + rust(".$setterName(_config.$paramName)") + } else { + rust(".$setterName(_config.$paramName.clone())") + } + } - Ok(()) - """, - *codegenScope, - "endpoint_prefix" to endpointPrefix(operationShape), - ) + idx.getStaticContextParams(operationShape).orNull()?.parameters?.forEach { (name, param) -> + val setterName = EndpointParamsGenerator.setterName(name) + val value = param.value.toWritable() + rust(".$setterName(#W)", value) + } + + // lastly, allow these to be overridden by members + memberParams.forEach { (memberShape, param) -> + val memberName = codegenContext.symbolProvider.toMemberName(memberShape) + rust( + ".${EndpointParamsGenerator.setterName(param.name)}(_input.$memberName.clone())", + ) + } + } + + private fun Node.toWritable(): Writable { + val node = this + return writable { + when (node) { + is StringNode -> rust("Some(${node.value.dq()}.to_string())") + is BooleanNode -> rust("Some(${node.value})") + else -> PANIC("unsupported default value: $node") + } + } } private fun endpointPrefix(operationShape: OperationShape): Writable = writable { @@ -124,25 +166,4 @@ class EndpointParamsInterceptorGenerator( rust("cfg.put(endpoint_prefix);") } } - - private val implInterceptorBodyForEndpointParamsFinalizer: Writable = writable { - rustTemplate( - """ - let _ = context; - let params_builder = cfg - .get::<#{ParamsBuilder}>() - .ok_or("missing endpoint params builder")? - .clone(); - let params = params_builder - .build() - .map_err(|err| #{ContextAttachedError}::new("endpoint params could not be built", err))?; - cfg.put( - #{EndpointResolverParams}::new(params) - ); - - Ok(()) - """, - *codegenScope, - ) - } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index a570d4df91..dad89ae04e 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -70,7 +70,6 @@ class ServiceRuntimePluginGenerator( val runtimeApi = RuntimeType.smithyRuntimeApi(rc) arrayOf( "AnonymousIdentityResolver" to runtimeApi.resolve("client::identity::AnonymousIdentityResolver"), - "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), @@ -85,6 +84,7 @@ class ServiceRuntimePluginGenerator( "ResolveEndpoint" to http.resolve("endpoint::ResolveEndpoint"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), "SharedEndpointResolver" to http.resolve("endpoint::SharedEndpointResolver"), + "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "TraceProbe" to runtimeApi.resolve("client::orchestrator::TraceProbe"), ) } @@ -106,6 +106,9 @@ class ServiceRuntimePluginGenerator( fn configure(&self, cfg: &mut #{ConfigBag}) -> Result<(), #{BoxError}> { use #{ConfigBagAccessors}; + // HACK: Put the handle into the config bag to work around config not being fully implemented yet + cfg.put(self.handle.clone()); + let http_auth_schemes = #{HttpAuthSchemes}::builder() #{http_auth_scheme_customizations} .build(); @@ -118,9 +121,6 @@ class ServiceRuntimePluginGenerator( #{SharedEndpointResolver}::from(self.handle.conf.endpoint_resolver())); cfg.set_endpoint_resolver(endpoint_resolver); - ${"" /* TODO(EndpointResolver): Create endpoint params builder from service config */} - cfg.put(#{Params}::builder()); - // TODO(RuntimePlugins): Wire up standard retry cfg.set_retry_strategy(#{NeverRetryStrategy}::new()); From 2fb21f6405d72045dc4e6ad6bdfbccf32ec00c0d Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 1 May 2023 12:01:06 -0400 Subject: [PATCH 071/253] split out interceptors (#2639) ## Motivation and Context - split out interceptors from config bag to simplify orchestrator ## Description - update runtime plugin trait to split out interceptors - remove interceptor generics - update interceptors struct to remove locking and indirection ## Testing - [x] ```./gradlew :aws:sra-test:assemble && (cd aws/sra-test/integration-tests/aws-sdk-s3 && cargo test)``` ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Zelda Hessler --- .../aws-runtime/src/invocation_id.rs | 11 +- .../aws-runtime/src/recursion_detection.rs | 5 +- .../aws-runtime/src/user_agent.rs | 10 +- .../benches/middleware_vs_orchestrator.rs | 2 + .../aws-sdk-s3/tests/sra_test.rs | 2 + .../EndpointParamsInterceptorGenerator.kt | 4 +- .../OperationRuntimePluginGenerator.kt | 10 +- .../ServiceRuntimePluginGenerator.kt | 11 +- .../config/ServiceConfigGenerator.kt | 3 +- .../src/client/interceptors.rs | 107 ++++-------------- .../src/client/interceptors/context.rs | 8 +- .../src/client/retries.rs | 4 +- .../src/client/runtime_plugin.rs | 38 +++++-- .../src/client/orchestrator.rs | 13 +-- .../src/client/orchestrator/endpoints.rs | 3 +- .../src/client/orchestrator/phase.rs | 23 ++-- .../src/client/retries/strategy/never.rs | 4 +- 17 files changed, 101 insertions(+), 157 deletions(-) diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 55d2e981ac..89bcbb8d5b 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -5,7 +5,6 @@ use aws_smithy_runtime_api::client::interceptors::error::BoxError; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; -use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; use aws_smithy_runtime_api::config_bag::ConfigBag; use http::{HeaderName, HeaderValue}; use uuid::Uuid; @@ -35,10 +34,10 @@ impl Default for InvocationIdInterceptor { } } -impl Interceptor for InvocationIdInterceptor { +impl Interceptor for InvocationIdInterceptor { fn modify_before_retry_loop( &self, - context: &mut InterceptorContext, + context: &mut InterceptorContext, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let headers = context.request_mut()?.headers_mut(); @@ -74,15 +73,11 @@ mod tests { use crate::invocation_id::InvocationIdInterceptor; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; - use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_runtime_api::type_erasure::TypedBox; use http::HeaderValue; - fn expect_header<'a>( - context: &'a InterceptorContext, - header_name: &str, - ) -> &'a HeaderValue { + fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a HeaderValue { context .request() .unwrap() diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs index c14f1d141b..deafdd973f 100644 --- a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -4,7 +4,6 @@ */ use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; -use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_types::os_shim_internal::Env; use http::HeaderValue; @@ -37,10 +36,10 @@ impl RecursionDetectionInterceptor { } } -impl Interceptor for RecursionDetectionInterceptor { +impl Interceptor for RecursionDetectionInterceptor { fn modify_before_signing( &self, - context: &mut InterceptorContext, + context: &mut InterceptorContext, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let request = context.request_mut()?; diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index efa8e3c0d5..3babb8c758 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -6,7 +6,6 @@ use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; use aws_smithy_runtime_api::client::interceptors::error::BoxError; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; -use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_types::app_name::AppName; use aws_types::os_shim_internal::Env; @@ -70,10 +69,10 @@ fn header_values( )) } -impl Interceptor for UserAgentInterceptor { +impl Interceptor for UserAgentInterceptor { fn modify_before_signing( &self, - context: &mut InterceptorContext, + context: &mut InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let api_metadata = cfg @@ -113,10 +112,7 @@ mod tests { use aws_smithy_runtime_api::type_erasure::TypedBox; use aws_smithy_types::error::display::DisplayErrorContext; - fn expect_header<'a>( - context: &'a InterceptorContext, - header_name: &str, - ) -> &'a str { + fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a str { context .request() .unwrap() diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs index 622cdaca44..c6659cb7c1 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs @@ -6,6 +6,7 @@ #[macro_use] extern crate criterion; use aws_sdk_s3 as s3; +use aws_smithy_runtime_api::client::interceptors::Interceptors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::config_bag::ConfigBag; use criterion::{BenchmarkId, Criterion}; @@ -94,6 +95,7 @@ async fn orchestrator(client: &s3::Client) { fn configure( &self, cfg: &mut ConfigBag, + _interceptors: &mut Interceptors, ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { let params_builder = s3::endpoint::Params::builder() .set_region(Some(self.region.clone())) diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index bb7c5d4d31..6dd793e46b 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -10,6 +10,7 @@ use aws_sdk_s3::Client; use aws_smithy_client::dvr; use aws_smithy_client::dvr::MediaType; use aws_smithy_client::erase::DynConnector; +use aws_smithy_runtime_api::client::interceptors::Interceptors; use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::config_bag::ConfigBag; @@ -56,6 +57,7 @@ impl RuntimePlugin for FixupPlugin { fn configure( &self, cfg: &mut ConfigBag, + _interceptors: &mut Interceptors, ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { cfg.set_request_time(RequestTime::new(self.timestamp.clone())); cfg.put(AwsUserAgent::for_tests()); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index d79ad7b600..4137857346 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -63,10 +63,10 @@ class EndpointParamsInterceptorGenerator( ##[derive(Debug)] struct $interceptorName; - impl #{Interceptor}<#{HttpRequest}, #{HttpResponse}> for $interceptorName { + impl #{Interceptor} for $interceptorName { fn read_before_execution( &self, - context: &#{InterceptorContext}<#{HttpRequest}, #{HttpResponse}>, + context: &#{InterceptorContext}, cfg: &mut #{ConfigBag}, ) -> Result<(), #{BoxError}> { let _input = context.input()?; diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index a2ba50cb6f..45da1913dc 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -23,15 +23,14 @@ sealed class OperationRuntimePluginSection(name: String) : Section(name) { */ data class AdditionalConfig( val configBagName: String, + val interceptorName: String, val operationShape: OperationShape, ) : OperationRuntimePluginSection("AdditionalConfig") { fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) writer.rustTemplate( """ - $configBagName.get::<#{Interceptors}<#{HttpRequest}, #{HttpResponse}>>() - .expect("interceptors set") - .register_operation_interceptor(std::sync::Arc::new(#{interceptor}) as _); + $interceptorName.register_operation_interceptor(std::sync::Arc::new(#{interceptor}) as _); """, "HttpRequest" to smithyRuntimeApi.resolve("client::orchestrator::HttpRequest"), "HttpResponse" to smithyRuntimeApi.resolve("client::orchestrator::HttpResponse"), @@ -83,6 +82,7 @@ class OperationRuntimePluginGenerator( "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), + "Interceptors" to runtimeApi.resolve("client::interceptors::Interceptors"), ) } @@ -95,7 +95,7 @@ class OperationRuntimePluginGenerator( writer.rustTemplate( """ impl #{RuntimePlugin} for $operationStructName { - fn configure(&self, cfg: &mut #{ConfigBag}) -> Result<(), #{BoxError}> { + fn configure(&self, cfg: &mut #{ConfigBag}, _interceptors: &mut #{Interceptors}) -> Result<(), #{BoxError}> { use #{ConfigBagAccessors} as _; cfg.set_request_serializer(${operationStructName}RequestSerializer); cfg.set_response_deserializer(${operationStructName}ResponseDeserializer); @@ -119,7 +119,7 @@ class OperationRuntimePluginGenerator( "additional_config" to writable { writeCustomizations( customizations, - OperationRuntimePluginSection.AdditionalConfig("cfg", operationShape), + OperationRuntimePluginSection.AdditionalConfig("cfg", "_interceptors", operationShape), ) }, "retry_classifier_customizations" to writable { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index dad89ae04e..f35fe7446c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -32,7 +32,7 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { /** * Hook for adding additional things to config inside service runtime plugins. */ - data class AdditionalConfig(val configBagName: String) : ServiceRuntimePluginSection("AdditionalConfig") { + data class AdditionalConfig(val configBagName: String, val interceptorName: String) : ServiceRuntimePluginSection("AdditionalConfig") { /** Adds a value to the config bag */ fun putConfigValue(writer: RustWriter, value: Writable) { writer.rust("$configBagName.put(#T);", value) @@ -43,9 +43,7 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) writer.rustTemplate( """ - $configBagName.get::<#{Interceptors}<#{HttpRequest}, #{HttpResponse}>>() - .expect("interceptors set") - .register_client_interceptor(std::sync::Arc::new(#{interceptor}) as _); + $interceptorName.register_client_interceptor(std::sync::Arc::new(#{interceptor}) as _); """, "HttpRequest" to smithyRuntimeApi.resolve("client::orchestrator::HttpRequest"), "HttpResponse" to smithyRuntimeApi.resolve("client::orchestrator::HttpResponse"), @@ -83,6 +81,7 @@ class ServiceRuntimePluginGenerator( "Params" to endpointTypesGenerator.paramsStruct(), "ResolveEndpoint" to http.resolve("endpoint::ResolveEndpoint"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), + "Interceptors" to runtimeApi.resolve("client::interceptors::Interceptors"), "SharedEndpointResolver" to http.resolve("endpoint::SharedEndpointResolver"), "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "TraceProbe" to runtimeApi.resolve("client::orchestrator::TraceProbe"), @@ -103,7 +102,7 @@ class ServiceRuntimePluginGenerator( } impl #{RuntimePlugin} for ServiceRuntimePlugin { - fn configure(&self, cfg: &mut #{ConfigBag}) -> Result<(), #{BoxError}> { + fn configure(&self, cfg: &mut #{ConfigBag}, _interceptors: &mut #{Interceptors}) -> Result<(), #{BoxError}> { use #{ConfigBagAccessors}; // HACK: Put the handle into the config bag to work around config not being fully implemented yet @@ -154,7 +153,7 @@ class ServiceRuntimePluginGenerator( writeCustomizations(customizations, ServiceRuntimePluginSection.HttpAuthScheme("cfg")) }, "additional_config" to writable { - writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg")) + writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg", "_interceptors")) }, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index bb0653f327..39ecaf164e 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -294,7 +294,7 @@ class ServiceConfigGenerator(private val customizations: List Result<(), #{BoxError}> { + fn configure(&self, _cfg: &mut #{ConfigBag}, _inter: &mut #{Interceptors}) -> Result<(), #{BoxError}> { // TODO(RuntimePlugins): Put into `cfg` the fields in `self.config_override` that are not `None`. Ok(()) @@ -302,6 +302,7 @@ class ServiceConfigGenerator(private val customizations: List { #[doc = $docs] - fn $name( - &self, - context: &InterceptorContext, - cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { + fn $name(&self, context: &InterceptorContext, cfg: &mut ConfigBag) -> Result<(), BoxError> { let _ctx = context; let _cfg = cfg; Ok(()) @@ -29,7 +25,7 @@ macro_rules! interceptor_trait_fn { #[doc = $docs] fn $name( &self, - context: &mut InterceptorContext, + context: &mut InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let _ctx = context; @@ -49,7 +45,7 @@ macro_rules! interceptor_trait_fn { /// of the SDK ’s request execution pipeline. Hooks are either "read" hooks, which make it possible /// to read in-flight request or response messages, or "read/write" hooks, which make it possible /// to modify in-flight request or output messages. -pub trait Interceptor: std::fmt::Debug { +pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_execution, " @@ -541,47 +537,12 @@ pub trait Interceptor: std::fmt::Debug { ); } -pub type SharedInterceptor = Arc + Send + Sync>; - -#[derive(Debug)] -struct Inner { - client_interceptors: Vec>, - operation_interceptors: Vec>, -} - -// The compiler isn't smart enough to realize that TxReq and TxRes don't need to implement `Clone` -impl Clone for Inner { - fn clone(&self) -> Self { - Self { - client_interceptors: self.client_interceptors.clone(), - operation_interceptors: self.operation_interceptors.clone(), - } - } -} - -#[derive(Debug)] -pub struct Interceptors { - inner: Arc>>, -} +pub type SharedInterceptor = Arc; -// The compiler isn't smart enough to realize that TxReq and TxRes don't need to implement `Clone` -impl Clone for Interceptors { - fn clone(&self) -> Self { - Self { - inner: self.inner.clone(), - } - } -} - -impl Default for Interceptors { - fn default() -> Self { - Self { - inner: Arc::new(Mutex::new(Inner { - client_interceptors: Vec::new(), - operation_interceptors: Vec::new(), - })), - } - } +#[derive(Debug, Clone, Default)] +pub struct Interceptors { + client_interceptors: Vec, + operation_interceptors: Vec, } macro_rules! interceptor_impl_fn { @@ -592,16 +553,10 @@ macro_rules! interceptor_impl_fn { interceptor_impl_fn!(mut context, $name, $name); }; (context, $outer_name:ident, $inner_name:ident) => { - interceptor_impl_fn!( - $outer_name, - $inner_name(context: &InterceptorContext) - ); + interceptor_impl_fn!($outer_name, $inner_name(context: &InterceptorContext)); }; (mut context, $outer_name:ident, $inner_name:ident) => { - interceptor_impl_fn!( - $outer_name, - $inner_name(context: &mut InterceptorContext) - ); + interceptor_impl_fn!($outer_name, $inner_name(context: &mut InterceptorContext)); }; ($outer_name:ident, $inner_name:ident ($context:ident : $context_ty:ty)) => { pub fn $outer_name( @@ -623,48 +578,26 @@ macro_rules! interceptor_impl_fn { }; } -impl Interceptors { +impl Interceptors { pub fn new() -> Self { Self::default() } - fn interceptors(&self) -> Vec> { + fn interceptors(&self) -> impl Iterator { // Since interceptors can modify the interceptor list (since its in the config bag), copy the list ahead of time. // This should be cheap since the interceptors inside the list are Arcs. - // TODO(enableNewSmithyRuntime): Remove the ability for interceptors to modify the interceptor list and then simplify this - let mut interceptors = self.inner.lock().unwrap().client_interceptors.clone(); - interceptors.extend( - self.inner - .lock() - .unwrap() - .operation_interceptors - .iter() - .cloned(), - ); - interceptors + self.client_interceptors + .iter() + .chain(self.operation_interceptors.iter()) } - pub fn register_client_interceptor( - &self, - interceptor: SharedInterceptor, - ) -> &Self { - self.inner - .lock() - .unwrap() - .client_interceptors - .push(interceptor); + pub fn register_client_interceptor(&mut self, interceptor: SharedInterceptor) -> &mut Self { + self.client_interceptors.push(interceptor); self } - pub fn register_operation_interceptor( - &self, - interceptor: SharedInterceptor, - ) -> &Self { - self.inner - .lock() - .unwrap() - .operation_interceptors - .push(interceptor); + pub fn register_operation_interceptor(&mut self, interceptor: SharedInterceptor) -> &mut Self { + self.operation_interceptors.push(interceptor); self } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 60e6461fb8..61d7c7fd5d 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -4,6 +4,7 @@ */ use super::InterceptorError; +use crate::client::orchestrator::{HttpRequest, HttpResponse}; use crate::type_erasure::TypeErasedBox; pub type Input = TypeErasedBox; @@ -11,8 +12,11 @@ pub type Output = TypeErasedBox; pub type Error = TypeErasedBox; pub type OutputOrError = Result; +type Request = HttpRequest; +type Response = HttpResponse; + /// A container for the data currently available to an interceptor. -pub struct InterceptorContext { +pub struct InterceptorContext { input: Option, output_or_error: Option, request: Option, @@ -21,7 +25,7 @@ pub struct InterceptorContext { // TODO(interceptors) we could use types to ensure that people calling methods on interceptor context can't access // field that haven't been set yet. -impl InterceptorContext { +impl InterceptorContext { pub fn new(input: Input) -> Self { Self { input: Some(input), diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index a53f1d242c..1331317afe 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -5,7 +5,7 @@ use crate::client::interceptors::context::Error; use crate::client::interceptors::InterceptorContext; -use crate::client::orchestrator::{BoxError, HttpRequest, HttpResponse}; +use crate::client::orchestrator::BoxError; use crate::config_bag::ConfigBag; use aws_smithy_types::retry::ErrorKind; use std::fmt::Debug; @@ -23,7 +23,7 @@ pub trait RetryStrategy: Send + Sync + Debug { fn should_attempt_retry( &self, - context: &InterceptorContext, + context: &InterceptorContext, cfg: &ConfigBag, ) -> Result; } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index 835686f337..8c2604700b 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -3,17 +3,26 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::client::interceptors::Interceptors; use crate::config_bag::ConfigBag; pub type BoxError = Box; pub trait RuntimePlugin { - fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError>; + fn configure( + &self, + cfg: &mut ConfigBag, + interceptors: &mut Interceptors, + ) -> Result<(), BoxError>; } impl RuntimePlugin for Box { - fn configure(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { - self.as_ref().configure(cfg) + fn configure( + &self, + cfg: &mut ConfigBag, + interceptors: &mut Interceptors, + ) -> Result<(), BoxError> { + self.as_ref().configure(cfg, interceptors) } } @@ -38,17 +47,25 @@ impl RuntimePlugins { self } - pub fn apply_client_configuration(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { + pub fn apply_client_configuration( + &self, + cfg: &mut ConfigBag, + interceptors: &mut Interceptors, + ) -> Result<(), BoxError> { for plugin in self.client_plugins.iter() { - plugin.configure(cfg)?; + plugin.configure(cfg, interceptors)?; } Ok(()) } - pub fn apply_operation_configuration(&self, cfg: &mut ConfigBag) -> Result<(), BoxError> { + pub fn apply_operation_configuration( + &self, + cfg: &mut ConfigBag, + interceptors: &mut Interceptors, + ) -> Result<(), BoxError> { for plugin in self.operation_plugins.iter() { - plugin.configure(cfg)?; + plugin.configure(cfg, interceptors)?; } Ok(()) @@ -58,12 +75,17 @@ impl RuntimePlugins { #[cfg(test)] mod tests { use super::{BoxError, RuntimePlugin, RuntimePlugins}; + use crate::client::interceptors::Interceptors; use crate::config_bag::ConfigBag; struct SomeStruct; impl RuntimePlugin for SomeStruct { - fn configure(&self, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + fn configure( + &self, + _cfg: &mut ConfigBag, + _inters: &mut Interceptors, + ) -> Result<(), BoxError> { todo!() } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index adba3dfb04..7dfef49dde 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -10,9 +10,7 @@ use crate::client::orchestrator::phase::Phase; use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output}; use aws_smithy_runtime_api::client::interceptors::{InterceptorContext, Interceptors}; -use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, ConfigBagAccessors, HttpRequest, HttpResponse, -}; +use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpResponse}; use aws_smithy_runtime_api::client::retries::ShouldAttempt; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; use aws_smithy_runtime_api::config_bag::ConfigBag; @@ -31,15 +29,14 @@ pub async fn invoke( let mut cfg = ConfigBag::base(); let cfg = &mut cfg; - let interceptors = Interceptors::new(); - cfg.put(interceptors.clone()); + let mut interceptors = Interceptors::new(); let context = Phase::construction(InterceptorContext::new(input)) // Client configuration - .include(|_| runtime_plugins.apply_client_configuration(cfg))? + .include(|_| runtime_plugins.apply_client_configuration(cfg, &mut interceptors))? .include(|ctx| interceptors.client_read_before_execution(ctx, cfg))? // Operation configuration - .include(|_| runtime_plugins.apply_operation_configuration(cfg))? + .include(|_| runtime_plugins.apply_operation_configuration(cfg, &mut interceptors))? .include(|ctx| interceptors.operation_read_before_execution(ctx, cfg))? // Before serialization .include(|ctx| interceptors.read_before_serialization(ctx, cfg))? @@ -117,7 +114,7 @@ pub async fn invoke( async fn make_an_attempt( dispatch_phase: Phase, cfg: &mut ConfigBag, - interceptors: &Interceptors, + interceptors: &Interceptors, ) -> Result> { let dispatch_phase = dispatch_phase .include(|ctx| interceptors.read_before_attempt(ctx, cfg))? diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 981ef3239c..89570c1ef6 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -10,7 +10,6 @@ use aws_smithy_http::endpoint::{ use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, EndpointResolver, EndpointResolverParams, HttpRequest, - HttpResponse, }; use aws_smithy_runtime_api::config_bag::ConfigBag; use http::header::HeaderName; @@ -113,7 +112,7 @@ where } pub(super) fn orchestrate_endpoint( - ctx: &mut InterceptorContext, + ctx: &mut InterceptorContext, cfg: &ConfigBag, ) -> Result<(), BoxError> { let params = cfg.endpoint_resolver_params(); diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs index f8af01c4b5..9e101050b7 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs @@ -6,7 +6,7 @@ use aws_smithy_http::result::{ConnectorError, SdkError}; use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpRequest, HttpResponse}; +use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpResponse}; #[derive(Copy, Clone, Eq, PartialEq)] enum OrchestrationPhase { @@ -17,26 +17,21 @@ enum OrchestrationPhase { pub(super) struct Phase { phase: OrchestrationPhase, - context: InterceptorContext, + context: InterceptorContext, } impl Phase { - pub(crate) fn construction(context: InterceptorContext) -> Self { + pub(crate) fn construction(context: InterceptorContext) -> Self { Self::start(OrchestrationPhase::Construction, context) } - pub(crate) fn dispatch(context: InterceptorContext) -> Self { + pub(crate) fn dispatch(context: InterceptorContext) -> Self { Self::start(OrchestrationPhase::Dispatch, context) } - pub(crate) fn response_handling( - context: InterceptorContext, - ) -> Self { + pub(crate) fn response_handling(context: InterceptorContext) -> Self { Self::start(OrchestrationPhase::ResponseHandling, context) } - fn start( - phase: OrchestrationPhase, - context: InterceptorContext, - ) -> Self { + fn start(phase: OrchestrationPhase, context: InterceptorContext) -> Self { match phase { OrchestrationPhase::Construction => {} OrchestrationPhase::Dispatch => {} @@ -47,7 +42,7 @@ impl Phase { pub(crate) fn include_mut>( mut self, - c: impl FnOnce(&mut InterceptorContext) -> Result<(), E>, + c: impl FnOnce(&mut InterceptorContext) -> Result<(), E>, ) -> Result> { match c(&mut self.context) { Ok(_) => Ok(self), @@ -57,7 +52,7 @@ impl Phase { pub(crate) fn include>( self, - c: impl FnOnce(&InterceptorContext) -> Result<(), E>, + c: impl FnOnce(&InterceptorContext) -> Result<(), E>, ) -> Result> { match c(&self.context) { Ok(_) => Ok(self), @@ -113,7 +108,7 @@ impl Phase { } } - pub(crate) fn finish(self) -> InterceptorContext { + pub(crate) fn finish(self) -> InterceptorContext { self.context } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs index 49366a273d..f415af3691 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs @@ -4,7 +4,7 @@ */ use aws_smithy_runtime_api::client::interceptors::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpRequest, HttpResponse}; +use aws_smithy_runtime_api::client::orchestrator::BoxError; use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; use aws_smithy_runtime_api::config_bag::ConfigBag; @@ -24,7 +24,7 @@ impl RetryStrategy for NeverRetryStrategy { fn should_attempt_retry( &self, - _context: &InterceptorContext, + _context: &InterceptorContext, _cfg: &ConfigBag, ) -> Result { Ok(ShouldAttempt::No) From d3957a6ee9b50517991946d9f557832097699797 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Mon, 1 May 2023 16:51:00 -0500 Subject: [PATCH 072/253] Allow canary-runner to specify expected speech text by Transcribe (#2660) ## Motivation and Context During a release for `aws-sdk-rust`, we've found that the canary test received an improved speech text from Transcribe. This broke the canary test because the test hard-coded the string that should be expected by Transcribe. This PR updates the runner so that the expected speech text can be passed-in from outside of the program. ## Testing Added unit tests, mainly for argument parsing. ## Checklist If the next release is created off of the main branch (because it's a non-breaking release), we only need to merge this PR the main branch. If we need to release from a release branch at some point in the future, this PR will need to be duplicated and merged to the release branch as well. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- tools/ci-cdk/canary-runner/src/run.rs | 104 +++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 12 deletions(-) diff --git a/tools/ci-cdk/canary-runner/src/run.rs b/tools/ci-cdk/canary-runner/src/run.rs index 5d03cd44fd..b5e0058d74 100644 --- a/tools/ci-cdk/canary-runner/src/run.rs +++ b/tools/ci-cdk/canary-runner/src/run.rs @@ -83,6 +83,11 @@ pub struct RunArgs { #[clap(long)] musl: bool, + /// Expected speech text generated by Transcribe. This needs to be passed-in + /// because it can change as the accuracy of generated text improves over time. + #[clap(long)] + expected_speech_text_by_transcribe: Option, + /// File path to a CDK outputs JSON file. This can be used instead /// of all the --lambda... args. #[clap(long)] @@ -101,12 +106,13 @@ pub struct RunArgs { lambda_execution_role_arn: Option, } -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] struct Options { rust_version: Option, sdk_release_tag: Option, sdk_path: Option, musl: bool, + expected_speech_text_by_transcribe: Option, lambda_code_s3_bucket_name: String, lambda_test_s3_bucket_name: String, lambda_execution_role_arn: String, @@ -139,6 +145,7 @@ impl Options { sdk_release_tag: run_opt.sdk_release_tag, sdk_path: run_opt.sdk_path, musl: run_opt.musl, + expected_speech_text_by_transcribe: run_opt.expected_speech_text_by_transcribe, lambda_code_s3_bucket_name: value.inner.lambda_code_s3_bucket_name, lambda_test_s3_bucket_name: value.inner.lambda_test_s3_bucket_name, lambda_execution_role_arn: value.inner.lambda_execution_role_arn, @@ -149,6 +156,7 @@ impl Options { sdk_release_tag: run_opt.sdk_release_tag, sdk_path: run_opt.sdk_path, musl: run_opt.musl, + expected_speech_text_by_transcribe: run_opt.expected_speech_text_by_transcribe, lambda_code_s3_bucket_name: run_opt.lambda_code_s3_bucket_name.expect("required"), lambda_test_s3_bucket_name: run_opt.lambda_test_s3_bucket_name.expect("required"), lambda_execution_role_arn: run_opt.lambda_execution_role_arn.expect("required"), @@ -250,6 +258,7 @@ async fn run_canary(options: &Options, config: &aws_config::SdkConfig) -> Result bundle_name, bundle_file_name, &options.lambda_execution_role_arn, + options.expected_speech_text_by_transcribe.as_ref(), &options.lambda_code_s3_bucket_name, &options.lambda_test_s3_bucket_name, ) @@ -324,11 +333,27 @@ async fn create_lambda_fn( bundle_name: &str, bundle_file_name: &str, execution_role: &str, + expected_speech_text_by_transcribe: Option<&String>, code_s3_bucket: &str, test_s3_bucket: &str, ) -> Result<()> { use lambda::model::*; + let env_builder = match expected_speech_text_by_transcribe { + Some(expected_speech_text_by_transcribe) => Environment::builder() + .variables("RUST_BACKTRACE", "1") + .variables("RUST_LOG", "info") + .variables("CANARY_S3_BUCKET_NAME", test_s3_bucket) + .variables( + "CANARY_EXPECTED_TRANSCRIBE_RESULT", + expected_speech_text_by_transcribe, + ), + None => Environment::builder() + .variables("RUST_BACKTRACE", "1") + .variables("RUST_LOG", "info") + .variables("CANARY_S3_BUCKET_NAME", test_s3_bucket), + }; + lambda_client .create_function() .function_name(bundle_name) @@ -342,17 +367,7 @@ async fn create_lambda_fn( .build(), ) .publish(true) - .environment( - Environment::builder() - .variables("RUST_BACKTRACE", "1") - .variables("RUST_LOG", "info") - .variables("CANARY_S3_BUCKET_NAME", test_s3_bucket) - .variables( - "CANARY_EXPECTED_TRANSCRIBE_RESULT", - "Good day to you transcribe. This is Polly talking to you from the Rust ST K.", - ) - .build(), - ) + .environment(env_builder.build()) .timeout(60) .send() .await @@ -419,3 +434,68 @@ async fn delete_lambda_fn(lambda_client: lambda::Client, bundle_name: &str) -> R .context(here!("failed to delete Lambda"))?; Ok(()) } + +#[cfg(test)] +mod tests { + use crate::run::Options; + use crate::run::RunArgs; + use clap::Parser; + + #[test] + fn args_parsing() { + assert_eq!( + RunArgs { + rust_version: None, + sdk_release_tag: None, + sdk_path: Some("artifact-aws-sdk-rust/sdk".into()), + musl: false, + expected_speech_text_by_transcribe: Some("Good day to you transcribe.".to_owned()), + cdk_output: Some("../cdk-outputs.json".into()), + lambda_code_s3_bucket_name: None, + lambda_test_s3_bucket_name: None, + lambda_execution_role_arn: None + }, + RunArgs::try_parse_from([ + "run", + "--sdk-path", + "artifact-aws-sdk-rust/sdk", + "--expected-speech-text-by-transcribe", + "Good day to you transcribe.", + "--cdk-output", + "../cdk-outputs.json", + ]) + .unwrap() + ); + } + + #[test] + fn options_from_args() { + let run_args = RunArgs::try_parse_from([ + "run", + "--sdk-path", + "artifact-aws-sdk-rust/sdk", + "--expected-speech-text-by-transcribe", + "Good day to you transcribe.", + "--lambda-code-s3-bucket-name", + "bucket-for-code", + "--lambda-test-s3-bucket-name", + "bucket-for-test", + "--lambda-execution-role-arn", + "arn:aws:lambda::role/exe-role", + ]) + .unwrap(); + assert_eq!( + Options { + rust_version: None, + sdk_release_tag: None, + sdk_path: Some("artifact-aws-sdk-rust/sdk".into()), + musl: false, + expected_speech_text_by_transcribe: Some("Good day to you transcribe.".to_owned()), + lambda_code_s3_bucket_name: "bucket-for-code".to_owned(), + lambda_test_s3_bucket_name: "bucket-for-test".to_owned(), + lambda_execution_role_arn: "arn:aws:lambda::role/exe-role".to_owned(), + }, + Options::load_from(run_args).unwrap(), + ); + } +} From bf23d57aa3551b49d49165a84db788cee1529509 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 2 May 2023 10:00:49 -0400 Subject: [PATCH 073/253] Adds an experimental store/load add to config bag (#2654) ## Motivation and Context - store multiple objects of the same type in the bag - obtain mutable access to items in the bag ## Description This adds `store_append` to enable storing multiple items of the same type in the bag and `get_mut`, `get_mut_or_else` and `get_mut_or_default` to reduce clones when possible ## Testing UTs ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-smithy-runtime-api/src/config_bag.rs | 547 ++++++++++++++---- .../src/config_bag/typeid_map.rs | 32 + 2 files changed, 482 insertions(+), 97 deletions(-) create mode 100644 rust-runtime/aws-smithy-runtime-api/src/config_bag/typeid_map.rs diff --git a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs index 418fbd9b13..530c8da317 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs @@ -9,9 +9,18 @@ //! with the following properties: //! 1. A new layer of configuration may be applied onto an existing configuration structure without modifying it or taking ownership. //! 2. No lifetime shenanigans to deal with -use aws_smithy_http::property_bag::PropertyBag; +mod typeid_map; + +use crate::config_bag::typeid_map::TypeIdMap; + +use std::any::{type_name, Any, TypeId}; +use std::borrow::Cow; + use std::fmt::{Debug, Formatter}; +use std::iter::Rev; +use std::marker::PhantomData; use std::ops::Deref; +use std::slice; use std::sync::Arc; /// Layered Configuration Structure @@ -28,14 +37,7 @@ impl Debug for ConfigBag { struct Layers<'a>(&'a ConfigBag); impl Debug for Layers<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut list = f.debug_list(); - list.entry(&self.0.head); - let mut us = self.0.tail.as_ref(); - while let Some(bag) = us { - list.entry(&bag.head); - us = bag.tail.as_ref() - } - list.finish() + f.debug_list().entries(self.0.layers()).finish() } } f.debug_struct("ConfigBag") @@ -59,42 +61,201 @@ impl Deref for FrozenConfigBag { } } -pub trait Persist { - fn layer_name(&self) -> &'static str; - fn persist(&self, layer: &mut ConfigBag); +/// Private module to keep Value type while avoiding "private type in public latest" +pub(crate) mod value { + #[derive(Debug)] + pub enum Value { + Set(T), + ExplicitlyUnset(&'static str), + } +} +use value::Value; + +impl Default for Value { + fn default() -> Self { + Self::Set(Default::default()) + } +} + +struct DebugErased { + field: Box, + #[allow(dead_code)] + type_name: &'static str, + #[allow(clippy::type_complexity)] + debug: Box) -> std::fmt::Result + Send + Sync>, +} + +impl Debug for DebugErased { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + (self.debug)(self, f) + } +} + +impl DebugErased { + fn new(value: T) -> Self { + let debug = |value: &DebugErased, f: &mut Formatter<'_>| { + Debug::fmt(value.as_ref::().expect("typechecked"), f) + }; + let name = type_name::(); + Self { + field: Box::new(value), + type_name: name, + debug: Box::new(debug), + } + } + + fn as_ref(&self) -> Option<&T> { + self.field.downcast_ref() + } + + fn as_mut(&mut self) -> Option<&mut T> { + self.field.downcast_mut() + } +} + +pub struct Layer { + name: Cow<'static, str>, + props: TypeIdMap, +} + +/// Trait defining how types can be stored and loaded from the config bag +pub trait Store: Sized + Send + Sync + 'static { + type ReturnedType<'a>: Send + Sync; + type StoredType: Send + Sync + Debug; + + /// Create a returned type from an iterable of items + fn merge_iter(iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_>; +} + +/// Store an item in the config bag by replacing the existing value +#[non_exhaustive] +pub struct StoreReplace(PhantomData); + +/// Store an item in the config bag by effectively appending it to a list +#[non_exhaustive] +pub struct StoreAppend(PhantomData); + +pub trait Storable: Send + Sync + Debug + 'static { + type Storer: Store; } -pub trait Load: Sized { - fn load(bag: &ConfigBag) -> Option; +impl Store for StoreReplace { + type ReturnedType<'a> = Option<&'a U>; + type StoredType = Value; + + fn merge_iter(mut iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_> { + iter.next().and_then(|item| match item { + Value::Set(item) => Some(item), + Value::ExplicitlyUnset(_) => None, + }) + } } -pub trait ConfigLayer: Persist + Load {} +impl Store for StoreAppend { + type ReturnedType<'a> = AppendItemIter<'a, U>; + type StoredType = Value>; + + fn merge_iter(iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_> { + AppendItemIter { + inner: iter, + cur: None, + } + } +} -enum Value { - Set(T), - ExplicitlyUnset, +/// Iterator of items returned by [`StoreAppend`] +pub struct AppendItemIter<'a, U> { + inner: ItemIter<'a, StoreAppend>, + cur: Option>>, } -struct Layer { - name: &'static str, - props: PropertyBag, +impl<'a, U: 'a> Iterator for AppendItemIter<'a, U> +where + U: Send + Sync + Debug + 'static, +{ + type Item = &'a U; + + fn next(&mut self) -> Option { + if let Some(buf) = &mut self.cur { + match buf.next() { + Some(item) => return Some(item), + None => self.cur = None, + } + } + match self.inner.next() { + None => None, + Some(Value::Set(u)) => { + self.cur = Some(u.iter().rev()); + self.next() + } + Some(Value::ExplicitlyUnset(_)) => None, + } + } } impl Debug for Layer { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - struct Contents<'a>(&'a Layer); - impl Debug for Contents<'_> { + struct Items<'a>(&'a Layer); + impl Debug for Items<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_list().entries(self.0.props.contents()).finish() + f.debug_list().entries(self.0.props.values()).finish() } } f.debug_struct("Layer") .field("name", &self.name) - .field("properties", &Contents(self)) + .field("items", &Items(self)) .finish() } } +impl Layer { + pub fn put(&mut self, value: T::StoredType) -> &mut Self { + self.props + .insert(TypeId::of::(), DebugErased::new(value)); + self + } + + pub fn get(&self) -> Option<&T::StoredType> { + self.props + .get(&TypeId::of::()) + .map(|t| t.as_ref().expect("typechecked")) + } + + pub fn get_mut(&mut self) -> Option<&mut T::StoredType> { + self.props + .get_mut(&TypeId::of::()) + .map(|t| t.as_mut().expect("typechecked")) + } + + pub fn get_mut_or_default(&mut self) -> &mut T::StoredType + where + T::StoredType: Default, + { + self.props + .entry(TypeId::of::()) + .or_insert_with(|| DebugErased::new(T::StoredType::default())) + .as_mut() + .expect("typechecked") + } + + pub fn is_empty(&self) -> bool { + self.props.is_empty() + } + + pub fn len(&self) -> usize { + self.props.len() + } +} + +pub trait Accessor { + type Setter: Setter; + fn config(&self) -> &ConfigBag; +} + +pub trait Setter { + fn config(&mut self) -> &mut ConfigBag; +} + fn no_op(_: &mut ConfigBag) {} impl FrozenConfigBag { @@ -119,19 +280,19 @@ impl FrozenConfigBag { /// add_more_config(&mut bag); /// let bag = bag.freeze(); /// ``` - pub fn add_layer(&self, name: &'static str) -> ConfigBag { + pub fn add_layer(&self, name: impl Into>) -> ConfigBag { self.with_fn(name, no_op) } - pub fn with(&self, layer: impl Persist) -> ConfigBag { - self.with_fn(layer.layer_name(), |bag| layer.persist(bag)) - } - /// Add more items to the config bag - pub fn with_fn(&self, name: &'static str, next: impl Fn(&mut ConfigBag)) -> ConfigBag { + pub fn with_fn( + &self, + name: impl Into>, + next: impl Fn(&mut ConfigBag), + ) -> ConfigBag { let new_layer = Layer { - name, - props: PropertyBag::new(), + name: name.into(), + props: Default::default(), }; let mut bag = ConfigBag { head: new_layer, @@ -146,29 +307,154 @@ impl ConfigBag { pub fn base() -> Self { ConfigBag { head: Layer { - name: "base", + name: Cow::Borrowed("base"), props: Default::default(), }, tail: None, } } + pub fn store_put(&mut self, item: T) -> &mut Self + where + T: Storable>, + { + self.head.put::>(Value::Set(item)); + self + } + + pub fn store_or_unset(&mut self, item: Option) -> &mut Self + where + T: Storable>, + { + let item = match item { + Some(item) => Value::Set(item), + None => Value::ExplicitlyUnset(type_name::()), + }; + self.head.put::>(item); + self + } + + /// This can only be used for types that use [`StoreAppend`] + /// ``` + /// use aws_smithy_runtime_api::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; + /// let mut bag = ConfigBag::base(); + /// #[derive(Debug, PartialEq, Eq)] + /// struct Interceptor(&'static str); + /// impl Storable for Interceptor { + /// type Storer = StoreAppend; + /// } + /// + /// bag.store_append(Interceptor("123")); + /// bag.store_append(Interceptor("456")); + /// + /// assert_eq!( + /// bag.load::().collect::>(), + /// vec![&Interceptor("456"), &Interceptor("123")] + /// ); + /// ``` + pub fn store_append(&mut self, item: T) -> &mut Self + where + T: Storable>, + { + match self.head.get_mut_or_default::>() { + Value::Set(list) => list.push(item), + v @ Value::ExplicitlyUnset(_) => *v = Value::Set(vec![item]), + } + self + } + + pub fn clear(&mut self) + where + T: Storable>, + { + self.head + .put::>(Value::ExplicitlyUnset(type_name::())); + } + + pub fn load(&self) -> ::ReturnedType<'_> { + self.sourced_get::() + } + /// Retrieve the value of type `T` from the bag if exists pub fn get(&self) -> Option<&T> { - let mut source = vec![]; - let out = self.sourced_get(&mut source); + let out = self.sourced_get::>(); out } + /// Returns a mutable reference to `T` if it is stored in the top layer of the bag + pub fn get_mut(&mut self) -> Option<&mut T> + where + T: Storable>, + { + // this code looks weird to satisfy the borrow checker—we can't keep the result of `get_mut` + // alive (even in a returned branch) and then call `store_put`. So: drop the borrow immediately + // store, the value, then pull it right back + if matches!(self.head.get_mut::>(), None) { + let new_item = match self.tail.as_deref().and_then(|b| b.load::()) { + Some(item) => item.clone(), + None => return None, + }; + self.store_put(new_item); + self.get_mut() + } else if matches!( + self.head.get::>(), + Some(Value::ExplicitlyUnset(_)) + ) { + None + } else if let Some(Value::Set(t)) = self.head.get_mut::>() { + Some(t) + } else { + unreachable!() + } + } + + /// Returns a mutable reference to `T` if it is stored in the top layer of the bag + /// + /// - If `T` is in a deeper layer of the bag, that value will be cloned and inserted into the top layer + /// - If `T` is not present in the bag, the [`Default`] implementation will be used. + pub fn get_mut_or_default( + &mut self, + ) -> &mut T + where + T: Storable>, + { + self.get_mut_or_else(|| T::default()) + } + + /// Returns a mutable reference to `T` if it is stored in the top layer of the bag + /// + /// - If `T` is in a deeper layer of the bag, that value will be cloned and inserted into the top layer + /// - If `T` is not present in the bag, `default` will be used to construct a new value + pub fn get_mut_or_else( + &mut self, + default: impl Fn() -> T, + ) -> &mut T + where + T: Storable>, + { + // this code looks weird to satisfy the borrow checker—we can't keep the result of `get_mut` + // alive (even in a returned branch) and then call `store_put`. So: drop the borrow immediately + // store, the value, then pull it right back + if self.get_mut::().is_none() { + self.store_put((default)()); + return self + .get_mut() + .expect("item was just stored in the top layer"); + } + // above it was None + self.get_mut().unwrap() + } + /// Insert `value` into the bag pub fn put(&mut self, value: T) -> &mut Self { - self.head.props.insert(Value::Set(value)); + self.head.put::>(Value::Set(value)); self } /// Remove `T` from this bag - pub fn unset(&mut self) -> &mut Self { - self.head.props.insert(Value::::ExplicitlyUnset); + pub fn unset(&mut self) -> &mut Self { + self.head + .put::>(Value::ExplicitlyUnset(type_name::())); self } @@ -200,37 +486,56 @@ impl ConfigBag { self.freeze().with_fn(name, next) } - pub fn with(self, layer: impl Persist) -> ConfigBag { - self.freeze().with(layer) - } - - pub fn add_layer(self, name: &'static str) -> ConfigBag { + pub fn add_layer(self, name: impl Into>) -> ConfigBag { self.freeze().add_layer(name) } - pub fn sourced_get( - &self, - source_trail: &mut Vec, - ) -> Option<&T> { - // todo: optimize so we don't need to compute the source if it's unused - let bag = &self.head; - let inner_item = self - .tail - .as_ref() - .and_then(|bag| bag.sourced_get(source_trail)); - let (item, source) = match bag.props.get::>() { - Some(Value::ExplicitlyUnset) => (None, SourceInfo::Unset { layer: bag.name }), - Some(Value::Set(v)) => ( - Some(v), - SourceInfo::Set { - layer: bag.name, - value: format!("{:?}", v), - }, - ), - None => (inner_item, SourceInfo::Inherit { layer: bag.name }), + pub fn sourced_get(&self) -> T::ReturnedType<'_> { + let stored_type_iter = ItemIter { + inner: self.layers(), + t: PhantomData::default(), }; - source_trail.push(source); - item + T::merge_iter(stored_type_iter) + } + + fn layers(&self) -> BagIter<'_> { + BagIter { bag: Some(self) } + } +} + +/// Iterator of items returned from config_bag +pub struct ItemIter<'a, T> { + inner: BagIter<'a>, + t: PhantomData, +} +impl<'a, T: 'a> Iterator for ItemIter<'a, T> +where + T: Store, +{ + type Item = &'a T::StoredType; + + fn next(&mut self) -> Option { + match self.inner.next() { + Some(layer) => layer.get::().or_else(|| self.next()), + None => None, + } + } +} + +/// Iterator over the layers of a config bag +struct BagIter<'a> { + bag: Option<&'a ConfigBag>, +} + +impl<'a> Iterator for BagIter<'a> { + type Item = &'a Layer; + + fn next(&mut self) -> Option { + let next = self.bag.map(|b| &b.head); + if let Some(bag) = &mut self.bag { + self.bag = bag.tail.as_deref(); + } + next } } @@ -250,7 +555,7 @@ pub enum SourceInfo { #[cfg(test)] mod test { use super::ConfigBag; - use crate::config_bag::{Load, Persist}; + use crate::config_bag::{Storable, StoreAppend, StoreReplace}; #[test] fn layered_property_bag() { @@ -318,47 +623,95 @@ mod test { let mut open_bag = operation_config.with_fn("my_custom_info", |_bag: &mut ConfigBag| {}); open_bag.put("foo"); + + assert_eq!(open_bag.layers().count(), 4); } #[test] - fn persist_trait() { - #[derive(Debug, Eq, PartialEq, Clone)] - struct MyConfig { - a: bool, - b: String, + fn store_append() { + let mut bag = ConfigBag::base(); + #[derive(Debug, PartialEq, Eq)] + struct Interceptor(&'static str); + impl Storable for Interceptor { + type Storer = StoreAppend; } - #[derive(Debug)] - struct A(bool); - #[derive(Debug)] - struct B(String); - - impl Persist for MyConfig { - fn layer_name(&self) -> &'static str { - "my_config" - } + bag.clear::(); + // you can only call store_append because interceptor is marked with a vec + bag.store_append(Interceptor("123")); + bag.store_append(Interceptor("456")); + + let mut bag = bag.add_layer("next"); + bag.store_append(Interceptor("789")); + + assert_eq!( + bag.load::().collect::>(), + vec![ + &Interceptor("789"), + &Interceptor("456"), + &Interceptor("123") + ] + ); + + bag.clear::(); + assert_eq!(bag.load::().count(), 0); + } - fn persist(&self, layer: &mut ConfigBag) { - layer.put(A(self.a)); - layer.put(B(self.b.clone())); - } + #[test] + fn store_append_many_layers() { + #[derive(Debug, PartialEq, Eq, Clone)] + struct TestItem(i32, i32); + impl Storable for TestItem { + type Storer = StoreAppend; } - impl Load for MyConfig { - fn load(bag: &ConfigBag) -> Option { - Some(MyConfig { - a: bag.get::().unwrap().0, - b: bag.get::().unwrap().0.clone(), - }) + let mut expected = vec![]; + let mut bag = ConfigBag::base(); + for layer in 0..100 { + bag = bag.add_layer(format!("{}", layer)); + for item in 0..100 { + expected.push(TestItem(layer, item)); + bag.store_append(TestItem(layer, item)); } } + expected.reverse(); + assert_eq!( + bag.load::() + .map(|i| i.clone()) + .collect::>(), + expected + ); + } - let conf = MyConfig { - a: true, - b: "hello!".to_string(), - }; + #[test] + fn get_mut_or_else() { + #[derive(Clone, Debug, PartialEq, Eq, Default)] + struct Foo(usize); + impl Storable for Foo { + type Storer = StoreReplace; + } + + let mut bag = ConfigBag::base(); + assert_eq!(bag.get_mut::(), None); + assert_eq!(bag.get_mut_or_default::(), &Foo(0)); + bag.get_mut_or_default::().0 += 1; + assert_eq!(bag.get::(), Some(&Foo(1))); - let bag = ConfigBag::base().with(conf.clone()); + let bag = bag.freeze(); - assert_eq!(MyConfig::load(&bag), Some(conf)); + let old_ref = bag.load::().unwrap(); + assert_eq!(old_ref, &Foo(1)); + + // there is one in the bag, so it can be returned + let mut next = bag.add_layer("next"); + next.get_mut::().unwrap().0 += 1; + let new_ref = next.load::().unwrap(); + assert_eq!(new_ref, &Foo(2)); + // no funny business + assert_eq!(old_ref, &Foo(1)); + + next.unset::(); + // if it was unset, we can't clone the current one, that would be wrong + assert_eq!(next.get_mut::(), None); + assert_eq!(next.get_mut_or_default::(), &Foo(0)); } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/config_bag/typeid_map.rs b/rust-runtime/aws-smithy-runtime-api/src/config_bag/typeid_map.rs new file mode 100644 index 0000000000..68818c9b58 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/config_bag/typeid_map.rs @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::any::TypeId; +use std::collections::HashMap; +use std::hash::{BuildHasherDefault, Hasher}; + +pub(super) type TypeIdMap = HashMap>; + +// With TypeIds as keys, there's no need to hash them. They are already hashes +// themselves, coming from the compiler. The IdHasher just holds the u64 of +// the TypeId, and then returns it, instead of doing any bit fiddling. +#[derive(Default)] +pub(super) struct IdHasher(u64); + +impl Hasher for IdHasher { + #[inline] + fn finish(&self) -> u64 { + self.0 + } + + fn write(&mut self, _: &[u8]) { + unreachable!("TypeId calls write_u64"); + } + + #[inline] + fn write_u64(&mut self, id: u64) { + self.0 = id; + } +} From 23d0f5ec67317c86d7ad29db6f6cb5ec5961ad58 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 2 May 2023 10:56:57 -0700 Subject: [PATCH 074/253] Implement timeouts in the orchestrator (#2661) ## Motivation and Context This PR implements timeouts in the orchestrator. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../s3/tests/alternative-async-runtime.rs | 8 +- .../integration-tests/s3/tests/timeouts.rs | 7 +- .../ResiliencyConfigCustomization.kt | 21 ++ .../customize/RequiredCustomizations.kt | 8 + rust-runtime/aws-smithy-async/src/lib.rs | 5 +- .../src/client/orchestrator.rs | 17 ++ rust-runtime/aws-smithy-runtime/Cargo.toml | 5 +- rust-runtime/aws-smithy-runtime/src/client.rs | 2 + .../src/client/orchestrator.rs | 27 ++ .../aws-smithy-runtime/src/client/timeout.rs | 240 ++++++++++++++++++ 10 files changed, 335 insertions(+), 5 deletions(-) create mode 100644 rust-runtime/aws-smithy-runtime/src/client/timeout.rs diff --git a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs index 209403de4a..7d6e84a90f 100644 --- a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs +++ b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs @@ -15,6 +15,7 @@ use aws_smithy_async::assert_elapsed; use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; use aws_smithy_client::never::NeverConnector; use aws_smithy_http::result::SdkError; +use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::timeout::TimeoutConfig; use std::fmt::Debug; use std::sync::Arc; @@ -117,7 +118,12 @@ async fn timeout_test(sleep_impl: Arc) -> Result<(), Box, + ): List = + baseCustomizations + listOf(ResiliencyServiceRuntimePluginCustomization()) } diff --git a/rust-runtime/aws-smithy-async/src/lib.rs b/rust-runtime/aws-smithy-async/src/lib.rs index 7e106da5db..f9c4f9d9d9 100644 --- a/rust-runtime/aws-smithy-async/src/lib.rs +++ b/rust-runtime/aws-smithy-async/src/lib.rs @@ -37,12 +37,13 @@ macro_rules! assert_elapsed { ($start:expr, $dur:expr, $margin_of_error:expr) => {{ let elapsed = $start.elapsed(); // type ascription improves compiler error when wrong type is passed - let lower: std::time::Duration = $dur; let margin_of_error: std::time::Duration = $margin_of_error; + let lower: std::time::Duration = $dur - margin_of_error; + let upper: std::time::Duration = $dur + margin_of_error; // Handles ms rounding assert!( - elapsed >= lower && elapsed <= lower + margin_of_error, + elapsed >= lower && elapsed <= upper, "actual = {:?}, expected = {:?}", elapsed, lower diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index db36e762d9..a8ebb01aab 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -11,12 +11,14 @@ use crate::client::retries::RetryStrategy; use crate::config_bag::ConfigBag; use crate::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_async::future::now_or_later::NowOrLater; +use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_http::body::SdkBody; use aws_smithy_http::endpoint::EndpointPrefix; use std::any::Any; use std::fmt::Debug; use std::future::Future as StdFuture; use std::pin::Pin; +use std::sync::Arc; use std::time::SystemTime; pub type HttpRequest = http::Request; @@ -142,6 +144,9 @@ pub trait ConfigBagAccessors { fn request_time(&self) -> Option; fn set_request_time(&mut self, request_time: RequestTime); + + fn sleep_impl(&self) -> Option>; + fn set_sleep_impl(&mut self, async_sleep: Option>); } impl ConfigBagAccessors for ConfigBag { @@ -276,4 +281,16 @@ impl ConfigBagAccessors for ConfigBag { fn set_request_time(&mut self, request_time: RequestTime) { self.put::(request_time); } + + fn sleep_impl(&self) -> Option> { + self.get::>().cloned() + } + + fn set_sleep_impl(&mut self, sleep_impl: Option>) { + if let Some(sleep_impl) = sleep_impl { + self.put::>(sleep_impl); + } else { + self.unset::>(); + } + } } diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 5c8e098462..79e5c7f9fe 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -14,6 +14,7 @@ http-auth = ["aws-smithy-runtime-api/http-auth"] test-util = ["dep:aws-smithy-protocol-test"] [dependencies] +aws-smithy-async = { path = "../aws-smithy-async" } aws-smithy-client = { path = "../aws-smithy-client" } aws-smithy-http = { path = "../aws-smithy-http" } aws-smithy-protocol-test = { path = "../aws-smithy-protocol-test", optional = true } @@ -22,12 +23,14 @@ aws-smithy-types = { path = "../aws-smithy-types" } bytes = "1" http = "0.2.8" http-body = "0.4.5" +pin-project-lite = "0.2.7" pin-utils = "0.1.0" tokio = { version = "1.25", features = [] } tracing = "0.1" [dev-dependencies] -tokio = { version = "1.25", features = ["macros", "rt"] } +aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio"] } +tokio = { version = "1.25", features = ["macros", "rt", "test-util"] } [package.metadata.docs.rs] all-features = true diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index d477ce9eae..14d1c95ac1 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -15,3 +15,5 @@ pub mod connections; /// This code defines when and how failed requests should be retried. It also defines the behavior /// used to limit the rate at which requests are sent. pub mod retries; + +mod timeout; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 7dfef49dde..c0f4b4f06b 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -7,6 +7,7 @@ use self::auth::orchestrate_auth; use crate::client::orchestrator::endpoints::orchestrate_endpoint; use crate::client::orchestrator::http::read_body; use crate::client::orchestrator::phase::Phase; +use crate::client::timeout::{MaybeTimeout, ProvideMaybeTimeoutConfig, TimeoutKind}; use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output}; use aws_smithy_runtime_api::client::interceptors::{InterceptorContext, Interceptors}; @@ -25,6 +26,15 @@ pub(self) mod phase; pub async fn invoke( input: Input, runtime_plugins: &RuntimePlugins, +) -> Result> { + invoke_pre_config(input, runtime_plugins) + .instrument(debug_span!("invoke")) + .await +} + +async fn invoke_pre_config( + input: Input, + runtime_plugins: &RuntimePlugins, ) -> Result> { let mut cfg = ConfigBag::base(); let cfg = &mut cfg; @@ -38,6 +48,20 @@ pub async fn invoke( // Operation configuration .include(|_| runtime_plugins.apply_operation_configuration(cfg, &mut interceptors))? .include(|ctx| interceptors.operation_read_before_execution(ctx, cfg))? + .finish(); + + let operation_timeout_config = cfg.maybe_timeout_config(TimeoutKind::Operation); + invoke_post_config(cfg, context, interceptors) + .maybe_timeout_with_config(operation_timeout_config) + .await +} + +async fn invoke_post_config( + cfg: &mut ConfigBag, + context: InterceptorContext, + interceptors: Interceptors, +) -> Result> { + let context = Phase::construction(context) // Before serialization .include(|ctx| interceptors.read_before_serialization(ctx, cfg))? .include_mut(|ctx| interceptors.modify_before_serialization(ctx, cfg))? @@ -76,8 +100,11 @@ pub async fn invoke( let mut context = context; let handling_phase = loop { + let attempt_timeout_config = cfg.maybe_timeout_config(TimeoutKind::OperationAttempt); let dispatch_phase = Phase::dispatch(context); context = make_an_attempt(dispatch_phase, cfg, &interceptors) + .instrument(debug_span!("make_an_attempt")) + .maybe_timeout_with_config(attempt_timeout_config) .await? .include(|ctx| interceptors.read_after_attempt(ctx, cfg))? .include_mut(|ctx| interceptors.modify_before_attempt_completion(ctx, cfg))? diff --git a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs new file mode 100644 index 0000000000..66c43d8eb3 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs @@ -0,0 +1,240 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_async::future::timeout::Timeout; +use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; +use aws_smithy_client::SdkError; +use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpResponse}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::timeout::TimeoutConfig; +use pin_project_lite::pin_project; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; +use std::time::Duration; + +#[derive(Debug)] +struct MaybeTimeoutError { + kind: TimeoutKind, + duration: Duration, +} + +impl MaybeTimeoutError { + fn new(kind: TimeoutKind, duration: Duration) -> Self { + Self { kind, duration } + } +} + +impl std::fmt::Display for MaybeTimeoutError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} occurred after {:?}", + match self.kind { + TimeoutKind::Operation => "operation timeout (all attempts including retries)", + TimeoutKind::OperationAttempt => "operation attempt timeout (single attempt)", + }, + self.duration + ) + } +} + +impl std::error::Error for MaybeTimeoutError {} + +pin_project! { + #[non_exhaustive] + #[must_use = "futures do nothing unless you `.await` or poll them"] + // This allow is needed because otherwise Clippy will get mad we didn't document the + // generated MaybeTimeoutFutureProj + #[allow(missing_docs)] + #[project = MaybeTimeoutFutureProj] + /// A timeout future that may or may not have a timeout depending on + /// whether or not one was set. A `kind` can be set so that when a timeout occurs, there + /// is additional context attached to the error. + pub(super) enum MaybeTimeoutFuture { + /// A wrapper around an inner future that will output an [`SdkError`] if it runs longer than + /// the given duration + Timeout { + #[pin] + future: Timeout, + timeout_kind: TimeoutKind, + duration: Duration, + }, + /// A thin wrapper around an inner future that will never time out + NoTimeout { + #[pin] + future: F + } + } +} + +impl Future for MaybeTimeoutFuture +where + InnerFuture: Future>>, +{ + type Output = Result>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let (future, kind, duration) = match self.project() { + MaybeTimeoutFutureProj::NoTimeout { future } => return future.poll(cx), + MaybeTimeoutFutureProj::Timeout { + future, + timeout_kind, + duration, + } => (future, timeout_kind, duration), + }; + match future.poll(cx) { + Poll::Ready(Ok(response)) => Poll::Ready(response), + Poll::Ready(Err(_timeout)) => Poll::Ready(Err(SdkError::timeout_error( + MaybeTimeoutError::new(*kind, *duration), + ))), + Poll::Pending => Poll::Pending, + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub(super) enum TimeoutKind { + Operation, + OperationAttempt, +} + +#[derive(Clone, Debug)] +pub(super) struct MaybeTimeoutConfig { + sleep_impl: Option>, + timeout: Option, + timeout_kind: TimeoutKind, +} + +pub(super) trait ProvideMaybeTimeoutConfig { + fn maybe_timeout_config(&self, timeout_kind: TimeoutKind) -> MaybeTimeoutConfig; +} + +impl ProvideMaybeTimeoutConfig for ConfigBag { + fn maybe_timeout_config(&self, timeout_kind: TimeoutKind) -> MaybeTimeoutConfig { + if let Some(timeout_config) = self.get::() { + let sleep_impl = self.sleep_impl(); + let timeout = match (sleep_impl.as_ref(), timeout_kind) { + (None, _) => None, + (Some(_), TimeoutKind::Operation) => timeout_config.operation_timeout(), + (Some(_), TimeoutKind::OperationAttempt) => { + timeout_config.operation_attempt_timeout() + } + }; + MaybeTimeoutConfig { + sleep_impl, + timeout, + timeout_kind, + } + } else { + MaybeTimeoutConfig { + sleep_impl: None, + timeout: None, + timeout_kind, + } + } + } +} + +/// Trait to conveniently wrap a future with an optional timeout. +pub(super) trait MaybeTimeout: Sized { + /// Wraps a future in a timeout if one is set. + fn maybe_timeout_with_config( + self, + timeout_config: MaybeTimeoutConfig, + ) -> MaybeTimeoutFuture; + + /// Wraps a future in a timeout if one is set. + fn maybe_timeout(self, cfg: &ConfigBag, kind: TimeoutKind) -> MaybeTimeoutFuture; +} + +impl MaybeTimeout for T +where + T: Future, +{ + fn maybe_timeout_with_config( + self, + timeout_config: MaybeTimeoutConfig, + ) -> MaybeTimeoutFuture { + match timeout_config { + MaybeTimeoutConfig { + sleep_impl: Some(sleep_impl), + timeout: Some(timeout), + timeout_kind, + } => MaybeTimeoutFuture::Timeout { + future: Timeout::new(self, sleep_impl.sleep(timeout)), + timeout_kind, + duration: timeout, + }, + _ => MaybeTimeoutFuture::NoTimeout { future: self }, + } + } + + fn maybe_timeout(self, cfg: &ConfigBag, kind: TimeoutKind) -> MaybeTimeoutFuture { + self.maybe_timeout_with_config(cfg.maybe_timeout_config(kind)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_async::assert_elapsed; + use aws_smithy_async::future::never::Never; + use aws_smithy_async::rt::sleep::TokioSleep; + + #[tokio::test] + async fn test_no_timeout() { + let sleep_impl: Arc = Arc::new(TokioSleep::new()); + let sleep_future = sleep_impl.sleep(Duration::from_millis(250)); + let underlying_future = async { + sleep_future.await; + Result::<_, SdkError<(), HttpResponse>>::Ok(()) + }; + + let now = tokio::time::Instant::now(); + tokio::time::pause(); + + let mut cfg = ConfigBag::base(); + cfg.put(TimeoutConfig::builder().build()); + cfg.set_sleep_impl(Some(sleep_impl)); + + underlying_future + .maybe_timeout(&cfg, TimeoutKind::Operation) + .await + .expect("success"); + + assert_elapsed!(now, Duration::from_secs_f32(0.25)); + } + + #[tokio::test] + async fn test_operation_timeout() { + let sleep_impl: Arc = Arc::new(TokioSleep::new()); + let never = Never::new(); + let underlying_future = async { + never.await; + Result::<_, SdkError<(), HttpResponse>>::Ok(()) + }; + + let now = tokio::time::Instant::now(); + tokio::time::pause(); + + let mut cfg = ConfigBag::base(); + cfg.put( + TimeoutConfig::builder() + .operation_timeout(Duration::from_millis(250)) + .build(), + ); + cfg.set_sleep_impl(Some(sleep_impl)); + + let result = underlying_future + .maybe_timeout(&cfg, TimeoutKind::Operation) + .await; + let err = result.expect_err("should have timed out"); + + assert_eq!(format!("{:?}", err), "TimeoutError(TimeoutError { source: MaybeTimeoutError { kind: Operation, duration: 250ms } })"); + assert_elapsed!(now, Duration::from_secs_f32(0.25)); + } +} From e1cf72e52d152519f57a66d95a23322fe1de0759 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 2 May 2023 14:37:47 -0700 Subject: [PATCH 075/253] Make the `Debug` impl on `TypeErasedBox` useful (#2665) ## Motivation and Context This PR ports @rcoh's addition of `Debug` in `ConfigBag` to the `TypeErasedBox`. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../protocol/ResponseDeserializerGenerator.kt | 4 +- .../aws-smithy-runtime-api/src/client/auth.rs | 13 ++- .../src/client/orchestrator.rs | 17 ++- .../aws-smithy-runtime-api/src/config_bag.rs | 53 ++------- .../src/type_erasure.rs | 102 ++++++++++-------- 5 files changed, 82 insertions(+), 107 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index 81c0494c17..4b355cf8ba 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -156,8 +156,8 @@ class ResponseDeserializerGenerator( """ pub(crate) fn $fnName(result: Result) -> Result<#{Output}, #{Error}> where - O: Send + Sync + 'static, - E: Send + Sync + 'static, + O: std::fmt::Debug + Send + Sync + 'static, + E: std::fmt::Debug + Send + Sync + 'static, { result.map(|output| #{TypedBox}::new(output).erase()) .map_err(|error| #{TypedBox}::new(error).erase()) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 3d8cccad47..fefc3c5025 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -7,9 +7,8 @@ use crate::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use crate::client::orchestrator::{BoxError, HttpRequest}; use crate::config_bag::ConfigBag; use crate::type_erasure::{TypeErasedBox, TypedBox}; -use std::any::Any; use std::borrow::Cow; -use std::fmt::Debug; +use std::fmt; use std::sync::Arc; #[cfg(feature = "http-auth")] @@ -39,16 +38,16 @@ impl AuthSchemeId { pub struct AuthOptionResolverParams(TypeErasedBox); impl AuthOptionResolverParams { - pub fn new(params: T) -> Self { + pub fn new(params: T) -> Self { Self(TypedBox::new(params).erase()) } - pub fn get(&self) -> Option<&T> { + pub fn get(&self) -> Option<&T> { self.0.downcast_ref() } } -pub trait AuthOptionResolver: Send + Sync + Debug { +pub trait AuthOptionResolver: Send + Sync + fmt::Debug { fn resolve_auth_options<'a>( &'a self, params: &AuthOptionResolverParams, @@ -87,7 +86,7 @@ impl HttpAuthSchemes { } } -pub trait HttpAuthScheme: Send + Sync + Debug { +pub trait HttpAuthScheme: Send + Sync + fmt::Debug { fn scheme_id(&self) -> AuthSchemeId; fn identity_resolver<'a>( @@ -98,7 +97,7 @@ pub trait HttpAuthScheme: Send + Sync + Debug { fn request_signer(&self) -> &dyn HttpRequestSigner; } -pub trait HttpRequestSigner: Send + Sync + Debug { +pub trait HttpRequestSigner: Send + Sync + fmt::Debug { /// Return a signed version of the given request using the given identity. /// /// If the provided identity is incompatible with this signer, an error must be returned. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index a8ebb01aab..30751d2120 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -14,8 +14,7 @@ use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_http::body::SdkBody; use aws_smithy_http::endpoint::EndpointPrefix; -use std::any::Any; -use std::fmt::Debug; +use std::fmt; use std::future::Future as StdFuture; use std::pin::Pin; use std::sync::Arc; @@ -27,15 +26,15 @@ pub type BoxError = Box; pub type BoxFuture = Pin>>>; pub type Future = NowOrLater, BoxFuture>; -pub trait TraceProbe: Send + Sync + Debug { +pub trait TraceProbe: Send + Sync + fmt::Debug { fn dispatch_events(&self); } -pub trait RequestSerializer: Send + Sync + Debug { +pub trait RequestSerializer: Send + Sync + fmt::Debug { fn serialize_input(&self, input: Input) -> Result; } -pub trait ResponseDeserializer: Send + Sync + Debug { +pub trait ResponseDeserializer: Send + Sync + fmt::Debug { fn deserialize_streaming(&self, response: &mut HttpResponse) -> Option { let _ = response; None @@ -44,7 +43,7 @@ pub trait ResponseDeserializer: Send + Sync + Debug { fn deserialize_nonstreaming(&self, response: &HttpResponse) -> OutputOrError; } -pub trait Connection: Send + Sync + Debug { +pub trait Connection: Send + Sync + fmt::Debug { fn call(&self, request: HttpRequest) -> BoxFuture; } @@ -58,16 +57,16 @@ impl Connection for Box { pub struct EndpointResolverParams(TypeErasedBox); impl EndpointResolverParams { - pub fn new(params: T) -> Self { + pub fn new(params: T) -> Self { Self(TypedBox::new(params).erase()) } - pub fn get(&self) -> Option<&T> { + pub fn get(&self) -> Option<&T> { self.0.downcast_ref() } } -pub trait EndpointResolver: Send + Sync + Debug { +pub trait EndpointResolver: Send + Sync + fmt::Debug { fn resolve_and_apply_endpoint( &self, params: &EndpointResolverParams, diff --git a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs index 530c8da317..78a28538fb 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs @@ -12,10 +12,9 @@ mod typeid_map; use crate::config_bag::typeid_map::TypeIdMap; - -use std::any::{type_name, Any, TypeId}; +use crate::type_erasure::TypeErasedBox; +use std::any::{type_name, TypeId}; use std::borrow::Cow; - use std::fmt::{Debug, Formatter}; use std::iter::Rev; use std::marker::PhantomData; @@ -77,45 +76,9 @@ impl Default for Value { } } -struct DebugErased { - field: Box, - #[allow(dead_code)] - type_name: &'static str, - #[allow(clippy::type_complexity)] - debug: Box) -> std::fmt::Result + Send + Sync>, -} - -impl Debug for DebugErased { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - (self.debug)(self, f) - } -} - -impl DebugErased { - fn new(value: T) -> Self { - let debug = |value: &DebugErased, f: &mut Formatter<'_>| { - Debug::fmt(value.as_ref::().expect("typechecked"), f) - }; - let name = type_name::(); - Self { - field: Box::new(value), - type_name: name, - debug: Box::new(debug), - } - } - - fn as_ref(&self) -> Option<&T> { - self.field.downcast_ref() - } - - fn as_mut(&mut self) -> Option<&mut T> { - self.field.downcast_mut() - } -} - pub struct Layer { name: Cow<'static, str>, - props: TypeIdMap, + props: TypeIdMap, } /// Trait defining how types can be stored and loaded from the config bag @@ -211,20 +174,20 @@ impl Debug for Layer { impl Layer { pub fn put(&mut self, value: T::StoredType) -> &mut Self { self.props - .insert(TypeId::of::(), DebugErased::new(value)); + .insert(TypeId::of::(), TypeErasedBox::new(value)); self } pub fn get(&self) -> Option<&T::StoredType> { self.props .get(&TypeId::of::()) - .map(|t| t.as_ref().expect("typechecked")) + .map(|t| t.downcast_ref().expect("typechecked")) } pub fn get_mut(&mut self) -> Option<&mut T::StoredType> { self.props .get_mut(&TypeId::of::()) - .map(|t| t.as_mut().expect("typechecked")) + .map(|t| t.downcast_mut().expect("typechecked")) } pub fn get_mut_or_default(&mut self) -> &mut T::StoredType @@ -233,8 +196,8 @@ impl Layer { { self.props .entry(TypeId::of::()) - .or_insert_with(|| DebugErased::new(T::StoredType::default())) - .as_mut() + .or_insert_with(|| TypeErasedBox::new(T::StoredType::default())) + .downcast_mut() .expect("typechecked") } diff --git a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs b/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs index 50c852f0cf..4c20c6c4d4 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs @@ -3,7 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::any::Any; +use std::any::{type_name, Any}; +use std::fmt; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; @@ -18,7 +19,6 @@ use std::ops::{Deref, DerefMut}; /// and to avoid the monomorphization that brings with it. This `TypedBox` will primarily be useful /// for operation-specific or service-specific interceptors that need to operate on the actual /// input/output/error types. -#[derive(Debug)] pub struct TypedBox { inner: TypeErasedBox, _phantom: PhantomData, @@ -26,12 +26,12 @@ pub struct TypedBox { impl TypedBox where - T: Send + Sync + 'static, + T: fmt::Debug + Send + Sync + 'static, { // Creates a new `TypedBox`. pub fn new(inner: T) -> Self { Self { - inner: TypeErasedBox::new(Box::new(inner) as _), + inner: TypeErasedBox::new(inner), _phantom: Default::default(), } } @@ -62,7 +62,17 @@ where } } -impl Deref for TypedBox { +impl fmt::Debug for TypedBox +where + T: Send + Sync + 'static, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("TypedBox:")?; + (self.inner.debug)(&self.inner, f) + } +} + +impl Deref for TypedBox { type Target = T; fn deref(&self) -> &Self::Target { @@ -70,67 +80,66 @@ impl Deref for TypedBox { } } -impl DerefMut for TypedBox { +impl DerefMut for TypedBox { fn deref_mut(&mut self) -> &mut Self::Target { self.inner.downcast_mut().expect("type checked") } } -#[derive(Debug)] -pub struct TypedRef<'a, T> { - inner: &'a TypeErasedBox, - _phantom: PhantomData, -} - -impl<'a, T: 'static> TypedRef<'a, T> { - pub fn assume_from(type_erased: &'a TypeErasedBox) -> Option> { - if type_erased.downcast_ref::().is_some() { - Some(TypedRef { - inner: type_erased, - _phantom: Default::default(), - }) - } else { - None - } - } +/// A new-type around `Box` +pub struct TypeErasedBox { + field: Box, + #[allow(dead_code)] + type_name: &'static str, + #[allow(clippy::type_complexity)] + debug: Box) -> fmt::Result + Send + Sync>, } -impl<'a, T: 'static> Deref for TypedRef<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.inner.downcast_ref().expect("type checked") +impl fmt::Debug for TypeErasedBox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("TypeErasedBox:")?; + (self.debug)(self, f) } } -/// A new-type around `Box` -#[derive(Debug)] -pub struct TypeErasedBox { - inner: Box, -} - impl TypeErasedBox { - // Creates a new `TypeErasedBox`. - pub fn new(inner: Box) -> Self { - Self { inner } + pub fn new(value: T) -> Self { + let debug = |value: &TypeErasedBox, f: &mut fmt::Formatter<'_>| { + fmt::Debug::fmt(value.downcast_ref::().expect("typechecked"), f) + }; + let name = type_name::(); + Self { + field: Box::new(value), + type_name: name, + debug: Box::new(debug), + } } // Downcast into a `Box`, or return `Self` if it is not a `T`. - pub fn downcast(self) -> Result, Self> { - match self.inner.downcast() { + pub fn downcast(self) -> Result, Self> { + let TypeErasedBox { + field, + type_name, + debug, + } = self; + match field.downcast() { Ok(t) => Ok(t), - Err(s) => Err(Self { inner: s }), + Err(s) => Err(Self { + field: s, + type_name, + debug, + }), } } /// Downcast as a `&T`, or return `None` if it is not a `T`. - pub fn downcast_ref(&self) -> Option<&T> { - self.inner.downcast_ref() + pub fn downcast_ref(&self) -> Option<&T> { + self.field.downcast_ref() } /// Downcast as a `&mut T`, or return `None` if it is not a `T`. - pub fn downcast_mut(&mut self) -> Option<&mut T> { - self.inner.downcast_mut() + pub fn downcast_mut(&mut self) -> Option<&mut T> { + self.field.downcast_mut() } } @@ -148,6 +157,9 @@ mod tests { let foo = TypedBox::new(Foo("1")); let bar = TypedBox::new(Bar(2)); + assert_eq!("TypedBox:Foo(\"1\")", format!("{foo:?}")); + assert_eq!("TypedBox:Bar(2)", format!("{bar:?}")); + let mut foo_erased = foo.erase(); foo_erased .downcast_mut::() @@ -155,6 +167,8 @@ mod tests { .0 = "3"; let bar_erased = bar.erase(); + assert_eq!("TypeErasedBox:Foo(\"3\")", format!("{foo_erased:?}")); + assert_eq!("TypeErasedBox:Bar(2)", format!("{bar_erased:?}")); let bar_erased = TypedBox::::assume_from(bar_erased).expect_err("it's not a Foo"); let mut bar = TypedBox::::assume_from(bar_erased).expect("it's a Bar"); From c75807ce37c7d50c2a72e47c87b5fe5cff8c0685 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Wed, 3 May 2023 10:46:18 -0500 Subject: [PATCH 076/253] fix/format: various code that's escaped the linter (#2667) _Every once in a while I run clippy and the pre-commit checks on everything b/c stuff seems to fall through the cracks._ - fix: various code that's escaped the linter - format: various code that's escaped the formatter - update: `chrono` because a test was using a method only available in v0.4.23 _(I don't know why that didn't fail in CI)_ _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-http/src/user_agent.rs | 2 +- .../aws-runtime/src/user_agent.rs | 2 +- .../serialize/JsonSerializerGenerator.kt | 2 +- .../PythonServerEventStreamErrorGenerator.kt | 2 +- ...PythonServerEventStreamWrapperGenerator.kt | 12 ++++----- .../ServerHttpBoundProtocolGenerator.kt | 8 +++--- .../ServerOperationErrorGeneratorTest.kt | 4 +-- examples/README.md | 26 +++++++++---------- .../aws-smithy-runtime-api/src/config_bag.rs | 4 +-- .../aws-smithy-types-convert/Cargo.toml | 2 +- rust-runtime/aws-smithy-types/src/number.rs | 4 +-- 11 files changed, 33 insertions(+), 35 deletions(-) diff --git a/aws/rust-runtime/aws-http/src/user_agent.rs b/aws/rust-runtime/aws-http/src/user_agent.rs index 8adc03cc3a..8d338bff70 100644 --- a/aws/rust-runtime/aws-http/src/user_agent.rs +++ b/aws/rust-runtime/aws-http/src/user_agent.rs @@ -778,7 +778,7 @@ mod test { .get(USER_AGENT) .expect("UA header should be set"); req.headers() - .get(&X_AMZ_USER_AGENT) + .get(X_AMZ_USER_AGENT) .expect("UA header should be set"); } } diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 3babb8c758..a6c6ff1e85 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -183,7 +183,7 @@ mod tests { let api_metadata = ApiMetadata::new("some-service", "some-version"); let mut config = ConfigBag::base(); - config.put(api_metadata.clone()); + config.put(api_metadata); config.put(AppName::new("my_awesome_app").unwrap()); let interceptor = UserAgentInterceptor::new(); diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt index efe178db8d..87562dc2ab 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/serialize/JsonSerializerGenerator.kt @@ -325,7 +325,7 @@ class JsonSerializerGenerator( pub fn $fnName( #{AllowUnusedVariables:W} object: &mut #{JsonObjectWriter}, #{AllowUnusedVariables:W} input: &#{StructureSymbol}, - ) -> Result<(), #{Error}> + ) -> Result<(), #{Error}> """, "StructureSymbol" to symbolProvider.toSymbol(context.shape), "AllowUnusedVariables" to allowUnusedVariables, diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamErrorGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamErrorGenerator.kt index 02a545b7eb..b915e953cd 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamErrorGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamErrorGenerator.kt @@ -61,7 +61,7 @@ class PythonServerEventStreamErrorGenerator( """ if let Ok(it) = obj.extract::<#T>() { return Ok(Self::${symbol.name}(it)); - } + } """, symbol, ) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamWrapperGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamWrapperGenerator.kt index 676b56a708..8594d703c1 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamWrapperGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerEventStreamWrapperGenerator.kt @@ -139,8 +139,8 @@ class PythonServerEventStreamWrapperGenerator( let stream = #{PyO3Asyncio}::tokio::into_stream_v1(obj)?; let stream = stream.filter_map(|res| { #{PyO3}::Python::with_gil(|py| { - // TODO(EventStreamImprovements): Add `InternalServerError` variant to all event streaming - // errors and return that variant in case of errors here? + // TODO(EventStreamImprovements): Add `InternalServerError` variant to all event streaming + // errors and return that variant in case of errors here? match res { Ok(obj) => { match obj.extract::<#{Inner}>(py) { @@ -165,11 +165,11 @@ class PythonServerEventStreamWrapperGenerator( } }) }); - + Ok($name { inner: #{Arc}::new(#{Mutex}::new(Some(stream.into()))) }) } } - + impl #{PyO3}::IntoPy<#{PyO3}::PyObject> for $name { fn into_py(self, py: #{PyO3}::Python<'_>) -> #{PyO3}::PyObject { #{PyO3}::exceptions::PyAttributeError::new_err("this is a write-only object").into_py(py) @@ -198,7 +198,7 @@ class PythonServerEventStreamWrapperGenerator( writer.rustTemplate( """ pub fn new( - unmarshaller: impl #{UnmarshallMessage} + #{Send} + #{Sync} + 'static, + unmarshaller: impl #{UnmarshallMessage} + #{Send} + #{Sync} + 'static, body: #{Body} ) -> $name { let inner = #{Wrapped}::new(unmarshaller, body); @@ -227,7 +227,7 @@ class PythonServerEventStreamWrapperGenerator( match next { Ok(Some(data)) => Ok(#{PyO3}::Python::with_gil(|py| #{PyO3}::IntoPy::into_py(data, py))), Ok(None) => Err(#{PyO3}::exceptions::PyStopAsyncIteration::new_err("stream exhausted")), - Err(#{SmithyHttp}::result::SdkError::ServiceError(service_err)) => Err(service_err.into_err().into()), + Err(#{SmithyHttp}::result::SdkError::ServiceError(service_err)) => Err(service_err.into_err().into()), Err(err) => Err(#{PyO3}::exceptions::PyRuntimeError::new_err(err.to_string())), } })?; diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index c61947a397..2530f04404 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -200,10 +200,10 @@ class ServerHttpBoundProtocolTraitImplGenerator( "application/x-www-form-urlencoded" -> "const $staticContentType: #{Mime}::Mime = #{Mime}::APPLICATION_WWW_FORM_URLENCODED;" else -> """ - static $staticContentType: #{OnceCell}::sync::Lazy<#{Mime}::Mime> = #{OnceCell}::sync::Lazy::new(|| { - ${contentType.dq()}.parse::<#{Mime}::Mime>().expect("BUG: MIME parsing failed, content_type is not valid") - }); - """ + static $staticContentType: #{OnceCell}::sync::Lazy<#{Mime}::Mime> = #{OnceCell}::sync::Lazy::new(|| { + ${contentType.dq()}.parse::<#{Mime}::Mime>().expect("BUG: MIME parsing failed, content_type is not valid") + }); + """ } rustTemplate(init, *codegenScope) } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGeneratorTest.kt index 7d5636a151..a73466277a 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationErrorGeneratorTest.kt @@ -21,9 +21,9 @@ import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestSymb class ServerOperationErrorGeneratorTest { private val baseModel = """ namespace error - + use aws.protocols#restJson1 - + @restJson1 service MyService { operations: [Greeting] diff --git a/examples/README.md b/examples/README.md index a82327bc64..f179bc1a44 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,13 +1,13 @@ # Smithy Rust Server SDK examples -This folder contains some example services showcasing Smithy Rust Server SDK, +This folder contains some example services showcasing Smithy Rust Server SDK, also known as the Rust service framework, capabilities and to run benchmarks. Three server implementations are available: - `/pokemon-service`, a HTTP server demonstrating [middleware] and [extractors]. -- `/pokemon-service-tls`, a HTTPS server. This server can do - its own TLS negotiation, rather than relying on a load balancer. +- `/pokemon-service-tls`, a HTTPS server. This server can do + its own TLS negotiation, rather than relying on a load balancer. - `/pokemon-service-lambda`, a server that can be deployed onto AWS Lambda. These servers, and their clients, are generated using smithy-rs. You're invited @@ -23,7 +23,7 @@ a suitable choice for implementing your web service. You will need install Java 11 to run the smithy-rs code generator and an installation of Rust, including `cargo`, to compile the generated code. -(Optional) The [Cargo Lambda](https://cargo-lambda.info/) sub-command for +(Optional) The [Cargo Lambda](https://cargo-lambda.info/) sub-command for `cargo` is required to support the AWS Lambda integration. @@ -43,9 +43,9 @@ can be used directly. - `build`: compiles the generated client and server - `clean`: deletes build artifacts - `clippy`: lints the code -- `distclean`: delete generated code and build artifacts +- `distclean`: delete generated code and build artifacts - `doc-open`: builds and opens the rustdoc documentation -- `lambda_invoke`: invokes a running server +- `lambda_invoke`: invokes a running server - `lambda_watch`: runs the service on an emulated AWS Lambda environment - `run`: runs the Pokémon service - `test`: runs integration and unit tests @@ -53,14 +53,14 @@ can be used directly. ## Running services -To run one of the three server implementations locally, provide the appropriate +To run one of the three server implementations locally, provide the appropriate service name to the `--bin` flag: ```bash cargo run --bin pokemon-service[(-lambda|-tls)] ``` -CLI arguments can be passed to the server binaries by adding them after `--`. +CLI arguments can be passed to the server binaries by adding them after `--`. For example, to see a service's help information, use the following: ```bash @@ -69,11 +69,11 @@ cargo run --bin -- --help ## Testing -The `/pokemon-test*/tests` folders provide integration tests involving the +The `/pokemon-test*/tests` folders provide integration tests involving the generated clients. -They can be invoked with `cargo test`. This will spawn each service in turn -and run some integration tests against it. Use `-p ` to filter by +They can be invoked with `cargo test`. This will spawn each service in turn +and run some integration tests against it. Use `-p ` to filter by package. More info can be found in the `tests` folder of each package. @@ -81,8 +81,8 @@ More info can be found in the `tests` folder of each package. ## Benchmarking -Servers running locally (see "Running services") can be benchmarked with any +Servers running locally (see "Running services") can be benchmarked with any load testing tool, such as Artillery or `wrk`. -Please see [BENCHMARKS.md](/examples/BENCHMARKS.md) for benchmarking results +Please see [BENCHMARKS.md](/examples/BENCHMARKS.md) for benchmarking results produced by the smithy-rs team. diff --git a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs index 78a28538fb..47806267b2 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs @@ -638,9 +638,7 @@ mod test { } expected.reverse(); assert_eq!( - bag.load::() - .map(|i| i.clone()) - .collect::>(), + bag.load::().cloned().collect::>(), expected ); } diff --git a/rust-runtime/aws-smithy-types-convert/Cargo.toml b/rust-runtime/aws-smithy-types-convert/Cargo.toml index c9ffbd22a0..f657571a48 100644 --- a/rust-runtime/aws-smithy-types-convert/Cargo.toml +++ b/rust-runtime/aws-smithy-types-convert/Cargo.toml @@ -13,7 +13,7 @@ convert-time = ["aws-smithy-types", "time"] [dependencies] aws-smithy-types = { path = "../aws-smithy-types", optional = true } -chrono = { version = "0.4.19", optional = true, default-features = false, features = ["std"] } +chrono = { version = "0.4.23", optional = true, default-features = false, features = ["std"] } time = { version = "0.3.4", optional = true } [package.metadata.docs.rs] diff --git a/rust-runtime/aws-smithy-types/src/number.rs b/rust-runtime/aws-smithy-types/src/number.rs index 76fc08a218..bb771798d0 100644 --- a/rust-runtime/aws-smithy-types/src/number.rs +++ b/rust-runtime/aws-smithy-types/src/number.rs @@ -184,8 +184,8 @@ impl TryFrom for f32 { } #[cfg(test)] -mod number { - use super::*; +mod test { + use super::Number; use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; macro_rules! to_unsigned_converter_tests { From beedd2c70d7586a6f0fa1af236829829b9ccd801 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 3 May 2023 14:29:40 -0700 Subject: [PATCH 077/253] Refactor interceptor phases to improve optionality inside interceptors (#2670) ## Motivation and Context This PR: - Deletes `TraceProbe` - Replaces the orchestrator's `Phase` helper with a couple of macros - Introduces the concept of phases into `InterceptorContext` so that input/output/error/request/response accessors don't need option wrappers - Adds `TypeErasedError` so that `orchestrator::Error` can implement `Error` - Rewinds the interceptor context in the retry loop ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-runtime/src/invocation_id.rs | 23 +- .../aws-runtime/src/recursion_detection.rs | 12 +- .../aws-runtime/src/user_agent.rs | 32 +- .../rustsdk/RetryClassifierDecorator.kt | 2 +- .../EndpointParamsInterceptorGenerator.kt | 6 +- .../ServiceRuntimePluginGenerator.kt | 13 - .../client/FluentClientGenerator.kt | 2 +- .../protocol/ResponseDeserializerGenerator.kt | 4 +- .../src/client/interceptors.rs | 114 ++- .../src/client/interceptors/context.rs | 790 ++++++++++++++++-- .../src/client/orchestrator.rs | 23 +- .../src/client/retries.rs | 3 +- .../src/type_erasure.rs | 123 ++- .../src/client/orchestrator.rs | 217 ++--- .../src/client/orchestrator/auth.rs | 66 +- .../src/client/orchestrator/endpoints.rs | 5 +- .../src/client/orchestrator/phase.rs | 114 --- .../src/client/retries/strategy/never.rs | 3 +- 18 files changed, 1108 insertions(+), 444 deletions(-) delete mode 100644 rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 89bcbb8d5b..98d5405ecb 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; use aws_smithy_runtime_api::client::interceptors::error::BoxError; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; use aws_smithy_runtime_api::config_bag::ConfigBag; @@ -37,10 +38,10 @@ impl Default for InvocationIdInterceptor { impl Interceptor for InvocationIdInterceptor { fn modify_before_retry_loop( &self, - context: &mut InterceptorContext, + context: &mut InterceptorContext, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let headers = context.request_mut()?.headers_mut(); + let headers = context.request_mut().headers_mut(); let id = _cfg.get::().unwrap_or(&self.id); headers.append(AMZ_SDK_INVOCATION_ID, id.0.clone()); Ok(()) @@ -72,24 +73,26 @@ impl InvocationId { mod tests { use crate::invocation_id::InvocationIdInterceptor; use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_runtime_api::type_erasure::TypedBox; use http::HeaderValue; - fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a HeaderValue { - context - .request() - .unwrap() - .headers() - .get(header_name) - .unwrap() + fn expect_header<'a>( + context: &'a InterceptorContext, + header_name: &str, + ) -> &'a HeaderValue { + context.request().headers().get(header_name).unwrap() } #[test] fn test_id_is_generated_and_set() { - let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + let mut context = InterceptorContext::<()>::new(TypedBox::new("doesntmatter").erase()) + .into_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = context.take_input(); + let mut context = context.into_before_transmit_phase(); let mut config = ConfigBag::base(); let interceptor = InvocationIdInterceptor::new(); diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs index deafdd973f..38d2cf2725 100644 --- a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_types::os_shim_internal::Env; @@ -39,10 +40,10 @@ impl RecursionDetectionInterceptor { impl Interceptor for RecursionDetectionInterceptor { fn modify_before_signing( &self, - context: &mut InterceptorContext, + context: &mut InterceptorContext, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let request = context.request_mut()?; + let request = context.request_mut(); if request.headers().contains_key(TRACE_ID_HEADER) { return Ok(()); } @@ -145,14 +146,17 @@ mod tests { request = request.header(name, value); } let request = request.body(SdkBody::empty()).expect("must be valid"); - let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + let mut context = InterceptorContext::<()>::new(TypedBox::new("doesntmatter").erase()) + .into_serialization_phase(); context.set_request(request); + let _ = context.take_input(); + let mut context = context.into_before_transmit_phase(); let mut config = ConfigBag::base(); RecursionDetectionInterceptor { env } .modify_before_signing(&mut context, &mut config) .expect("interceptor must succeed"); - let mutated_request = context.request().expect("request is still set"); + let mutated_request = context.request(); for name in mutated_request.headers().keys() { assert_eq!( mutated_request.headers().get_all(name).iter().count(), diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index a6c6ff1e85..cdd3003da6 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -4,6 +4,7 @@ */ use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; use aws_smithy_runtime_api::client::interceptors::error::BoxError; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; use aws_smithy_runtime_api::config_bag::ConfigBag; @@ -72,7 +73,7 @@ fn header_values( impl Interceptor for UserAgentInterceptor { fn modify_before_signing( &self, - context: &mut InterceptorContext, + context: &mut InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let api_metadata = cfg @@ -95,7 +96,7 @@ impl Interceptor for UserAgentInterceptor { Cow::Owned(ua) }); - let headers = context.request_mut()?.headers_mut(); + let headers = context.request_mut().headers_mut(); let (user_agent, x_amz_user_agent) = header_values(&ua)?; headers.append(USER_AGENT, user_agent); headers.append(X_AMZ_USER_AGENT, x_amz_user_agent); @@ -112,10 +113,12 @@ mod tests { use aws_smithy_runtime_api::type_erasure::TypedBox; use aws_smithy_types::error::display::DisplayErrorContext; - fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a str { + fn expect_header<'a>( + context: &'a InterceptorContext, + header_name: &str, + ) -> &'a str { context .request() - .unwrap() .headers() .get(header_name) .unwrap() @@ -123,10 +126,17 @@ mod tests { .unwrap() } + fn context() -> InterceptorContext { + let mut context = InterceptorContext::<()>::new(TypedBox::new("doesntmatter").erase()) + .into_serialization_phase(); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = context.take_input(); + context.into_before_transmit_phase() + } + #[test] fn test_overridden_ua() { - let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); - context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let mut context = context(); let mut config = ConfigBag::base(); config.put(AwsUserAgent::for_tests()); @@ -149,8 +159,7 @@ mod tests { #[test] fn test_default_ua() { - let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); - context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let mut context = context(); let api_metadata = ApiMetadata::new("some-service", "some-version"); let mut config = ConfigBag::base(); @@ -178,8 +187,7 @@ mod tests { #[test] fn test_app_name() { - let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); - context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let mut context = context(); let api_metadata = ApiMetadata::new("some-service", "some-version"); let mut config = ConfigBag::base(); @@ -207,9 +215,7 @@ mod tests { #[test] fn test_api_metadata_missing() { - let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); - context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); - + let mut context = context(); let mut config = ConfigBag::base(); let interceptor = UserAgentInterceptor::new(); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt index ef730207a7..f3f1da9720 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt @@ -74,7 +74,7 @@ class OperationRetryClassifiersFeature( "RetryClassifiers" to smithyRuntimeApi.resolve("client::retries::RetryClassifiers"), "OperationError" to codegenContext.symbolProvider.symbolForOperationError(operation), "SdkError" to RuntimeType.smithyHttp(runtimeConfig).resolve("result::SdkError"), - "ErasedError" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypeErasedBox"), + "ErasedError" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypeErasedError"), ) override fun section(section: OperationRuntimePluginSection) = when (section) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 4137857346..ee1975d65d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -49,6 +49,7 @@ class EndpointParamsInterceptorGenerator( "HttpResponse" to orchestrator.resolve("HttpResponse"), "Interceptor" to interceptors.resolve("Interceptor"), "InterceptorContext" to interceptors.resolve("InterceptorContext"), + "BeforeSerializationPhase" to interceptors.resolve("context::phase::BeforeSerialization"), "InterceptorError" to interceptors.resolve("error::InterceptorError"), "Params" to endpointTypesGenerator.paramsStruct(), ) @@ -66,11 +67,10 @@ class EndpointParamsInterceptorGenerator( impl #{Interceptor} for $interceptorName { fn read_before_execution( &self, - context: &#{InterceptorContext}, + context: &#{InterceptorContext}<#{BeforeSerializationPhase}>, cfg: &mut #{ConfigBag}, ) -> Result<(), #{BoxError}> { - let _input = context.input()?; - let _input = _input + let _input = context.input() .downcast_ref::<${operationInput.name}>() .ok_or("failed to downcast to ${operationInput.name}")?; diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index f35fe7446c..7714850f71 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -84,7 +84,6 @@ class ServiceRuntimePluginGenerator( "Interceptors" to runtimeApi.resolve("client::interceptors::Interceptors"), "SharedEndpointResolver" to http.resolve("endpoint::SharedEndpointResolver"), "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), - "TraceProbe" to runtimeApi.resolve("client::orchestrator::TraceProbe"), ) } @@ -131,18 +130,6 @@ class ServiceRuntimePluginGenerator( .expect("connection set"); cfg.set_connection(connection); - // TODO(RuntimePlugins): Add the TraceProbe to the config bag - cfg.set_trace_probe({ - ##[derive(Debug)] - struct StubTraceProbe; - impl #{TraceProbe} for StubTraceProbe { - fn dispatch_events(&self) { - // no-op - } - } - StubTraceProbe - }); - #{additional_config} Ok(()) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index f10de15ee4..c4714038f0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -388,7 +388,7 @@ class FluentClientGenerator( .await .map_err(|err| { err.map_service_error(|err| { - #{TypedBox}::<#{OperationError}>::assume_from(err) + #{TypedBox}::<#{OperationError}>::assume_from(err.into()) .expect("correct error type") .unwrap() }) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index 4b355cf8ba..654463df74 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -157,10 +157,10 @@ class ResponseDeserializerGenerator( pub(crate) fn $fnName(result: Result) -> Result<#{Output}, #{Error}> where O: std::fmt::Debug + Send + Sync + 'static, - E: std::fmt::Debug + Send + Sync + 'static, + E: std::error::Error + std::fmt::Debug + Send + Sync + 'static, { result.map(|output| #{TypedBox}::new(output).erase()) - .map_err(|error| #{TypedBox}::new(error).erase()) + .map_err(|error| #{TypedBox}::new(error).erase_error()) } """, *codegenScope, diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index 20a34f6a76..f025cb5a70 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -6,6 +6,9 @@ pub mod context; pub mod error; +use crate::client::interceptors::context::phase::{ + AfterDeserialization, BeforeDeserialization, BeforeSerialization, BeforeTransmit, +}; use crate::config_bag::ConfigBag; use aws_smithy_types::error::display::DisplayErrorContext; pub use context::InterceptorContext; @@ -13,19 +16,23 @@ pub use error::{BoxError, InterceptorError}; use std::sync::Arc; macro_rules! interceptor_trait_fn { - ($name:ident, $docs:tt) => { + ($name:ident, $phase:ident, $docs:tt) => { #[doc = $docs] - fn $name(&self, context: &InterceptorContext, cfg: &mut ConfigBag) -> Result<(), BoxError> { + fn $name( + &self, + context: &InterceptorContext<$phase>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { let _ctx = context; let _cfg = cfg; Ok(()) } }; - (mut $name:ident, $docs:tt) => { + (mut $name:ident, $phase:ident, $docs:tt) => { #[doc = $docs] fn $name( &self, - context: &mut InterceptorContext, + context: &mut InterceptorContext<$phase>, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let _ctx = context; @@ -48,6 +55,7 @@ macro_rules! interceptor_trait_fn { pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_execution, + BeforeSerialization, " A hook called at the start of an execution, before the SDK does anything else. @@ -71,6 +79,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_serialization, + BeforeSerialization, " A hook called before the input message is marshalled into a transport message. @@ -98,6 +107,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_serialization, + BeforeSerialization, " A hook called before the input message is marshalled into a transport @@ -119,6 +129,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_after_serialization, + BeforeTransmit, " /// A hook called after the input message is marshalled into /// a transport message. @@ -140,6 +151,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_retry_loop, + BeforeTransmit, " A hook called before the retry loop is entered. This method has the ability to modify and return a new transport request @@ -161,6 +173,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_attempt, + BeforeTransmit, " A hook called before each attempt at sending the transmission request message to the service. @@ -187,6 +200,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_signing, + BeforeTransmit, " A hook called before the transport request message is signed. This method has the ability to modify and return a new transport @@ -218,6 +232,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_signing, + BeforeTransmit, " A hook called before the transport request message is signed. @@ -241,6 +256,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_after_signing, + BeforeTransmit, " A hook called after the transport request message is signed. @@ -264,6 +280,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_transmit, + BeforeTransmit, " /// A hook called before the transport request message is sent to the /// service. This method has the ability to modify and return @@ -295,6 +312,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_transmit, + BeforeTransmit, " A hook called before the transport request message is sent to the service. @@ -322,6 +340,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_after_transmit, + BeforeDeserialization, " A hook called after the transport request message is sent to the service and a transport response message is received. @@ -349,6 +368,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_deserialization, + BeforeDeserialization, " A hook called before the transport response message is unmarshalled. This method has the ability to modify and return a new transport @@ -380,6 +400,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_deserialization, + BeforeDeserialization, " A hook called before the transport response message is unmarshalled @@ -406,6 +427,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_after_deserialization, + AfterDeserialization, " A hook called after the transport response message is unmarshalled. @@ -432,6 +454,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_attempt_completion, + AfterDeserialization, " A hook called when an attempt is completed. This method has the ability to modify and return a new output message or error @@ -460,6 +483,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_after_attempt, + AfterDeserialization, " A hook called when an attempt is completed. @@ -488,6 +512,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_completion, + AfterDeserialization, " A hook called when an execution is completed. This method has the ability to modify and return a new @@ -514,6 +539,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_after_execution, + AfterDeserialization, " A hook called when an execution is completed. @@ -546,17 +572,23 @@ pub struct Interceptors { } macro_rules! interceptor_impl_fn { - (context, $name:ident) => { - interceptor_impl_fn!(context, $name, $name); + (context, $name:ident, $phase:ident) => { + interceptor_impl_fn!(context, $name, $name, $phase); }; - (mut context, $name:ident) => { - interceptor_impl_fn!(mut context, $name, $name); + (mut context, $name:ident, $phase:ident) => { + interceptor_impl_fn!(mut context, $name, $name, $phase); }; - (context, $outer_name:ident, $inner_name:ident) => { - interceptor_impl_fn!($outer_name, $inner_name(context: &InterceptorContext)); + (context, $outer_name:ident, $inner_name:ident, $phase:ident) => { + interceptor_impl_fn!( + $outer_name, + $inner_name(context: &InterceptorContext<$phase>) + ); }; - (mut context, $outer_name:ident, $inner_name:ident) => { - interceptor_impl_fn!($outer_name, $inner_name(context: &mut InterceptorContext)); + (mut context, $outer_name:ident, $inner_name:ident, $phase:ident) => { + interceptor_impl_fn!( + $outer_name, + $inner_name(context: &mut InterceptorContext<$phase>) + ); }; ($outer_name:ident, $inner_name:ident ($context:ident : $context_ty:ty)) => { pub fn $outer_name( @@ -601,28 +633,46 @@ impl Interceptors { self } - interceptor_impl_fn!(context, client_read_before_execution, read_before_execution); + interceptor_impl_fn!( + context, + client_read_before_execution, + read_before_execution, + BeforeSerialization + ); interceptor_impl_fn!( context, operation_read_before_execution, - read_before_execution + read_before_execution, + BeforeSerialization + ); + interceptor_impl_fn!( + mut context, + modify_before_serialization, + BeforeSerialization + ); + interceptor_impl_fn!(context, read_before_serialization, BeforeSerialization); + interceptor_impl_fn!(context, read_after_serialization, BeforeTransmit); + interceptor_impl_fn!(mut context, modify_before_retry_loop, BeforeTransmit); + interceptor_impl_fn!(context, read_before_attempt, BeforeTransmit); + interceptor_impl_fn!(mut context, modify_before_signing, BeforeTransmit); + interceptor_impl_fn!(context, read_before_signing, BeforeTransmit); + interceptor_impl_fn!(context, read_after_signing, BeforeTransmit); + interceptor_impl_fn!(mut context, modify_before_transmit, BeforeTransmit); + interceptor_impl_fn!(context, read_before_transmit, BeforeTransmit); + interceptor_impl_fn!(context, read_after_transmit, BeforeDeserialization); + interceptor_impl_fn!( + mut context, + modify_before_deserialization, + BeforeDeserialization + ); + interceptor_impl_fn!(context, read_before_deserialization, BeforeDeserialization); + interceptor_impl_fn!(context, read_after_deserialization, AfterDeserialization); + interceptor_impl_fn!( + mut context, + modify_before_attempt_completion, + AfterDeserialization ); - interceptor_impl_fn!(mut context, modify_before_serialization); - interceptor_impl_fn!(context, read_before_serialization); - interceptor_impl_fn!(context, read_after_serialization); - interceptor_impl_fn!(mut context, modify_before_retry_loop); - interceptor_impl_fn!(context, read_before_attempt); - interceptor_impl_fn!(mut context, modify_before_signing); - interceptor_impl_fn!(context, read_before_signing); - interceptor_impl_fn!(context, read_after_signing); - interceptor_impl_fn!(mut context, modify_before_transmit); - interceptor_impl_fn!(context, read_before_transmit); - interceptor_impl_fn!(context, read_after_transmit); - interceptor_impl_fn!(mut context, modify_before_deserialization); - interceptor_impl_fn!(context, read_before_deserialization); - interceptor_impl_fn!(context, read_after_deserialization); - interceptor_impl_fn!(mut context, modify_before_attempt_completion); - interceptor_impl_fn!(context, read_after_attempt); - interceptor_impl_fn!(mut context, modify_before_completion); - interceptor_impl_fn!(context, read_after_execution); + interceptor_impl_fn!(context, read_after_attempt, AfterDeserialization); + interceptor_impl_fn!(mut context, modify_before_completion, AfterDeserialization); + interceptor_impl_fn!(context, read_after_execution, AfterDeserialization); } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 61d7c7fd5d..309b1ca9d9 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -3,158 +3,802 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::InterceptorError; +//! Interceptor context. +//! +//! Interceptors have access to varying pieces of context during the course of an operation. +//! +//! An operation is composed of multiple phases. The initial phase is [`phase::BeforeSerialization`], which +//! has the original input as context. The next phase is [`phase::BeforeTransmit`], which has the serialized +//! request as context. Depending on which hook is being called with the dispatch context, +//! the serialized request may or may not be signed (which should be apparent from the hook name). +//! Following the [`phase::BeforeTransmit`] phase is the [`phase::BeforeDeserialization`] phase, which has +//! the raw response available as context. Finally, the [`phase::AfterDeserialization`] phase +//! has both the raw and parsed response available. +//! +//! To summarize: +//! 1. [`phase::BeforeSerialization`]: Only has the operation input. +//! 2. [`phase::BeforeTransmit`]: Only has the serialized request. +//! 3. [`phase::BeforeDeserialization`]: Has the raw response. +//! 3. [`phase::AfterDeserialization`]: Has the raw response and the parsed response. +//! +//! When implementing hooks, if information from a previous phase is required, then implement +//! an earlier hook to examine that context, and save off any necessary information into the +//! [`crate::config_bag::ConfigBag`] for later hooks to examine. Interior mutability is **NOT** +//! recommended for storing request-specific information in your interceptor implementation. +//! Use the [`crate::config_bag::ConfigBag`] instead. + +use crate::client::interceptors::BoxError; use crate::client::orchestrator::{HttpRequest, HttpResponse}; -use crate::type_erasure::TypeErasedBox; +use crate::config_bag::ConfigBag; +use crate::type_erasure::{TypeErasedBox, TypeErasedError}; +use aws_smithy_http::result::SdkError; pub type Input = TypeErasedBox; pub type Output = TypeErasedBox; -pub type Error = TypeErasedBox; +pub type Error = TypeErasedError; pub type OutputOrError = Result; type Request = HttpRequest; type Response = HttpResponse; +/// Operation phases. +pub mod phase { + use crate::client::interceptors::context::{Error, Output}; + use crate::client::interceptors::BoxError; + use crate::client::orchestrator::HttpResponse; + use aws_smithy_http::result::{ConnectorError, SdkError}; + + macro_rules! impl_phase { + ($phase:ty, $convert_err:ident) => { + impl Phase for $phase { + fn convert_error( + &self, + error: BoxError, + output_or_error: Option>, + response: Option, + ) -> SdkError { + $convert_err(error, output_or_error, response) + } + } + }; + } + + #[doc(hidden)] + pub trait Phase { + fn convert_error( + &self, + error: BoxError, + output_or_error: Option>, + response: Option, + ) -> SdkError; + } + + fn convert_construction_failure( + error: BoxError, + _: Option>, + _: Option, + ) -> SdkError { + SdkError::construction_failure(error) + } + + fn convert_dispatch_error( + error: BoxError, + _: Option>, + response: Option, + ) -> SdkError { + let error = match error.downcast::() { + Ok(connector_error) => { + return SdkError::dispatch_failure(*connector_error); + } + Err(e) => e, + }; + if let Some(response) = response { + SdkError::response_error(error, response) + } else { + SdkError::dispatch_failure(ConnectorError::other(error, None)) + } + } + + fn convert_response_handling_error( + error: BoxError, + output_or_error: Option>, + response: Option, + ) -> SdkError { + match (response, output_or_error) { + (Some(response), Some(Err(error))) => SdkError::service_error(error, response), + (Some(response), _) => SdkError::response_error(error, response), + _ => unreachable!("phase has a response"), + } + } + + /// Represents the phase of an operation prior to serialization. + #[derive(Default, Debug)] + #[non_exhaustive] + pub struct BeforeSerialization; + impl_phase!(BeforeSerialization, convert_construction_failure); + + #[doc(hidden)] // This one isn't exposed in the interceptors, but is used internally + #[derive(Default, Debug)] + #[non_exhaustive] + pub struct Serialization; + impl_phase!(Serialization, convert_construction_failure); + + /// Represents the phase of an operation prior to transmitting a request over the network. + #[derive(Default, Debug)] + #[non_exhaustive] + pub struct BeforeTransmit; + impl_phase!(BeforeTransmit, convert_dispatch_error); + + #[doc(hidden)] // This one isn't exposed in the interceptors, but is used internally + #[derive(Default, Debug)] + #[non_exhaustive] + pub struct Transmit; + impl_phase!(Transmit, convert_dispatch_error); + + /// Represents the phase of an operation after receiving a response, but before parsing that response. + #[derive(Default, Debug)] + #[non_exhaustive] + pub struct BeforeDeserialization; + impl_phase!(BeforeDeserialization, convert_response_handling_error); + + #[doc(hidden)] // This one isn't exposed in the interceptors, but is used internally + #[derive(Default, Debug)] + #[non_exhaustive] + pub struct Deserialization; + impl_phase!(Deserialization, convert_response_handling_error); + + /// Represents the phase of an operation after parsing a response. + #[derive(Default, Debug)] + #[non_exhaustive] + pub struct AfterDeserialization; + impl_phase!(AfterDeserialization, convert_response_handling_error); +} + /// A container for the data currently available to an interceptor. -pub struct InterceptorContext { - input: Option, - output_or_error: Option, +/// +/// Different context is available based on which phase the operation is currently in. For example, +/// context in the [`phase::BeforeSerialization`] phase won't have a `request` yet since the input hasn't been +/// serialized at that point. But once it gets into the [`phase::BeforeTransmit`] phase, the `request` will be set. +pub struct InterceptorContext { + input: Option, + output_or_error: Option>, request: Option, response: Option, + phase: Phase, } -// TODO(interceptors) we could use types to ensure that people calling methods on interceptor context can't access -// field that haven't been set yet. -impl InterceptorContext { - pub fn new(input: Input) -> Self { - Self { +// +// All phases +// +impl InterceptorContext<(), Input, Output, Error> { + /// Creates a new interceptor context in the [`phase::BeforeSerialization`] phase. + pub fn new( + input: Input, + ) -> InterceptorContext { + InterceptorContext { input: Some(input), output_or_error: None, request: None, response: None, + phase: Default::default(), } } +} +impl InterceptorContext { + /// Decomposes the context into its constituent parts. + #[doc(hidden)] + #[allow(clippy::type_complexity)] + pub fn into_parts( + self, + ) -> ( + Option, + Option>, + Option, + Option, + Phase, + ) { + ( + self.input, + self.output_or_error, + self.request, + self.response, + self.phase, + ) + } +} +// +// BeforeSerialization phase methods +// +impl InterceptorContext { /// Retrieve the input for the operation being invoked. - pub fn input(&self) -> Result<&Input, InterceptorError> { + pub fn input(&self) -> &I { self.input .as_ref() - .ok_or_else(InterceptorError::invalid_input_access) + .expect("input is present in phase::BeforeSerialization") } /// Retrieve the input for the operation being invoked. - pub fn input_mut(&mut self) -> Result<&mut Input, InterceptorError> { + pub fn input_mut(&mut self) -> &mut I { self.input .as_mut() - .ok_or_else(InterceptorError::invalid_input_access) + .expect("input is present in phase::BeforeSerialization") } - /// Takes ownership of the input. + /// Advance to the next phase. #[doc(hidden)] - pub fn take_input(&mut self) -> Option { + pub fn into_serialization_phase(self) -> InterceptorContext { + InterceptorContext { + input: self.input, + output_or_error: self.output_or_error, + request: self.request, + response: self.response, + phase: phase::Serialization::default(), + } + } +} + +// +// Serialization phase methods +// +impl InterceptorContext { + /// Takes ownership of the input. + pub fn take_input(&mut self) -> Option { self.input.take() } + pub fn set_request(&mut self, request: Request) { + debug_assert!( + self.request.is_none(), + "called set_request but a request was already set" + ); + self.request = Some(request); + } + + /// Advance to the next phase. + #[doc(hidden)] + pub fn into_before_transmit_phase(self) -> InterceptorContext { + debug_assert!( + self.input.is_none(), + "input must be taken before going into phase::BeforeTransmit" + ); + debug_assert!( + self.request.is_some(), + "request must be set before going into phase::BeforeTransmit" + ); + InterceptorContext { + input: self.input, + output_or_error: self.output_or_error, + request: self.request, + response: self.response, + phase: Default::default(), + } + } +} + +// +// BeforeTransmit phase methods +// +impl InterceptorContext { + /// Creates a new interceptor context in the [`phase::BeforeTransmit`] phase. + pub fn new( + input: Option, + request: HttpRequest, + ) -> InterceptorContext { + InterceptorContext { + input, + output_or_error: None, + request: Some(request), + response: None, + phase: Default::default(), + } + } + /// Retrieve the transmittable request for the operation being invoked. /// This will only be available once request marshalling has completed. - pub fn request(&self) -> Result<&Request, InterceptorError> { + pub fn request(&self) -> &Request { self.request .as_ref() - .ok_or_else(InterceptorError::invalid_request_access) + .expect("request populated in phase::BeforeTransmit") } /// Retrieve the transmittable request for the operation being invoked. /// This will only be available once request marshalling has completed. - pub fn request_mut(&mut self) -> Result<&mut Request, InterceptorError> { + pub fn request_mut(&mut self) -> &mut Request { self.request .as_mut() - .ok_or_else(InterceptorError::invalid_request_access) + .expect("request populated in phase::BeforeTransmit") } + #[doc(hidden)] + pub fn into_transmit_phase(self) -> InterceptorContext { + InterceptorContext { + input: self.input, + output_or_error: self.output_or_error, + request: self.request, + response: self.response, + phase: Default::default(), + } + } +} + +// +// Transmit phase methods +// +impl InterceptorContext { /// Takes ownership of the request. #[doc(hidden)] - pub fn take_request(&mut self) -> Option { - self.request.take() + pub fn take_request(&mut self) -> Request { + debug_assert!(self.request.is_some()); + self.request + .take() + .expect("take request once during transmit") + } + + #[doc(hidden)] + pub fn set_response(&mut self, response: Response) { + debug_assert!( + self.response.is_none(), + "called set_response but a response was already set" + ); + self.response = Some(response); } - /// Retrieve the response to the transmittable response for the operation - /// being invoked. This will only be available once transmission has - /// completed. - pub fn response(&self) -> Result<&Response, InterceptorError> { + #[doc(hidden)] + pub fn into_before_deserialization_phase( + self, + ) -> InterceptorContext { + debug_assert!( + self.request.is_none(), + "request must be taken before going into phase::BeforeDeserialization" + ); + debug_assert!( + self.response.is_some(), + "response must be set to before going into phase::BeforeDeserialization" + ); + InterceptorContext { + input: self.input, + output_or_error: self.output_or_error, + request: self.request, + response: self.response, + phase: Default::default(), + } + } +} + +impl InterceptorContext { + /// Returns the response. + pub fn response(&self) -> &Response { + self.response + .as_ref() + .expect("response set in phase::BeforeDeserialization") + } + + /// Returns a mutable reference to the response. + pub fn response_mut(&mut self) -> &mut Response { + self.response + .as_mut() + .expect("response set in phase::BeforeDeserialization") + } + + #[doc(hidden)] + pub fn into_deserialization_phase(self) -> InterceptorContext { + InterceptorContext { + input: self.input, + output_or_error: self.output_or_error, + request: self.request, + response: self.response, + phase: Default::default(), + } + } +} + +impl InterceptorContext { + /// Returns the response. + pub fn response(&self) -> &Response { self.response .as_ref() - .ok_or_else(InterceptorError::invalid_response_access) + .expect("response set in phase::Deserialization") } - /// Retrieve the response to the transmittable response for the operation - /// being invoked. This will only be available once transmission has - /// completed. - pub fn response_mut(&mut self) -> Result<&mut Response, InterceptorError> { + /// Returns a mutable reference to the response. + pub fn response_mut(&mut self) -> &mut Response { self.response .as_mut() - .ok_or_else(InterceptorError::invalid_response_access) + .expect("response set in phase::Deserialization") + } + + #[doc(hidden)] + pub fn set_output_or_error(&mut self, output: Result) { + debug_assert!(self.output_or_error.is_none()); + self.output_or_error = Some(output); } - /// Retrieve the response to the customer. This will only be available - /// once the `response` has been unmarshalled or the attempt/execution has failed. - pub fn output_or_error(&self) -> Result, InterceptorError> { + #[doc(hidden)] + pub fn into_after_deserialization_phase( + self, + ) -> InterceptorContext { + debug_assert!( + self.output_or_error.is_some(), + "output must be set to before going into phase::AfterDeserialization" + ); + InterceptorContext { + input: self.input, + output_or_error: self.output_or_error, + request: self.request, + response: self.response, + phase: Default::default(), + } + } +} + +impl InterceptorContext { + /// Returns the response. + pub fn response(&self) -> &Response { + self.response + .as_ref() + .expect("response set in phase::BeforeDeserialization") + } + + /// Returns a mutable reference to the response. + pub fn response_mut(&mut self) -> &mut Response { + self.response + .as_mut() + .expect("response set in phase::BeforeDeserialization") + } + + /// Returns the deserialized output or error. + pub fn output_or_error(&self) -> Result<&O, &E> { self.output_or_error .as_ref() - .ok_or_else(InterceptorError::invalid_output_access) - .map(|res| res.as_ref()) + .expect("output set in phase::AfterDeserialization") + .as_ref() } - /// Retrieve the response to the customer. This will only be available - /// once the `response` has been unmarshalled or the - /// attempt/execution has failed. - pub fn output_or_error_mut(&mut self) -> Result<&mut Result, InterceptorError> { + /// Returns the mutable reference to the deserialized output or error. + pub fn output_or_error_mut(&mut self) -> &mut Result { self.output_or_error .as_mut() - .ok_or_else(InterceptorError::invalid_output_access) + .expect("output set in phase::AfterDeserialization") } - // There is no set_input method because that can only be set once, during context construction + #[doc(hidden)] + pub fn finalize(self) -> Result> { + self.output_or_error + .expect("output already populated in the response handling phase") + .map_err(|error| { + SdkError::service_error( + error, + self.response + .expect("raw response already populated in the response handling phase"), + ) + }) + } +} - pub fn set_request(&mut self, request: Request) { - if self.request.is_some() { - panic!("Called set_request but a request was already set. This is a bug. Please report it."); +// This isn't great since it relies on a lot of runtime checking, but the +// compiler doesn't exactly make it easy to handle phase changes in a `loop`. +#[doc(hidden)] +pub struct AttemptCheckpoint { + tainted: bool, + checkpointed_request: Option, + before_transmit: Option>, + transmit: Option>, + before_deserialization: Option>, + deserialization: Option>, + after_deserialization: Option>, +} + +impl AttemptCheckpoint { + pub fn new(before_transmit: InterceptorContext) -> Self { + Self { + tainted: false, + checkpointed_request: Self::try_clone(before_transmit.request()), + before_transmit: Some(before_transmit), + transmit: None, + before_deserialization: None, + deserialization: None, + after_deserialization: None, } + } - self.request = Some(request); + pub fn before_transmit(&mut self) -> &mut InterceptorContext { + self.tainted = true; + self.before_transmit + .as_mut() + .expect("must be in the before transmit phase") } - pub fn set_response(&mut self, response: Response) { - if self.response.is_some() { - panic!("Called set_response but a transmit_response was already set. This is a bug. Please report it."); - } + pub fn transmit(&mut self) -> &mut InterceptorContext { + self.transmit + .as_mut() + .expect("must be in the transmit phase") + } - self.response = Some(response); + pub fn before_deser(&mut self) -> &mut InterceptorContext { + self.before_deserialization + .as_mut() + .expect("must be in the before deserialization phase") } - pub fn set_output_or_error(&mut self, output: Result) { - if self.output_or_error.is_some() { - panic!( - "Called set_output but an output was already set. This is a bug. Please report it." - ); + pub fn deser(&mut self) -> &mut InterceptorContext { + self.deserialization + .as_mut() + .expect("must be in the deserialization phase") + } + + pub fn after_deser(&mut self) -> &mut InterceptorContext { + self.after_deserialization + .as_mut() + .expect("must be in the after deserialization phase") + } + + pub fn transition_to_transmit(&mut self) { + self.transmit = Some( + self.before_transmit + .take() + .expect("must be in the before transmit phase") + .into_transmit_phase(), + ); + } + + pub fn transition_to_deserialization(&mut self) { + self.deserialization = Some( + self.before_deserialization + .take() + .expect("must be in the before deserialization phase") + .into_deserialization_phase(), + ) + } + + pub fn transition_to_before_deserialization(&mut self) { + self.before_deserialization = Some( + self.transmit + .take() + .expect("must be in the transmit phase") + .into_before_deserialization_phase(), + ) + } + + pub fn transition_to_after_deserialization(&mut self) { + self.after_deserialization = Some( + self.deserialization + .take() + .expect("must be in the deserialization phase") + .into_after_deserialization_phase(), + ) + } + + // Returns false if rewinding isn't possible + pub fn rewind(&mut self, _cfg: &mut ConfigBag) -> bool { + // If before transmit was never touched, then we don't need to rewind + if !self.tainted { + return true; } + // If checkpointed_request was never set, then this is not a retryable request + if self.checkpointed_request.is_none() { + return false; + } + // Otherwise, rewind back to the beginning of BeforeTransmit + // TODO(enableNewSmithyRuntime): Also rewind the ConfigBag + fn into_input(context: InterceptorContext

) -> Option { + context.into_parts().0 + } + // Take the input from the current phase + let input = None + .or(self.before_transmit.take().map(into_input)) + .or(self.transmit.take().map(into_input)) + .or(self.before_deserialization.take().map(into_input)) + .or(self.deserialization.take().map(into_input)) + .or(self.after_deserialization.take().map(into_input)) + .expect("at least one phase must be in progress"); + let fresh_request = + Self::try_clone(self.checkpointed_request.as_ref().expect("checked above")) + .expect("cloneable request"); + self.before_transmit = Some(InterceptorContext::::new( + input, + fresh_request, + )); + true + } - self.output_or_error = Some(output); + pub fn into_error(self, reason: BoxError) -> SdkError { + fn err( + context: InterceptorContext

, + ) -> Box SdkError> { + Box::new(move |reason| { + let (_input, output_or_error, _request, response, phase) = context.into_parts(); + phase.convert_error(reason, output_or_error, response) + }) + } + // Convert the current phase into an error + (None + .or(self.before_transmit.map(err)) + .or(self.transmit.map(err)) + .or(self.before_deserialization.map(err)) + .or(self.deserialization.map(err)) + .or(self.after_deserialization.map(err)) + .expect("at least one phase must be in progress"))(reason) } - #[doc(hidden)] - pub fn into_parts( - self, - ) -> ( - Option, - Option, - Option, - Option, - ) { - ( - self.input, - self.output_or_error, - self.request, - self.response, + pub fn finalize(self) -> Result> { + self.after_deserialization + .expect("must be in the after deserialization phase") + .finalize() + } + + pub fn try_clone(request: &HttpRequest) -> Option { + let cloned_body = request.body().try_clone()?; + let mut cloned_request = ::http::Request::builder() + .uri(request.uri().clone()) + .method(request.method()); + *cloned_request + .headers_mut() + .expect("builder has not been modified, headers must be valid") = + request.headers().clone(); + Some( + cloned_request + .body(cloned_body) + .expect("a clone of a valid request should be a valid request"), ) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::type_erasure::TypedBox; + use aws_smithy_http::body::SdkBody; + use http::header::{AUTHORIZATION, CONTENT_LENGTH}; + use http::{HeaderValue, Uri}; + + #[test] + fn test_success_transitions() { + let input = TypedBox::new("input".to_string()).erase(); + let output = TypedBox::new("output".to_string()).erase(); + + let mut context = InterceptorContext::<()>::new(input); + assert_eq!("input", context.input().downcast_ref::().unwrap()); + context.input_mut(); + + let mut context = context.into_serialization_phase(); + let _ = context.take_input(); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + let mut checkpoint = AttemptCheckpoint::new(context.into_before_transmit_phase()); + checkpoint.before_transmit().request(); + checkpoint.before_transmit().request_mut(); + + checkpoint.transition_to_transmit(); + let _ = checkpoint.transmit().take_request(); + checkpoint + .transmit() + .set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); + + checkpoint.transition_to_before_deserialization(); + checkpoint.before_deser().response(); + checkpoint.before_deser().response_mut(); + + checkpoint.transition_to_deserialization(); + checkpoint.deser().response(); + checkpoint.deser().response_mut(); + checkpoint.deser().set_output_or_error(Ok(output)); + + checkpoint.transition_to_after_deserialization(); + checkpoint.after_deser().response(); + checkpoint.after_deser().response_mut(); + let _ = checkpoint.after_deser().output_or_error(); + let _ = checkpoint.after_deser().output_or_error_mut(); + + let output = checkpoint.finalize().expect("success"); + assert_eq!("output", output.downcast_ref::().unwrap()); + } + + #[test] + fn test_rewind_for_retry() { + use std::fmt; + #[derive(Debug)] + struct Error; + impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("dontcare") + } + } + impl std::error::Error for Error {} + + let mut cfg = ConfigBag::base(); + let input = TypedBox::new("input".to_string()).erase(); + let output = TypedBox::new("output".to_string()).erase(); + let error = TypedBox::new(Error).erase_error(); + + let context = InterceptorContext::<()>::new(input); + assert_eq!("input", context.input().downcast_ref::().unwrap()); + + let mut context = context.into_serialization_phase(); + let _ = context.take_input(); + context.set_request( + http::Request::builder() + .header("test", "the-original-unmutated-request") + .body(SdkBody::empty()) + .unwrap(), + ); + + let mut checkpoint = AttemptCheckpoint::new(context.into_before_transmit_phase()); + + // Modify the test header post-checkpoint to simulate modifying the request for signing or a mutating interceptor + checkpoint + .before_transmit() + .request_mut() + .headers_mut() + .remove("test"); + checkpoint + .before_transmit() + .request_mut() + .headers_mut() + .insert( + "test", + HeaderValue::from_static("request-modified-after-signing"), + ); + + checkpoint.transition_to_transmit(); + let request = checkpoint.transmit().take_request(); + assert_eq!( + "request-modified-after-signing", + request.headers().get("test").unwrap() + ); + checkpoint + .transmit() + .set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); + + checkpoint.transition_to_before_deserialization(); + checkpoint.transition_to_deserialization(); + checkpoint.deser().set_output_or_error(Err(error)); + + assert!(checkpoint.rewind(&mut cfg)); + + // Now after rewinding, the test header should be its original value + assert_eq!( + "the-original-unmutated-request", + checkpoint + .before_transmit() + .request() + .headers() + .get("test") + .unwrap() + ); + + checkpoint.transition_to_transmit(); + let _ = checkpoint.transmit().take_request(); + checkpoint + .transmit() + .set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); + + checkpoint.transition_to_before_deserialization(); + checkpoint.transition_to_deserialization(); + checkpoint.deser().set_output_or_error(Ok(output)); + + checkpoint.transition_to_after_deserialization(); + + let output = checkpoint.finalize().expect("success"); + assert_eq!("output", output.downcast_ref::().unwrap()); + } + + #[test] + fn try_clone_clones_all_data() { + let request = ::http::Request::builder() + .uri(Uri::from_static("http://www.amazon.com")) + .method("POST") + .header(CONTENT_LENGTH, 456) + .header(AUTHORIZATION, "Token: hello") + .body(SdkBody::from("hello world!")) + .expect("valid request"); + let cloned = AttemptCheckpoint::try_clone(&request).expect("request is cloneable"); + + assert_eq!(&Uri::from_static("http://www.amazon.com"), cloned.uri()); + assert_eq!("POST", cloned.method()); + assert_eq!(2, cloned.headers().len()); + assert_eq!("Token: hello", cloned.headers().get(AUTHORIZATION).unwrap(),); + assert_eq!("456", cloned.headers().get(CONTENT_LENGTH).unwrap()); + assert_eq!("hello world!".as_bytes(), cloned.body().bytes().unwrap()); + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 30751d2120..404d03b697 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -5,7 +5,7 @@ use crate::client::auth::{AuthOptionResolver, AuthOptionResolverParams, HttpAuthSchemes}; use crate::client::identity::IdentityResolvers; -use crate::client::interceptors::context::{Input, OutputOrError}; +use crate::client::interceptors::context::{Error, Input, Output}; use crate::client::retries::RetryClassifiers; use crate::client::retries::RetryStrategy; use crate::config_bag::ConfigBag; @@ -26,21 +26,17 @@ pub type BoxError = Box; pub type BoxFuture = Pin>>>; pub type Future = NowOrLater, BoxFuture>; -pub trait TraceProbe: Send + Sync + fmt::Debug { - fn dispatch_events(&self); -} - pub trait RequestSerializer: Send + Sync + fmt::Debug { fn serialize_input(&self, input: Input) -> Result; } pub trait ResponseDeserializer: Send + Sync + fmt::Debug { - fn deserialize_streaming(&self, response: &mut HttpResponse) -> Option { + fn deserialize_streaming(&self, response: &mut HttpResponse) -> Option> { let _ = response; None } - fn deserialize_nonstreaming(&self, response: &HttpResponse) -> OutputOrError; + fn deserialize_nonstreaming(&self, response: &HttpResponse) -> Result; } pub trait Connection: Send + Sync + fmt::Debug { @@ -138,9 +134,6 @@ pub trait ConfigBagAccessors { fn retry_strategy(&self) -> &dyn RetryStrategy; fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static); - fn trace_probe(&self) -> &dyn TraceProbe; - fn set_trace_probe(&mut self, trace_probe: impl TraceProbe + 'static); - fn request_time(&self) -> Option; fn set_request_time(&mut self, request_time: RequestTime); @@ -263,16 +256,6 @@ impl ConfigBagAccessors for ConfigBag { self.put::>(Box::new(retry_strategy)); } - fn trace_probe(&self) -> &dyn TraceProbe { - &**self - .get::>() - .expect("missing trace probe") - } - - fn set_trace_probe(&mut self, trace_probe: impl TraceProbe + 'static) { - self.put::>(Box::new(trace_probe)); - } - fn request_time(&self) -> Option { self.get::().cloned() } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index 1331317afe..0d6678d359 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::client::interceptors::context::phase::AfterDeserialization; use crate::client::interceptors::context::Error; use crate::client::interceptors::InterceptorContext; use crate::client::orchestrator::BoxError; @@ -23,7 +24,7 @@ pub trait RetryStrategy: Send + Sync + Debug { fn should_attempt_retry( &self, - context: &InterceptorContext, + context: &InterceptorContext, cfg: &ConfigBag, ) -> Result; } diff --git a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs b/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs index 4c20c6c4d4..f2269c1c44 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs @@ -3,7 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::any::{type_name, Any}; +use std::any::Any; +use std::error::Error as StdError; use std::fmt; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; @@ -62,13 +63,24 @@ where } } +impl TypedBox +where + T: StdError + fmt::Debug + Send + Sync + 'static, +{ + /// Converts `TypedBox` to a `TypeErasedError` where `T` implements `Error`. + pub fn erase_error(self) -> TypeErasedError { + let inner = self.inner.downcast::().expect("typechecked"); + TypeErasedError::new(inner) + } +} + impl fmt::Debug for TypedBox where T: Send + Sync + 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("TypedBox:")?; - (self.inner.debug)(&self.inner, f) + (self.inner.debug)(&self.inner.field, f) } } @@ -89,56 +101,125 @@ impl DerefMut for TypedBox { /// A new-type around `Box` pub struct TypeErasedBox { field: Box, - #[allow(dead_code)] - type_name: &'static str, #[allow(clippy::type_complexity)] - debug: Box) -> fmt::Result + Send + Sync>, + debug: Box< + dyn Fn(&Box, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, + >, } impl fmt::Debug for TypeErasedBox { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("TypeErasedBox:")?; - (self.debug)(self, f) + (self.debug)(&self.field, f) } } impl TypeErasedBox { pub fn new(value: T) -> Self { - let debug = |value: &TypeErasedBox, f: &mut fmt::Formatter<'_>| { + let debug = |value: &Box, f: &mut fmt::Formatter<'_>| { fmt::Debug::fmt(value.downcast_ref::().expect("typechecked"), f) }; - let name = type_name::(); Self { field: Box::new(value), - type_name: name, debug: Box::new(debug), } } // Downcast into a `Box`, or return `Self` if it is not a `T`. pub fn downcast(self) -> Result, Self> { - let TypeErasedBox { + let TypeErasedBox { field, debug } = self; + field.downcast().map_err(|field| Self { field, debug }) + } + + /// Downcast as a `&T`, or return `None` if it is not a `T`. + pub fn downcast_ref(&self) -> Option<&T> { + self.field.downcast_ref() + } + + /// Downcast as a `&mut T`, or return `None` if it is not a `T`. + pub fn downcast_mut(&mut self) -> Option<&mut T> { + self.field.downcast_mut() + } +} + +impl From for TypeErasedBox { + fn from(value: TypeErasedError) -> Self { + TypeErasedBox { + field: value.field, + debug: value.debug, + } + } +} + +/// A new-type around `Box` that also implements `Error` +pub struct TypeErasedError { + field: Box, + #[allow(clippy::type_complexity)] + debug: Box< + dyn Fn(&Box, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, + >, + #[allow(clippy::type_complexity)] + as_error: Box Fn(&'a TypeErasedError) -> &'a (dyn StdError) + Send + Sync>, +} + +impl fmt::Debug for TypeErasedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("TypeErasedError:")?; + (self.debug)(&self.field, f) + } +} + +impl fmt::Display for TypeErasedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt((self.as_error)(self), f) + } +} + +impl StdError for TypeErasedError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + (self.as_error)(self).source() + } +} + +impl TypeErasedError { + pub fn new(value: T) -> Self { + let debug = |value: &Box, f: &mut fmt::Formatter<'_>| { + fmt::Debug::fmt(value.downcast_ref::().expect("typechecked"), f) + }; + Self { + field: Box::new(value), + debug: Box::new(debug), + as_error: Box::new(|value: &TypeErasedError| { + value.downcast_ref::().expect("typechecked") as _ + }), + } + } + + // Downcast into a `Box`, or return `Self` if it is not a `T`. + pub fn downcast( + self, + ) -> Result, Self> { + let TypeErasedError { field, - type_name, debug, + as_error, } = self; - match field.downcast() { - Ok(t) => Ok(t), - Err(s) => Err(Self { - field: s, - type_name, - debug, - }), - } + field.downcast().map_err(|field| Self { + field, + debug, + as_error, + }) } /// Downcast as a `&T`, or return `None` if it is not a `T`. - pub fn downcast_ref(&self) -> Option<&T> { + pub fn downcast_ref(&self) -> Option<&T> { self.field.downcast_ref() } /// Downcast as a `&mut T`, or return `None` if it is not a `T`. - pub fn downcast_mut(&mut self) -> Option<&mut T> { + pub fn downcast_mut( + &mut self, + ) -> Option<&mut T> { self.field.downcast_mut() } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index c0f4b4f06b..b174122dc8 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -6,10 +6,12 @@ use self::auth::orchestrate_auth; use crate::client::orchestrator::endpoints::orchestrate_endpoint; use crate::client::orchestrator::http::read_body; -use crate::client::orchestrator::phase::Phase; use crate::client::timeout::{MaybeTimeout, ProvideMaybeTimeoutConfig, TimeoutKind}; use aws_smithy_http::result::SdkError; -use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output}; +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeSerialization; +use aws_smithy_runtime_api::client::interceptors::context::{ + AttemptCheckpoint, Error, Input, Output, +}; use aws_smithy_runtime_api::client::interceptors::{InterceptorContext, Interceptors}; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpResponse}; use aws_smithy_runtime_api::client::retries::ShouldAttempt; @@ -21,18 +23,46 @@ mod auth; /// Defines types that implement a trait for endpoint resolution pub mod endpoints; mod http; -pub(self) mod phase; -pub async fn invoke( - input: Input, - runtime_plugins: &RuntimePlugins, -) -> Result> { - invoke_pre_config(input, runtime_plugins) - .instrument(debug_span!("invoke")) - .await +#[doc(hidden)] +#[macro_export] +macro_rules! handle_err { + ([$checkpoint:expr] => $expr:expr) => { + match $expr { + Ok(ok) => ok, + Err(err) => { + return Err($checkpoint.into_error(err.into())); + } + } + }; + ($ctx:expr => $expr:expr) => { + match $expr { + Ok(ok) => ok, + Err(err) => { + use aws_smithy_runtime_api::client::interceptors::context::phase::Phase; + let (_input, output_or_error, _request, response, phase) = $ctx.into_parts(); + return Err(phase.convert_error(err.into(), output_or_error, response)); + } + } + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! bail { + ([$checkpoint:expr], $reason:expr) => {{ + return Err($checkpoint.into_error($reason.into())); + }}; + ($ctx:expr, $reason:expr) => {{ + use aws_smithy_runtime_api::client::interceptors::context::phase::Phase; + let reason: BoxError = $reason.into(); + let (_input, output_or_error, _request, response, phase) = $ctx.into_parts(); + return Err(phase.convert_error(reason, output_or_error, response)); + }}; } -async fn invoke_pre_config( +#[tracing::instrument(skip_all)] +pub async fn invoke( input: Input, runtime_plugins: &RuntimePlugins, ) -> Result> { @@ -40,15 +70,14 @@ async fn invoke_pre_config( let cfg = &mut cfg; let mut interceptors = Interceptors::new(); + let context = InterceptorContext::<()>::new(input); - let context = Phase::construction(InterceptorContext::new(input)) - // Client configuration - .include(|_| runtime_plugins.apply_client_configuration(cfg, &mut interceptors))? - .include(|ctx| interceptors.client_read_before_execution(ctx, cfg))? - // Operation configuration - .include(|_| runtime_plugins.apply_operation_configuration(cfg, &mut interceptors))? - .include(|ctx| interceptors.operation_read_before_execution(ctx, cfg))? - .finish(); + // Client configuration + handle_err!(context => runtime_plugins.apply_client_configuration(cfg, &mut interceptors)); + handle_err!(context => interceptors.client_read_before_execution(&context, cfg)); + // Operation configuration + handle_err!(context => runtime_plugins.apply_operation_configuration(cfg, &mut interceptors)); + handle_err!(context => interceptors.operation_read_before_execution(&context, cfg)); let operation_timeout_config = cfg.maybe_timeout_config(TimeoutKind::Operation); invoke_post_config(cfg, context, interceptors) @@ -58,26 +87,26 @@ async fn invoke_pre_config( async fn invoke_post_config( cfg: &mut ConfigBag, - context: InterceptorContext, + mut before_serialization: InterceptorContext, interceptors: Interceptors, ) -> Result> { - let context = Phase::construction(context) - // Before serialization - .include(|ctx| interceptors.read_before_serialization(ctx, cfg))? - .include_mut(|ctx| interceptors.modify_before_serialization(ctx, cfg))? - // Serialization - .include_mut(|ctx| { - let request_serializer = cfg.request_serializer(); - let request = request_serializer - .serialize_input(ctx.take_input().expect("input set at this point"))?; - ctx.set_request(request); - Result::<(), BoxError>::Ok(()) - })? - // After serialization - .include(|ctx| interceptors.read_after_serialization(ctx, cfg))? - // Before retry loop - .include_mut(|ctx| interceptors.modify_before_retry_loop(ctx, cfg))? - .finish(); + // Before serialization + handle_err!(before_serialization => interceptors.read_before_serialization(&before_serialization, cfg)); + handle_err!(before_serialization => interceptors.modify_before_serialization(&mut before_serialization, cfg)); + + // Serialization + let mut serialization = before_serialization.into_serialization_phase(); + { + let request_serializer = cfg.request_serializer(); + let request = handle_err!(serialization => request_serializer + .serialize_input(serialization.take_input().expect("input set at this point"))); + serialization.set_request(request); + } + + // Before transmit + let mut before_transmit = serialization.into_before_transmit_phase(); + handle_err!(before_transmit => interceptors.read_after_serialization(&before_transmit, cfg)); + handle_err!(before_transmit => interceptors.modify_before_retry_loop(&mut before_transmit, cfg)); { let retry_strategy = cfg.retry_strategy(); @@ -86,97 +115,89 @@ async fn invoke_post_config( Ok(ShouldAttempt::Yes) => {} // No, this request shouldn't be sent Ok(ShouldAttempt::No) => { - return Err(Phase::dispatch(context).fail( - "The retry strategy indicates that an initial request shouldn't be made, but it didn't specify why.", - )) + bail!(before_transmit, "The retry strategy indicates that an initial request shouldn't be made, but it didn't specify why."); } // No, we shouldn't make a request because... - Err(err) => return Err(Phase::dispatch(context).fail(err)), + Err(err) => bail!(before_transmit, err), Ok(ShouldAttempt::YesAfterDelay(_)) => { unreachable!("Delaying the initial request is currently unsupported. If this feature is important to you, please file an issue in GitHub.") } } } - let mut context = context; - let handling_phase = loop { + let mut checkpoint = AttemptCheckpoint::new(before_transmit); + checkpoint = loop { + if !checkpoint.rewind(cfg) { + break checkpoint; + } let attempt_timeout_config = cfg.maybe_timeout_config(TimeoutKind::OperationAttempt); - let dispatch_phase = Phase::dispatch(context); - context = make_an_attempt(dispatch_phase, cfg, &interceptors) - .instrument(debug_span!("make_an_attempt")) + + checkpoint = make_an_attempt(checkpoint, cfg, &interceptors) .maybe_timeout_with_config(attempt_timeout_config) - .await? - .include(|ctx| interceptors.read_after_attempt(ctx, cfg))? - .include_mut(|ctx| interceptors.modify_before_attempt_completion(ctx, cfg))? - .finish(); + .await?; + handle_err!([checkpoint] => interceptors.read_after_attempt(checkpoint.after_deser(), cfg)); + handle_err!([checkpoint] => interceptors.modify_before_attempt_completion(checkpoint.after_deser(), cfg)); let retry_strategy = cfg.retry_strategy(); - match retry_strategy.should_attempt_retry(&context, cfg) { + match retry_strategy.should_attempt_retry(checkpoint.after_deser(), cfg) { // Yes, let's retry the request Ok(ShouldAttempt::Yes) => continue, // No, this request shouldn't be retried Ok(ShouldAttempt::No) => {} Ok(ShouldAttempt::YesAfterDelay(_delay)) => { + // TODO(enableNewSmithyRuntime): implement retries with explicit delay todo!("implement retries with an explicit delay.") } // I couldn't determine if the request should be retried because an error occurred. - Err(err) => { - return Err(Phase::response_handling(context).fail(err)); - } + Err(err) => bail!([checkpoint], err), } - let handling_phase = Phase::response_handling(context) - .include_mut(|ctx| interceptors.modify_before_completion(ctx, cfg))?; - cfg.trace_probe().dispatch_events(); - - break handling_phase.include(|ctx| interceptors.read_after_execution(ctx, cfg))?; + break checkpoint; }; - handling_phase.finalize() + handle_err!([checkpoint] => interceptors.modify_before_completion(checkpoint.after_deser(), cfg)); + handle_err!([checkpoint] => interceptors.read_after_execution(checkpoint.after_deser(), cfg)); + + checkpoint.finalize() } // Making an HTTP request can fail for several reasons, but we still need to // call lifecycle events when that happens. Therefore, we define this // `make_an_attempt` function to make error handling simpler. +#[tracing::instrument(skip_all)] async fn make_an_attempt( - dispatch_phase: Phase, + mut checkpoint: AttemptCheckpoint, cfg: &mut ConfigBag, interceptors: &Interceptors, -) -> Result> { - let dispatch_phase = dispatch_phase - .include(|ctx| interceptors.read_before_attempt(ctx, cfg))? - .include_mut(|ctx| orchestrate_endpoint(ctx, cfg))? - .include_mut(|ctx| interceptors.modify_before_signing(ctx, cfg))? - .include(|ctx| interceptors.read_before_signing(ctx, cfg))?; +) -> Result> { + handle_err!([checkpoint] => interceptors.read_before_attempt(checkpoint.before_transmit(), cfg)); + handle_err!([checkpoint] => orchestrate_endpoint(checkpoint.before_transmit(), cfg)); + handle_err!([checkpoint] => interceptors.modify_before_signing(checkpoint.before_transmit(), cfg)); + handle_err!([checkpoint] => interceptors.read_before_signing(checkpoint.before_transmit(), cfg)); - let dispatch_phase = orchestrate_auth(dispatch_phase, cfg).await?; + checkpoint = orchestrate_auth(checkpoint, cfg).await?; - let mut context = dispatch_phase - .include(|ctx| interceptors.read_after_signing(ctx, cfg))? - .include_mut(|ctx| interceptors.modify_before_transmit(ctx, cfg))? - .include(|ctx| interceptors.read_before_transmit(ctx, cfg))? - .finish(); + handle_err!([checkpoint] => interceptors.read_after_signing(checkpoint.before_transmit(), cfg)); + handle_err!([checkpoint] => interceptors.modify_before_transmit(checkpoint.before_transmit(), cfg)); + handle_err!([checkpoint] => interceptors.read_before_transmit(checkpoint.before_transmit(), cfg)); // The connection consumes the request but we need to keep a copy of it // within the interceptor context, so we clone it here. - let call_result = { - let request = context.take_request().expect("request has been set"); - let connection = cfg.connection(); - connection.call(request).await - }; - - let mut context = Phase::dispatch(context) - .include_mut(move |ctx| { - ctx.set_response(call_result?); - Result::<(), BoxError>::Ok(()) - })? - .include(|ctx| interceptors.read_after_transmit(ctx, cfg))? - .include_mut(|ctx| interceptors.modify_before_deserialization(ctx, cfg))? - .include(|ctx| interceptors.read_before_deserialization(ctx, cfg))? - .finish(); - - let output_or_error = { - let response = context.response_mut().expect("response has been set"); + checkpoint.transition_to_transmit(); + let call_result = handle_err!([checkpoint] => { + let request = checkpoint.transmit().take_request(); + cfg.connection().call(request).await + }); + checkpoint.transmit().set_response(call_result); + checkpoint.transition_to_before_deserialization(); + + handle_err!([checkpoint] => interceptors.read_after_transmit(checkpoint.before_deser(), cfg)); + handle_err!([checkpoint] => interceptors.modify_before_deserialization(checkpoint.before_deser(), cfg)); + handle_err!([checkpoint] => interceptors.read_before_deserialization(checkpoint.before_deser(), cfg)); + + checkpoint.transition_to_deserialization(); + let output_or_error = handle_err!([checkpoint] => { + let response = checkpoint.deser().response_mut(); let response_deserializer = cfg.response_deserializer(); match response_deserializer.deserialize_streaming(response) { Some(output_or_error) => Ok(output_or_error), @@ -185,12 +206,12 @@ async fn make_an_attempt( .await .map(|_| response_deserializer.deserialize_nonstreaming(response)), } - }; + }); + + checkpoint.deser().set_output_or_error(output_or_error); + + checkpoint.transition_to_after_deserialization(); + handle_err!([checkpoint] => interceptors.read_after_deserialization(checkpoint.after_deser(), cfg)); - Phase::response_handling(context) - .include_mut(move |ctx| { - ctx.set_output_or_error(output_or_error?); - Result::<(), BoxError>::Ok(()) - })? - .include(|ctx| interceptors.read_after_deserialization(ctx, cfg)) + Ok(checkpoint) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 9818593f05..374795d3d4 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -3,16 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::phase::Phase; +use crate::client::orchestrator::AttemptCheckpoint; +use crate::{bail, handle_err}; use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::client::interceptors::context::Error; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpResponse}; use aws_smithy_runtime_api::config_bag::ConfigBag; pub(super) async fn orchestrate_auth( - dispatch_phase: Phase, + mut checkpoint: AttemptCheckpoint, cfg: &ConfigBag, -) -> Result> { +) -> Result> { fn construction_failure(err: impl Into) -> SdkError { SdkError::construction_failure(err) } @@ -39,18 +40,17 @@ pub(super) async fn orchestrate_auth( .resolve_identity(cfg) .await .map_err(construction_failure)?; - return dispatch_phase.include_mut(|ctx| { - let request = ctx.request_mut()?; - request_signer.sign_request(request, &identity, cfg)?; - Result::<_, BoxError>::Ok(()) - }); + let request = checkpoint.before_transmit().request_mut(); + handle_err!([checkpoint] => request_signer.sign_request(request, &identity, cfg)); + return Ok(checkpoint); } } } - Err(construction_failure( - "no auth scheme matched auth options. This is a bug. Please file an issue.", - )) + bail!( + [checkpoint], + "no auth scheme matched auth options. This is a bug. Please file an issue." + ); } #[cfg(test)] @@ -117,8 +117,10 @@ mod tests { } let input = TypedBox::new("doesnt-matter").erase(); - let mut context = InterceptorContext::new(input); + let mut context = InterceptorContext::<()>::new(input).into_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = context.take_input(); + let checkpoint = AttemptCheckpoint::new(context.into_before_transmit_phase()); let mut cfg = ConfigBag::base(); cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); @@ -134,17 +136,13 @@ mod tests { .build(), ); - let phase = Phase::dispatch(context); - let context = orchestrate_auth(phase, &cfg) - .await - .expect("success") - .finish(); + let mut checkpoint = orchestrate_auth(checkpoint, &cfg).await.expect("success"); assert_eq!( "success!", - context + checkpoint + .before_transmit() .request() - .unwrap() .headers() .get("Authorization") .unwrap() @@ -160,8 +158,11 @@ mod tests { }; use aws_smithy_runtime_api::client::identity::http::{Login, Token}; - let mut context = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); + let mut context = InterceptorContext::<()>::new(TypedBox::new("doesnt-matter").erase()) + .into_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = context.take_input(); + let checkpoint = AttemptCheckpoint::new(context.into_before_transmit_phase()); let mut cfg = ConfigBag::base(); cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); @@ -183,18 +184,14 @@ mod tests { .build(), ); - let phase = Phase::dispatch(context); - let context = orchestrate_auth(phase, &cfg) - .await - .expect("success") - .finish(); + let mut checkpoint = orchestrate_auth(checkpoint, &cfg).await.expect("success"); assert_eq!( // "YTpi" == "a:b" in base64 "Basic YTpi", - context + checkpoint + .before_transmit() .request() - .unwrap() .headers() .get("Authorization") .unwrap() @@ -207,19 +204,18 @@ mod tests { .build(), ); - let mut context = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); + let mut context = InterceptorContext::<()>::new(TypedBox::new("doesnt-matter").erase()) + .into_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = context.take_input(); + let checkpoint = AttemptCheckpoint::new(context.into_before_transmit_phase()); - let context = orchestrate_auth(Phase::dispatch(context), &cfg) - .await - .expect("success") - .finish(); - + let mut checkpoint = orchestrate_auth(checkpoint, &cfg).await.expect("success"); assert_eq!( "Bearer t", - context + checkpoint + .before_transmit() .request() - .unwrap() .headers() .get("Authorization") .unwrap() diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 89570c1ef6..08551f5bee 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -7,6 +7,7 @@ use aws_smithy_http::endpoint::error::ResolveEndpointError; use aws_smithy_http::endpoint::{ apply_endpoint, EndpointPrefix, ResolveEndpoint, SharedEndpointResolver, }; +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, EndpointResolver, EndpointResolverParams, HttpRequest, @@ -112,12 +113,12 @@ where } pub(super) fn orchestrate_endpoint( - ctx: &mut InterceptorContext, + ctx: &mut InterceptorContext, cfg: &ConfigBag, ) -> Result<(), BoxError> { let params = cfg.endpoint_resolver_params(); let endpoint_prefix = cfg.get::(); - let request = ctx.request_mut()?; + let request = ctx.request_mut(); let endpoint_resolver = cfg.endpoint_resolver(); endpoint_resolver.resolve_and_apply_endpoint(params, endpoint_prefix, request)?; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs deleted file mode 100644 index 9e101050b7..0000000000 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/phase.rs +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_smithy_http::result::{ConnectorError, SdkError}; -use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; -use aws_smithy_runtime_api::client::interceptors::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpResponse}; - -#[derive(Copy, Clone, Eq, PartialEq)] -enum OrchestrationPhase { - Construction, - Dispatch, - ResponseHandling, -} - -pub(super) struct Phase { - phase: OrchestrationPhase, - context: InterceptorContext, -} - -impl Phase { - pub(crate) fn construction(context: InterceptorContext) -> Self { - Self::start(OrchestrationPhase::Construction, context) - } - pub(crate) fn dispatch(context: InterceptorContext) -> Self { - Self::start(OrchestrationPhase::Dispatch, context) - } - pub(crate) fn response_handling(context: InterceptorContext) -> Self { - Self::start(OrchestrationPhase::ResponseHandling, context) - } - - fn start(phase: OrchestrationPhase, context: InterceptorContext) -> Self { - match phase { - OrchestrationPhase::Construction => {} - OrchestrationPhase::Dispatch => {} - OrchestrationPhase::ResponseHandling => debug_assert!(context.response().is_ok()), - } - Self { phase, context } - } - - pub(crate) fn include_mut>( - mut self, - c: impl FnOnce(&mut InterceptorContext) -> Result<(), E>, - ) -> Result> { - match c(&mut self.context) { - Ok(_) => Ok(self), - Err(e) => Err(self.fail(e)), - } - } - - pub(crate) fn include>( - self, - c: impl FnOnce(&InterceptorContext) -> Result<(), E>, - ) -> Result> { - match c(&self.context) { - Ok(_) => Ok(self), - Err(e) => Err(self.fail(e)), - } - } - - pub(crate) fn fail(self, e: impl Into) -> SdkError { - self.into_sdk_error(e.into()) - } - - pub(crate) fn finalize(self) -> Result> { - debug_assert!(self.phase == OrchestrationPhase::ResponseHandling); - let (_input, output_or_error, _request, response) = self.context.into_parts(); - match output_or_error { - Some(output_or_error) => match output_or_error { - Ok(output) => Ok(output), - Err(error) => Err(SdkError::service_error( - error, - response.expect("response must be set by this point"), - )), - }, - None => unreachable!("phase can't get this far without bubbling up a failure"), - } - } - - fn into_sdk_error(self, e: BoxError) -> SdkError { - let e = match e.downcast::() { - Ok(connector_error) => { - debug_assert!( - self.phase == OrchestrationPhase::Dispatch, - "connector errors should only occur during the dispatch phase" - ); - return SdkError::dispatch_failure(*connector_error); - } - Err(e) => e, - }; - let (_input, output_or_error, _request, response) = self.context.into_parts(); - match self.phase { - OrchestrationPhase::Construction => SdkError::construction_failure(e), - OrchestrationPhase::Dispatch => { - if let Some(response) = response { - SdkError::response_error(e, response) - } else { - SdkError::dispatch_failure(ConnectorError::other(e, None)) - } - } - OrchestrationPhase::ResponseHandling => match (response, output_or_error) { - (Some(response), Some(Err(error))) => SdkError::service_error(error, response), - (Some(response), _) => SdkError::response_error(e, response), - _ => unreachable!("response handling phase at least has a response"), - }, - } - } - - pub(crate) fn finish(self) -> InterceptorContext { - self.context - } -} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs index f415af3691..3b1b56ffb7 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_runtime_api::client::interceptors::context::phase::AfterDeserialization; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::BoxError; use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; @@ -24,7 +25,7 @@ impl RetryStrategy for NeverRetryStrategy { fn should_attempt_retry( &self, - _context: &InterceptorContext, + _context: &InterceptorContext, _cfg: &ConfigBag, ) -> Result { Ok(ShouldAttempt::No) From 97be65a59456e51ccdaac14eabaceb9c7a6652d2 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 4 May 2023 14:12:20 -0700 Subject: [PATCH 078/253] Fix compilation when generating both the middleware and orchestrator implementations (#2672) ## Motivation and Context When setting the `enableNewSmithyRuntime` feature gate to `both_default_middleware` or `both_default_orchestrator`, the generated code would fail to compile due to the a checksum response decorator assuming the property bag always existed. This PR makes that decorator aware of the conditional existence of the property bag so that it can elect to not customize the code for the orchestrator use case (which will ultimately replace the decorator with an interceptor). ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../rustsdk/HttpRequestChecksumDecorator.kt | 4 +-- .../rustsdk/HttpResponseChecksumDecorator.kt | 4 +++ .../protocol/ProtocolParserGenerator.kt | 30 ++++++++++++++----- .../protocol/ResponseDeserializerGenerator.kt | 2 +- .../protocols/HttpBoundProtocolGenerator.kt | 2 +- .../customize/OperationCustomization.kt | 4 +++ 6 files changed, 34 insertions(+), 12 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt index b72293b1fa..05af48214a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt @@ -42,9 +42,9 @@ class HttpRequestChecksumDecorator : ClientCodegenDecorator { override val name: String = "HttpRequestChecksum" override val order: Byte = 0 - // TODO(enableNewSmithyRuntime): Implement checksumming via interceptor and delete this decorator + // TODO(enableNewSmithyRuntime): Implement checksumming via interceptor and delete this decorator upon launching the orchestrator private fun applies(codegenContext: ClientCodegenContext): Boolean = - codegenContext.smithyRuntimeMode.exclusivelyGenerateMiddleware + codegenContext.smithyRuntimeMode.generateMiddleware override fun operationCustomizations( codegenContext: ClientCodegenContext, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt index cebc4e5cba..5cdfbe073f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt @@ -83,6 +83,10 @@ class HttpResponseChecksumCustomization( } } is OperationSection.MutateOutput -> { + if (!section.propertyBagAvailable) { + return emptySection + } + // CRC32, CRC32C, SHA256, SHA1 -> "crc32", "crc32c", "sha256", "sha1" val responseAlgorithms = checksumTrait.responseAlgorithms .map { algorithm -> algorithm.lowercase() }.joinToString(", ") { algorithm -> "\"$algorithm\"" } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt index 8b247cd917..ec89dbc6ca 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt @@ -59,11 +59,17 @@ class ProtocolParserGenerator( "PropertyBag" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("property_bag::PropertyBag"), ) - fun parseResponseFn(operationShape: OperationShape, customizations: List): RuntimeType { + fun parseResponseFn( + operationShape: OperationShape, + // TODO(enableNewSmithyRuntime): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator + propertyBagAvailable: Boolean, + customizations: List, + ): RuntimeType { val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) val errorSymbol = symbolProvider.symbolForOperationError(operationShape) - return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = "http_response") { fnName -> + val fnNameSuffix = if (propertyBagAvailable) "http_response_with_props" else "http_response" + return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = fnNameSuffix) { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) rustBlockTemplate( "pub fn $fnName(_response_status: u16, _response_headers: &#{http}::header::HeaderMap, _response_body: &[u8]) -> std::result::Result<#{O}, #{E}>", @@ -77,6 +83,7 @@ class ProtocolParserGenerator( outputShape, httpBindingResolver.responseBindings(operationShape), errorSymbol, + propertyBagAvailable, customizations, ) } @@ -84,7 +91,10 @@ class ProtocolParserGenerator( } } - fun parseErrorFn(operationShape: OperationShape, customizations: List): RuntimeType { + fun parseErrorFn( + operationShape: OperationShape, + customizations: List, + ): RuntimeType { val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) val errorSymbol = symbolProvider.symbolForOperationError(operationShape) @@ -143,6 +153,7 @@ class ProtocolParserGenerator( errorShape, httpBindingResolver.errorResponseBindings(errorShape), errorSymbol, + false, listOf( object : OperationCustomization() { override fun section(section: OperationSection): Writable = { @@ -178,17 +189,17 @@ class ProtocolParserGenerator( fun parseStreamingResponseFn( operationShape: OperationShape, - // TODO(enableNewSmithyRuntime): Remove the `includeProperties` flag as if it were always set to `false` - includeProperties: Boolean, + // TODO(enableNewSmithyRuntime): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator + propertyBagAvailable: Boolean, customizations: List, ): RuntimeType { val outputShape = operationShape.outputShape(model) val outputSymbol = symbolProvider.toSymbol(outputShape) val errorSymbol = symbolProvider.symbolForOperationError(operationShape) - val fnNameSuffix = if (includeProperties) "http_response_with_props" else "http_response" + val fnNameSuffix = if (propertyBagAvailable) "http_response_with_props" else "http_response" return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = fnNameSuffix) { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) - val propertiesArg = if (includeProperties) { + val propertiesArg = if (propertyBagAvailable) { Attribute.AllowUnusedVariables.render(this) ", properties: &#{PropertyBag}" } else { @@ -217,6 +228,7 @@ class ProtocolParserGenerator( outputShape, httpBindingResolver.responseBindings(operationShape), errorSymbol, + propertyBagAvailable, customizations, ) } @@ -229,6 +241,8 @@ class ProtocolParserGenerator( outputShape: StructureShape, bindings: List, errorSymbol: Symbol, + // TODO(enableNewSmithyRuntime): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator + propertyBagAvailable: Boolean, customizations: List, ) { val httpBindingGenerator = ResponseBindingGenerator(protocol, codegenContext, operationShape) @@ -270,7 +284,7 @@ class ProtocolParserGenerator( writeCustomizations( customizations, - OperationSection.MutateOutput(customizations, operationShape, "_response_headers"), + OperationSection.MutateOutput(customizations, operationShape, "_response_headers", propertyBagAvailable), ) rust("output.build()$err") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index 654463df74..1df6e33c6a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -144,7 +144,7 @@ class ResponseDeserializerGenerator( """, *codegenScope, "parse_error" to parserGenerator.parseErrorFn(operationShape, customizations), - "parse_response" to parserGenerator.parseResponseFn(operationShape, customizations), + "parse_response" to parserGenerator.parseResponseFn(operationShape, false, customizations), "BeforeParseResponse" to writable { writeCustomizations(customizations, OperationSection.BeforeParseResponse(customizations, "response")) }, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index e6f3e6e1f7..83578b2ac5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -101,7 +101,7 @@ open class HttpBoundProtocolTraitImplGenerator( "O" to outputSymbol, "E" to symbolProvider.symbolForOperationError(operationShape), "parse_error" to parserGenerator.parseErrorFn(operationShape, customizations), - "parse_response" to parserGenerator.parseResponseFn(operationShape, customizations), + "parse_response" to parserGenerator.parseResponseFn(operationShape, true, customizations), "BeforeParseResponse" to writable { writeCustomizations(customizations, OperationSection.BeforeParseResponse(customizations, "response")) }, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt index e11936c704..4c77ee748d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt @@ -54,6 +54,10 @@ sealed class OperationSection(name: String) : Section(name) { val operationShape: OperationShape, /** Name of the response headers map (for referring to it in Rust code) */ val responseHeadersName: String, + + // TODO(enableNewSmithyRuntime): Remove this flag when switching to the orchestrator + /** Whether the property bag exists in this context */ + val propertyBagAvailable: Boolean, ) : OperationSection("MutateOutput") /** From c6d3679093c4698d1446385d679677d035ad4d7f Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Thu, 4 May 2023 21:41:06 -0500 Subject: [PATCH 079/253] Only include in versions.toml crates under the sdk directory (#2663) ## Motivation and Context We have found that `aws-sdk-rust` release-2023-05-01 brought in the `aws-wasm` crate to `versions.toml`. The crate comes from an example in the `aws-doc-sdk-examples` repository. This PR fixes gradle build in `sdk` so that the `publisher` only considers crates under the `sdk` directory when generating `versions.toml`. ## Testing Manually ran ``` ./gradlew :aws:sdk:assemble ``` and confirmed that `versions.toml` got created under `smithy-rs/aws/sdk/build/aws-sdk` ~~`smithy-rs/aws/sdk/build/aws-sdk/sdk`~~ and `README` was successfully hydrated. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- aws/sdk/build.gradle.kts | 4 +++- .../src/subcommand/generate_version_manifest.rs | 12 ++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index 1f92a77b75..875710a598 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -384,7 +384,9 @@ tasks.register("generateVersionManifest") { binaryName = "publisher" arguments = mutableListOf( "generate-version-manifest", - "--location", + "--input-location", + sdkOutputDir.absolutePath, + "--output-location", outputDir.absolutePath, "--smithy-build", buildDir.resolve("smithy-build.json").normalize().absolutePath, diff --git a/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs b/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs index e06c79c6fa..35d1bef9a4 100644 --- a/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs +++ b/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs @@ -28,7 +28,10 @@ pub struct GenerateVersionManifestArgs { examples_revision: String, /// Path containing the generated SDK to generate a version manifest for #[clap(long)] - location: PathBuf, + input_location: PathBuf, + /// Path to a directory in which a version manifest is generated + #[clap(long)] + output_location: PathBuf, /// Optional path to the `versions.toml` manifest from the previous SDK release #[clap(long)] previous_release_versions: Option, @@ -38,7 +41,8 @@ pub async fn subcommand_generate_version_manifest( GenerateVersionManifestArgs { smithy_build, examples_revision, - location, + input_location, + output_location, previous_release_versions, .. }: &GenerateVersionManifestArgs, @@ -52,7 +56,7 @@ pub async fn subcommand_generate_version_manifest( info!("Resolved smithy-rs revision to {}", smithy_rs_revision); let smithy_build_root = SmithyBuildRoot::from_file(smithy_build)?; - let manifests = discover_package_manifests(location.into()) + let manifests = discover_package_manifests(input_location.into()) .await .context("discover package manifests")?; let packages = read_packages(Fs::Real, manifests) @@ -99,7 +103,7 @@ pub async fn subcommand_generate_version_manifest( }; versions_manifest.release = generate_release_metadata(&versions_manifest, previous_release_versions)?; - let manifest_file_name = location.join("versions.toml"); + let manifest_file_name = output_location.join("versions.toml"); info!("Writing {:?}...", manifest_file_name); versions_manifest.write_to_file(&manifest_file_name)?; Ok(()) From 97c70c429c9bb2a3885d3487e22fd24adaf12d1c Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Mon, 8 May 2023 16:28:50 -0500 Subject: [PATCH 080/253] add: tests to ensure all interceptors are run if an error occurs in one (#2664) ## Motivation and Context #2662 ## Description This PR adds tests for all interceptor hooks to ensure the correct error handling behavior and defines a set of stub components for orchestrator testing. ## Testing This PR includes tests. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-smithy-runtime-api/src/client/auth.rs | 12 +- .../src/client/auth/option_resolver.rs | 14 +- .../src/client/identity.rs | 24 -- rust-runtime/aws-smithy-runtime/Cargo.toml | 3 + rust-runtime/aws-smithy-runtime/src/client.rs | 12 + .../aws-smithy-runtime/src/client/identity.rs | 7 + .../src/client/identity/anonymous.rs | 32 ++ .../src/client/orchestrator.rs | 337 ++++++++++++++++++ .../src/client/orchestrator/auth.rs | 3 +- .../src/client/orchestrator/endpoints.rs | 17 + .../src/client/runtime_plugin.rs | 7 + .../client/runtime_plugin/anonymous_auth.rs | 99 +++++ .../src/client/test_util.rs | 8 + .../src/client/test_util/connector.rs | 27 ++ .../src/client/test_util/deserializer.rs | 55 +++ .../src/client/test_util/serializer.rs | 62 ++++ 16 files changed, 683 insertions(+), 36 deletions(-) create mode 100644 rust-runtime/aws-smithy-runtime/src/client/identity.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/test_util.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/test_util/connector.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index fefc3c5025..648684d4ef 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -48,17 +48,17 @@ impl AuthOptionResolverParams { } pub trait AuthOptionResolver: Send + Sync + fmt::Debug { - fn resolve_auth_options<'a>( - &'a self, + fn resolve_auth_options( + &self, params: &AuthOptionResolverParams, - ) -> Result, BoxError>; + ) -> Result, BoxError>; } impl AuthOptionResolver for Box { - fn resolve_auth_options<'a>( - &'a self, + fn resolve_auth_options( + &self, params: &AuthOptionResolverParams, - ) -> Result, BoxError> { + ) -> Result, BoxError> { (**self).resolve_auth_options(params) } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs index 851233486b..8f61bc8e0c 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs @@ -23,10 +23,10 @@ impl StaticAuthOptionResolver { } impl AuthOptionResolver for StaticAuthOptionResolver { - fn resolve_auth_options<'a>( - &'a self, + fn resolve_auth_options( + &self, _params: &AuthOptionResolverParams, - ) -> Result, BoxError> { + ) -> Result, BoxError> { Ok(Cow::Borrowed(&self.auth_options)) } } @@ -36,8 +36,14 @@ impl AuthOptionResolver for StaticAuthOptionResolver { pub struct StaticAuthOptionResolverParams; impl StaticAuthOptionResolverParams { - /// Creates new `StaticAuthOptionResolverParams`. + /// Creates a new `StaticAuthOptionResolverParams`. pub fn new() -> Self { Self } } + +impl From for AuthOptionResolverParams { + fn from(params: StaticAuthOptionResolverParams) -> Self { + AuthOptionResolverParams::new(params) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index 6d5cf9ff82..368d51ffff 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -65,30 +65,6 @@ impl Identity { } } -#[derive(Debug)] -pub struct AnonymousIdentity; - -impl AnonymousIdentity { - pub fn new() -> Self { - Self - } -} - -#[derive(Debug)] -pub struct AnonymousIdentityResolver; - -impl AnonymousIdentityResolver { - pub fn new() -> Self { - AnonymousIdentityResolver - } -} - -impl IdentityResolver for AnonymousIdentityResolver { - fn resolve_identity(&self, _: &ConfigBag) -> Future { - Future::ready(Ok(Identity::new(AnonymousIdentity::new(), None))) - } -} - pub mod builders { use super::*; use crate::client::auth::AuthSchemeId; diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 79e5c7f9fe..68b7f66319 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://github.com/awslabs/smithy-rs" [features] http-auth = ["aws-smithy-runtime-api/http-auth"] +anonymous-auth = [] test-util = ["dep:aws-smithy-protocol-test"] [dependencies] @@ -31,6 +32,8 @@ tracing = "0.1" [dev-dependencies] aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio"] } tokio = { version = "1.25", features = ["macros", "rt", "test-util"] } +tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } +tracing-test = "0.2.1" [package.metadata.docs.rs] all-features = true diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index 14d1c95ac1..c2edaced8b 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -16,4 +16,16 @@ pub mod connections; /// used to limit the rate at which requests are sent. pub mod retries; +/// Utilities for testing orchestrators. An orchestrator missing required components will panic when +/// run. This module contains stub components that can be used when you only care about testing some +/// specific aspect of the orchestrator. +#[cfg(feature = "test-util")] +pub mod test_util; + mod timeout; + +/// Runtime plugins for Smithy clients. +pub mod runtime_plugin; + +/// Smithy identity used by auth and signing. +pub mod identity; diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity.rs b/rust-runtime/aws-smithy-runtime/src/client/identity.rs new file mode 100644 index 0000000000..181bc8575e --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/identity.rs @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(feature = "anonymous-auth")] +pub mod anonymous; diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs b/rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs new file mode 100644 index 0000000000..93fb7ad2e4 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver}; +use aws_smithy_runtime_api::client::orchestrator::Future; +use aws_smithy_runtime_api::config_bag::ConfigBag; + +#[derive(Debug, Default)] +pub struct AnonymousIdentity; + +impl AnonymousIdentity { + pub fn new() -> Self { + Self + } +} + +#[derive(Debug, Default)] +pub struct AnonymousIdentityResolver; + +impl AnonymousIdentityResolver { + pub fn new() -> Self { + Self + } +} + +impl IdentityResolver for AnonymousIdentityResolver { + fn resolve_identity(&self, _: &ConfigBag) -> Future { + Future::ready(Ok(Identity::new(AnonymousIdentity::new(), None))) + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index b174122dc8..0717fe5400 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -215,3 +215,340 @@ async fn make_an_attempt( Ok(checkpoint) } + +#[cfg(all(test, feature = "test-util"))] +mod tests { + use super::invoke; + use crate::client::orchestrator::endpoints::{ + StaticUriEndpointResolver, StaticUriEndpointResolverParams, + }; + use crate::client::retries::strategy::NeverRetryStrategy; + use crate::client::runtime_plugin::anonymous_auth::AnonymousAuthRuntimePlugin; + use crate::client::test_util::{ + connector::OkConnector, deserializer::CannedResponseDeserializer, + serializer::CannedRequestSerializer, + }; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::interceptors::context::phase::{ + AfterDeserialization, BeforeDeserialization, BeforeSerialization, BeforeTransmit, + }; + use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; + use aws_smithy_runtime_api::client::interceptors::{ + Interceptor, InterceptorContext, Interceptors, + }; + use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; + use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin, RuntimePlugins}; + use aws_smithy_runtime_api::config_bag::ConfigBag; + use aws_smithy_runtime_api::type_erasure::TypeErasedBox; + use http::StatusCode; + use std::sync::Arc; + use tracing_test::traced_test; + + fn new_request_serializer() -> CannedRequestSerializer { + CannedRequestSerializer::success( + http::Request::builder() + .body(SdkBody::empty()) + .expect("request is valid"), + ) + } + + fn new_response_deserializer() -> CannedResponseDeserializer { + CannedResponseDeserializer::new( + http::Response::builder() + .status(StatusCode::OK) + .body(SdkBody::empty()) + .map_err(|err| Error::new(Box::new(err))) + .map(|res| Output::new(Box::new(res))), + ) + } + + struct TestOperationRuntimePlugin; + + impl RuntimePlugin for TestOperationRuntimePlugin { + fn configure( + &self, + cfg: &mut ConfigBag, + _interceptors: &mut Interceptors, + ) -> Result<(), BoxError> { + cfg.set_request_serializer(new_request_serializer()); + cfg.set_response_deserializer(new_response_deserializer()); + cfg.set_retry_strategy(NeverRetryStrategy::new()); + cfg.set_endpoint_resolver(StaticUriEndpointResolver::http_localhost(8080)); + cfg.set_endpoint_resolver_params(StaticUriEndpointResolverParams::new().into()); + cfg.set_connection(OkConnector::new()); + + Ok(()) + } + } + + macro_rules! interceptor_error_handling_test { + ($interceptor:ident, $ctx:ty, $expected:expr) => { + #[derive(Debug)] + struct FailingInterceptorA; + impl Interceptor for FailingInterceptorA { + fn $interceptor(&self, _ctx: $ctx, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + tracing::debug!("FailingInterceptorA called!"); + Err("FailingInterceptorA".into()) + } + } + + #[derive(Debug)] + struct FailingInterceptorB; + impl Interceptor for FailingInterceptorB { + fn $interceptor(&self, _ctx: $ctx, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + tracing::debug!("FailingInterceptorB called!"); + Err("FailingInterceptorB".into()) + } + } + + #[derive(Debug)] + struct FailingInterceptorC; + impl Interceptor for FailingInterceptorC { + fn $interceptor(&self, _ctx: $ctx, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + tracing::debug!("FailingInterceptorC called!"); + Err("FailingInterceptorC".into()) + } + } + + struct FailingInterceptorsOperationRuntimePlugin; + + impl RuntimePlugin for FailingInterceptorsOperationRuntimePlugin { + fn configure( + &self, + _cfg: &mut ConfigBag, + interceptors: &mut Interceptors, + ) -> Result<(), BoxError> { + interceptors.register_client_interceptor(Arc::new(FailingInterceptorA)); + interceptors.register_operation_interceptor(Arc::new(FailingInterceptorB)); + interceptors.register_operation_interceptor(Arc::new(FailingInterceptorC)); + + Ok(()) + } + } + + let input = TypeErasedBox::new(Box::new(())); + let runtime_plugins = RuntimePlugins::new() + .with_operation_plugin(TestOperationRuntimePlugin) + .with_operation_plugin(AnonymousAuthRuntimePlugin) + .with_operation_plugin(FailingInterceptorsOperationRuntimePlugin); + let actual = invoke(input, &runtime_plugins) + .await + .expect_err("should error"); + let actual = format!("{:?}", actual); + assert_eq!($expected, format!("{:?}", actual)); + + assert!(logs_contain("FailingInterceptorA called!")); + assert!(logs_contain("FailingInterceptorB called!")); + assert!(logs_contain("FailingInterceptorC called!")); + }; + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_execution_error_handling() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ReadBeforeExecution, source: Some(\"FailingInterceptorC\") } })""#.to_string(); + interceptor_error_handling_test!( + read_before_execution, + &InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_serialization_error_handling() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeSerialization, source: Some(\"FailingInterceptorC\") } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_serialization, + &mut InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_serialization_error_handling() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ReadBeforeSerialization, source: Some(\"FailingInterceptorC\") } })""#.to_string(); + interceptor_error_handling_test!( + read_before_serialization, + &InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_serialization_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadAfterSerialization, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + read_after_serialization, + &InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_retry_loop_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeRetryLoop, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_retry_loop, + &mut InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_attempt_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeAttempt, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + read_before_attempt, + &InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_signing_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeSigning, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_signing, + &mut InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_signing_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeSigning, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + read_before_signing, + &InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_signing_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadAfterSigning, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + read_after_signing, + &InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_transmit_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeTransmit, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_transmit, + &mut InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_transmit_error_handling() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeTransmit, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + interceptor_error_handling_test!( + read_before_transmit, + &InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_transmit_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterTransmit, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + read_after_transmit, + &InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_deserialization_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeDeserialization, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_deserialization, + &mut InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_deserialization_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadBeforeDeserialization, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + read_before_deserialization, + &InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_deserialization_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterDeserialization, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + read_after_deserialization, + &InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_attempt_completion_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_attempt_completion, + &mut InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_attempt_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterAttempt, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + read_after_attempt, + &InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_completion_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + modify_before_completion, + &mut InterceptorContext, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_execution_error_handling() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterExecution, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_handling_test!( + read_after_execution, + &InterceptorContext, + expected + ); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 374795d3d4..a3b053837a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -3,10 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::client::orchestrator::AttemptCheckpoint; use crate::{bail, handle_err}; use aws_smithy_http::result::SdkError; -use aws_smithy_runtime_api::client::interceptors::context::Error; +use aws_smithy_runtime_api::client::interceptors::context::{AttemptCheckpoint, Error}; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpResponse}; use aws_smithy_runtime_api::config_bag::ConfigBag; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 08551f5bee..8761bc5d60 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -48,6 +48,23 @@ impl EndpointResolver for StaticUriEndpointResolver { } } +/// Empty params to be used with [`StaticUriEndpointResolver`]. +#[derive(Debug, Default)] +pub struct StaticUriEndpointResolverParams; + +impl StaticUriEndpointResolverParams { + /// Creates a new `StaticUriEndpointResolverParams`. + pub fn new() -> Self { + Self + } +} + +impl From for EndpointResolverParams { + fn from(params: StaticUriEndpointResolverParams) -> Self { + EndpointResolverParams::new(params) + } +} + #[derive(Debug, Clone)] pub struct DefaultEndpointResolver { inner: SharedEndpointResolver, diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs new file mode 100644 index 0000000000..784de5cd71 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(feature = "anonymous-auth")] +pub mod anonymous_auth; diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs new file mode 100644 index 0000000000..f1a893319f --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs @@ -0,0 +1,99 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! The [AnonymousAuthRuntimePlugin] and supporting code. + +use crate::client::identity::anonymous::AnonymousIdentityResolver; +use aws_smithy_runtime_api::client::auth::option_resolver::{ + StaticAuthOptionResolver, StaticAuthOptionResolverParams, +}; +use aws_smithy_runtime_api::client::auth::{ + AuthSchemeId, HttpAuthScheme, HttpAuthSchemes, HttpRequestSigner, +}; +use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; +use aws_smithy_runtime_api::client::interceptors::Interceptors; +use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpRequest}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::config_bag::ConfigBag; + +const ANONYMOUS_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("anonymous"); + +/// A [RuntimePlugin] to provide anonymous authentication. This runtime plugin sets its own: +/// - [AuthOptionResolver](aws_smithy_runtime_api::client::auth::AuthOptionResolver) +/// - [AuthOptionResolverParams](aws_smithy_runtime_api::client::auth::AuthOptionResolverParams) +/// - [IdentityResolvers] +/// - [HttpAuthSchemes] +/// +/// **The above components will replace any existing ones!** As such, don't use this plugin unless: +/// - You only need to make anonymous requests, such as when interacting with [Open Data](https://aws.amazon.com/opendata/). +/// - You're writing orchestrator tests and don't care about authentication. +pub struct AnonymousAuthRuntimePlugin; + +impl RuntimePlugin for AnonymousAuthRuntimePlugin { + fn configure( + &self, + cfg: &mut ConfigBag, + _interceptors: &mut Interceptors, + ) -> Result<(), BoxError> { + cfg.set_auth_option_resolver_params(StaticAuthOptionResolverParams::new().into()); + cfg.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![ + ANONYMOUS_AUTH_SCHEME_ID, + ])); + cfg.set_identity_resolvers( + IdentityResolvers::builder() + .identity_resolver(ANONYMOUS_AUTH_SCHEME_ID, AnonymousIdentityResolver::new()) + .build(), + ); + cfg.set_http_auth_schemes( + HttpAuthSchemes::builder() + .auth_scheme(ANONYMOUS_AUTH_SCHEME_ID, AnonymousAuthScheme::new()) + .build(), + ); + + Ok(()) + } +} + +#[derive(Debug, Default)] +pub struct AnonymousAuthScheme { + signer: AnonymousSigner, +} + +impl AnonymousAuthScheme { + pub fn new() -> Self { + Self::default() + } +} + +#[derive(Debug, Default)] +struct AnonymousSigner; + +impl HttpRequestSigner for AnonymousSigner { + fn sign_request( + &self, + _request: &mut HttpRequest, + _identity: &Identity, + _config_bag: &ConfigBag, + ) -> Result<(), BoxError> { + Ok(()) + } +} + +impl HttpAuthScheme for AnonymousAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + ANONYMOUS_AUTH_SCHEME_ID + } + + fn identity_resolver<'a>( + &self, + identity_resolvers: &'a IdentityResolvers, + ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers.identity_resolver(ANONYMOUS_AUTH_SCHEME_ID) + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + &self.signer + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs new file mode 100644 index 0000000000..c85ab8f245 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs @@ -0,0 +1,8 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +pub mod connector; +pub mod deserializer; +pub mod serializer; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/connector.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/connector.rs new file mode 100644 index 0000000000..73507bf9fb --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/connector.rs @@ -0,0 +1,27 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::body::SdkBody; +use aws_smithy_runtime_api::client::orchestrator::{ + BoxFuture, Connection, Future, HttpRequest, HttpResponse, +}; + +#[derive(Debug, Default)] +pub struct OkConnector {} + +impl OkConnector { + pub fn new() -> Self { + Self::default() + } +} + +impl Connection for OkConnector { + fn call(&self, _request: HttpRequest) -> BoxFuture { + Box::pin(Future::ready(Ok(http::Response::builder() + .status(200) + .body(SdkBody::empty()) + .expect("OK response is valid")))) + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs new file mode 100644 index 0000000000..0c20268150 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs @@ -0,0 +1,55 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; +use aws_smithy_runtime_api::client::interceptors::Interceptors; +use aws_smithy_runtime_api::client::orchestrator::{ + ConfigBagAccessors, HttpResponse, ResponseDeserializer, +}; +use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use std::sync::Mutex; + +#[derive(Default, Debug)] +pub struct CannedResponseDeserializer { + inner: Mutex>>, +} + +impl CannedResponseDeserializer { + pub fn new(output: Result) -> Self { + Self { + inner: Mutex::new(Some(output)), + } + } + + pub fn take(&self) -> Option> { + match self.inner.lock() { + Ok(mut guard) => guard.take(), + Err(_) => None, + } + } +} + +impl ResponseDeserializer for CannedResponseDeserializer { + fn deserialize_nonstreaming(&self, _response: &HttpResponse) -> Result { + self.take() + .ok_or("CannedResponseDeserializer's inner value has already been taken.") + .unwrap() + } +} + +impl RuntimePlugin for CannedResponseDeserializer { + fn configure( + &self, + cfg: &mut ConfigBag, + _interceptors: &mut Interceptors, + ) -> Result<(), BoxError> { + cfg.set_response_deserializer(Self { + inner: Mutex::new(self.take()), + }); + + Ok(()) + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs new file mode 100644 index 0000000000..50e20b9022 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -0,0 +1,62 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::interceptors::context::Input; +use aws_smithy_runtime_api::client::interceptors::Interceptors; +use aws_smithy_runtime_api::client::orchestrator::{ + ConfigBagAccessors, HttpRequest, RequestSerializer, +}; +use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use std::sync::Mutex; + +#[derive(Default, Debug)] +pub struct CannedRequestSerializer { + inner: Mutex>>, +} + +impl CannedRequestSerializer { + pub fn success(request: HttpRequest) -> Self { + Self { + inner: Mutex::new(Some(Ok(request))), + } + } + + pub fn failure(error: BoxError) -> Self { + Self { + inner: Mutex::new(Some(Err(error))), + } + } + + pub fn take(&self) -> Option> { + match self.inner.lock() { + Ok(mut guard) => guard.take(), + Err(_) => None, + } + } +} + +impl RequestSerializer for CannedRequestSerializer { + fn serialize_input(&self, _input: Input) -> Result { + let req = self + .take() + .ok_or("CannedRequestSerializer's inner value has already been taken.")?; + req + } +} + +impl RuntimePlugin for CannedRequestSerializer { + fn configure( + &self, + cfg: &mut ConfigBag, + _interceptors: &mut Interceptors, + ) -> Result<(), BoxError> { + cfg.set_request_serializer(Self { + inner: Mutex::new(self.take()), + }); + + Ok(()) + } +} From a37b7382c14461709ec09b3a5a7c7fc6819e6173 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 9 May 2023 12:30:47 +0200 Subject: [PATCH 081/253] Call `setup_tracing` in the Lambda example (#2677) To initialize tracing. It's the only example that currently doesn't; the rest of our binary examples (`pokemon-service`, `pokemon-service-tls`) already do this. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- examples/pokemon-service-lambda/src/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/pokemon-service-lambda/src/main.rs b/examples/pokemon-service-lambda/src/main.rs index 2e80cb9802..d81034d27e 100644 --- a/examples/pokemon-service-lambda/src/main.rs +++ b/examples/pokemon-service-lambda/src/main.rs @@ -8,13 +8,16 @@ use std::sync::Arc; use aws_smithy_http_server::{routing::LambdaHandler, AddExtensionLayer}; use pokemon_service_common::{ - capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, State, + capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, + setup_tracing, State, }; use pokemon_service_lambda::get_storage_lambda; use pokemon_service_server_sdk::PokemonService; #[tokio::main] pub async fn main() { + setup_tracing(); + let app = PokemonService::builder_without_plugins() // Build a registry containing implementations to all the operations in the service. These // are async functions or async closures that take as input the operation's input and From 2b165037fd785ce122c993c1a59d3c8d5a3e522c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Sj=C3=B6=C3=B6h?= Date: Wed, 10 May 2023 01:31:51 +0200 Subject: [PATCH 082/253] Use `as_nanos` in `Ord` impl for `DateTime` (#2656) ## Motivation and Context This PR adresses on [feedback from my previous PR](https://github.com/awslabs/smithy-rs/pull/2653#pullrequestreview-1404515899). ## Description - use `as_nanos` in `Ord` impl for `DateTime` instead of manual impl. ## Testing - add proptest that checks that `Ord` impl matches RFC 3339 comparison. ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ --- CHANGELOG.next.toml | 2 +- .../aws-smithy-types/src/date_time/mod.rs | 43 ++++++++++++++++--- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 9f69d7e109..d85ecdf734 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -21,5 +21,5 @@ meta = { "breaking" = false, "tada" = false, "bug" = false } [[smithy-rs]] message = "Implement `Ord` and `PartialOrd` for `DateTime`." author = "henriiik" -references = ["smithy-rs#2653"] +references = ["smithy-rs#2653", "smithy-rs#2656"] meta = { "breaking" = false, "tada" = false, "bug" = false } diff --git a/rust-runtime/aws-smithy-types/src/date_time/mod.rs b/rust-runtime/aws-smithy-types/src/date_time/mod.rs index ea3f5baf6c..6bc099d25a 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/mod.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs @@ -310,10 +310,7 @@ impl PartialOrd for DateTime { impl Ord for DateTime { fn cmp(&self, other: &Self) -> Ordering { - match self.seconds.cmp(&other.seconds) { - Ordering::Equal => self.subsecond_nanos.cmp(&other.subsecond_nanos), - ordering => ordering, - } + self.as_nanos().cmp(&other.as_nanos()) } } @@ -350,6 +347,7 @@ pub enum Format { mod test { use crate::date_time::Format; use crate::DateTime; + use proptest::proptest; use std::convert::TryFrom; use std::time::SystemTime; use time::format_description::well_known::Rfc3339; @@ -572,28 +570,59 @@ mod test { #[test] fn ord() { let first = DateTime::from_secs_and_nanos(-1, 0); - let second = DateTime::from_secs_and_nanos(0, 0); - let third = DateTime::from_secs_and_nanos(0, 1); - let fourth = DateTime::from_secs_and_nanos(1, 0); + let second = DateTime::from_secs_and_nanos(-1, 1); + let third = DateTime::from_secs_and_nanos(0, 0); + let fourth = DateTime::from_secs_and_nanos(0, 1); + let fifth = DateTime::from_secs_and_nanos(1, 0); assert!(first == first); assert!(first < second); assert!(first < third); assert!(first < fourth); + assert!(first < fifth); assert!(second > first); assert!(second == second); assert!(second < third); assert!(second < fourth); + assert!(second < fifth); assert!(third > first); assert!(third > second); assert!(third == third); assert!(third < fourth); + assert!(third < fifth); assert!(fourth > first); assert!(fourth > second); assert!(fourth > third); assert!(fourth == fourth); + assert!(fourth < fifth); + + assert!(fifth > first); + assert!(fifth > second); + assert!(fifth > third); + assert!(fifth > fourth); + assert!(fifth == fifth); + } + + const MIN_RFC_3339_MILLIS: i64 = -62135596800000; + const MAX_RFC_3339_MILLIS: i64 = 253402300799999; + + // This test uses milliseconds, because `Format::DateTime` does not support nanoseconds. + proptest! { + #[test] + fn ord_proptest( + left_millis in MIN_RFC_3339_MILLIS..MAX_RFC_3339_MILLIS, + right_millis in MIN_RFC_3339_MILLIS..MAX_RFC_3339_MILLIS, + ) { + let left = DateTime::from_millis(left_millis); + let right = DateTime::from_millis(right_millis); + + let left_str = left.fmt(Format::DateTime).unwrap(); + let right_str = right.fmt(Format::DateTime).unwrap(); + + assert_eq!(left.cmp(&right), left_str.cmp(&right_str)); + } } } From c3e6ed96df98c3ef25b26c751987f429b0ac2d88 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 10 May 2023 10:18:21 +0200 Subject: [PATCH 083/253] Document compatibility of `aws-smithy-http-server` with `lambda_http` (#2683) See #2676. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- examples/pokemon-service-lambda/Cargo.toml | 3 +++ .../aws-smithy-http-server/src/routing/lambda_handler.rs | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/examples/pokemon-service-lambda/Cargo.toml b/examples/pokemon-service-lambda/Cargo.toml index 96a2d18230..72c1d4cd01 100644 --- a/examples/pokemon-service-lambda/Cargo.toml +++ b/examples/pokemon-service-lambda/Cargo.toml @@ -13,6 +13,9 @@ hyper = {version = "0.14.25", features = ["server"] } tokio = "1.26.0" tracing = "0.1" +# `aws-smithy-http-server` is only guaranteed to be compatible with this +# version of `lambda_http`, or semver-compatible versions of this version. +# Depending on other versions of `lambda_http` may not work. lambda_http = "0.7.3" # Local paths diff --git a/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs b/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs index b338062782..6e3348187c 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs @@ -16,6 +16,10 @@ type HyperRequest = http::Request; /// A [`Service`] that takes a `lambda_http::Request` and converts /// it to `http::Request`. /// +/// **This version is only guaranteed to be compatible with +/// [`lambda_http`](https://docs.rs/lambda_http) ^0.7.0.** Please ensure that your service crate's +/// `Cargo.toml` depends on a compatible version. +/// /// [`Service`]: tower::Service #[derive(Debug, Clone)] pub struct LambdaHandler { From 04db5e3572448cb38e3b5f1de78ce8695567c5f3 Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 10 May 2023 11:39:15 +0200 Subject: [PATCH 084/253] Bump `lambda_http` dependency of `aws-smithy-http-server` to 0.8.0 (#2685) This patch also removes the unneeded dependency on `lambda_runtime` by `aws-smithy-http-server-python`. This patch also refactors `LambdaHandler`'s `convert_event` to avoid cloning the URI path when not needed. This is a breaking change. See #2676 why. This patch also bumps `aws-smithy-http-server` dependency on `mime` to 0.3.4. `cargo +nightly-2022-11-16 minimal-versions check --all-features` otherwise fails when using 0.3.0, because we require `impl fmt::Display for mime::FromStrError`, which was first introduced in 0.3.4. As to why `minimal-versions` is now picking `mime` 0.3.0 with this patch, it's because in `lambda_http` 0.7.3, they had [`mime = "0.3.16"`](https://github.com/awslabs/aws-lambda-rust-runtime/blob/99dba6447253ac87cf3cefeb2ba130b50514f9df/lambda-http/Cargo.toml#L35-L35), and in `lambda_http` 0.8.0, they've now relaxed that to [`mime = "0.3"`](https://github.com/awslabs/aws-lambda-rust-runtime/blob/393d6447bea0502e1f939d197f4facc228e6e007/lambda-http/Cargo.toml#L36). ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ --- CHANGELOG.next.toml | 6 ++++++ examples/pokemon-service-lambda/Cargo.toml | 2 +- .../aws-smithy-http-server-python/Cargo.toml | 7 +------ rust-runtime/aws-smithy-http-server/Cargo.toml | 4 ++-- .../src/routing/lambda_handler.rs | 16 ++++++++++------ 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index d85ecdf734..f152a6eabf 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -23,3 +23,9 @@ message = "Implement `Ord` and `PartialOrd` for `DateTime`." author = "henriiik" references = ["smithy-rs#2653", "smithy-rs#2656"] meta = { "breaking" = false, "tada" = false, "bug" = false } + +[[smithy-rs]] +message = "Bump dependency on `lambda_http` by `aws-smithy-http-server` to 0.8.0. This version of `aws-smithy-http-server` is only guaranteed to be compatible with 0.8.0, or semver-compatible versions of 0.8.0 of the `lambda_http` crate. It will not work with versions prior to 0.8.0 _at runtime_, making requests to your smithy-rs service unroutable, so please make sure you're running your service in a compatible configuration" +author = "david-perez" +references = ["smithy-rs#2676", "smithy-rs#2685"] +meta = { "breaking" = true, "tada" = false, "bug" = false } diff --git a/examples/pokemon-service-lambda/Cargo.toml b/examples/pokemon-service-lambda/Cargo.toml index 72c1d4cd01..b806c53891 100644 --- a/examples/pokemon-service-lambda/Cargo.toml +++ b/examples/pokemon-service-lambda/Cargo.toml @@ -16,7 +16,7 @@ tracing = "0.1" # `aws-smithy-http-server` is only guaranteed to be compatible with this # version of `lambda_http`, or semver-compatible versions of this version. # Depending on other versions of `lambda_http` may not work. -lambda_http = "0.7.3" +lambda_http = "0.8.0" # Local paths aws-smithy-http-server = { path = "../../rust-runtime/aws-smithy-http-server", features = ["aws-lambda"] } diff --git a/rust-runtime/aws-smithy-http-server-python/Cargo.toml b/rust-runtime/aws-smithy-http-server-python/Cargo.toml index 738b86418b..334b049191 100644 --- a/rust-runtime/aws-smithy-http-server-python/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server-python/Cargo.toml @@ -25,12 +25,7 @@ hyper = { version = "0.14.20", features = ["server", "http1", "http2", "tcp", "s tls-listener = { version = "0.7.0", features = ["rustls", "hyper-h2"] } rustls-pemfile = "1.0.1" tokio-rustls = "0.24.0" -lambda_http = { version = "0.7.1" } -# There is a breaking change in `lambda_runtime` between `0.7.0` and `0.7.1`, -# and `lambda_http` depends on `0.7` which by default resolves to `0.7.1` but in our CI -# we are running `minimal-versions` which downgrades `lambda_runtime` to `0.7.0` and fails to compile -# because of the breaking change. Here we are forcing it to use `lambda_runtime = 0.7.1`. -lambda_runtime = { version = "0.7.1" } +lambda_http = { version = "0.8.0" } num_cpus = "1.13.1" parking_lot = "0.12.1" pin-project-lite = "0.2" diff --git a/rust-runtime/aws-smithy-http-server/Cargo.toml b/rust-runtime/aws-smithy-http-server/Cargo.toml index 96ee56f79b..57f6bc761a 100644 --- a/rust-runtime/aws-smithy-http-server/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server/Cargo.toml @@ -28,8 +28,8 @@ futures-util = { version = "0.3.16", default-features = false } http = "0.2" http-body = "0.4" hyper = { version = "0.14.12", features = ["server", "http1", "http2", "tcp", "stream"] } -lambda_http = { version = "0.7.1", optional = true } -mime = "0.3" +lambda_http = { version = "0.8.0", optional = true } +mime = "0.3.4" nom = "7" pin-project-lite = "0.2" once_cell = "1.13" diff --git a/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs b/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs index 6e3348187c..44704e2dd1 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs @@ -58,12 +58,12 @@ where /// /// [API Gateway Stage]: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-stages.html fn convert_event(request: Request) -> HyperRequest { - let raw_path = request.raw_http_path(); - let (mut parts, body) = request.into_parts(); - let mut path = String::from(parts.uri.path()); + let raw_path: &str = request.extensions().raw_http_path(); + let path: &str = request.uri().path(); - if !raw_path.is_empty() && raw_path != path { - path = raw_path; + let (parts, body) = if !raw_path.is_empty() && raw_path != path { + let mut path = raw_path.to_owned(); // Clone only when we need to strip out the stage. + let (mut parts, body) = request.into_parts(); let uri_parts: uri::Parts = parts.uri.into(); let path_and_query = uri_parts @@ -81,7 +81,11 @@ fn convert_event(request: Request) -> HyperRequest { .path_and_query(path) .build() .expect("unable to construct new URI"); - } + + (parts, body) + } else { + request.into_parts() + }; let body = match body { lambda_http::Body::Empty => hyper::Body::empty(), From a78ac591fe4d969158bddabd4d6b1bf7a396cd5c Mon Sep 17 00:00:00 2001 From: david-perez Date: Wed, 10 May 2023 12:41:28 +0200 Subject: [PATCH 085/253] Revert "Bump `lambda_http` dependency of `aws-smithy-http-server` to 0.8.0 (#2685)" (#2690) This reverts commit 04db5e3572448cb38e3b5f1de78ce8695567c5f3. This is a breaking change that needs to wait to be merged in until the next breaking release. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- CHANGELOG.next.toml | 6 ------ examples/pokemon-service-lambda/Cargo.toml | 2 +- .../aws-smithy-http-server-python/Cargo.toml | 7 ++++++- rust-runtime/aws-smithy-http-server/Cargo.toml | 4 ++-- .../src/routing/lambda_handler.rs | 16 ++++++---------- 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index f152a6eabf..d85ecdf734 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -23,9 +23,3 @@ message = "Implement `Ord` and `PartialOrd` for `DateTime`." author = "henriiik" references = ["smithy-rs#2653", "smithy-rs#2656"] meta = { "breaking" = false, "tada" = false, "bug" = false } - -[[smithy-rs]] -message = "Bump dependency on `lambda_http` by `aws-smithy-http-server` to 0.8.0. This version of `aws-smithy-http-server` is only guaranteed to be compatible with 0.8.0, or semver-compatible versions of 0.8.0 of the `lambda_http` crate. It will not work with versions prior to 0.8.0 _at runtime_, making requests to your smithy-rs service unroutable, so please make sure you're running your service in a compatible configuration" -author = "david-perez" -references = ["smithy-rs#2676", "smithy-rs#2685"] -meta = { "breaking" = true, "tada" = false, "bug" = false } diff --git a/examples/pokemon-service-lambda/Cargo.toml b/examples/pokemon-service-lambda/Cargo.toml index b806c53891..72c1d4cd01 100644 --- a/examples/pokemon-service-lambda/Cargo.toml +++ b/examples/pokemon-service-lambda/Cargo.toml @@ -16,7 +16,7 @@ tracing = "0.1" # `aws-smithy-http-server` is only guaranteed to be compatible with this # version of `lambda_http`, or semver-compatible versions of this version. # Depending on other versions of `lambda_http` may not work. -lambda_http = "0.8.0" +lambda_http = "0.7.3" # Local paths aws-smithy-http-server = { path = "../../rust-runtime/aws-smithy-http-server", features = ["aws-lambda"] } diff --git a/rust-runtime/aws-smithy-http-server-python/Cargo.toml b/rust-runtime/aws-smithy-http-server-python/Cargo.toml index 334b049191..738b86418b 100644 --- a/rust-runtime/aws-smithy-http-server-python/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server-python/Cargo.toml @@ -25,7 +25,12 @@ hyper = { version = "0.14.20", features = ["server", "http1", "http2", "tcp", "s tls-listener = { version = "0.7.0", features = ["rustls", "hyper-h2"] } rustls-pemfile = "1.0.1" tokio-rustls = "0.24.0" -lambda_http = { version = "0.8.0" } +lambda_http = { version = "0.7.1" } +# There is a breaking change in `lambda_runtime` between `0.7.0` and `0.7.1`, +# and `lambda_http` depends on `0.7` which by default resolves to `0.7.1` but in our CI +# we are running `minimal-versions` which downgrades `lambda_runtime` to `0.7.0` and fails to compile +# because of the breaking change. Here we are forcing it to use `lambda_runtime = 0.7.1`. +lambda_runtime = { version = "0.7.1" } num_cpus = "1.13.1" parking_lot = "0.12.1" pin-project-lite = "0.2" diff --git a/rust-runtime/aws-smithy-http-server/Cargo.toml b/rust-runtime/aws-smithy-http-server/Cargo.toml index 57f6bc761a..96ee56f79b 100644 --- a/rust-runtime/aws-smithy-http-server/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server/Cargo.toml @@ -28,8 +28,8 @@ futures-util = { version = "0.3.16", default-features = false } http = "0.2" http-body = "0.4" hyper = { version = "0.14.12", features = ["server", "http1", "http2", "tcp", "stream"] } -lambda_http = { version = "0.8.0", optional = true } -mime = "0.3.4" +lambda_http = { version = "0.7.1", optional = true } +mime = "0.3" nom = "7" pin-project-lite = "0.2" once_cell = "1.13" diff --git a/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs b/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs index 44704e2dd1..6e3348187c 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs @@ -58,12 +58,12 @@ where /// /// [API Gateway Stage]: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-stages.html fn convert_event(request: Request) -> HyperRequest { - let raw_path: &str = request.extensions().raw_http_path(); - let path: &str = request.uri().path(); + let raw_path = request.raw_http_path(); + let (mut parts, body) = request.into_parts(); + let mut path = String::from(parts.uri.path()); - let (parts, body) = if !raw_path.is_empty() && raw_path != path { - let mut path = raw_path.to_owned(); // Clone only when we need to strip out the stage. - let (mut parts, body) = request.into_parts(); + if !raw_path.is_empty() && raw_path != path { + path = raw_path; let uri_parts: uri::Parts = parts.uri.into(); let path_and_query = uri_parts @@ -81,11 +81,7 @@ fn convert_event(request: Request) -> HyperRequest { .path_and_query(path) .build() .expect("unable to construct new URI"); - - (parts, body) - } else { - request.into_parts() - }; + } let body = match body { lambda_http::Body::Empty => hyper::Body::empty(), From 91194e786cced31d256fe51bee4f43769345426d Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Wed, 10 May 2023 19:46:33 -0500 Subject: [PATCH 086/253] Avoid extending IMDS credentials expiry unconditionally (#2694) ## Motivation and Context Fixes https://github.com/awslabs/smithy-rs/issues/2687 ## Description The implementation for IMDS static stability support introduced a bug where returned credentials from IMDS are extended unconditionally, even though the credentials are not stale. The amount by which credentials are extended is randomized and it can incorrectly extend the expiry beyond what's originally set. IMDS produces credentials that last 6 hours, and extending them by at most 25 minutes usually won't be an issue but when other tools such as Kube2iam and AWSVault are used, the expiry can be set much shorter than that, causing the issue to occur. This PR will conditionally extend the credentials' expiry only when the returned credentials have been expired with respect to the current wall clock time. Also, the constant values have been adjusted according to our internal spec. ## Testing - Added a new unit test for the IMDS credentials provider ## Checklist - [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._ --------- Co-authored-by: Yuki Saito --- CHANGELOG.next.toml | 6 ++ .../aws-config/src/imds/credentials.rs | 86 +++++++++++++++---- 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index d85ecdf734..7267f1f4e3 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -23,3 +23,9 @@ message = "Implement `Ord` and `PartialOrd` for `DateTime`." author = "henriiik" references = ["smithy-rs#2653", "smithy-rs#2656"] meta = { "breaking" = false, "tada" = false, "bug" = false } + +[[aws-sdk-rust]] +message = "Avoid extending IMDS credentials' expiry unconditionally, which may incorrectly extend it beyond what is originally defined; If returned credentials are not stale, use them as they are." +references = ["smithy-rs#2687", "smithy-rs#2694"] +meta = { "breaking" = false, "tada" = false, "bug" = true } +author = "ysaito1001" diff --git a/aws/rust-runtime/aws-config/src/imds/credentials.rs b/aws/rust-runtime/aws-config/src/imds/credentials.rs index 665d03f801..fd7fd02bab 100644 --- a/aws/rust-runtime/aws-config/src/imds/credentials.rs +++ b/aws/rust-runtime/aws-config/src/imds/credentials.rs @@ -23,7 +23,10 @@ use std::fmt; use std::sync::{Arc, RwLock}; use std::time::{Duration, SystemTime}; -const CREDENTIAL_EXPIRATION_INTERVAL: Duration = Duration::from_secs(15 * 60); +const CREDENTIAL_EXPIRATION_INTERVAL: Duration = Duration::from_secs(10 * 60); +const WARNING_FOR_EXTENDING_CREDENTIALS_EXPIRY: &str = + "Attempting credential expiration extension due to a credential service availability issue. \ + A refresh of these credentials will be attempted again within the next"; #[derive(Debug)] struct ImdsCommunicationError { @@ -192,25 +195,26 @@ impl ImdsCredentialsProvider { // // This allows continued use of the credentials even when IMDS returns expired ones. fn maybe_extend_expiration(&self, expiration: SystemTime) -> SystemTime { + let now = self.time_source.now(); + // If credentials from IMDS are not stale, use them as they are. + if now < expiration { + return expiration; + } + let rng = fastrand::Rng::with_seed( - self.time_source - .now() - .duration_since(SystemTime::UNIX_EPOCH) + now.duration_since(SystemTime::UNIX_EPOCH) .expect("now should be after UNIX EPOCH") .as_secs(), ); - // calculate credentials' refresh offset with jitter - let refresh_offset = - CREDENTIAL_EXPIRATION_INTERVAL + Duration::from_secs(rng.u64(120..=600)); - let new_expiry = self.time_source.now() + refresh_offset; - - if new_expiry < expiration { - return expiration; - } + // Calculate credentials' refresh offset with jitter, which should be less than 15 minutes + // the smallest amount of time credentials are valid for. + // Setting it to something longer than that may have the risk of the credentials expiring + // before the next refresh. + let refresh_offset = CREDENTIAL_EXPIRATION_INTERVAL + Duration::from_secs(rng.u64(0..=300)); + let new_expiry = now + refresh_offset; tracing::warn!( - "Attempting credential expiration extension due to a credential service availability issue. \ - A refresh of these credentials will be attempted again within the next {:.2} minutes.", + "{WARNING_FOR_EXTENDING_CREDENTIALS_EXPIRY} {:.2} minutes.", refresh_offset.as_secs_f64() / 60.0, ); @@ -297,7 +301,9 @@ mod test { use crate::imds::client::test::{ imds_request, imds_response, make_client, token_request, token_response, }; - use crate::imds::credentials::ImdsCredentialsProvider; + use crate::imds::credentials::{ + ImdsCredentialsProvider, WARNING_FOR_EXTENDING_CREDENTIALS_EXPIRY, + }; use crate::provider_config::ProviderConfig; use aws_credential_types::provider::ProvideCredentials; use aws_credential_types::time_source::{TestingTimeSource, TimeSource}; @@ -342,6 +348,54 @@ mod test { connection.assert_requests_match(&[]); } + #[tokio::test] + #[traced_test] + async fn credentials_not_stale_should_be_used_as_they_are() { + let connection = TestConnection::new(vec![ + ( + token_request("http://169.254.169.254", 21600), + token_response(21600, TOKEN_A), + ), + ( + imds_request("http://169.254.169.254/latest/meta-data/iam/security-credentials/", TOKEN_A), + imds_response(r#"profile-name"#), + ), + ( + imds_request("http://169.254.169.254/latest/meta-data/iam/security-credentials/profile-name", TOKEN_A), + imds_response("{\n \"Code\" : \"Success\",\n \"LastUpdated\" : \"2021-09-20T21:42:26Z\",\n \"Type\" : \"AWS-HMAC\",\n \"AccessKeyId\" : \"ASIARTEST\",\n \"SecretAccessKey\" : \"testsecret\",\n \"Token\" : \"testtoken\",\n \"Expiration\" : \"2021-09-21T04:16:53Z\"\n}"), + ), + ]); + + // set to 2021-09-21T04:16:50Z that makes returned credentials' expiry (2021-09-21T04:16:53Z) + // not stale + let time_of_request_to_fetch_credentials = UNIX_EPOCH + Duration::from_secs(1632197810); + let time_source = TimeSource::testing(&TestingTimeSource::new( + time_of_request_to_fetch_credentials, + )); + + tokio::time::pause(); + + let provider_config = ProviderConfig::no_configuration() + .with_http_connector(DynConnector::new(connection.clone())) + .with_time_source(time_source) + .with_sleep(TokioSleep::new()); + let client = crate::imds::Client::builder() + .configure(&provider_config) + .build() + .await + .expect("valid client"); + let provider = ImdsCredentialsProvider::builder() + .configure(&provider_config) + .imds_client(client) + .build(); + let creds = provider.provide_credentials().await.expect("valid creds"); + // The expiry should be equal to what is originally set (==2021-09-21T04:16:53Z). + assert!(creds.expiry() == UNIX_EPOCH.checked_add(Duration::from_secs(1632197813))); + connection.assert_requests_match(&[]); + + // There should not be logs indicating credentials are extended for stability. + assert!(!logs_contain(WARNING_FOR_EXTENDING_CREDENTIALS_EXPIRY)); + } #[tokio::test] #[traced_test] async fn expired_credentials_should_be_extended() { @@ -386,7 +440,7 @@ mod test { connection.assert_requests_match(&[]); // We should inform customers that expired credentials are being used for stability. - assert!(logs_contain("Attempting credential expiration extension")); + assert!(logs_contain(WARNING_FOR_EXTENDING_CREDENTIALS_EXPIRY)); } #[tokio::test] From 56766816d25376e9f68abbf81f20d73d93033b9a Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 10 May 2023 18:44:54 -0700 Subject: [PATCH 087/253] Delete the `old_presigning` inlineable module (#2689) ## Motivation and Context This module was supposed to be removed with the crate reorganization cleanup, but was missed. This PR removes it. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-inlineable/src/lib.rs | 2 - .../aws-inlineable/src/old_presigning.rs | 282 ------------------ 2 files changed, 284 deletions(-) delete mode 100644 aws/rust-runtime/aws-inlineable/src/old_presigning.rs diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index d5ed3b8be3..ed582f0e54 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -24,8 +24,6 @@ pub mod no_credentials; /// Support types required for adding presigning to an operation in a generated service. pub mod presigning; -// TODO(CrateReorganization): Delete the `old_presigning` module -pub mod old_presigning; /// Special logic for extracting request IDs from S3's responses. pub mod s3_request_id; diff --git a/aws/rust-runtime/aws-inlineable/src/old_presigning.rs b/aws/rust-runtime/aws-inlineable/src/old_presigning.rs deleted file mode 100644 index cf95c3901d..0000000000 --- a/aws/rust-runtime/aws-inlineable/src/old_presigning.rs +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! Presigned request types and configuration. - -/// Presigning config and builder -pub mod config { - use std::fmt; - use std::time::{Duration, SystemTime}; - - const ONE_WEEK: Duration = Duration::from_secs(604800); - - /// Presigning config values required for creating a presigned request. - #[non_exhaustive] - #[derive(Debug, Clone)] - pub struct PresigningConfig { - start_time: SystemTime, - expires_in: Duration, - } - - impl PresigningConfig { - /// Creates a `PresigningConfig` with the given `expires_in` duration. - /// - /// The `expires_in` duration is the total amount of time the presigned request should - /// be valid for. Other config values are defaulted. - /// - /// Credential expiration time takes priority over the `expires_in` value. - /// If the credentials used to sign the request expire before the presigned request is - /// set to expire, then the presigned request will become invalid. - pub fn expires_in(expires_in: Duration) -> Result { - Self::builder().expires_in(expires_in).build() - } - - /// Creates a new builder for creating a `PresigningConfig`. - pub fn builder() -> Builder { - Builder::default() - } - - /// Returns the amount of time the presigned request should be valid for. - pub fn expires(&self) -> Duration { - self.expires_in - } - - /// Returns the start time. The presigned request will be valid between this and the end - /// time produced by adding the `expires()` value to it. - pub fn start_time(&self) -> SystemTime { - self.start_time - } - } - - #[derive(Debug)] - enum ErrorKind { - /// Presigned requests cannot be valid for longer than one week. - ExpiresInDurationTooLong, - - /// The `PresigningConfig` builder requires a value for `expires_in`. - ExpiresInRequired, - } - - /// `PresigningConfig` build errors. - #[derive(Debug)] - pub struct Error { - kind: ErrorKind, - } - - impl std::error::Error for Error {} - - impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.kind { - ErrorKind::ExpiresInDurationTooLong => { - write!(f, "`expires_in` must be no longer than one week") - } - ErrorKind::ExpiresInRequired => write!(f, "`expires_in` is required"), - } - } - } - - impl From for Error { - fn from(kind: ErrorKind) -> Self { - Self { kind } - } - } - - /// Builder used to create `PresigningConfig`. - #[non_exhaustive] - #[derive(Default, Debug)] - pub struct Builder { - start_time: Option, - expires_in: Option, - } - - impl Builder { - /// Sets the start time for the presigned request. - /// - /// The request will start to be valid at this time, and will cease to be valid after - /// the end time, which can be determined by adding the `expires_in` duration to this - /// start time. If not specified, this will default to the current time. - /// - /// Optional. - pub fn start_time(mut self, start_time: SystemTime) -> Self { - self.set_start_time(Some(start_time)); - self - } - - /// Sets the start time for the presigned request. - /// - /// The request will start to be valid at this time, and will cease to be valid after - /// the end time, which can be determined by adding the `expires_in` duration to this - /// start time. If not specified, this will default to the current time. - /// - /// Optional. - pub fn set_start_time(&mut self, start_time: Option) { - self.start_time = start_time; - } - - /// Sets how long the request should be valid after the `start_time` (which defaults - /// to the current time). - /// - /// Credential expiration time takes priority over the `expires_in` value. - /// If the credentials used to sign the request expire before the presigned request is - /// set to expire, then the presigned request will become invalid. - /// - /// Required. - pub fn expires_in(mut self, expires_in: Duration) -> Self { - self.set_expires_in(Some(expires_in)); - self - } - - /// Sets how long the request should be valid after the `start_time` (which defaults - /// to the current time). - /// - /// Credential expiration time takes priority over the `expires_in` value. - /// If the credentials used to sign the request expire before the presigned request is - /// set to expire, then the presigned request will become invalid. - /// - /// Required. - pub fn set_expires_in(&mut self, expires_in: Option) { - self.expires_in = expires_in; - } - - /// Builds the `PresigningConfig`. This will error if `expires_in` is not - /// given, or if it's longer than one week. - pub fn build(self) -> Result { - let expires_in = self.expires_in.ok_or(ErrorKind::ExpiresInRequired)?; - if expires_in > ONE_WEEK { - return Err(ErrorKind::ExpiresInDurationTooLong.into()); - } - Ok(PresigningConfig { - start_time: self.start_time.unwrap_or_else(SystemTime::now), - expires_in, - }) - } - } -} - -/// Presigned request -pub mod request { - use std::fmt::{Debug, Formatter}; - - /// Represents a presigned request. This only includes the HTTP request method, URI, and headers. - /// - /// **This struct has conversion convenience functions:** - /// - /// - [`PresignedRequest::to_http_request`][Self::to_http_request] returns an [`http::Request`](https://docs.rs/http/0.2.6/http/request/struct.Request.html) - /// - [`PresignedRequest::into`](#impl-From) returns an [`http::request::Builder`](https://docs.rs/http/0.2.6/http/request/struct.Builder.html) - #[non_exhaustive] - pub struct PresignedRequest(http::Request<()>); - - impl PresignedRequest { - pub(crate) fn new(inner: http::Request<()>) -> Self { - Self(inner) - } - - /// Returns the HTTP request method. - pub fn method(&self) -> &http::Method { - self.0.method() - } - - /// Returns the HTTP request URI. - pub fn uri(&self) -> &http::Uri { - self.0.uri() - } - - /// Returns any HTTP headers that need to go along with the request, except for `Host`, - /// which should be sent based on the endpoint in the URI by the HTTP client rather than - /// added directly. - pub fn headers(&self) -> &http::HeaderMap { - self.0.headers() - } - - /// Given a body, convert this `PresignedRequest` into an `http::Request` - pub fn to_http_request(self, body: B) -> Result, http::Error> { - let builder: http::request::Builder = self.into(); - - builder.body(body) - } - } - - impl Debug for PresignedRequest { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("PresignedRequest") - .field("method", self.method()) - .field("uri", self.uri()) - .field("headers", self.headers()) - .finish() - } - } - - impl From for http::request::Builder { - fn from(req: PresignedRequest) -> Self { - let mut builder = http::request::Builder::new() - .uri(req.uri()) - .method(req.method()); - - if let Some(headers) = builder.headers_mut() { - *headers = req.headers().clone(); - } - - builder - } - } -} - -/// Tower middleware service for creating presigned requests -#[allow(dead_code)] -pub(crate) mod service { - use super::request::PresignedRequest; - use aws_smithy_http::operation; - use http::header::USER_AGENT; - use std::future::{ready, Ready}; - use std::marker::PhantomData; - use std::task::{Context, Poll}; - - /// Tower [`Service`](tower::Service) for generated a [`PresignedRequest`] from the AWS middleware. - #[derive(Default, Debug)] - #[non_exhaustive] - pub(crate) struct PresignedRequestService { - _phantom: PhantomData, - } - - // Required because of the derive Clone on MapRequestService. - // Manually implemented to avoid requiring errors to implement Clone. - impl Clone for PresignedRequestService { - fn clone(&self) -> Self { - Self { - _phantom: Default::default(), - } - } - } - - impl PresignedRequestService { - /// Creates a new `PresignedRequestService` - pub(crate) fn new() -> Self { - Self { - _phantom: Default::default(), - } - } - } - - impl tower::Service for PresignedRequestService { - type Response = PresignedRequest; - type Error = E; - type Future = Ready>; - - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: operation::Request) -> Self::Future { - let (mut req, _) = req.into_parts(); - - // Remove user agent headers since the request will not be executed by the AWS Rust SDK. - req.headers_mut().remove(USER_AGENT); - req.headers_mut().remove("X-Amz-User-Agent"); - - ready(Ok(PresignedRequest::new(req.map(|_| ())))) - } - } -} From 38fbae5cdf2049128b9a049c036766eb4690322f Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 10 May 2023 18:45:07 -0700 Subject: [PATCH 088/253] Enable paginators when code generating with the orchestrator (#2688) ## Motivation and Context The paginators were originally turned off for the orchestrator feature flag since there were issues with them, but those issues seem to have been resolved since. This PR re-enables them. ## Testing - [x] Generated the AWS SDK in `orchestrator` mode and verified the paginator tests pass - [x] Added a unit test that validates the paginators compile for generic clients in `orchestrator` mode ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../client/FluentClientGenerator.kt | 31 +++++++++---------- .../generators/PaginatorGeneratorTest.kt | 27 +++++++++++++++- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index c4714038f0..8460a6aaa3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -458,23 +458,20 @@ class FluentClientGenerator( ) } - // TODO(enableNewSmithyRuntime): Port paginators to the orchestrator - if (smithyRuntimeMode.generateMiddleware) { - PaginatorGenerator.paginatorType(codegenContext, generics, operation, retryClassifier) - ?.also { paginatorType -> - rustTemplate( - """ - /// Create a paginator for this request - /// - /// Paginators are used by calling [`send().await`](#{Paginator}::send) which returns a `Stream`. - pub fn into_paginator(self) -> #{Paginator}${generics.inst} { - #{Paginator}::new(self.handle, self.inner) - } - """, - "Paginator" to paginatorType, - ) - } - } + PaginatorGenerator.paginatorType(codegenContext, generics, operation, retryClassifier) + ?.also { paginatorType -> + rustTemplate( + """ + /// Create a paginator for this request + /// + /// Paginators are used by calling [`send().await`](#{Paginator}::send) which returns a `Stream`. + pub fn into_paginator(self) -> #{Paginator}${generics.inst} { + #{Paginator}::new(self.handle, self.inner) + } + """, + "Paginator" to paginatorType, + ) + } writeCustomizations( customizations, FluentClientSection.FluentBuilderImpl( diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt index 96eff3ad79..f7dbbdafff 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt @@ -6,9 +6,12 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import org.junit.jupiter.api.Test +import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.model.node.StringNode import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest @@ -67,8 +70,9 @@ internal class PaginatorGeneratorTest { } """.asSmithyModel() + // TODO(enableNewSmithyRuntime): Remove this middleware test when launching @Test - fun `generate paginators that compile`() { + fun `generate paginators that compile with middleware`() { clientIntegrationTest(model) { clientCodegenContext, rustCrate -> rustCrate.integrationTest("paginators_generated") { Attribute.AllowUnusedImports.render(this) @@ -76,4 +80,25 @@ internal class PaginatorGeneratorTest { } } } + + private fun enableNewSmithyRuntime(): ObjectNode = ObjectNode.objectNodeBuilder() + .withMember( + "codegen", + ObjectNode.objectNodeBuilder() + .withMember("enableNewSmithyRuntime", StringNode.from("orchestrator")).build(), + ) + .build() + + @Test + fun `generate paginators that compile`() { + clientIntegrationTest( + model, + params = IntegrationTestParams(additionalSettings = enableNewSmithyRuntime()), + ) { clientCodegenContext, rustCrate -> + rustCrate.integrationTest("paginators_generated") { + Attribute.AllowUnusedImports.render(this) + rust("use ${clientCodegenContext.moduleUseName()}::operation::paginated_list::paginator::PaginatedListPaginator;") + } + } + } } From 9b340fb89e0f0e303d0c4f3af5dfc8216d95ba8f Mon Sep 17 00:00:00 2001 From: 82marbag <69267416+82marbag@users.noreply.github.com> Date: Thu, 11 May 2023 16:02:03 +0100 Subject: [PATCH 089/253] OperationShape::NAME as ShapeId (#2678) See https://github.com/awslabs/smithy-rs/issues/2634 ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --------- Signed-off-by: Daniele Ahmed --- CHANGELOG.next.toml | 8 ++ .../generators/ServerOperationGenerator.kt | 4 +- .../generators/ServerServiceGenerator.kt | 7 +- examples/pokemon-service/src/plugin.rs | 11 +-- .../aws-smithy-http-server/src/extension.rs | 80 ++++++------------- .../src/instrumentation/layer.rs | 14 ++-- .../src/instrumentation/mod.rs | 4 +- .../src/instrumentation/plugin.rs | 3 +- .../src/instrumentation/service.rs | 18 +++-- .../aws-smithy-http-server/src/lib.rs | 1 + .../src/operation/mod.rs | 6 +- .../src/operation/shape.rs | 3 +- .../src/plugin/closure.rs | 29 ++++--- .../src/plugin/filter.rs | 26 +++--- .../aws-smithy-http-server/src/plugin/mod.rs | 25 +++--- .../src/plugin/pipeline.rs | 11 +-- .../aws-smithy-http-server/src/shape_id.rs | 59 ++++++++++++++ 17 files changed, 184 insertions(+), 125 deletions(-) create mode 100644 rust-runtime/aws-smithy-http-server/src/shape_id.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 7267f1f4e3..93b581e99d 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -29,3 +29,11 @@ message = "Avoid extending IMDS credentials' expiry unconditionally, which may i references = ["smithy-rs#2687", "smithy-rs#2694"] meta = { "breaking" = false, "tada" = false, "bug" = true } author = "ysaito1001" + +[[smithy-rs]] +message = """`ShapeId` is the new structure used to represent a shape, with its absolute name, namespace and name. +`OperationExtension`'s members are replaced by the `ShapeId` and operations' names are now replced by a `ShapeId`. +""" +author = "82marbag" +references = ["smithy-rs#2678"] +meta = { "breaking" = true, "tada" = false, "bug" = false } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt index 451aa65a85..a8ce8cbf06 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt @@ -13,6 +13,7 @@ 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.CodegenContext +import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency @@ -49,12 +50,13 @@ class ServerOperationGenerator( val requestFmt = generator.requestFmt() val responseFmt = generator.responseFmt() + val operationIdAbsolute = operationId.toString().replace("#", "##") writer.rustTemplate( """ pub struct $operationName; impl #{SmithyHttpServer}::operation::OperationShape for $operationName { - const NAME: &'static str = "${operationId.toString().replace("#", "##")}"; + const NAME: #{SmithyHttpServer}::shape_id::ShapeId = #{SmithyHttpServer}::shape_id::ShapeId::new(${operationIdAbsolute.dq()}, ${operationId.namespace.dq()}, ${operationId.name.dq()}); type Input = crate::input::${operationName}Input; type Output = crate::output::${operationName}Output; diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt index 160f9fd685..d78a7aa903 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt @@ -478,13 +478,13 @@ class ServerServiceGenerator( } private fun missingOperationsError(): Writable = writable { - rust( + rustTemplate( """ /// The error encountered when calling the [`$builderName::build`] method if one or more operation handlers are not /// specified. ##[derive(Debug)] pub struct MissingOperationsError { - operation_names2setter_methods: std::collections::HashMap<&'static str, &'static str>, + operation_names2setter_methods: std::collections::HashMap<#{SmithyHttpServer}::shape_id::ShapeId, &'static str>, } impl std::fmt::Display for MissingOperationsError { @@ -495,7 +495,7 @@ class ServerServiceGenerator( We are missing handlers for the following operations:\n", )?; for operation_name in self.operation_names2setter_methods.keys() { - writeln!(f, "- {}", operation_name)?; + writeln!(f, "- {}", operation_name.absolute())?; } writeln!(f, "\nUse the dedicated methods on `$builderName` to register the missing handlers:")?; @@ -508,6 +508,7 @@ class ServerServiceGenerator( impl std::error::Error for MissingOperationsError {} """, + *codegenScope, ) } diff --git a/examples/pokemon-service/src/plugin.rs b/examples/pokemon-service/src/plugin.rs index 8ce0e50d09..ea6ee09d91 100644 --- a/examples/pokemon-service/src/plugin.rs +++ b/examples/pokemon-service/src/plugin.rs @@ -8,6 +8,7 @@ use aws_smithy_http_server::{ operation::{Operation, OperationShape}, plugin::{Plugin, PluginPipeline, PluginStack}, + shape_id::ShapeId, }; use tower::{layer::util::Stack, Layer, Service}; @@ -17,7 +18,7 @@ use std::task::{Context, Poll}; #[derive(Clone, Debug)] pub struct PrintService { inner: S, - name: &'static str, + id: ShapeId, } impl Service for PrintService @@ -33,7 +34,7 @@ where } fn call(&mut self, req: R) -> Self::Future { - println!("Hi {}", self.name); + println!("Hi {}", self.id.absolute()); self.inner.call(req) } } @@ -41,7 +42,7 @@ where /// A [`Layer`] which constructs the [`PrintService`]. #[derive(Debug)] pub struct PrintLayer { - name: &'static str, + id: ShapeId, } impl Layer for PrintLayer { type Service = PrintService; @@ -49,7 +50,7 @@ impl Layer for PrintLayer { fn layer(&self, service: S) -> Self::Service { PrintService { inner: service, - name: self.name, + id: self.id.clone(), } } } @@ -66,7 +67,7 @@ where type Layer = Stack; fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { name: Op::NAME }) + input.layer(PrintLayer { id: Op::NAME }) } } diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index 2a8b664cdd..371df01529 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -19,15 +19,18 @@ //! //! [extensions]: https://docs.rs/http/latest/http/struct.Extensions.html -use std::{fmt, future::Future, ops::Deref, pin::Pin, task::Context, task::Poll}; +use std::hash::Hash; +use std::{fmt, fmt::Debug, future::Future, ops::Deref, pin::Pin, task::Context, task::Poll}; +use crate::extension; use futures_util::ready; use futures_util::TryFuture; use thiserror::Error; use tower::{layer::util::Stack, Layer, Service}; use crate::operation::{Operation, OperationShape}; -use crate::plugin::{plugin_from_operation_name_fn, OperationNameFn, Plugin, PluginPipeline, PluginStack}; +use crate::plugin::{plugin_from_operation_id_fn, OperationIdFn, Plugin, PluginPipeline, PluginStack}; +use crate::shape_id::ShapeId; pub use crate::request::extension::{Extension, MissingExtension}; @@ -35,13 +38,8 @@ pub use crate::request::extension::{Extension, MissingExtension}; /// This extension type is inserted, via the [`OperationExtensionPlugin`], whenever it has been correctly determined /// that the request should be routed to a particular operation. The operation handler might not even get invoked /// because the request fails to deserialize into the modeled operation input. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct OperationExtension { - absolute: &'static str, - - namespace: &'static str, - name: &'static str, -} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct OperationExtension(pub ShapeId); /// An error occurred when parsing an absolute operation shape ID. #[derive(Debug, Clone, Error, PartialEq, Eq)] @@ -51,36 +49,6 @@ pub enum ParseError { MissingNamespace, } -#[allow(deprecated)] -impl OperationExtension { - /// Creates a new [`OperationExtension`] from the absolute shape ID. - pub fn new(absolute_operation_id: &'static str) -> Result { - let (namespace, name) = absolute_operation_id - .rsplit_once('#') - .ok_or(ParseError::MissingNamespace)?; - Ok(Self { - absolute: absolute_operation_id, - namespace, - name, - }) - } - - /// Returns the Smithy model namespace. - pub fn namespace(&self) -> &'static str { - self.namespace - } - - /// Returns the Smithy operation name. - pub fn name(&self) -> &'static str { - self.name - } - - /// Returns the absolute operation shape ID. - pub fn absolute(&self) -> &'static str { - self.absolute - } -} - pin_project_lite::pin_project! { /// The [`Service::Future`] of [`OperationExtensionService`] - inserts an [`OperationExtension`] into the /// [`http::Response]`. @@ -154,7 +122,7 @@ impl Layer for OperationExtensionLayer { } /// A [`Plugin`] which applies [`OperationExtensionLayer`] to every operation. -pub struct OperationExtensionPlugin(OperationNameFn OperationExtensionLayer>); +pub struct OperationExtensionPlugin(OperationIdFn OperationExtensionLayer>); impl fmt::Debug for OperationExtensionPlugin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -170,7 +138,7 @@ where type Layer = Stack; fn map(&self, input: Operation) -> Operation { - OperationExtensionLayer> as Plugin>::map(&self.0, input) + OperationExtensionLayer> as Plugin>::map(&self.0, input) } } @@ -184,9 +152,8 @@ pub trait OperationExtensionExt

{ impl

OperationExtensionExt

for PluginPipeline

{ fn insert_operation_extension(self) -> PluginPipeline> { - let plugin = OperationExtensionPlugin(plugin_from_operation_name_fn(|name| { - let operation_extension = OperationExtension::new(name).expect("Operation name is malformed, this should never happen. Please file an issue against https://github.com/awslabs/smithy-rs"); - OperationExtensionLayer(operation_extension) + let plugin = OperationExtensionPlugin(plugin_from_operation_id_fn(|shape_id| { + OperationExtensionLayer(extension::OperationExtension(shape_id)) })); self.push(plugin) } @@ -243,28 +210,27 @@ mod tests { #[test] fn ext_accept() { let value = "com.amazonaws.ebs#CompleteSnapshot"; - let ext = OperationExtension::new(value).unwrap(); + let ext = ShapeId::new( + "com.amazonaws.ebs#CompleteSnapshot", + "com.amazonaws.ebs", + "CompleteSnapshot", + ); assert_eq!(ext.absolute(), value); assert_eq!(ext.namespace(), "com.amazonaws.ebs"); assert_eq!(ext.name(), "CompleteSnapshot"); } - #[test] - fn ext_reject() { - let value = "CompleteSnapshot"; - assert_eq!( - OperationExtension::new(value).unwrap_err(), - ParseError::MissingNamespace - ) - } - #[tokio::test] async fn plugin() { struct DummyOp; impl OperationShape for DummyOp { - const NAME: &'static str = "com.amazonaws.ebs#CompleteSnapshot"; + const NAME: ShapeId = ShapeId::new( + "com.amazonaws.ebs#CompleteSnapshot", + "com.amazonaws.ebs", + "CompleteSnapshot", + ); type Input = (); type Output = (); @@ -283,8 +249,8 @@ mod tests { // Check for `OperationExtension`. let response = svc.oneshot(http::Request::new(())).await.unwrap(); - let expected = OperationExtension::new(DummyOp::NAME).unwrap(); + let expected = DummyOp::NAME; let actual = response.extensions().get::().unwrap(); - assert_eq!(*actual, expected); + assert_eq!(actual.0, expected); } } diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs index 956fd6b24d..c070d1297e 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs @@ -5,21 +5,23 @@ use tower::Layer; +use crate::shape_id::ShapeId; + use super::{InstrumentOperation, MakeIdentity}; /// A [`Layer`] used to apply [`InstrumentOperation`]. #[derive(Debug)] pub struct InstrumentLayer { - operation_name: &'static str, + operation_id: ShapeId, make_request: RequestMakeFmt, make_response: ResponseMakeFmt, } impl InstrumentLayer { /// Constructs a new [`InstrumentLayer`] with no data redacted. - pub fn new(operation_name: &'static str) -> Self { + pub fn new(operation_id: ShapeId) -> Self { Self { - operation_name, + operation_id, make_request: MakeIdentity, make_response: MakeIdentity, } @@ -32,7 +34,7 @@ impl InstrumentLayer(self, make_request: R) -> InstrumentLayer { InstrumentLayer { - operation_name: self.operation_name, + operation_id: self.operation_id, make_request, make_response: self.make_response, } @@ -43,7 +45,7 @@ impl InstrumentLayer(self, make_response: R) -> InstrumentLayer { InstrumentLayer { - operation_name: self.operation_name, + operation_id: self.operation_id, make_request: self.make_request, make_response, } @@ -58,7 +60,7 @@ where type Service = InstrumentOperation; fn layer(&self, service: S) -> Self::Service { - InstrumentOperation::new(service, self.operation_name) + InstrumentOperation::new(service, self.operation_id.clone()) .request_fmt(self.make_request.clone()) .response_fmt(self.make_response.clone()) } diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs index 72fd2af2e6..2519e10137 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs @@ -13,6 +13,7 @@ //! ``` //! # use std::convert::Infallible; //! # use aws_smithy_http_server::instrumentation::{*, sensitivity::{*, headers::*, uri::*}}; +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # use http::{Request, Response}; //! # use tower::{util::service_fn, Service}; //! # async fn service(request: Request<()>) -> Result, Infallible> { @@ -20,6 +21,7 @@ //! # } //! # async fn example() { //! # let service = service_fn(service); +//! # const NAME: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); //! let request = Request::get("http://localhost/a/b/c/d?bar=hidden") //! .header("header-name-a", "hidden") //! .body(()) @@ -47,7 +49,7 @@ //! } //! }) //! .status_code(); -//! let mut service = InstrumentOperation::new(service, "foo-operation") +//! let mut service = InstrumentOperation::new(service, NAME) //! .request_fmt(request_fmt) //! .response_fmt(response_fmt); //! diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs index ce77218603..7da5e2fbeb 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs @@ -26,7 +26,8 @@ where type Layer = Stack>; fn map(&self, operation: Operation) -> Operation { - let layer = InstrumentLayer::new(Op::NAME) + let operation_id = Op::NAME; + let layer = InstrumentLayer::new(operation_id) .request_fmt(Op::request_fmt()) .response_fmt(Op::response_fmt()); operation.layer(layer) diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs index 1047167ffb..76410cfa65 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs @@ -16,6 +16,8 @@ use http::{HeaderMap, Request, Response, StatusCode, Uri}; use tower::Service; use tracing::{debug, debug_span, instrument::Instrumented, Instrument}; +use crate::shape_id::ShapeId; + use super::{MakeDebug, MakeDisplay, MakeIdentity}; pin_project_lite::pin_project! { @@ -87,15 +89,17 @@ where /// /// ``` /// # use aws_smithy_http_server::instrumentation::{sensitivity::{*, uri::*, headers::*}, *}; +/// # use aws_smithy_http_server::shape_id::ShapeId; /// # use tower::{Service, service_fn}; /// # use http::{Request, Response}; /// # async fn f(request: Request<()>) -> Result, ()> { Ok(Response::new(())) } /// # let mut svc = service_fn(f); +/// # const NAME: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); /// let request_fmt = RequestFmt::new() /// .label(|index| index == 1, None) /// .query(|_| QueryMarker { key: false, value: true }); /// let response_fmt = ResponseFmt::new().status_code(); -/// let mut svc = InstrumentOperation::new(svc, "foo-operation") +/// let mut svc = InstrumentOperation::new(svc, NAME) /// .request_fmt(request_fmt) /// .response_fmt(response_fmt); /// # svc.call(Request::new(())); @@ -103,17 +107,17 @@ where #[derive(Debug, Clone)] pub struct InstrumentOperation { inner: S, - operation_name: &'static str, + operation_id: ShapeId, make_request: RequestMakeFmt, make_response: ResponseMakeFmt, } impl InstrumentOperation { /// Constructs a new [`InstrumentOperation`] with no data redacted. - pub fn new(inner: S, operation_name: &'static str) -> Self { + pub fn new(inner: S, operation_id: ShapeId) -> Self { Self { inner, - operation_name, + operation_id, make_request: MakeIdentity, make_response: MakeIdentity, } @@ -127,7 +131,7 @@ impl InstrumentOperation(self, make_request: R) -> InstrumentOperation { InstrumentOperation { inner: self.inner, - operation_name: self.operation_name, + operation_id: self.operation_id, make_request, make_response: self.make_response, } @@ -139,7 +143,7 @@ impl InstrumentOperation(self, make_response: R) -> InstrumentOperation { InstrumentOperation { inner: self.inner, - operation_name: self.operation_name, + operation_id: self.operation_id, make_request: self.make_request, make_response, } @@ -170,7 +174,7 @@ where let span = { let headers = self.make_request.make_debug(request.headers()); let uri = self.make_request.make_display(request.uri()); - debug_span!("request", operation = %self.operation_name, method = %request.method(), %uri, ?headers) + debug_span!("request", operation = %self.operation_id.absolute(), method = %request.method(), %uri, ?headers) }; InstrumentedFuture { diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 6031c53e2a..c6d1175c37 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -28,6 +28,7 @@ pub mod response; pub mod routing; #[doc(hidden)] pub mod runtime_error; +pub mod shape_id; #[doc(inline)] pub(crate) use self::error::Error; diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index 3abf9e2540..d94596b8ad 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -24,6 +24,7 @@ //! is identified with the implementation //! //! ```rust,no_run +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # use aws_smithy_http_server::operation::OperationShape; //! # pub struct CartIdentifier; //! # pub struct ShoppingCart; @@ -31,7 +32,7 @@ //! pub struct GetShopping; //! //! impl OperationShape for GetShopping { -//! const NAME: &'static str = "GetShopping"; +//! const NAME: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); //! //! type Input = CartIdentifier; //! type Output = ShoppingCart; @@ -105,12 +106,13 @@ //! # use std::task::{Poll, Context}; //! # use aws_smithy_http_server::operation::*; //! # use tower::Service; +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # pub struct CartIdentifier; //! # pub struct ShoppingCart; //! # pub enum GetShoppingError {} //! # pub struct GetShopping; //! # impl OperationShape for GetShopping { -//! # const NAME: &'static str = "GetShopping"; +//! # const NAME: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); //! # //! # type Input = CartIdentifier; //! # type Output = ShoppingCart; diff --git a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs index 9990326279..40a915a35b 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs @@ -4,13 +4,14 @@ */ use super::{Handler, IntoService, Normalize, Operation, OperationService}; +use crate::shape_id::ShapeId; /// Models the [Smithy Operation shape]. /// /// [Smithy Operation shape]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#operation pub trait OperationShape { /// The name of the operation. - const NAME: &'static str; + const NAME: ShapeId; /// The operation input. type Input; diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs index 75685c97b9..f1f5951e8a 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs @@ -6,56 +6,59 @@ use tower::layer::util::Stack; use crate::operation::{Operation, OperationShape}; +use crate::shape_id::ShapeId; use super::Plugin; -/// An adapter to convert a `Fn(&'static str) -> Layer` closure into a [`Plugin`]. See [`plugin_from_operation_name_fn`] for more details. -pub struct OperationNameFn { +/// An adapter to convert a `Fn(ShapeId) -> Layer` closure into a [`Plugin`]. See [`plugin_from_operation_id_fn`] for more details. +pub struct OperationIdFn { f: F, } -impl Plugin for OperationNameFn +impl Plugin for OperationIdFn where - F: Fn(&'static str) -> NewLayer, + F: Fn(ShapeId) -> NewLayer, Op: OperationShape, { type Service = S; type Layer = Stack; fn map(&self, input: Operation) -> Operation { - input.layer((self.f)(Op::NAME)) + let operation_id = Op::NAME; + input.layer((self.f)(operation_id)) } } -/// Constructs a [`Plugin`] using a closure over the operation name `F: Fn(&'static str) -> L` where `L` is a HTTP +/// Constructs a [`Plugin`] using a closure over the operation name `F: Fn(ShapeId) -> L` where `L` is a HTTP /// [`Layer`](tower::Layer). /// /// # Example /// /// ```rust -/// use aws_smithy_http_server::plugin::plugin_from_operation_name_fn; +/// use aws_smithy_http_server::plugin::plugin_from_operation_id_fn; +/// use aws_smithy_http_server::shape_id::ShapeId; /// use tower::layer::layer_fn; /// /// // A `Service` which prints the operation name before calling `S`. /// struct PrintService { -/// operation_name: &'static str, +/// operation_name: ShapeId, /// inner: S /// } /// /// // A `Layer` applying `PrintService`. /// struct PrintLayer { -/// operation_name: &'static str +/// operation_name: ShapeId /// } /// /// // Defines a closure taking the operation name to `PrintLayer`. /// let f = |operation_name| PrintLayer { operation_name }; /// /// // This plugin applies the `PrintService` middleware around every operation. -/// let plugin = plugin_from_operation_name_fn(f); +/// let plugin = plugin_from_operation_id_fn(f); /// ``` -pub fn plugin_from_operation_name_fn(f: F) -> OperationNameFn +pub fn plugin_from_operation_id_fn(f: F) -> OperationIdFn where - F: Fn(&'static str) -> L, + F: Fn(ShapeId) -> L, { - OperationNameFn { f } + OperationIdFn { f } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs index 814398b089..a977db8bf2 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs @@ -6,14 +6,15 @@ use super::{either::Either, IdentityPlugin}; use crate::operation::{Operation, OperationShape}; +use crate::shape_id::ShapeId; use super::Plugin; /// Filters the application of an inner [`Plugin`] using a predicate over the /// [`OperationShape::NAME`](crate::operation::OperationShape). /// -/// See [`filter_by_operation_name`] for more details. -pub struct FilterByOperationName { +/// See [`filter_by_operation_id`] for more details. +pub struct FilterByOperationId { inner: Inner, predicate: F, } @@ -24,35 +25,36 @@ pub struct FilterByOperationName { /// # Example /// /// ```rust -/// use aws_smithy_http_server::plugin::filter_by_operation_name; +/// use aws_smithy_http_server::plugin::filter_by_operation_id; +/// use aws_smithy_http_server::shape_id::ShapeId; /// # use aws_smithy_http_server::{plugin::Plugin, operation::{Operation, OperationShape}}; /// # struct Pl; /// # struct CheckHealth; -/// # impl OperationShape for CheckHealth { const NAME: &'static str = ""; type Input = (); type Output = (); type Error = (); } +/// # impl OperationShape for CheckHealth { const NAME: ShapeId = ShapeId::new("ns#CheckHealth", "ns", "CheckHealth"); type Input = (); type Output = (); type Error = (); } /// # impl Plugin<(), CheckHealth, (), ()> for Pl { type Service = (); type Layer = (); fn map(&self, input: Operation<(), ()>) -> Operation<(), ()> { input }} /// # let plugin = Pl; /// # let operation = Operation { inner: (), layer: () }; /// // Prevents `plugin` from being applied to the `CheckHealth` operation. -/// let filtered_plugin = filter_by_operation_name(plugin, |name| name != CheckHealth::NAME); +/// let filtered_plugin = filter_by_operation_id(plugin, |name| name != CheckHealth::NAME); /// let new_operation = filtered_plugin.map(operation); /// ``` -pub fn filter_by_operation_name(plugins: Inner, predicate: F) -> FilterByOperationName +pub fn filter_by_operation_id(plugins: Inner, predicate: F) -> FilterByOperationId where - F: Fn(&str) -> bool, + F: Fn(ShapeId) -> bool, { - FilterByOperationName::new(plugins, predicate) + FilterByOperationId::new(plugins, predicate) } -impl FilterByOperationName { - /// Creates a new [`FilterByOperationName`]. +impl FilterByOperationId { + /// Creates a new [`FilterByOperationId`]. fn new(inner: Inner, predicate: F) -> Self { Self { inner, predicate } } } -impl Plugin for FilterByOperationName +impl Plugin for FilterByOperationId where - F: Fn(&str) -> bool, + F: Fn(ShapeId) -> bool, Inner: Plugin, Op: OperationShape, { diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs index ab3c385720..19fa4b9d32 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -12,27 +12,29 @@ //! //! ``` //! # use aws_smithy_http_server::plugin::*; +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # let layer = (); //! # struct GetPokemonSpecies; -//! # impl GetPokemonSpecies { const NAME: &'static str = ""; }; +//! # impl GetPokemonSpecies { const NAME: ShapeId = ShapeId::new("namespace#name", "namespace", "name"); }; //! // Create a `Plugin` from a HTTP `Layer` //! let plugin = HttpLayer(layer); //! //! // Only apply the layer to operations with name "GetPokemonSpecies" -//! let plugin = filter_by_operation_name(plugin, |name| name == GetPokemonSpecies::NAME); +//! let plugin = filter_by_operation_id(plugin, |name| name == GetPokemonSpecies::NAME); //! ``` //! //! # Construct a [`Plugin`] from a closure that takes as input the operation name //! //! ``` //! # use aws_smithy_http_server::plugin::*; +//! # use aws_smithy_http_server::shape_id::ShapeId; //! // A `tower::Layer` which requires the operation name //! struct PrintLayer { -//! name: &'static str +//! name: ShapeId, //! } //! //! // Create a `Plugin` using `PrintLayer` -//! let plugin = plugin_from_operation_name_fn(|name| PrintLayer { name }); +//! let plugin = plugin_from_operation_id_fn(|name| PrintLayer { name }); //! ``` //! //! # Combine [`Plugin`]s @@ -55,6 +57,7 @@ //! use aws_smithy_http_server::{ //! operation::{Operation, OperationShape}, //! plugin::{Plugin, PluginPipeline, PluginStack}, +//! shape_id::ShapeId, //! }; //! # use tower::{layer::util::Stack, Layer, Service}; //! # use std::task::{Context, Poll}; @@ -63,7 +66,7 @@ //! #[derive(Clone, Debug)] //! pub struct PrintService { //! inner: S, -//! name: &'static str, +//! id: ShapeId, //! } //! //! impl Service for PrintService @@ -79,7 +82,7 @@ //! } //! //! fn call(&mut self, req: R) -> Self::Future { -//! println!("Hi {}", self.name); +//! println!("Hi {}", self.id.absolute()); //! self.inner.call(req) //! } //! } @@ -87,7 +90,7 @@ //! /// A [`Layer`] which constructs the [`PrintService`]. //! #[derive(Debug)] //! pub struct PrintLayer { -//! name: &'static str, +//! id: ShapeId, //! } //! impl Layer for PrintLayer { //! type Service = PrintService; @@ -95,7 +98,7 @@ //! fn layer(&self, service: S) -> Self::Service { //! PrintService { //! inner: service, -//! name: self.name, +//! id: self.id.clone(), //! } //! } //! } @@ -112,7 +115,7 @@ //! type Layer = Stack; //! //! fn map(&self, input: Operation) -> Operation { -//! input.layer(PrintLayer { name: Op::NAME }) +//! input.layer(PrintLayer { id: Op::NAME }) //! } //! } //! ``` @@ -129,9 +132,9 @@ mod stack; use crate::operation::Operation; -pub use closure::{plugin_from_operation_name_fn, OperationNameFn}; +pub use closure::{plugin_from_operation_id_fn, OperationIdFn}; pub use either::Either; -pub use filter::{filter_by_operation_name, FilterByOperationName}; +pub use filter::{filter_by_operation_id, FilterByOperationId}; pub use identity::IdentityPlugin; pub use layer::HttpLayer; pub use pipeline::PluginPipeline; diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs index 2a956922e4..d38ecfd782 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs @@ -35,19 +35,20 @@ use super::HttpLayer; /// /// `PluginPipeline` is itself a [`Plugin`]: you can apply any transformation that expects a /// [`Plugin`] to an entire pipeline. In this case, we want to use -/// [`filter_by_operation_name`](crate::plugin::filter_by_operation_name) to limit the scope of +/// [`filter_by_operation_name`](crate::plugin::filter_by_operation_id) to limit the scope of /// the logging and metrics plugins to the `CheckHealth` operation: /// /// ```rust -/// use aws_smithy_http_server::plugin::{filter_by_operation_name, PluginPipeline}; +/// use aws_smithy_http_server::plugin::{filter_by_operation_id, PluginPipeline}; /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; +/// use aws_smithy_http_server::shape_id::ShapeId; /// # struct CheckHealth; -/// # impl CheckHealth { const NAME: &'static str = "MyName"; } +/// # impl CheckHealth { const NAME: ShapeId = ShapeId::new("namespace#MyName", "namespace", "MyName"); } /// /// // The logging and metrics plugins will only be applied to the `CheckHealth` operation. -/// let operation_specific_pipeline = filter_by_operation_name( +/// let operation_specific_pipeline = filter_by_operation_id( /// PluginPipeline::new() /// .push(LoggingPlugin) /// .push(MetricsPlugin), @@ -156,7 +157,7 @@ impl

PluginPipeline

{ /// { /// // [...] /// fn map(&self, input: Operation) -> Operation { - /// input.layer(PrintLayer { name: Op::NAME }) + /// input.layer(PrintLayer { id: Op::NAME }) /// } /// } /// ``` diff --git a/rust-runtime/aws-smithy-http-server/src/shape_id.rs b/rust-runtime/aws-smithy-http-server/src/shape_id.rs new file mode 100644 index 0000000000..1cb13f0921 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/shape_id.rs @@ -0,0 +1,59 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Extension types. +//! +//! Shape ID is a type that describes a Smithy shape. +//! +//! # Example +//! +//! In the following model: +//! ```smithy +//! namespace smithy.example +//! +//! operation CheckHealth {} +//! ``` +//! +//! - `absolute` is `"smithy.example#CheckHealth"` +//! - `namespace` is `"smithy.example"` +//! - `name` is `"CheckHealth"` + +pub use crate::request::extension::{Extension, MissingExtension}; + +/// Shape ID for a modelled Smithy shape. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ShapeId { + absolute: &'static str, + + namespace: &'static str, + name: &'static str, +} + +impl ShapeId { + /// Constructs a new [`ShapeId`]. This is used by the code-generator which preserves the invariants of the Shape ID format. + #[doc(hidden)] + pub const fn new(absolute: &'static str, namespace: &'static str, name: &'static str) -> Self { + Self { + absolute, + namespace, + name, + } + } + + /// Returns the Smithy operation namespace. + pub fn namespace(&self) -> &'static str { + self.namespace + } + + /// Returns the Smithy operation name. + pub fn name(&self) -> &'static str { + self.name + } + + /// Returns the absolute operation shape ID. + pub fn absolute(&self) -> &'static str { + self.absolute + } +} From 0e90d6502d7474c746670ce879a3c3036623d17e Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 11 May 2023 12:50:13 -0700 Subject: [PATCH 090/253] Fix KMS tests by fixing `Send` bound on fluent send method (#2695) ## Motivation and Context This PR fixes the KMS tests when the generating code in orchestrator mode by ensuring the `Future` returned by `send()` implements `Send`, and also adds a codegen unit test for this so that it's checked at the generic client level. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-sdk-s3/tests/sra_test.rs | 1 + .../ServiceRuntimePluginGenerator.kt | 1 + .../client/FluentClientGenerator.kt | 4 +- .../customizations/HttpAuthDecoratorTest.kt | 22 ++---- .../client/FluentClientGeneratorTest.kt | 79 +++++++++++++++++++ .../client/testutil/TestCodegenSettings.kt | 38 +++++++++ .../aws-smithy-runtime-api/src/client/auth.rs | 2 +- .../src/client/orchestrator.rs | 2 +- .../src/client/runtime_plugin.rs | 23 ++++-- .../src/client/orchestrator.rs | 2 + .../client/runtime_plugin/anonymous_auth.rs | 8 ++ 11 files changed, 155 insertions(+), 27 deletions(-) create mode 100644 codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt create mode 100644 codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestCodegenSettings.kt diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index 6dd793e46b..8ff073d021 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -50,6 +50,7 @@ async fn sra_test() { conn.full_validate(MediaType::Xml).await.expect("failed") } +#[derive(Debug)] struct FixupPlugin { timestamp: SystemTime, } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 7714850f71..25f9a19a5f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -90,6 +90,7 @@ class ServiceRuntimePluginGenerator( fun render(writer: RustWriter, customizations: List) { writer.rustTemplate( """ + ##[derive(Debug)] pub(crate) struct ServiceRuntimePlugin { handle: std::sync::Arc, } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 8460a6aaa3..551a33a14a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -366,13 +366,13 @@ class FluentClientGenerator( """ ##[doc(hidden)] pub async fn send_orchestrator(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { - self.send_orchestrator_with_plugin(Option::>::None).await + self.send_orchestrator_with_plugin(Option::>::None).await } ##[doc(hidden)] // TODO(enableNewSmithyRuntime): Delete when unused /// Equivalent to [`Self::send_orchestrator`] but adds a final runtime plugin to shim missing behavior - pub async fn send_orchestrator_with_plugin(self, final_plugin: Option) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + pub async fn send_orchestrator_with_plugin(self, final_plugin: Option) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { let mut runtime_plugins = #{RuntimePlugins}::new() .with_client_plugin(crate::config::ServiceRuntimePlugin::new(self.handle.clone())); if let Some(config_override) = self.config_override { diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt index bcb5ec2f4e..d0f6d756b2 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt @@ -6,26 +6,16 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import org.junit.jupiter.api.Test -import software.amazon.smithy.model.node.ObjectNode -import software.amazon.smithy.model.node.StringNode +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute 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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest -private fun additionalSettings(): ObjectNode = ObjectNode.objectNodeBuilder() - .withMember( - "codegen", - ObjectNode.objectNodeBuilder() - .withMember("enableNewSmithyRuntime", StringNode.from("orchestrator")).build(), - ) - .build() - class HttpAuthDecoratorTest { private fun codegenScope(runtimeConfig: RuntimeConfig): Array> = arrayOf( "TestConnection" to CargoDependency.smithyClient(runtimeConfig) @@ -38,7 +28,7 @@ class HttpAuthDecoratorTest { fun multipleAuthSchemesSchemeSelection() { clientIntegrationTest( TestModels.allSchemes, - IntegrationTestParams(additionalSettings = additionalSettings()), + TestCodegenSettings.orchestratorModeTestParams, ) { codegenContext, rustCrate -> rustCrate.integrationTest("tests") { val moduleName = codegenContext.moduleUseName() @@ -117,7 +107,7 @@ class HttpAuthDecoratorTest { fun apiKeyInQueryString() { clientIntegrationTest( TestModels.apiKeyInQueryString, - IntegrationTestParams(additionalSettings = additionalSettings()), + TestCodegenSettings.orchestratorModeTestParams, ) { codegenContext, rustCrate -> rustCrate.integrationTest("api_key_applied_to_query_string") { val moduleName = codegenContext.moduleUseName() @@ -162,7 +152,7 @@ class HttpAuthDecoratorTest { fun apiKeyInHeaders() { clientIntegrationTest( TestModels.apiKeyInHeaders, - IntegrationTestParams(additionalSettings = additionalSettings()), + TestCodegenSettings.orchestratorModeTestParams, ) { codegenContext, rustCrate -> rustCrate.integrationTest("api_key_applied_to_headers") { val moduleName = codegenContext.moduleUseName() @@ -208,7 +198,7 @@ class HttpAuthDecoratorTest { fun basicAuth() { clientIntegrationTest( TestModels.basicAuth, - IntegrationTestParams(additionalSettings = additionalSettings()), + TestCodegenSettings.orchestratorModeTestParams, ) { codegenContext, rustCrate -> rustCrate.integrationTest("basic_auth") { val moduleName = codegenContext.moduleUseName() @@ -254,7 +244,7 @@ class HttpAuthDecoratorTest { fun bearerAuth() { clientIntegrationTest( TestModels.bearerAuth, - IntegrationTestParams(additionalSettings = additionalSettings()), + TestCodegenSettings.orchestratorModeTestParams, ) { codegenContext, rustCrate -> rustCrate.integrationTest("bearer_auth") { val moduleName = codegenContext.moduleUseName() diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt new file mode 100644 index 0000000000..6caee6aa0c --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt @@ -0,0 +1,79 @@ +/* + * 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.client + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +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.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.integrationTest + +class FluentClientGeneratorTest { + val model = """ + namespace com.example + use aws.protocols#awsJson1_0 + + @awsJson1_0 + service HelloService { + operations: [SayHello], + version: "1" + } + + operation SayHello { input: TestInput } + structure TestInput {} + """.asSmithyModel() + + @Test + fun `send() future implements Send`() { + val test: (ClientCodegenContext, RustCrate) -> Unit = { codegenContext, rustCrate -> + rustCrate.integrationTest("send_future_is_send") { + val moduleName = codegenContext.moduleUseName() + rustTemplate( + """ + fn check_send(_: T) {} + + ##[test] + fn test() { + let connector = #{TestConnection}::<#{SdkBody}>::new(Vec::new()); + let config = $moduleName::Config::builder() + .endpoint_resolver("http://localhost:1234") + #{set_http_connector} + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + check_send(client.say_hello().send()); + } + """, + "TestConnection" to CargoDependency.smithyClient(codegenContext.runtimeConfig) + .withFeature("test-util").toType() + .resolve("test_connection::TestConnection"), + "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), + "set_http_connector" to writable { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + rust(".http_connector(connector.clone())") + } + }, + ) + } + } + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams, test = test) + clientIntegrationTest( + model, + TestCodegenSettings.orchestratorModeTestParams, + test = test, + ) + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestCodegenSettings.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestCodegenSettings.kt new file mode 100644 index 0000000000..abe664bcc4 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestCodegenSettings.kt @@ -0,0 +1,38 @@ +/* + * 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.model.node.ObjectNode +import software.amazon.smithy.model.node.StringNode +import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams + +object TestCodegenSettings { + // TODO(enableNewSmithyRuntime): Delete this when removing `enableNewSmithyRuntime` feature gate + fun middlewareMode(): ObjectNode = ObjectNode.objectNodeBuilder() + .withMember( + "codegen", + ObjectNode.objectNodeBuilder() + .withMember("enableNewSmithyRuntime", StringNode.from("middleware")).build(), + ) + .build() + + // TODO(enableNewSmithyRuntime): Delete this when removing `enableNewSmithyRuntime` feature gate + fun orchestratorMode(): ObjectNode = ObjectNode.objectNodeBuilder() + .withMember( + "codegen", + ObjectNode.objectNodeBuilder() + .withMember("enableNewSmithyRuntime", StringNode.from("orchestrator")).build(), + ) + .build() + + // TODO(enableNewSmithyRuntime): Delete this when removing `enableNewSmithyRuntime` feature gate + val middlewareModeTestParams get(): IntegrationTestParams = + IntegrationTestParams(additionalSettings = middlewareMode()) + + // TODO(enableNewSmithyRuntime): Delete this when removing `enableNewSmithyRuntime` feature gate + val orchestratorModeTestParams get(): IntegrationTestParams = + IntegrationTestParams(additionalSettings = orchestratorMode()) +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 648684d4ef..8dfedb5148 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -67,7 +67,7 @@ impl AuthOptionResolver for Box { struct HttpAuthSchemesInner { schemes: Vec<(AuthSchemeId, Box)>, } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct HttpAuthSchemes { inner: Arc, } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 404d03b697..37c379a109 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -23,7 +23,7 @@ use std::time::SystemTime; pub type HttpRequest = http::Request; pub type HttpResponse = http::Response; pub type BoxError = Box; -pub type BoxFuture = Pin>>>; +pub type BoxFuture = Pin> + Send>>; pub type Future = NowOrLater, BoxFuture>; pub trait RequestSerializer: Send + Sync + fmt::Debug { diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index 8c2604700b..63aa46ff69 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -5,10 +5,12 @@ use crate::client::interceptors::Interceptors; use crate::config_bag::ConfigBag; +use std::fmt::Debug; -pub type BoxError = Box; +pub type BoxError = Box; +pub type BoxRuntimePlugin = Box; -pub trait RuntimePlugin { +pub trait RuntimePlugin: Debug { fn configure( &self, cfg: &mut ConfigBag, @@ -16,7 +18,7 @@ pub trait RuntimePlugin { ) -> Result<(), BoxError>; } -impl RuntimePlugin for Box { +impl RuntimePlugin for BoxRuntimePlugin { fn configure( &self, cfg: &mut ConfigBag, @@ -28,8 +30,8 @@ impl RuntimePlugin for Box { #[derive(Default)] pub struct RuntimePlugins { - client_plugins: Vec>, - operation_plugins: Vec>, + client_plugins: Vec, + operation_plugins: Vec, } impl RuntimePlugins { @@ -37,12 +39,18 @@ impl RuntimePlugins { Default::default() } - pub fn with_client_plugin(mut self, plugin: impl RuntimePlugin + 'static) -> Self { + pub fn with_client_plugin( + mut self, + plugin: impl RuntimePlugin + Send + Sync + 'static, + ) -> Self { self.client_plugins.push(Box::new(plugin)); self } - pub fn with_operation_plugin(mut self, plugin: impl RuntimePlugin + 'static) -> Self { + pub fn with_operation_plugin( + mut self, + plugin: impl RuntimePlugin + Send + Sync + 'static, + ) -> Self { self.operation_plugins.push(Box::new(plugin)); self } @@ -78,6 +86,7 @@ mod tests { use crate::client::interceptors::Interceptors; use crate::config_bag::ConfigBag; + #[derive(Debug)] struct SomeStruct; impl RuntimePlugin for SomeStruct { diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 0717fe5400..270c1411b5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -262,6 +262,7 @@ mod tests { ) } + #[derive(Debug)] struct TestOperationRuntimePlugin; impl RuntimePlugin for TestOperationRuntimePlugin { @@ -310,6 +311,7 @@ mod tests { } } + #[derive(Debug)] struct FailingInterceptorsOperationRuntimePlugin; impl RuntimePlugin for FailingInterceptorsOperationRuntimePlugin { diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs index f1a893319f..7e958bbbb1 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs @@ -29,8 +29,16 @@ const ANONYMOUS_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("anonymous"); /// **The above components will replace any existing ones!** As such, don't use this plugin unless: /// - You only need to make anonymous requests, such as when interacting with [Open Data](https://aws.amazon.com/opendata/). /// - You're writing orchestrator tests and don't care about authentication. +#[non_exhaustive] +#[derive(Debug, Default)] pub struct AnonymousAuthRuntimePlugin; +impl AnonymousAuthRuntimePlugin { + pub fn new() -> Self { + Self + } +} + impl RuntimePlugin for AnonymousAuthRuntimePlugin { fn configure( &self, From c2da4cf16d062f21f2e458f2b82b3c5ce5134c6c Mon Sep 17 00:00:00 2001 From: 82marbag <69267416+82marbag@users.noreply.github.com> Date: Fri, 12 May 2023 10:07:52 +0100 Subject: [PATCH 091/253] Revert "OperationShape::NAME as ShapeId (#2678)" (#2698) This reverts https://github.com/awslabs/smithy-rs/pull/2678. It is a breaking change and it will be included in the next release. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- CHANGELOG.next.toml | 8 -- .../generators/ServerOperationGenerator.kt | 4 +- .../generators/ServerServiceGenerator.kt | 7 +- examples/pokemon-service/src/plugin.rs | 11 ++- .../aws-smithy-http-server/src/extension.rs | 80 +++++++++++++------ .../src/instrumentation/layer.rs | 14 ++-- .../src/instrumentation/mod.rs | 4 +- .../src/instrumentation/plugin.rs | 3 +- .../src/instrumentation/service.rs | 18 ++--- .../aws-smithy-http-server/src/lib.rs | 1 - .../src/operation/mod.rs | 6 +- .../src/operation/shape.rs | 3 +- .../src/plugin/closure.rs | 29 +++---- .../src/plugin/filter.rs | 26 +++--- .../aws-smithy-http-server/src/plugin/mod.rs | 25 +++--- .../src/plugin/pipeline.rs | 11 ++- .../aws-smithy-http-server/src/shape_id.rs | 59 -------------- 17 files changed, 125 insertions(+), 184 deletions(-) delete mode 100644 rust-runtime/aws-smithy-http-server/src/shape_id.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 93b581e99d..7267f1f4e3 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -29,11 +29,3 @@ message = "Avoid extending IMDS credentials' expiry unconditionally, which may i references = ["smithy-rs#2687", "smithy-rs#2694"] meta = { "breaking" = false, "tada" = false, "bug" = true } author = "ysaito1001" - -[[smithy-rs]] -message = """`ShapeId` is the new structure used to represent a shape, with its absolute name, namespace and name. -`OperationExtension`'s members are replaced by the `ShapeId` and operations' names are now replced by a `ShapeId`. -""" -author = "82marbag" -references = ["smithy-rs#2678"] -meta = { "breaking" = true, "tada" = false, "bug" = false } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt index a8ce8cbf06..451aa65a85 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt @@ -13,7 +13,6 @@ 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.CodegenContext -import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency @@ -50,13 +49,12 @@ class ServerOperationGenerator( val requestFmt = generator.requestFmt() val responseFmt = generator.responseFmt() - val operationIdAbsolute = operationId.toString().replace("#", "##") writer.rustTemplate( """ pub struct $operationName; impl #{SmithyHttpServer}::operation::OperationShape for $operationName { - const NAME: #{SmithyHttpServer}::shape_id::ShapeId = #{SmithyHttpServer}::shape_id::ShapeId::new(${operationIdAbsolute.dq()}, ${operationId.namespace.dq()}, ${operationId.name.dq()}); + const NAME: &'static str = "${operationId.toString().replace("#", "##")}"; type Input = crate::input::${operationName}Input; type Output = crate::output::${operationName}Output; diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt index d78a7aa903..160f9fd685 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt @@ -478,13 +478,13 @@ class ServerServiceGenerator( } private fun missingOperationsError(): Writable = writable { - rustTemplate( + rust( """ /// The error encountered when calling the [`$builderName::build`] method if one or more operation handlers are not /// specified. ##[derive(Debug)] pub struct MissingOperationsError { - operation_names2setter_methods: std::collections::HashMap<#{SmithyHttpServer}::shape_id::ShapeId, &'static str>, + operation_names2setter_methods: std::collections::HashMap<&'static str, &'static str>, } impl std::fmt::Display for MissingOperationsError { @@ -495,7 +495,7 @@ class ServerServiceGenerator( We are missing handlers for the following operations:\n", )?; for operation_name in self.operation_names2setter_methods.keys() { - writeln!(f, "- {}", operation_name.absolute())?; + writeln!(f, "- {}", operation_name)?; } writeln!(f, "\nUse the dedicated methods on `$builderName` to register the missing handlers:")?; @@ -508,7 +508,6 @@ class ServerServiceGenerator( impl std::error::Error for MissingOperationsError {} """, - *codegenScope, ) } diff --git a/examples/pokemon-service/src/plugin.rs b/examples/pokemon-service/src/plugin.rs index ea6ee09d91..8ce0e50d09 100644 --- a/examples/pokemon-service/src/plugin.rs +++ b/examples/pokemon-service/src/plugin.rs @@ -8,7 +8,6 @@ use aws_smithy_http_server::{ operation::{Operation, OperationShape}, plugin::{Plugin, PluginPipeline, PluginStack}, - shape_id::ShapeId, }; use tower::{layer::util::Stack, Layer, Service}; @@ -18,7 +17,7 @@ use std::task::{Context, Poll}; #[derive(Clone, Debug)] pub struct PrintService { inner: S, - id: ShapeId, + name: &'static str, } impl Service for PrintService @@ -34,7 +33,7 @@ where } fn call(&mut self, req: R) -> Self::Future { - println!("Hi {}", self.id.absolute()); + println!("Hi {}", self.name); self.inner.call(req) } } @@ -42,7 +41,7 @@ where /// A [`Layer`] which constructs the [`PrintService`]. #[derive(Debug)] pub struct PrintLayer { - id: ShapeId, + name: &'static str, } impl Layer for PrintLayer { type Service = PrintService; @@ -50,7 +49,7 @@ impl Layer for PrintLayer { fn layer(&self, service: S) -> Self::Service { PrintService { inner: service, - id: self.id.clone(), + name: self.name, } } } @@ -67,7 +66,7 @@ where type Layer = Stack; fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { id: Op::NAME }) + input.layer(PrintLayer { name: Op::NAME }) } } diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index 371df01529..2a8b664cdd 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -19,18 +19,15 @@ //! //! [extensions]: https://docs.rs/http/latest/http/struct.Extensions.html -use std::hash::Hash; -use std::{fmt, fmt::Debug, future::Future, ops::Deref, pin::Pin, task::Context, task::Poll}; +use std::{fmt, future::Future, ops::Deref, pin::Pin, task::Context, task::Poll}; -use crate::extension; use futures_util::ready; use futures_util::TryFuture; use thiserror::Error; use tower::{layer::util::Stack, Layer, Service}; use crate::operation::{Operation, OperationShape}; -use crate::plugin::{plugin_from_operation_id_fn, OperationIdFn, Plugin, PluginPipeline, PluginStack}; -use crate::shape_id::ShapeId; +use crate::plugin::{plugin_from_operation_name_fn, OperationNameFn, Plugin, PluginPipeline, PluginStack}; pub use crate::request::extension::{Extension, MissingExtension}; @@ -38,8 +35,13 @@ pub use crate::request::extension::{Extension, MissingExtension}; /// This extension type is inserted, via the [`OperationExtensionPlugin`], whenever it has been correctly determined /// that the request should be routed to a particular operation. The operation handler might not even get invoked /// because the request fails to deserialize into the modeled operation input. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct OperationExtension(pub ShapeId); +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct OperationExtension { + absolute: &'static str, + + namespace: &'static str, + name: &'static str, +} /// An error occurred when parsing an absolute operation shape ID. #[derive(Debug, Clone, Error, PartialEq, Eq)] @@ -49,6 +51,36 @@ pub enum ParseError { MissingNamespace, } +#[allow(deprecated)] +impl OperationExtension { + /// Creates a new [`OperationExtension`] from the absolute shape ID. + pub fn new(absolute_operation_id: &'static str) -> Result { + let (namespace, name) = absolute_operation_id + .rsplit_once('#') + .ok_or(ParseError::MissingNamespace)?; + Ok(Self { + absolute: absolute_operation_id, + namespace, + name, + }) + } + + /// Returns the Smithy model namespace. + pub fn namespace(&self) -> &'static str { + self.namespace + } + + /// Returns the Smithy operation name. + pub fn name(&self) -> &'static str { + self.name + } + + /// Returns the absolute operation shape ID. + pub fn absolute(&self) -> &'static str { + self.absolute + } +} + pin_project_lite::pin_project! { /// The [`Service::Future`] of [`OperationExtensionService`] - inserts an [`OperationExtension`] into the /// [`http::Response]`. @@ -122,7 +154,7 @@ impl Layer for OperationExtensionLayer { } /// A [`Plugin`] which applies [`OperationExtensionLayer`] to every operation. -pub struct OperationExtensionPlugin(OperationIdFn OperationExtensionLayer>); +pub struct OperationExtensionPlugin(OperationNameFn OperationExtensionLayer>); impl fmt::Debug for OperationExtensionPlugin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -138,7 +170,7 @@ where type Layer = Stack; fn map(&self, input: Operation) -> Operation { - OperationExtensionLayer> as Plugin>::map(&self.0, input) + OperationExtensionLayer> as Plugin>::map(&self.0, input) } } @@ -152,8 +184,9 @@ pub trait OperationExtensionExt

{ impl

OperationExtensionExt

for PluginPipeline

{ fn insert_operation_extension(self) -> PluginPipeline> { - let plugin = OperationExtensionPlugin(plugin_from_operation_id_fn(|shape_id| { - OperationExtensionLayer(extension::OperationExtension(shape_id)) + let plugin = OperationExtensionPlugin(plugin_from_operation_name_fn(|name| { + let operation_extension = OperationExtension::new(name).expect("Operation name is malformed, this should never happen. Please file an issue against https://github.com/awslabs/smithy-rs"); + OperationExtensionLayer(operation_extension) })); self.push(plugin) } @@ -210,27 +243,28 @@ mod tests { #[test] fn ext_accept() { let value = "com.amazonaws.ebs#CompleteSnapshot"; - let ext = ShapeId::new( - "com.amazonaws.ebs#CompleteSnapshot", - "com.amazonaws.ebs", - "CompleteSnapshot", - ); + let ext = OperationExtension::new(value).unwrap(); assert_eq!(ext.absolute(), value); assert_eq!(ext.namespace(), "com.amazonaws.ebs"); assert_eq!(ext.name(), "CompleteSnapshot"); } + #[test] + fn ext_reject() { + let value = "CompleteSnapshot"; + assert_eq!( + OperationExtension::new(value).unwrap_err(), + ParseError::MissingNamespace + ) + } + #[tokio::test] async fn plugin() { struct DummyOp; impl OperationShape for DummyOp { - const NAME: ShapeId = ShapeId::new( - "com.amazonaws.ebs#CompleteSnapshot", - "com.amazonaws.ebs", - "CompleteSnapshot", - ); + const NAME: &'static str = "com.amazonaws.ebs#CompleteSnapshot"; type Input = (); type Output = (); @@ -249,8 +283,8 @@ mod tests { // Check for `OperationExtension`. let response = svc.oneshot(http::Request::new(())).await.unwrap(); - let expected = DummyOp::NAME; + let expected = OperationExtension::new(DummyOp::NAME).unwrap(); let actual = response.extensions().get::().unwrap(); - assert_eq!(actual.0, expected); + assert_eq!(*actual, expected); } } diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs index c070d1297e..956fd6b24d 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs @@ -5,23 +5,21 @@ use tower::Layer; -use crate::shape_id::ShapeId; - use super::{InstrumentOperation, MakeIdentity}; /// A [`Layer`] used to apply [`InstrumentOperation`]. #[derive(Debug)] pub struct InstrumentLayer { - operation_id: ShapeId, + operation_name: &'static str, make_request: RequestMakeFmt, make_response: ResponseMakeFmt, } impl InstrumentLayer { /// Constructs a new [`InstrumentLayer`] with no data redacted. - pub fn new(operation_id: ShapeId) -> Self { + pub fn new(operation_name: &'static str) -> Self { Self { - operation_id, + operation_name, make_request: MakeIdentity, make_response: MakeIdentity, } @@ -34,7 +32,7 @@ impl InstrumentLayer(self, make_request: R) -> InstrumentLayer { InstrumentLayer { - operation_id: self.operation_id, + operation_name: self.operation_name, make_request, make_response: self.make_response, } @@ -45,7 +43,7 @@ impl InstrumentLayer(self, make_response: R) -> InstrumentLayer { InstrumentLayer { - operation_id: self.operation_id, + operation_name: self.operation_name, make_request: self.make_request, make_response, } @@ -60,7 +58,7 @@ where type Service = InstrumentOperation; fn layer(&self, service: S) -> Self::Service { - InstrumentOperation::new(service, self.operation_id.clone()) + InstrumentOperation::new(service, self.operation_name) .request_fmt(self.make_request.clone()) .response_fmt(self.make_response.clone()) } diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs index 2519e10137..72fd2af2e6 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs @@ -13,7 +13,6 @@ //! ``` //! # use std::convert::Infallible; //! # use aws_smithy_http_server::instrumentation::{*, sensitivity::{*, headers::*, uri::*}}; -//! # use aws_smithy_http_server::shape_id::ShapeId; //! # use http::{Request, Response}; //! # use tower::{util::service_fn, Service}; //! # async fn service(request: Request<()>) -> Result, Infallible> { @@ -21,7 +20,6 @@ //! # } //! # async fn example() { //! # let service = service_fn(service); -//! # const NAME: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); //! let request = Request::get("http://localhost/a/b/c/d?bar=hidden") //! .header("header-name-a", "hidden") //! .body(()) @@ -49,7 +47,7 @@ //! } //! }) //! .status_code(); -//! let mut service = InstrumentOperation::new(service, NAME) +//! let mut service = InstrumentOperation::new(service, "foo-operation") //! .request_fmt(request_fmt) //! .response_fmt(response_fmt); //! diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs index 7da5e2fbeb..ce77218603 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs @@ -26,8 +26,7 @@ where type Layer = Stack>; fn map(&self, operation: Operation) -> Operation { - let operation_id = Op::NAME; - let layer = InstrumentLayer::new(operation_id) + let layer = InstrumentLayer::new(Op::NAME) .request_fmt(Op::request_fmt()) .response_fmt(Op::response_fmt()); operation.layer(layer) diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs index 76410cfa65..1047167ffb 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs @@ -16,8 +16,6 @@ use http::{HeaderMap, Request, Response, StatusCode, Uri}; use tower::Service; use tracing::{debug, debug_span, instrument::Instrumented, Instrument}; -use crate::shape_id::ShapeId; - use super::{MakeDebug, MakeDisplay, MakeIdentity}; pin_project_lite::pin_project! { @@ -89,17 +87,15 @@ where /// /// ``` /// # use aws_smithy_http_server::instrumentation::{sensitivity::{*, uri::*, headers::*}, *}; -/// # use aws_smithy_http_server::shape_id::ShapeId; /// # use tower::{Service, service_fn}; /// # use http::{Request, Response}; /// # async fn f(request: Request<()>) -> Result, ()> { Ok(Response::new(())) } /// # let mut svc = service_fn(f); -/// # const NAME: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); /// let request_fmt = RequestFmt::new() /// .label(|index| index == 1, None) /// .query(|_| QueryMarker { key: false, value: true }); /// let response_fmt = ResponseFmt::new().status_code(); -/// let mut svc = InstrumentOperation::new(svc, NAME) +/// let mut svc = InstrumentOperation::new(svc, "foo-operation") /// .request_fmt(request_fmt) /// .response_fmt(response_fmt); /// # svc.call(Request::new(())); @@ -107,17 +103,17 @@ where #[derive(Debug, Clone)] pub struct InstrumentOperation { inner: S, - operation_id: ShapeId, + operation_name: &'static str, make_request: RequestMakeFmt, make_response: ResponseMakeFmt, } impl InstrumentOperation { /// Constructs a new [`InstrumentOperation`] with no data redacted. - pub fn new(inner: S, operation_id: ShapeId) -> Self { + pub fn new(inner: S, operation_name: &'static str) -> Self { Self { inner, - operation_id, + operation_name, make_request: MakeIdentity, make_response: MakeIdentity, } @@ -131,7 +127,7 @@ impl InstrumentOperation(self, make_request: R) -> InstrumentOperation { InstrumentOperation { inner: self.inner, - operation_id: self.operation_id, + operation_name: self.operation_name, make_request, make_response: self.make_response, } @@ -143,7 +139,7 @@ impl InstrumentOperation(self, make_response: R) -> InstrumentOperation { InstrumentOperation { inner: self.inner, - operation_id: self.operation_id, + operation_name: self.operation_name, make_request: self.make_request, make_response, } @@ -174,7 +170,7 @@ where let span = { let headers = self.make_request.make_debug(request.headers()); let uri = self.make_request.make_display(request.uri()); - debug_span!("request", operation = %self.operation_id.absolute(), method = %request.method(), %uri, ?headers) + debug_span!("request", operation = %self.operation_name, method = %request.method(), %uri, ?headers) }; InstrumentedFuture { diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index c6d1175c37..6031c53e2a 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -28,7 +28,6 @@ pub mod response; pub mod routing; #[doc(hidden)] pub mod runtime_error; -pub mod shape_id; #[doc(inline)] pub(crate) use self::error::Error; diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index d94596b8ad..3abf9e2540 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -24,7 +24,6 @@ //! is identified with the implementation //! //! ```rust,no_run -//! # use aws_smithy_http_server::shape_id::ShapeId; //! # use aws_smithy_http_server::operation::OperationShape; //! # pub struct CartIdentifier; //! # pub struct ShoppingCart; @@ -32,7 +31,7 @@ //! pub struct GetShopping; //! //! impl OperationShape for GetShopping { -//! const NAME: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); +//! const NAME: &'static str = "GetShopping"; //! //! type Input = CartIdentifier; //! type Output = ShoppingCart; @@ -106,13 +105,12 @@ //! # use std::task::{Poll, Context}; //! # use aws_smithy_http_server::operation::*; //! # use tower::Service; -//! # use aws_smithy_http_server::shape_id::ShapeId; //! # pub struct CartIdentifier; //! # pub struct ShoppingCart; //! # pub enum GetShoppingError {} //! # pub struct GetShopping; //! # impl OperationShape for GetShopping { -//! # const NAME: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); +//! # const NAME: &'static str = "GetShopping"; //! # //! # type Input = CartIdentifier; //! # type Output = ShoppingCart; diff --git a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs index 40a915a35b..9990326279 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs @@ -4,14 +4,13 @@ */ use super::{Handler, IntoService, Normalize, Operation, OperationService}; -use crate::shape_id::ShapeId; /// Models the [Smithy Operation shape]. /// /// [Smithy Operation shape]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#operation pub trait OperationShape { /// The name of the operation. - const NAME: ShapeId; + const NAME: &'static str; /// The operation input. type Input; diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs index f1f5951e8a..75685c97b9 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs @@ -6,59 +6,56 @@ use tower::layer::util::Stack; use crate::operation::{Operation, OperationShape}; -use crate::shape_id::ShapeId; use super::Plugin; -/// An adapter to convert a `Fn(ShapeId) -> Layer` closure into a [`Plugin`]. See [`plugin_from_operation_id_fn`] for more details. -pub struct OperationIdFn { +/// An adapter to convert a `Fn(&'static str) -> Layer` closure into a [`Plugin`]. See [`plugin_from_operation_name_fn`] for more details. +pub struct OperationNameFn { f: F, } -impl Plugin for OperationIdFn +impl Plugin for OperationNameFn where - F: Fn(ShapeId) -> NewLayer, + F: Fn(&'static str) -> NewLayer, Op: OperationShape, { type Service = S; type Layer = Stack; fn map(&self, input: Operation) -> Operation { - let operation_id = Op::NAME; - input.layer((self.f)(operation_id)) + input.layer((self.f)(Op::NAME)) } } -/// Constructs a [`Plugin`] using a closure over the operation name `F: Fn(ShapeId) -> L` where `L` is a HTTP +/// Constructs a [`Plugin`] using a closure over the operation name `F: Fn(&'static str) -> L` where `L` is a HTTP /// [`Layer`](tower::Layer). /// /// # Example /// /// ```rust -/// use aws_smithy_http_server::plugin::plugin_from_operation_id_fn; -/// use aws_smithy_http_server::shape_id::ShapeId; +/// use aws_smithy_http_server::plugin::plugin_from_operation_name_fn; /// use tower::layer::layer_fn; /// /// // A `Service` which prints the operation name before calling `S`. /// struct PrintService { -/// operation_name: ShapeId, +/// operation_name: &'static str, /// inner: S /// } /// /// // A `Layer` applying `PrintService`. /// struct PrintLayer { -/// operation_name: ShapeId +/// operation_name: &'static str /// } /// /// // Defines a closure taking the operation name to `PrintLayer`. /// let f = |operation_name| PrintLayer { operation_name }; /// /// // This plugin applies the `PrintService` middleware around every operation. -/// let plugin = plugin_from_operation_id_fn(f); +/// let plugin = plugin_from_operation_name_fn(f); /// ``` -pub fn plugin_from_operation_id_fn(f: F) -> OperationIdFn +pub fn plugin_from_operation_name_fn(f: F) -> OperationNameFn where - F: Fn(ShapeId) -> L, + F: Fn(&'static str) -> L, { - OperationIdFn { f } + OperationNameFn { f } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs index a977db8bf2..814398b089 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs @@ -6,15 +6,14 @@ use super::{either::Either, IdentityPlugin}; use crate::operation::{Operation, OperationShape}; -use crate::shape_id::ShapeId; use super::Plugin; /// Filters the application of an inner [`Plugin`] using a predicate over the /// [`OperationShape::NAME`](crate::operation::OperationShape). /// -/// See [`filter_by_operation_id`] for more details. -pub struct FilterByOperationId { +/// See [`filter_by_operation_name`] for more details. +pub struct FilterByOperationName { inner: Inner, predicate: F, } @@ -25,36 +24,35 @@ pub struct FilterByOperationId { /// # Example /// /// ```rust -/// use aws_smithy_http_server::plugin::filter_by_operation_id; -/// use aws_smithy_http_server::shape_id::ShapeId; +/// use aws_smithy_http_server::plugin::filter_by_operation_name; /// # use aws_smithy_http_server::{plugin::Plugin, operation::{Operation, OperationShape}}; /// # struct Pl; /// # struct CheckHealth; -/// # impl OperationShape for CheckHealth { const NAME: ShapeId = ShapeId::new("ns#CheckHealth", "ns", "CheckHealth"); type Input = (); type Output = (); type Error = (); } +/// # impl OperationShape for CheckHealth { const NAME: &'static str = ""; type Input = (); type Output = (); type Error = (); } /// # impl Plugin<(), CheckHealth, (), ()> for Pl { type Service = (); type Layer = (); fn map(&self, input: Operation<(), ()>) -> Operation<(), ()> { input }} /// # let plugin = Pl; /// # let operation = Operation { inner: (), layer: () }; /// // Prevents `plugin` from being applied to the `CheckHealth` operation. -/// let filtered_plugin = filter_by_operation_id(plugin, |name| name != CheckHealth::NAME); +/// let filtered_plugin = filter_by_operation_name(plugin, |name| name != CheckHealth::NAME); /// let new_operation = filtered_plugin.map(operation); /// ``` -pub fn filter_by_operation_id(plugins: Inner, predicate: F) -> FilterByOperationId +pub fn filter_by_operation_name(plugins: Inner, predicate: F) -> FilterByOperationName where - F: Fn(ShapeId) -> bool, + F: Fn(&str) -> bool, { - FilterByOperationId::new(plugins, predicate) + FilterByOperationName::new(plugins, predicate) } -impl FilterByOperationId { - /// Creates a new [`FilterByOperationId`]. +impl FilterByOperationName { + /// Creates a new [`FilterByOperationName`]. fn new(inner: Inner, predicate: F) -> Self { Self { inner, predicate } } } -impl Plugin for FilterByOperationId +impl Plugin for FilterByOperationName where - F: Fn(ShapeId) -> bool, + F: Fn(&str) -> bool, Inner: Plugin, Op: OperationShape, { diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs index 19fa4b9d32..ab3c385720 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -12,29 +12,27 @@ //! //! ``` //! # use aws_smithy_http_server::plugin::*; -//! # use aws_smithy_http_server::shape_id::ShapeId; //! # let layer = (); //! # struct GetPokemonSpecies; -//! # impl GetPokemonSpecies { const NAME: ShapeId = ShapeId::new("namespace#name", "namespace", "name"); }; +//! # impl GetPokemonSpecies { const NAME: &'static str = ""; }; //! // Create a `Plugin` from a HTTP `Layer` //! let plugin = HttpLayer(layer); //! //! // Only apply the layer to operations with name "GetPokemonSpecies" -//! let plugin = filter_by_operation_id(plugin, |name| name == GetPokemonSpecies::NAME); +//! let plugin = filter_by_operation_name(plugin, |name| name == GetPokemonSpecies::NAME); //! ``` //! //! # Construct a [`Plugin`] from a closure that takes as input the operation name //! //! ``` //! # use aws_smithy_http_server::plugin::*; -//! # use aws_smithy_http_server::shape_id::ShapeId; //! // A `tower::Layer` which requires the operation name //! struct PrintLayer { -//! name: ShapeId, +//! name: &'static str //! } //! //! // Create a `Plugin` using `PrintLayer` -//! let plugin = plugin_from_operation_id_fn(|name| PrintLayer { name }); +//! let plugin = plugin_from_operation_name_fn(|name| PrintLayer { name }); //! ``` //! //! # Combine [`Plugin`]s @@ -57,7 +55,6 @@ //! use aws_smithy_http_server::{ //! operation::{Operation, OperationShape}, //! plugin::{Plugin, PluginPipeline, PluginStack}, -//! shape_id::ShapeId, //! }; //! # use tower::{layer::util::Stack, Layer, Service}; //! # use std::task::{Context, Poll}; @@ -66,7 +63,7 @@ //! #[derive(Clone, Debug)] //! pub struct PrintService { //! inner: S, -//! id: ShapeId, +//! name: &'static str, //! } //! //! impl Service for PrintService @@ -82,7 +79,7 @@ //! } //! //! fn call(&mut self, req: R) -> Self::Future { -//! println!("Hi {}", self.id.absolute()); +//! println!("Hi {}", self.name); //! self.inner.call(req) //! } //! } @@ -90,7 +87,7 @@ //! /// A [`Layer`] which constructs the [`PrintService`]. //! #[derive(Debug)] //! pub struct PrintLayer { -//! id: ShapeId, +//! name: &'static str, //! } //! impl Layer for PrintLayer { //! type Service = PrintService; @@ -98,7 +95,7 @@ //! fn layer(&self, service: S) -> Self::Service { //! PrintService { //! inner: service, -//! id: self.id.clone(), +//! name: self.name, //! } //! } //! } @@ -115,7 +112,7 @@ //! type Layer = Stack; //! //! fn map(&self, input: Operation) -> Operation { -//! input.layer(PrintLayer { id: Op::NAME }) +//! input.layer(PrintLayer { name: Op::NAME }) //! } //! } //! ``` @@ -132,9 +129,9 @@ mod stack; use crate::operation::Operation; -pub use closure::{plugin_from_operation_id_fn, OperationIdFn}; +pub use closure::{plugin_from_operation_name_fn, OperationNameFn}; pub use either::Either; -pub use filter::{filter_by_operation_id, FilterByOperationId}; +pub use filter::{filter_by_operation_name, FilterByOperationName}; pub use identity::IdentityPlugin; pub use layer::HttpLayer; pub use pipeline::PluginPipeline; diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs index d38ecfd782..2a956922e4 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs @@ -35,20 +35,19 @@ use super::HttpLayer; /// /// `PluginPipeline` is itself a [`Plugin`]: you can apply any transformation that expects a /// [`Plugin`] to an entire pipeline. In this case, we want to use -/// [`filter_by_operation_name`](crate::plugin::filter_by_operation_id) to limit the scope of +/// [`filter_by_operation_name`](crate::plugin::filter_by_operation_name) to limit the scope of /// the logging and metrics plugins to the `CheckHealth` operation: /// /// ```rust -/// use aws_smithy_http_server::plugin::{filter_by_operation_id, PluginPipeline}; +/// use aws_smithy_http_server::plugin::{filter_by_operation_name, PluginPipeline}; /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; -/// use aws_smithy_http_server::shape_id::ShapeId; /// # struct CheckHealth; -/// # impl CheckHealth { const NAME: ShapeId = ShapeId::new("namespace#MyName", "namespace", "MyName"); } +/// # impl CheckHealth { const NAME: &'static str = "MyName"; } /// /// // The logging and metrics plugins will only be applied to the `CheckHealth` operation. -/// let operation_specific_pipeline = filter_by_operation_id( +/// let operation_specific_pipeline = filter_by_operation_name( /// PluginPipeline::new() /// .push(LoggingPlugin) /// .push(MetricsPlugin), @@ -157,7 +156,7 @@ impl

PluginPipeline

{ /// { /// // [...] /// fn map(&self, input: Operation) -> Operation { - /// input.layer(PrintLayer { id: Op::NAME }) + /// input.layer(PrintLayer { name: Op::NAME }) /// } /// } /// ``` diff --git a/rust-runtime/aws-smithy-http-server/src/shape_id.rs b/rust-runtime/aws-smithy-http-server/src/shape_id.rs deleted file mode 100644 index 1cb13f0921..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/shape_id.rs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! Extension types. -//! -//! Shape ID is a type that describes a Smithy shape. -//! -//! # Example -//! -//! In the following model: -//! ```smithy -//! namespace smithy.example -//! -//! operation CheckHealth {} -//! ``` -//! -//! - `absolute` is `"smithy.example#CheckHealth"` -//! - `namespace` is `"smithy.example"` -//! - `name` is `"CheckHealth"` - -pub use crate::request::extension::{Extension, MissingExtension}; - -/// Shape ID for a modelled Smithy shape. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct ShapeId { - absolute: &'static str, - - namespace: &'static str, - name: &'static str, -} - -impl ShapeId { - /// Constructs a new [`ShapeId`]. This is used by the code-generator which preserves the invariants of the Shape ID format. - #[doc(hidden)] - pub const fn new(absolute: &'static str, namespace: &'static str, name: &'static str) -> Self { - Self { - absolute, - namespace, - name, - } - } - - /// Returns the Smithy operation namespace. - pub fn namespace(&self) -> &'static str { - self.namespace - } - - /// Returns the Smithy operation name. - pub fn name(&self) -> &'static str { - self.name - } - - /// Returns the absolute operation shape ID. - pub fn absolute(&self) -> &'static str { - self.absolute - } -} From 85433cecd4c567b609c160b28080118698c3b17b Mon Sep 17 00:00:00 2001 From: Burak Date: Fri, 12 May 2023 14:50:03 +0100 Subject: [PATCH 092/253] Python: Link `stubgen.{py,sh}` to `resources/` and read them using `getResource()` (#2692) ## Motivation and Context Currently, we're relying on `runtimeConfig.runtimeCratesPath()` to read `stubgen.{py,sh}` files, but that function only returns a path if we're using `relativePath` for our runtime crates: ``` "runtimeConfig": { "relativePath": "../rust-runtime" }, ``` otherwise it returns `null` and in that case our code generator fails with: ``` Projection pokemon-service failed: java.io.FileNotFoundException: null/aws-smithy-http-server-python/stubgen.py (No such file or directory) java.io.FileNotFoundException: null/aws-smithy-http-server-python/stubgen.py (No such file or directory) ``` With this PR we're linking `stubgen.{py,sh}` to `resources/` folder and reading them using `getResource()` function. ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --- .../smithy/customizations/PythonServerCodegenDecorator.kt | 8 ++------ codegen-server/python/src/main/resources/stubgen.py | 1 + codegen-server/python/src/main/resources/stubgen.sh | 1 + 3 files changed, 4 insertions(+), 6 deletions(-) create mode 120000 codegen-server/python/src/main/resources/stubgen.py create mode 120000 codegen-server/python/src/main/resources/stubgen.sh diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt index ff4b6be482..daa4a7b4dd 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/customizations/PythonServerCodegenDecorator.kt @@ -23,7 +23,6 @@ import software.amazon.smithy.rust.codegen.server.python.smithy.generators.Pytho import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext import software.amazon.smithy.rust.codegen.server.smithy.customizations.AddInternalServerErrorToAllOperationsDecorator import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator -import java.io.File /** * Configure the [lib] section of `Cargo.toml`. @@ -206,14 +205,11 @@ class AddStubgenScriptDecorator : ServerCodegenDecorator { override val order: Byte = 0 override fun extras(codegenContext: ServerCodegenContext, rustCrate: RustCrate) { - val runtimeCratesPath = codegenContext.runtimeConfig.runtimeCratesPath() - val stubgenPythonLocation = "$runtimeCratesPath/aws-smithy-http-server-python/stubgen.py" - val stubgenPythonContent = File(stubgenPythonLocation).readText(Charsets.UTF_8) + val stubgenPythonContent = this::class.java.getResource("/stubgen.py").readText() rustCrate.withFile("stubgen.py") { writeWithNoFormatting("$stubgenPythonContent") } - val stubgenShellLocation = "$runtimeCratesPath/aws-smithy-http-server-python/stubgen.sh" - val stubgenShellContent = File(stubgenShellLocation).readText(Charsets.UTF_8) + val stubgenShellContent = this::class.java.getResource("/stubgen.sh").readText() rustCrate.withFile("stubgen.sh") { writeWithNoFormatting("$stubgenShellContent") } diff --git a/codegen-server/python/src/main/resources/stubgen.py b/codegen-server/python/src/main/resources/stubgen.py new file mode 120000 index 0000000000..6cc4240437 --- /dev/null +++ b/codegen-server/python/src/main/resources/stubgen.py @@ -0,0 +1 @@ +../../../../../rust-runtime/aws-smithy-http-server-python/stubgen.py \ No newline at end of file diff --git a/codegen-server/python/src/main/resources/stubgen.sh b/codegen-server/python/src/main/resources/stubgen.sh new file mode 120000 index 0000000000..5aee6f429d --- /dev/null +++ b/codegen-server/python/src/main/resources/stubgen.sh @@ -0,0 +1 @@ +../../../../../rust-runtime/aws-smithy-http-server-python/stubgen.sh \ No newline at end of file From 92b26df402c2676c14e0e7e861d7356bdb7e9abb Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 12 May 2023 13:12:05 -0500 Subject: [PATCH 093/253] Allow for configuring interceptors on generic client (#2697) ## Description This PR allows users to pass-in interceptors to a generic client. Client-level configured interceptors are eventually added to `client_interceptors` and operation-level interceptors to `operation_interceptors`, both of which are fields in the `aws-smithy-runtime-api::client::interceptors::Interceptors`. The relevant code is generated only in the orchestrator mode. The SDK registers a default set of (client-level & operation-level) interceptors, and the passed-in interceptors by a user will run _after_ those default interceptors. ## Testing - Added integration tests `operation_interceptor_test` and `interceptor_priority` in `sra_test`. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito Co-authored-by: John DiSanti --- aws/rust-runtime/aws-types/src/sdk_config.rs | 2 +- .../smithy/rustsdk/SdkConfigDecorator.kt | 1 - .../aws-sdk-s3/tests/interceptors.rs | 120 +++++++++++ .../aws-sdk-s3/tests/sra_test.rs | 33 +-- .../aws-sdk-s3/tests/util.rs | 56 ++++++ .../HttpConnectorConfigDecorator.kt | 1 - .../InterceptorConfigCustomization.kt | 189 ++++++++++++++++++ .../customize/RequiredCustomizations.kt | 10 +- .../OperationRuntimePluginGenerator.kt | 14 +- .../ServiceRuntimePluginGenerator.kt | 16 +- .../config/ServiceConfigGenerator.kt | 27 ++- .../src/client/interceptors.rs | 91 ++++++++- .../src/client/runtime_plugin.rs | 14 +- .../src/client/orchestrator.rs | 19 +- .../client/runtime_plugin/anonymous_auth.rs | 4 +- .../src/client/test_util/deserializer.rs | 4 +- .../src/client/test_util/serializer.rs | 4 +- 17 files changed, 518 insertions(+), 87 deletions(-) create mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs create mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt diff --git a/aws/rust-runtime/aws-types/src/sdk_config.rs b/aws/rust-runtime/aws-types/src/sdk_config.rs index 3c6c69b612..f3a0301ae8 100644 --- a/aws/rust-runtime/aws-types/src/sdk_config.rs +++ b/aws/rust-runtime/aws-types/src/sdk_config.rs @@ -442,7 +442,7 @@ impl Builder { /// use std::time::Duration; /// use aws_smithy_client::hyper_ext; /// use aws_smithy_client::http_connector::ConnectorSettings; - /// use aws_types::sdk_config::{SdkConfig, Builder}; + /// use aws_types::sdk_config::{Builder, SdkConfig}; /// /// fn override_http_connector(builder: &mut Builder) { /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt index 1d94cd6082..f5d8afcbe2 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt @@ -77,7 +77,6 @@ class GenericSmithySdkConfigSettings : ClientCodegenDecorator { ${section.serviceConfigBuilder}.set_sleep_impl(${section.sdkConfig}.sleep_impl()); ${section.serviceConfigBuilder}.set_http_connector(${section.sdkConfig}.http_connector().cloned()); - """, ) }, diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs new file mode 100644 index 0000000000..208bd8aa8b --- /dev/null +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs @@ -0,0 +1,120 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +mod util; + +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::Client; +use aws_smithy_client::dvr; +use aws_smithy_client::dvr::MediaType; +use aws_smithy_client::erase::DynConnector; +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; +use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; +use aws_smithy_runtime_api::client::orchestrator::RequestTime; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +const LIST_BUCKETS_PATH: &str = "test-data/list-objects-v2.json"; + +#[tokio::test] +async fn operation_interceptor_test() { + tracing_subscriber::fmt::init(); + + let conn = dvr::ReplayingConnection::from_file(LIST_BUCKETS_PATH).unwrap(); + + // Not setting `TestUserAgentInterceptor` here, expecting it to be set later by the + // operation-level config. + let config = aws_sdk_s3::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .http_connector(DynConnector::new(conn.clone())) + .build(); + let client = Client::from_conf(config); + let fixup = util::FixupPlugin { + timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), + }; + + let resp = dbg!( + client + .list_objects_v2() + .config_override( + aws_sdk_s3::Config::builder().interceptor(util::TestUserAgentInterceptor) + ) + .bucket("test-bucket") + .prefix("prefix~") + .send_orchestrator_with_plugin(Some(fixup)) + .await + ); + let resp = resp.expect("valid e2e test"); + assert_eq!(resp.name(), Some("test-bucket")); + conn.full_validate(MediaType::Xml).await.expect("success") +} + +#[derive(Debug)] +struct RequestTimeResetInterceptor; +impl Interceptor for RequestTimeResetInterceptor { + fn modify_before_signing( + &self, + _context: &mut InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { + cfg.set_request_time(RequestTime::new(UNIX_EPOCH)); + + Ok(()) + } +} + +#[derive(Debug)] +struct RequestTimeAdvanceInterceptor(Duration); +impl Interceptor for RequestTimeAdvanceInterceptor { + fn modify_before_signing( + &self, + _context: &mut InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { + let request_time = cfg.request_time().unwrap(); + let request_time = RequestTime::new(request_time.system_time() + self.0); + cfg.set_request_time(request_time); + + Ok(()) + } +} + +#[tokio::test] +async fn interceptor_priority() { + let conn = dvr::ReplayingConnection::from_file(LIST_BUCKETS_PATH).unwrap(); + + // `RequestTimeResetInterceptor` will reset a `RequestTime` to `UNIX_EPOCH`, whose previous + // value should be `SystemTime::now()` set by `FixupPlugin`. + let config = aws_sdk_s3::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .http_connector(DynConnector::new(conn.clone())) + .interceptor(util::TestUserAgentInterceptor) + .interceptor(RequestTimeResetInterceptor) + .build(); + let client = Client::from_conf(config); + let fixup = util::FixupPlugin { + timestamp: SystemTime::now(), + }; + + // `RequestTimeAdvanceInterceptor` configured at the operation level should run after, + // expecting the `RequestTime` to move forward by the specified amount since `UNIX_EPOCH`. + let resp = dbg!( + client + .list_objects_v2() + .config_override(aws_sdk_s3::Config::builder().interceptor( + RequestTimeAdvanceInterceptor(Duration::from_secs(1624036048)) + )) + .bucket("test-bucket") + .prefix("prefix~") + .send_orchestrator_with_plugin(Some(fixup)) + .await + ); + let resp = resp.expect("valid e2e test"); + assert_eq!(resp.name(), Some("test-bucket")); + conn.full_validate(MediaType::Xml).await.expect("success") +} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index 8ff073d021..69f89d1a9d 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -3,18 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_http::user_agent::AwsUserAgent; -use aws_runtime::invocation_id::InvocationId; +mod util; + use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::Client; use aws_smithy_client::dvr; use aws_smithy_client::dvr::MediaType; use aws_smithy_client::erase::DynConnector; -use aws_smithy_runtime_api::client::interceptors::Interceptors; -use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; -use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_runtime_api::config_bag::ConfigBag; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::{Duration, UNIX_EPOCH}; const LIST_BUCKETS_PATH: &str = "test-data/list-objects-v2.json"; @@ -28,16 +24,16 @@ async fn sra_test() { .credentials_provider(Credentials::for_tests()) .region(Region::new("us-east-1")) .http_connector(DynConnector::new(conn.clone())) + .interceptor(util::TestUserAgentInterceptor) .build(); let client = Client::from_conf(config); - let fixup = FixupPlugin { + let fixup = util::FixupPlugin { timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), }; let resp = dbg!( client .list_objects_v2() - .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) .bucket("test-bucket") .prefix("prefix~") .send_orchestrator_with_plugin(Some(fixup)) @@ -47,22 +43,5 @@ async fn sra_test() { // conn.dump_to_file("test-data/list-objects-v2.json").unwrap(); let resp = resp.expect("valid e2e test"); assert_eq!(resp.name(), Some("test-bucket")); - conn.full_validate(MediaType::Xml).await.expect("failed") -} - -#[derive(Debug)] -struct FixupPlugin { - timestamp: SystemTime, -} -impl RuntimePlugin for FixupPlugin { - fn configure( - &self, - cfg: &mut ConfigBag, - _interceptors: &mut Interceptors, - ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { - cfg.set_request_time(RequestTime::new(self.timestamp.clone())); - cfg.put(AwsUserAgent::for_tests()); - cfg.put(InvocationId::for_tests()); - Ok(()) - } + conn.full_validate(MediaType::Xml).await.expect("success") } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs new file mode 100644 index 0000000000..af3d53094f --- /dev/null +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs @@ -0,0 +1,56 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_http::user_agent::AwsUserAgent; +use aws_runtime::invocation_id::InvocationId; +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; +use aws_smithy_runtime_api::client::interceptors::{ + Interceptor, InterceptorContext, InterceptorRegistrar, +}; +use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use http::header::USER_AGENT; +use http::{HeaderName, HeaderValue}; +use std::time::SystemTime; + +pub const X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent"); + +#[derive(Debug)] +pub struct FixupPlugin { + pub timestamp: SystemTime, +} +impl RuntimePlugin for FixupPlugin { + fn configure( + &self, + cfg: &mut ConfigBag, + _interceptors: &mut InterceptorRegistrar, + ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { + cfg.set_request_time(RequestTime::new(self.timestamp.clone())); + cfg.put(InvocationId::for_tests()); + Ok(()) + } +} + +#[derive(Debug)] +pub struct TestUserAgentInterceptor; +impl Interceptor for TestUserAgentInterceptor { + fn modify_before_signing( + &self, + context: &mut InterceptorContext, + _cfg: &mut ConfigBag, + ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { + let headers = context.request_mut().headers_mut(); + let user_agent = AwsUserAgent::for_tests(); + // Overwrite user agent header values provided by `UserAgentInterceptor` + headers.insert(USER_AGENT, HeaderValue::try_from(user_agent.ua_header())?); + headers.insert( + X_AMZ_USER_AGENT, + HeaderValue::try_from(user_agent.aws_ua_header())?, + ); + + Ok(()) + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index d5ecd078b1..ba9c68f5f7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -111,7 +111,6 @@ private class HttpConnectorConfigCustomization( /// use std::time::Duration; /// use aws_smithy_client::hyper_ext; /// use aws_smithy_client::http_connector::ConnectorSettings; - /// use crate::sdk_config::{SdkConfig, Builder}; /// use $moduleUseName::config::{Builder, Config}; /// /// fn override_http_connector(builder: &mut Builder) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt new file mode 100644 index 0000000000..664880b96b --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt @@ -0,0 +1,189 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +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.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.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType + +class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCustomization() { + private val moduleUseName = codegenContext.moduleUseName() + private val runtimeConfig = codegenContext.runtimeConfig + private val interceptors = RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors") + private val codegenScope = arrayOf( + "Interceptor" to interceptors.resolve("Interceptor"), + "SharedInterceptor" to interceptors.resolve("SharedInterceptor"), + ) + + override fun section(section: ServiceConfig) = + writable { + when (section) { + ServiceConfig.ConfigStruct -> rustTemplate( + """ + pub(crate) interceptors: Vec<#{SharedInterceptor}>, + """, + *codegenScope, + ) + + ServiceConfig.BuilderStruct -> + rustTemplate( + """ + interceptors: Vec<#{SharedInterceptor}>, + """, + *codegenScope, + ) + + ServiceConfig.ConfigImpl -> rustTemplate( + """ + // TODO(enableNewSmithyRuntime): Remove this doc hidden upon launch + ##[doc(hidden)] + /// Returns interceptors currently registered by the user. + pub fn interceptors(&self) -> impl Iterator + '_ { + self.interceptors.iter() + } + """, + *codegenScope, + ) + + ServiceConfig.BuilderImpl -> + rustTemplate( + """ + // TODO(enableNewSmithyRuntime): Remove this doc hidden upon launch + ##[doc(hidden)] + /// Add an [`Interceptor`](#{Interceptor}) that runs at specific stages of the request execution pipeline. + /// + /// Interceptors targeted at a certain stage are executed according to the pre-defined priority. + /// The SDK provides a default set of interceptors. An interceptor configured by this method + /// will run after those default interceptors. + /// + /// ## Examples + /// ```no_run + /// ## ##[cfg(test)] + /// ## mod tests { + /// ## ##[test] + /// ## fn example() { + /// use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; + /// use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; + /// use aws_smithy_runtime_api::config_bag::ConfigBag; + /// use $moduleUseName::config::Config; + /// + /// fn base_url() -> String { + /// // ... + /// ## String::new() + /// } + /// + /// ##[derive(Debug)] + /// pub struct UriModifierInterceptor; + /// impl Interceptor for UriModifierInterceptor { + /// fn modify_before_signing( + /// &self, + /// context: &mut InterceptorContext, + /// _cfg: &mut ConfigBag, + /// ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { + /// let request = context.request_mut(); + /// let uri = format!("{}{}", base_url(), request.uri().path()); + /// *request.uri_mut() = uri.parse()?; + /// + /// Ok(()) + /// } + /// } + /// + /// let config = Config::builder() + /// .interceptor(UriModifierInterceptor) + /// .build(); + /// ## } + /// ## } + /// ``` + pub fn interceptor(mut self, interceptor: impl #{Interceptor} + Send + Sync + 'static) -> Self { + self.add_interceptor(#{SharedInterceptor}::new(interceptor)); + self + } + + // TODO(enableNewSmithyRuntime): Remove this doc hidden upon launch + ##[doc(hidden)] + /// Add a [`SharedInterceptor`](#{SharedInterceptor}) that runs at specific stages of the request execution pipeline. + /// + /// Interceptors targeted at a certain stage are executed according to the pre-defined priority. + /// The SDK provides a default set of interceptors. An interceptor configured by this method + /// will run after those default interceptors. + /// + /// ## Examples + /// ```no_run + /// ## ##[cfg(test)] + /// ## mod tests { + /// ## ##[test] + /// ## fn example() { + /// use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; + /// use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext, SharedInterceptor}; + /// use aws_smithy_runtime_api::config_bag::ConfigBag; + /// use $moduleUseName::config::{Builder, Config}; + /// + /// fn base_url() -> String { + /// // ... + /// ## String::new() + /// } + /// + /// fn modify_request_uri(builder: &mut Builder) { + /// ##[derive(Debug)] + /// pub struct UriModifierInterceptor; + /// impl Interceptor for UriModifierInterceptor { + /// fn modify_before_signing( + /// &self, + /// context: &mut InterceptorContext, + /// _cfg: &mut ConfigBag, + /// ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { + /// let request = context.request_mut(); + /// let uri = format!("{}{}", base_url(), request.uri().path()); + /// *request.uri_mut() = uri.parse()?; + /// + /// Ok(()) + /// } + /// } + /// builder.add_interceptor(SharedInterceptor::new(UriModifierInterceptor)); + /// } + /// + /// let mut builder = Config::builder(); + /// modify_request_uri(&mut builder); + /// let config = builder.build(); + /// ## } + /// ## } + /// ``` + pub fn add_interceptor(&mut self, interceptor: #{SharedInterceptor}) -> &mut Self { + self.interceptors.push(interceptor); + self + } + + // TODO(enableNewSmithyRuntime): Remove this doc hidden upon launch + ##[doc(hidden)] + /// Set [`SharedInterceptor`](#{SharedInterceptor})s for the builder. + pub fn set_interceptors(&mut self, interceptors: impl IntoIterator) -> &mut Self { + self.interceptors = interceptors.into_iter().collect(); + self + } + """, + *codegenScope, + ) + + ServiceConfig.BuilderBuild -> rust( + """ + interceptors: self.interceptors, + """, + ) + + ServiceConfig.ToRuntimePlugin -> rust( + """ + interceptors.extend(self.interceptors.iter().cloned()); + """, + ) + + else -> emptySection + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index 42bcc6c368..d43c5706ec 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.Endpoint import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpChecksumRequiredGenerator import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpVersionListCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.IdempotencyTokenGenerator +import software.amazon.smithy.rust.codegen.client.smithy.customizations.InterceptorConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyServiceRuntimePluginCustomization @@ -52,7 +53,14 @@ class RequiredCustomizations : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations + ResiliencyConfigCustomization(codegenContext) + // TODO(enableNewSmithyRuntime): Keep only then branch once we switch to orchestrator + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + baseCustomizations + ResiliencyConfigCustomization(codegenContext) + InterceptorConfigCustomization( + codegenContext, + ) + } else { + baseCustomizations + ResiliencyConfigCustomization(codegenContext) + } override fun libRsCustomizations( codegenContext: ClientCodegenContext, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index 45da1913dc..ac07a1d4b7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -23,19 +23,17 @@ sealed class OperationRuntimePluginSection(name: String) : Section(name) { */ data class AdditionalConfig( val configBagName: String, - val interceptorName: String, + val interceptorRegistrarName: String, val operationShape: OperationShape, ) : OperationRuntimePluginSection("AdditionalConfig") { fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) writer.rustTemplate( """ - $interceptorName.register_operation_interceptor(std::sync::Arc::new(#{interceptor}) as _); + $interceptorRegistrarName.register(#{SharedInterceptor}::new(#{interceptor}) as _); """, - "HttpRequest" to smithyRuntimeApi.resolve("client::orchestrator::HttpRequest"), - "HttpResponse" to smithyRuntimeApi.resolve("client::orchestrator::HttpResponse"), - "Interceptors" to smithyRuntimeApi.resolve("client::interceptors::Interceptors"), "interceptor" to interceptor, + "SharedInterceptor" to smithyRuntimeApi.resolve("client::interceptors::SharedInterceptor"), ) } } @@ -75,14 +73,14 @@ class OperationRuntimePluginGenerator( private val codegenScope = codegenContext.runtimeConfig.let { rc -> val runtimeApi = RuntimeType.smithyRuntimeApi(rc) arrayOf( - "StaticAuthOptionResolverParams" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolverParams"), "AuthOptionResolverParams" to runtimeApi.resolve("client::auth::AuthOptionResolverParams"), "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), + "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), - "Interceptors" to runtimeApi.resolve("client::interceptors::Interceptors"), + "StaticAuthOptionResolverParams" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolverParams"), ) } @@ -95,7 +93,7 @@ class OperationRuntimePluginGenerator( writer.rustTemplate( """ impl #{RuntimePlugin} for $operationStructName { - fn configure(&self, cfg: &mut #{ConfigBag}, _interceptors: &mut #{Interceptors}) -> Result<(), #{BoxError}> { + fn configure(&self, cfg: &mut #{ConfigBag}, _interceptors: &mut #{InterceptorRegistrar}) -> Result<(), #{BoxError}> { use #{ConfigBagAccessors} as _; cfg.set_request_serializer(${operationStructName}RequestSerializer); cfg.set_response_deserializer(${operationStructName}ResponseDeserializer); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 25f9a19a5f..8a9ea8f765 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -32,7 +32,7 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { /** * Hook for adding additional things to config inside service runtime plugins. */ - data class AdditionalConfig(val configBagName: String, val interceptorName: String) : ServiceRuntimePluginSection("AdditionalConfig") { + data class AdditionalConfig(val configBagName: String, val interceptorRegistrarName: String) : ServiceRuntimePluginSection("AdditionalConfig") { /** Adds a value to the config bag */ fun putConfigValue(writer: RustWriter, value: Writable) { writer.rust("$configBagName.put(#T);", value) @@ -43,12 +43,10 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) writer.rustTemplate( """ - $interceptorName.register_client_interceptor(std::sync::Arc::new(#{interceptor}) as _); + $interceptorRegistrarName.register(#{SharedInterceptor}::new(#{interceptor}) as _); """, - "HttpRequest" to smithyRuntimeApi.resolve("client::orchestrator::HttpRequest"), - "HttpResponse" to smithyRuntimeApi.resolve("client::orchestrator::HttpResponse"), - "Interceptors" to smithyRuntimeApi.resolve("client::interceptors::Interceptors"), "interceptor" to interceptor, + "SharedInterceptor" to smithyRuntimeApi.resolve("client::interceptors::SharedInterceptor"), ) } } @@ -77,11 +75,11 @@ class ServiceRuntimePluginGenerator( "DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"), "HttpAuthSchemes" to runtimeApi.resolve("client::auth::HttpAuthSchemes"), "IdentityResolvers" to runtimeApi.resolve("client::identity::IdentityResolvers"), + "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "NeverRetryStrategy" to runtime.resolve("client::retries::strategy::NeverRetryStrategy"), "Params" to endpointTypesGenerator.paramsStruct(), "ResolveEndpoint" to http.resolve("endpoint::ResolveEndpoint"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), - "Interceptors" to runtimeApi.resolve("client::interceptors::Interceptors"), "SharedEndpointResolver" to http.resolve("endpoint::SharedEndpointResolver"), "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), ) @@ -102,7 +100,7 @@ class ServiceRuntimePluginGenerator( } impl #{RuntimePlugin} for ServiceRuntimePlugin { - fn configure(&self, cfg: &mut #{ConfigBag}, _interceptors: &mut #{Interceptors}) -> Result<(), #{BoxError}> { + fn configure(&self, cfg: &mut #{ConfigBag}, _interceptors: &mut #{InterceptorRegistrar}) -> Result<(), #{BoxError}> { use #{ConfigBagAccessors}; // HACK: Put the handle into the config bag to work around config not being fully implemented yet @@ -132,6 +130,10 @@ class ServiceRuntimePluginGenerator( cfg.set_connection(connection); #{additional_config} + + // Client-level Interceptors are registered after default Interceptors. + _interceptors.extend(self.handle.conf.interceptors.iter().cloned()); + Ok(()) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 39ecaf164e..3112798fe0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -87,6 +87,11 @@ sealed class ServiceConfig(name: String) : Section(name) { */ object BuilderBuild : ServiceConfig("BuilderBuild") + /** + * A section for setting up a field to be used by RuntimePlugin + */ + object ToRuntimePlugin : ServiceConfig("ToRuntimePlugin") + /** * A section for extra functionality that needs to be defined with the config module */ @@ -146,6 +151,8 @@ fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : Conf rust("${param.name}: self.${param.name},") } + ServiceConfig.ToRuntimePlugin -> emptySection + else -> emptySection } } @@ -292,18 +299,22 @@ class ServiceConfigGenerator(private val customizations: List Result<(), #{BoxError}> { - // TODO(RuntimePlugins): Put into `cfg` the fields in `self.config_override` that are not `None`. - - Ok(()) - } + fn configure(&self, _cfg: &mut #{ConfigBag}, interceptors: &mut #{InterceptorRegistrar}) -> Result<(), #{BoxError}> """, "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), - "Interceptors" to runtimeApi.resolve("client::interceptors::Interceptors"), - ) + "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), + ) { + rust("// TODO(enableNewSmithyRuntime): Put into `cfg` the fields in `self.config_override` that are not `None`") + + customizations.forEach { + it.section(ServiceConfig.ToRuntimePlugin)(writer) + } + + rust("Ok(())") + } } } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index f025cb5a70..ce4195d68c 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -13,6 +13,7 @@ use crate::config_bag::ConfigBag; use aws_smithy_types::error::display::DisplayErrorContext; pub use context::InterceptorContext; pub use error::{BoxError, InterceptorError}; +use std::ops::Deref; use std::sync::Arc; macro_rules! interceptor_trait_fn { @@ -563,12 +564,58 @@ pub trait Interceptor: std::fmt::Debug { ); } -pub type SharedInterceptor = Arc; +/// Interceptor wrapper that may be shared +#[derive(Debug, Clone)] +pub struct SharedInterceptor(Arc); + +impl SharedInterceptor { + /// Create a new `SharedInterceptor` from `Interceptor` + pub fn new(interceptor: impl Interceptor + Send + Sync + 'static) -> Self { + Self(Arc::new(interceptor)) + } +} + +impl AsRef for SharedInterceptor { + fn as_ref(&self) -> &(dyn Interceptor + 'static) { + self.0.as_ref() + } +} + +impl From> for SharedInterceptor { + fn from(interceptor: Arc) -> Self { + SharedInterceptor(interceptor) + } +} + +impl Deref for SharedInterceptor { + type Target = Arc; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// Collection of [`SharedInterceptor`] that allows for only registration +#[derive(Debug, Clone, Default)] +pub struct InterceptorRegistrar(Vec); + +impl InterceptorRegistrar { + pub fn register(&mut self, interceptor: SharedInterceptor) { + self.0.push(interceptor); + } +} + +impl Extend for InterceptorRegistrar { + fn extend>(&mut self, iter: T) { + for interceptor in iter { + self.register(interceptor); + } + } +} #[derive(Debug, Clone, Default)] pub struct Interceptors { - client_interceptors: Vec, - operation_interceptors: Vec, + client_interceptors: InterceptorRegistrar, + operation_interceptors: InterceptorRegistrar, } macro_rules! interceptor_impl_fn { @@ -619,18 +666,17 @@ impl Interceptors { // Since interceptors can modify the interceptor list (since its in the config bag), copy the list ahead of time. // This should be cheap since the interceptors inside the list are Arcs. self.client_interceptors + .0 .iter() - .chain(self.operation_interceptors.iter()) + .chain(self.operation_interceptors.0.iter()) } - pub fn register_client_interceptor(&mut self, interceptor: SharedInterceptor) -> &mut Self { - self.client_interceptors.push(interceptor); - self + pub fn client_interceptors_mut(&mut self) -> &mut InterceptorRegistrar { + &mut self.client_interceptors } - pub fn register_operation_interceptor(&mut self, interceptor: SharedInterceptor) -> &mut Self { - self.operation_interceptors.push(interceptor); - self + pub fn operation_interceptors_mut(&mut self) -> &mut InterceptorRegistrar { + &mut self.operation_interceptors } interceptor_impl_fn!( @@ -676,3 +722,28 @@ impl Interceptors { interceptor_impl_fn!(mut context, modify_before_completion, AfterDeserialization); interceptor_impl_fn!(context, read_after_execution, AfterDeserialization); } + +#[cfg(test)] +mod tests { + use crate::client::interceptors::{Interceptor, InterceptorRegistrar, SharedInterceptor}; + + #[derive(Debug)] + struct TestInterceptor; + impl Interceptor for TestInterceptor {} + + #[test] + fn register_interceptor() { + let mut registrar = InterceptorRegistrar::default(); + registrar.register(SharedInterceptor::new(TestInterceptor)); + assert_eq!(1, registrar.0.len()); + } + + #[test] + fn bulk_register_interceptors() { + let mut registrar = InterceptorRegistrar::default(); + let number_of_interceptors = 3; + let interceptors = vec![SharedInterceptor::new(TestInterceptor); number_of_interceptors]; + registrar.extend(interceptors); + assert_eq!(number_of_interceptors, registrar.0.len()); + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index 63aa46ff69..ea3754cd06 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::client::interceptors::Interceptors; +use crate::client::interceptors::InterceptorRegistrar; use crate::config_bag::ConfigBag; use std::fmt::Debug; @@ -14,7 +14,7 @@ pub trait RuntimePlugin: Debug { fn configure( &self, cfg: &mut ConfigBag, - interceptors: &mut Interceptors, + interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError>; } @@ -22,7 +22,7 @@ impl RuntimePlugin for BoxRuntimePlugin { fn configure( &self, cfg: &mut ConfigBag, - interceptors: &mut Interceptors, + interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { self.as_ref().configure(cfg, interceptors) } @@ -58,7 +58,7 @@ impl RuntimePlugins { pub fn apply_client_configuration( &self, cfg: &mut ConfigBag, - interceptors: &mut Interceptors, + interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { for plugin in self.client_plugins.iter() { plugin.configure(cfg, interceptors)?; @@ -70,7 +70,7 @@ impl RuntimePlugins { pub fn apply_operation_configuration( &self, cfg: &mut ConfigBag, - interceptors: &mut Interceptors, + interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { for plugin in self.operation_plugins.iter() { plugin.configure(cfg, interceptors)?; @@ -83,7 +83,7 @@ impl RuntimePlugins { #[cfg(test)] mod tests { use super::{BoxError, RuntimePlugin, RuntimePlugins}; - use crate::client::interceptors::Interceptors; + use crate::client::interceptors::InterceptorRegistrar; use crate::config_bag::ConfigBag; #[derive(Debug)] @@ -93,7 +93,7 @@ mod tests { fn configure( &self, _cfg: &mut ConfigBag, - _inters: &mut Interceptors, + _inters: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { todo!() } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 270c1411b5..4b291a87d4 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -73,10 +73,10 @@ pub async fn invoke( let context = InterceptorContext::<()>::new(input); // Client configuration - handle_err!(context => runtime_plugins.apply_client_configuration(cfg, &mut interceptors)); + handle_err!(context => runtime_plugins.apply_client_configuration(cfg, interceptors.client_interceptors_mut())); handle_err!(context => interceptors.client_read_before_execution(&context, cfg)); // Operation configuration - handle_err!(context => runtime_plugins.apply_operation_configuration(cfg, &mut interceptors)); + handle_err!(context => runtime_plugins.apply_operation_configuration(cfg, interceptors.operation_interceptors_mut())); handle_err!(context => interceptors.operation_read_before_execution(&context, cfg)); let operation_timeout_config = cfg.maybe_timeout_config(TimeoutKind::Operation); @@ -216,7 +216,7 @@ async fn make_an_attempt( Ok(checkpoint) } -#[cfg(all(test, feature = "test-util"))] +#[cfg(all(test, feature = "test-util", feature = "anonymous-auth"))] mod tests { use super::invoke; use crate::client::orchestrator::endpoints::{ @@ -234,14 +234,13 @@ mod tests { }; use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; use aws_smithy_runtime_api::client::interceptors::{ - Interceptor, InterceptorContext, Interceptors, + Interceptor, InterceptorContext, InterceptorRegistrar, SharedInterceptor, }; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin, RuntimePlugins}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_runtime_api::type_erasure::TypeErasedBox; use http::StatusCode; - use std::sync::Arc; use tracing_test::traced_test; fn new_request_serializer() -> CannedRequestSerializer { @@ -269,7 +268,7 @@ mod tests { fn configure( &self, cfg: &mut ConfigBag, - _interceptors: &mut Interceptors, + _interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { cfg.set_request_serializer(new_request_serializer()); cfg.set_response_deserializer(new_response_deserializer()); @@ -318,11 +317,11 @@ mod tests { fn configure( &self, _cfg: &mut ConfigBag, - interceptors: &mut Interceptors, + interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { - interceptors.register_client_interceptor(Arc::new(FailingInterceptorA)); - interceptors.register_operation_interceptor(Arc::new(FailingInterceptorB)); - interceptors.register_operation_interceptor(Arc::new(FailingInterceptorC)); + interceptors.register(SharedInterceptor::new(FailingInterceptorA)); + interceptors.register(SharedInterceptor::new(FailingInterceptorB)); + interceptors.register(SharedInterceptor::new(FailingInterceptorC)); Ok(()) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs index 7e958bbbb1..1fbc44b1fd 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs @@ -13,7 +13,7 @@ use aws_smithy_runtime_api::client::auth::{ AuthSchemeId, HttpAuthScheme, HttpAuthSchemes, HttpRequestSigner, }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; -use aws_smithy_runtime_api::client::interceptors::Interceptors; +use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpRequest}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::config_bag::ConfigBag; @@ -43,7 +43,7 @@ impl RuntimePlugin for AnonymousAuthRuntimePlugin { fn configure( &self, cfg: &mut ConfigBag, - _interceptors: &mut Interceptors, + _interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { cfg.set_auth_option_resolver_params(StaticAuthOptionResolverParams::new().into()); cfg.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![ diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs index 0c20268150..3c7bc1610c 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs @@ -4,7 +4,7 @@ */ use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; -use aws_smithy_runtime_api::client::interceptors::Interceptors; +use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; use aws_smithy_runtime_api::client::orchestrator::{ ConfigBagAccessors, HttpResponse, ResponseDeserializer, }; @@ -44,7 +44,7 @@ impl RuntimePlugin for CannedResponseDeserializer { fn configure( &self, cfg: &mut ConfigBag, - _interceptors: &mut Interceptors, + _interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { cfg.set_response_deserializer(Self { inner: Mutex::new(self.take()), diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs index 50e20b9022..d9a2659782 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -4,7 +4,7 @@ */ use aws_smithy_runtime_api::client::interceptors::context::Input; -use aws_smithy_runtime_api::client::interceptors::Interceptors; +use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; use aws_smithy_runtime_api::client::orchestrator::{ ConfigBagAccessors, HttpRequest, RequestSerializer, }; @@ -51,7 +51,7 @@ impl RuntimePlugin for CannedRequestSerializer { fn configure( &self, cfg: &mut ConfigBag, - _interceptors: &mut Interceptors, + _interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { cfg.set_request_serializer(Self { inner: Mutex::new(self.take()), From 040d0e42d5a91c4b57340c3698c5b69c167dea73 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 12 May 2023 15:47:31 -0700 Subject: [PATCH 094/253] Fix client operation name collisions with the standard library prelude (#2696) ## Motivation and Context Operations named `Send` or `Sync` (and probably others) were colliding with the types in the standard library prelude and causing compiler errors. This PR adds tests that include all the type names from the Rust prelude, and fixes the compiler errors they cause. In the future, the `no_implicit_prelude` attribute can be added to certain code generated modules to better enforce that there can't be name collisions, but for now, the `tokio::test` macro doesn't compile with that attribute enabled (and likely other macros from other libraries). ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ --- CHANGELOG.next.toml | 6 + .../rustsdk/AwsFluentClientDecorator.kt | 9 +- .../smithy/rustsdk/AwsPresigningDecorator.kt | 3 +- .../rustsdk/customize/s3/S3Decorator.kt | 3 +- .../codegen/client/smithy/ClientRustModule.kt | 8 +- .../IdempotencyTokenGenerator.kt | 8 +- .../smithy/endpoint/EndpointsDecorator.kt | 14 +- .../smithy/generators/ClientEnumGenerator.kt | 14 +- .../generators/NestedAccessorGenerator.kt | 10 +- .../smithy/generators/PaginatorGenerator.kt | 28 +-- .../ServiceRuntimePluginGenerator.kt | 15 +- .../client/CustomizableOperationGenerator.kt | 58 +++--- .../client/FluentClientGenerator.kt | 43 +++-- .../error/OperationErrorGenerator.kt | 44 +++-- .../http/RequestBindingGenerator.kt | 24 ++- .../protocol/MakeOperationGenerator.kt | 13 +- .../protocol/ProtocolTestGenerator.kt | 2 +- .../protocols/HttpBoundProtocolGenerator.kt | 14 +- .../client/smithy/NamingObstacleCourseTest.kt | 35 ++++ .../StreamingShapeSymbolProviderTest.kt | 4 +- .../codegen/core/rustlang/CargoDependency.kt | 2 +- .../rust/codegen/core/rustlang/RustModule.kt | 21 ++- .../rust/codegen/core/rustlang/RustType.kt | 3 +- .../rust/codegen/core/rustlang/RustWriter.kt | 18 +- .../rust/codegen/core/smithy/RuntimeType.kt | 66 ++++++- .../smithy/generators/BuilderGenerator.kt | 23 ++- .../core/smithy/generators/EnumGenerator.kt | 11 +- .../core/smithy/generators/Instantiator.kt | 15 +- .../core/smithy/generators/UnionGenerator.kt | 18 +- .../generators/error/ErrorImplGenerator.kt | 6 +- .../parse/EventStreamUnmarshallerGenerator.kt | 2 +- .../NamingObstacleCourseTestModels.kt | 172 ++++++++++++++++++ .../codegen/core/rustlang/RustTypeTest.kt | 24 +-- .../codegen/core/rustlang/RustWriterTest.kt | 4 +- .../codegen/core/rustlang/WritableTest.kt | 4 +- .../SmithyTypesPubUseExtraTest.kt | 20 +- .../server/python/smithy/PythonType.kt | 7 + .../PythonServerSymbolProviderTest.kt | 4 +- .../ConstrainedCollectionGenerator.kt | 2 +- .../ConstrainedTraitForEnumGenerator.kt | 6 +- .../smithy/generators/ServerEnumGenerator.kt | 9 +- .../server/smithy/NamingObstacleCourseTest.kt | 32 ++++ .../UnconstrainedShapeSymbolProviderTest.kt | 2 +- .../ConstrainedBlobGeneratorTest.kt | 4 +- .../ConstrainedCollectionGeneratorTest.kt | 4 +- .../generators/ConstrainedMapGeneratorTest.kt | 4 +- .../ConstrainedNumberGeneratorTest.kt | 2 +- .../ConstrainedStringGeneratorTest.kt | 4 +- 48 files changed, 619 insertions(+), 225 deletions(-) create mode 100644 codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/NamingObstacleCourseTest.kt create mode 100644 codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/NamingObstacleCourseTestModels.kt create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/NamingObstacleCourseTest.kt diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 7267f1f4e3..06b3ab3d54 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -29,3 +29,9 @@ message = "Avoid extending IMDS credentials' expiry unconditionally, which may i references = ["smithy-rs#2687", "smithy-rs#2694"] meta = { "breaking" = false, "tada" = false, "bug" = true } author = "ysaito1001" + +[[smithy-rs]] +message = "Fix compiler errors in generated code when naming shapes after types in the Rust standard library prelude." +references = ["smithy-rs#2696"] +meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client"} +author = "jdisanti" diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index cca5c9f910..d0639dbb81 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -223,6 +223,7 @@ private fun renderCustomizableOperationSendMethod( val combinedGenerics = operationGenerics + handleGenerics val codegenScope = arrayOf( + *RuntimeType.preludeScope, "combined_generics_decl" to combinedGenerics.declaration(), "handle_generics_bounds" to handleGenerics.bounds(), "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), @@ -238,11 +239,11 @@ private fun renderCustomizableOperationSendMethod( #{handle_generics_bounds:W} { /// Sends this operation's request - pub async fn send(self) -> Result> + pub async fn send(self) -> #{Result}> where - E: std::error::Error + Send + Sync + 'static, - O: #{ParseHttpResponse}> + Send + Sync + Clone + 'static, - Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + Send + Sync + Clone, + E: std::error::Error + #{Send} + #{Sync} + 'static, + O: #{ParseHttpResponse}> + #{Send} + #{Sync} + #{Clone} + 'static, + Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + #{Send} + #{Sync} + #{Clone}, { self.handle.client.call(self.operation).await } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index 20d280ddf9..f6a3cd889c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -250,6 +250,7 @@ class AwsPresignedFluentBuilderMethod( ) : FluentClientCustomization() { private val codegenScope = ( presigningTypes + arrayOf( + *RuntimeType.preludeScope, "Error" to AwsRuntimeType.presigning().resolve("config::Error"), "SdkError" to RuntimeType.sdkError(runtimeConfig), ) @@ -264,7 +265,7 @@ class AwsPresignedFluentBuilderMethod( pub async fn presigned( self, presigning_config: #{PresigningConfig}, - ) -> Result<#{PresignedRequest}, #{SdkError}<#{OpError}>> + ) -> #{Result}<#{PresignedRequest}, #{SdkError}<#{OpError}>> """, *codegenScope, "OpError" to section.operationErrorType, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index 664494d021..0a2bd05212 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -131,6 +131,7 @@ class FilterEndpointTests( class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContext) { private val runtimeConfig = codegenContext.runtimeConfig private val errorScope = arrayOf( + *RuntimeType.preludeScope, "Bytes" to RuntimeType.Bytes, "ErrorMetadata" to RuntimeType.errorMetadata(runtimeConfig), "ErrorBuilder" to RuntimeType.errorMetadataBuilder(runtimeConfig), @@ -143,7 +144,7 @@ class S3ProtocolOverride(codegenContext: CodegenContext) : RestXml(codegenContex override fun parseHttpErrorMetadata(operationShape: OperationShape): RuntimeType { return ProtocolFunctions.crossOperationFn("parse_http_error_metadata") { fnName -> rustBlockTemplate( - "pub fn $fnName(response_status: u16, _response_headers: &#{HeaderMap}, response_body: &[u8]) -> Result<#{ErrorBuilder}, #{XmlDecodeError}>", + "pub fn $fnName(response_status: u16, _response_headers: &#{HeaderMap}, response_body: &[u8]) -> #{Result}<#{ErrorBuilder}, #{XmlDecodeError}>", *errorScope, ) { rustTemplate( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt index aa1f547dc4..988cac62f4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt @@ -26,6 +26,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider import software.amazon.smithy.rust.codegen.core.smithy.ModuleProvider import software.amazon.smithy.rust.codegen.core.smithy.ModuleProviderContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.contextName import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait @@ -122,7 +123,7 @@ class ClientModuleDocProvider( operation call. For example, this can be used to add an additional HTTP header: ```ignore - ## async fn wrapper() -> Result<(), $moduleUseName::Error> { + ## async fn wrapper() -> #{Result}<(), $moduleUseName::Error> { ## let client: $moduleUseName::Client = unimplemented!(); use #{http}::header::{HeaderName, HeaderValue}; @@ -142,6 +143,7 @@ class ClientModuleDocProvider( ## } ``` """.trimIndent(), + *RuntimeType.preludeScope, "http" to CargoDependency.Http.toDevDependency().toType(), ) } @@ -194,6 +196,10 @@ object ClientModuleProvider : ModuleProvider { operationModuleName, parent = ClientRustModule.Operation, documentationOverride = "Types for the `$contextName` operation.", + // TODO(https://github.com/tokio-rs/tokio/issues/5683): Uncomment the NoImplicitPrelude attribute once this Tokio issue is resolved + // // Disable the Rust prelude since every prelude type should be referenced with its + // // fully qualified name to avoid name collisions with the generated operation shapes. + // additionalAttributes = listOf(Attribute.NoImplicitPrelude) ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt index 8dd67bb9fe..24738036e1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt @@ -8,9 +8,10 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.IdempotencyTokenTrait 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.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.findMemberWithTrait @@ -28,12 +29,13 @@ class IdempotencyTokenGenerator(codegenContext: CodegenContext, operationShape: val memberName = symbolProvider.toMemberName(idempotencyTokenMember) return when (section) { is OperationSection.MutateInput -> writable { - rust( + rustTemplate( """ if ${section.input}.$memberName.is_none() { - ${section.input}.$memberName = Some(${section.config}.make_token.make_idempotency_token()); + ${section.input}.$memberName = #{Some}(${section.config}.make_token.make_idempotency_token()); } """, + *preludeScope, ) } else -> emptySection diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt index bc101412af..0678a4db40 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt @@ -25,6 +25,7 @@ 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.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection @@ -166,6 +167,7 @@ class EndpointsDecorator : ClientCodegenDecorator { override fun section(section: OperationSection): Writable { val codegenScope = arrayOf( + *RuntimeType.preludeScope, "Params" to typesGenerator.paramsStruct(), "ResolveEndpointError" to types.resolveEndpointError, ) @@ -174,10 +176,10 @@ class EndpointsDecorator : ClientCodegenDecorator { rustTemplate( """ let params_result = #{Params}::builder()#{builderFields:W}.build() - .map_err(|err|#{ResolveEndpointError}::from_source("could not construct endpoint parameters", err)); + .map_err(|err| #{ResolveEndpointError}::from_source("could not construct endpoint parameters", err)); let (endpoint_result, params) = match params_result { - Ok(params) => (${section.config}.endpoint_resolver.resolve_endpoint(¶ms), Some(params)), - Err(e) => (Err(e), None) + #{Ok}(params) => (${section.config}.endpoint_resolver.resolve_endpoint(¶ms), #{Some}(params)), + #{Err}(e) => (#{Err}(e), #{None}) }; """, "builderFields" to builderFields(typesGenerator.params, section), @@ -188,7 +190,7 @@ class EndpointsDecorator : ClientCodegenDecorator { is OperationSection.MutateRequest -> writable { // insert the endpoint the bag rustTemplate("${section.request}.properties_mut().insert(endpoint_result);") - rustTemplate("""if let Some(params) = params { ${section.request}.properties_mut().insert(params); }""") + rustTemplate("""if let #{Some}(params) = params { ${section.request}.properties_mut().insert(params); }""", *codegenScope) } else -> emptySection @@ -199,8 +201,8 @@ class EndpointsDecorator : ClientCodegenDecorator { val node = this return writable { when (node) { - is StringNode -> rust("Some(${node.value.dq()}.to_string())") - is BooleanNode -> rust("Some(${node.value})") + is StringNode -> rustTemplate("#{Some}(${node.value.dq()}.to_string())", *RuntimeType.preludeScope) + is BooleanNode -> rustTemplate("#{Some}(${node.value})", *RuntimeType.preludeScope) else -> PANIC("unsupported default value: $node") } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGenerator.kt index d1b87591cb..64f6d9446b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientEnumGenerator.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGeneratorContext import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumMemberModel @@ -60,16 +61,17 @@ data class InfallibleEnumType( } override fun implFromStr(context: EnumGeneratorContext): Writable = writable { - rust( + rustTemplate( """ - impl std::str::FromStr for ${context.enumName} { - type Err = std::convert::Infallible; + impl ::std::str::FromStr for ${context.enumName} { + type Err = ::std::convert::Infallible; - fn from_str(s: &str) -> std::result::Result { - Ok(${context.enumName}::from(s)) + fn from_str(s: &str) -> #{Result}::Err> { + #{Ok}(${context.enumName}::from(s)) } } """, + *preludeScope, ) } @@ -98,7 +100,7 @@ data class InfallibleEnumType( """.trimIndent(), ) context.enumMeta.render(this) - rust("struct $UnknownVariantValue(pub(crate) String);") + rustTemplate("struct $UnknownVariantValue(pub(crate) #{String});", *preludeScope) rustBlock("impl $UnknownVariantValue") { // The generated as_str is not pub as we need to prevent users from calling it on this opaque struct. rustBlock("pub(crate) fn as_str(&self) -> &str") { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt index 2c752814be..138c7eb123 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/NestedAccessorGenerator.kt @@ -15,6 +15,7 @@ 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.CodegenContext 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.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.smithy.mapRustType @@ -72,17 +73,18 @@ class NestedAccessorGenerator(private val codegenContext: CodegenContext) { "" } if (path.isEmpty()) { - rust("Some(input)") + rustTemplate("#{Some}(input)", *preludeScope) } else { val head = path.first() if (symbolProvider.toSymbol(head).isOptional()) { - rust( + rustTemplate( """ let input = match ${ref}input.${symbolProvider.toMemberName(head)} { - None => return None, - Some(t) => t + #{None} => return #{None}, + #{Some}(t) => t }; """, + *preludeScope, ) } else { rust("let input = input.${symbolProvider.toMemberName(head)};") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt index a20a33ecb0..d9709f5ff0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt @@ -16,11 +16,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.render -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.stripOuter 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.findMemberWithTrait @@ -86,6 +86,7 @@ class PaginatorGenerator private constructor( ) private val codegenScope = arrayOf( + *preludeScope, "generics" to generics.decl, "bounds" to generics.bounds, "page_size_setter" to pageSizeSetter(), @@ -158,31 +159,31 @@ class PaginatorGenerator private constructor( /// Create the pagination stream /// /// _Note:_ No requests will be dispatched until the stream is used (eg. with [`.next().await`](tokio_stream::StreamExt::next)). - pub fn send(self) -> impl #{Stream}>> + Unpin + pub fn send(self) -> impl #{Stream}>> + #{Unpin} #{send_bounds:W} { // Move individual fields out of self for the borrow checker let builder = self.builder; let handle = self.handle; - #{fn_stream}::FnStream::new(move |tx| Box::pin(async move { + #{fn_stream}::FnStream::new(move |tx| #{Box}::pin(async move { // Build the input for the first time. If required fields are missing, this is where we'll produce an early error. let mut input = match builder.build().map_err(#{SdkError}::construction_failure) { - Ok(input) => input, - Err(e) => { let _ = tx.send(Err(e)).await; return; } + #{Ok}(input) => input, + #{Err}(e) => { let _ = tx.send(#{Err}(e)).await; return; } }; loop { let op = match input.make_operation(&handle.conf) .await .map_err(#{SdkError}::construction_failure) { - Ok(op) => op, - Err(e) => { - let _ = tx.send(Err(e)).await; + #{Ok}(op) => op, + #{Err}(e) => { + let _ = tx.send(#{Err}(e)).await; return; } }; let resp = handle.client.call(op).await; // If the input member is None or it was an error let done = match resp { - Ok(ref resp) => { + #{Ok}(ref resp) => { let new_token = #{output_token}(resp); let is_empty = new_token.map(|token| token.is_empty()).unwrap_or(true); if !is_empty && new_token == input.$inputTokenMember.as_ref() && self.stop_on_duplicate_token { @@ -192,7 +193,7 @@ class PaginatorGenerator private constructor( is_empty } }, - Err(_) => true, + #{Err}(_) => true, }; if tx.send(resp).await.is_err() { // receiving end was dropped @@ -263,7 +264,7 @@ class PaginatorGenerator private constructor( /// _Note: No requests will be dispatched until the stream is used (eg. with [`.next().await`](tokio_stream::StreamExt::next))._ /// /// To read the entirety of the paginator, use [`.collect::, _>()`](tokio_stream::StreamExt::collect). - pub fn send(self) -> impl #{Stream}>> + Unpin + pub fn send(self) -> impl #{Stream}>> + #{Unpin} #{send_bounds:W} { #{fn_stream}::TryFlatMap::new(self.0.send()).flat_map(|page| #{extract_items}(page).unwrap_or_default().into_iter()) } @@ -284,16 +285,17 @@ class PaginatorGenerator private constructor( val memberName = symbolProvider.toMemberName(it) val pageSizeT = symbolProvider.toSymbol(it).rustType().stripOuter().render(true) - rust( + rustTemplate( """ /// Set the page size /// /// _Note: this method will override any previously set value for `$memberName`_ pub fn page_size(mut self, limit: $pageSizeT) -> Self { - self.builder.$memberName = Some(limit); + self.builder.$memberName = #{Some}(limit); self } """, + *preludeScope, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 8a9ea8f765..d00af9b66b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -14,6 +14,7 @@ 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.RuntimeConfig 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.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations @@ -65,6 +66,8 @@ class ServiceRuntimePluginGenerator( val runtime = RuntimeType.smithyRuntime(rc) val runtimeApi = RuntimeType.smithyRuntimeApi(rc) arrayOf( + *preludeScope, + "Arc" to RuntimeType.Arc, "AnonymousIdentityResolver" to runtimeApi.resolve("client::identity::AnonymousIdentityResolver"), "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), @@ -90,17 +93,17 @@ class ServiceRuntimePluginGenerator( """ ##[derive(Debug)] pub(crate) struct ServiceRuntimePlugin { - handle: std::sync::Arc, + handle: #{Arc}, } impl ServiceRuntimePlugin { - pub fn new(handle: std::sync::Arc) -> Self { + pub fn new(handle: #{Arc}) -> Self { Self { handle } } } impl #{RuntimePlugin} for ServiceRuntimePlugin { - fn configure(&self, cfg: &mut #{ConfigBag}, _interceptors: &mut #{InterceptorRegistrar}) -> Result<(), #{BoxError}> { + fn configure(&self, cfg: &mut #{ConfigBag}, _interceptors: &mut #{InterceptorRegistrar}) -> #{Result}<(), #{BoxError}> { use #{ConfigBagAccessors}; // HACK: Put the handle into the config bag to work around config not being fully implemented yet @@ -112,7 +115,7 @@ class ServiceRuntimePluginGenerator( cfg.set_http_auth_schemes(http_auth_schemes); // Set an empty auth option resolver to be overridden by operations that need auth. - cfg.set_auth_option_resolver(#{StaticAuthOptionResolver}::new(Vec::new())); + cfg.set_auth_option_resolver(#{StaticAuthOptionResolver}::new(#{Vec}::new())); let endpoint_resolver = #{DefaultEndpointResolver}::<#{Params}>::new( #{SharedEndpointResolver}::from(self.handle.conf.endpoint_resolver())); @@ -123,9 +126,9 @@ class ServiceRuntimePluginGenerator( // TODO(RuntimePlugins): Replace this with the correct long-term solution let sleep_impl = self.handle.conf.sleep_impl(); - let connection: Box = self.handle.conf.http_connector() + let connection: #{Box} = self.handle.conf.http_connector() .and_then(move |c| c.connector(&#{ConnectorSettings}::default(), sleep_impl)) - .map(|c| Box::new(#{DynConnectorAdapter}::new(c)) as _) + .map(|c| #{Box}::new(#{DynConnectorAdapter}::new(c)) as _) .expect("connection set"); cfg.set_connection(connection); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index a45bad43d0..d16d927d7f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig 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.smithy.RustCrate /** @@ -54,33 +55,27 @@ class CustomizableOperationGenerator( val combinedGenerics = operationGenerics + handleGenerics val codegenScope = arrayOf( + *preludeScope, + "Arc" to RuntimeType.Arc, + "Infallible" to RuntimeType.stdConvert.resolve("Infallible"), // SDK Types - "http_result" to smithyHttp.resolve("result"), - "http_body" to smithyHttp.resolve("body"), "HttpRequest" to RuntimeType.HttpRequest, "handle_generics_decl" to handleGenerics.declaration(), "handle_generics_bounds" to handleGenerics.bounds(), "operation_generics_decl" to operationGenerics.declaration(), "combined_generics_decl" to combinedGenerics.declaration(), "customize_module" to ClientRustModule.Client.customize, + "SdkBody" to RuntimeType.sdkBody(runtimeConfig), ) writer.rustTemplate( """ - use crate::client::Handle; - - use #{http_body}::SdkBody; - use #{http_result}::SdkError; - - use std::convert::Infallible; - use std::sync::Arc; - /// A wrapper type for [`Operation`](aws_smithy_http::operation::Operation)s that allows for /// customization of the operation before it is sent. A `CustomizableOperation` may be sent /// by calling its [`.send()`][#{customize_module}::CustomizableOperation::send] method. ##[derive(Debug)] pub struct CustomizableOperation#{combined_generics_decl:W} { - pub(crate) handle: Arc, + pub(crate) handle: #{Arc}, pub(crate) operation: Operation#{operation_generics_decl:W}, } @@ -91,19 +86,19 @@ class CustomizableOperationGenerator( /// Allows for customizing the operation's request pub fn map_request( mut self, - f: impl FnOnce(#{HttpRequest}) -> Result<#{HttpRequest}, E>, - ) -> Result { + f: impl #{FnOnce}(#{HttpRequest}<#{SdkBody}>) -> #{Result}<#{HttpRequest}<#{SdkBody}>, E>, + ) -> #{Result} { let (request, response) = self.operation.into_request_response(); let request = request.augment(|req, _props| f(req))?; self.operation = Operation::from_parts(request, response); - Ok(self) + #{Ok}(self) } /// Convenience for `map_request` where infallible direct mutation of request is acceptable - pub fn mutate_request(self, f: impl FnOnce(&mut #{HttpRequest})) -> Self { + pub fn mutate_request(self, f: impl #{FnOnce}(&mut #{HttpRequest}<#{SdkBody}>)) -> Self { self.map_request(|mut req| { f(&mut req); - Result::<_, Infallible>::Ok(req) + #{Result}::<_, #{Infallible}>::Ok(req) }) .expect("infallible") } @@ -111,19 +106,19 @@ class CustomizableOperationGenerator( /// Allows for customizing the entire operation pub fn map_operation( mut self, - f: impl FnOnce(Operation#{operation_generics_decl:W}) -> Result, - ) -> Result { + f: impl #{FnOnce}(Operation#{operation_generics_decl:W}) -> #{Result}, + ) -> #{Result} { self.operation = f(self.operation)?; - Ok(self) + #{Ok}(self) } /// Direct access to read the HTTP request - pub fn request(&self) -> &#{HttpRequest} { + pub fn request(&self) -> &#{HttpRequest}<#{SdkBody}> { self.operation.request() } /// Direct access to mutate the HTTP request - pub fn request_mut(&mut self) -> &mut #{HttpRequest} { + pub fn request_mut(&mut self) -> &mut #{HttpRequest}<#{SdkBody}> { self.operation.request_mut() } } @@ -142,6 +137,7 @@ fun renderCustomizableOperationSend(runtimeConfig: RuntimeConfig, generics: Flue val combinedGenerics = operationGenerics + handleGenerics val codegenScope = arrayOf( + *preludeScope, "combined_generics_decl" to combinedGenerics.declaration(), "handle_generics_bounds" to handleGenerics.bounds(), "ParseHttpResponse" to smithyHttp.resolve("response::ParseHttpResponse"), @@ -160,13 +156,13 @@ fun renderCustomizableOperationSend(runtimeConfig: RuntimeConfig, generics: Flue #{handle_generics_bounds:W} { /// Sends this operation's request - pub async fn send(self) -> Result> + pub async fn send(self) -> #{Result}> where - E: std::error::Error + Send + Sync + 'static, - O: #{ParseHttpResponse}> + Send + Sync + Clone + 'static, - Retry: Send + Sync + Clone, - Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + Send + Sync + Clone, - ::Policy: #{SmithyRetryPolicy} + Clone, + E: std::error::Error + #{Send} + #{Sync} + 'static, + O: #{ParseHttpResponse}> + #{Send} + #{Sync} + #{Clone} + 'static, + Retry: #{Send} + #{Sync} + #{Clone}, + Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + #{Send} + #{Sync} + #{Clone}, + ::Policy: #{SmithyRetryPolicy} + #{Clone}, { self.handle.client.call(self.operation).await } @@ -182,11 +178,11 @@ fun renderCustomizableOperationSend(runtimeConfig: RuntimeConfig, generics: Flue #{handle_generics_bounds:W} { /// Sends this operation's request - pub async fn send(self) -> Result> + pub async fn send(self) -> #{Result}> where - E: std::error::Error + Send + Sync + 'static, - O: #{ParseHttpResponse}> + Send + Sync + Clone + 'static, - Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + Send + Sync + Clone, + E: std::error::Error + #{Send} + #{Sync} + 'static, + O: #{ParseHttpResponse}> + #{Send} + #{Sync} + #{Clone} + 'static, + Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + #{Send} + #{Sync} + #{Clone}, { self.handle.client.call(self.operation).await } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 551a33a14a..10a4f0c02f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -41,6 +41,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations @@ -112,12 +113,12 @@ class FluentClientGenerator( } #{client_docs:W} - ##[derive(std::fmt::Debug)] + ##[derive(::std::fmt::Debug)] pub struct Client#{generics_decl:W} { - handle: std::sync::Arc + handle: #{Arc} } - impl${generics.inst} std::clone::Clone for Client${generics.inst} { + impl${generics.inst} #{Clone} for Client${generics.inst} { fn clone(&self) -> Self { Self { handle: self.handle.clone() } } @@ -133,7 +134,7 @@ class FluentClientGenerator( /// Creates a client with the given service configuration. pub fn with_config(client: #{client}::Client#{smithy_inst:W}, conf: crate::Config) -> Self { Self { - handle: std::sync::Arc::new(Handle { + handle: #{Arc}::new(Handle { client, conf, }) @@ -146,6 +147,8 @@ class FluentClientGenerator( } } """, + *preludeScope, + "Arc" to RuntimeType.Arc, "generics_decl" to generics.decl, "smithy_inst" to generics.smithyInst, "client" to RuntimeType.smithyClient(runtimeConfig), @@ -249,14 +252,15 @@ class FluentClientGenerator( ) { rustTemplate( """ - handle: std::sync::Arc, + handle: #{Arc}, inner: #{Inner}, """, "Inner" to symbolProvider.symbolForBuilder(input), + "Arc" to RuntimeType.Arc, "generics" to generics.decl, ) if (smithyRuntimeMode.generateOrchestrator) { - rust("config_override: std::option::Option,") + rustTemplate("config_override: #{Option},", *preludeScope) } } @@ -270,21 +274,23 @@ class FluentClientGenerator( rust("/// Creates a new `${operationSymbol.name}`.") withBlockTemplate( - "pub(crate) fn new(handle: std::sync::Arc) -> Self {", + "pub(crate) fn new(handle: #{Arc}) -> Self {", "}", + "Arc" to RuntimeType.Arc, "generics" to generics.decl, ) { withBlockTemplate( "Self {", "}", ) { - rust("handle, inner: Default::default(),") + rustTemplate("handle, inner: #{Default}::default(),", *preludeScope) if (smithyRuntimeMode.generateOrchestrator) { - rust("config_override: None,") + rustTemplate("config_override: #{None},", *preludeScope) } } } val middlewareScope = arrayOf( + *preludeScope, "CustomizableOperation" to ClientRustModule.Client.customize.toType() .resolve("CustomizableOperation"), "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), @@ -303,7 +309,7 @@ class FluentClientGenerator( """ /// Consume this builder, creating a customizable operation that can be modified before being /// sent. The operation's inner [http::Request] can be modified as well. - pub async fn customize(self) -> std::result::Result< + pub async fn customize(self) -> #{Result}< #{CustomizableOperation}#{customizable_op_type_params:W}, #{SdkError}<#{OperationError}> > #{send_bounds:W} { @@ -312,12 +318,12 @@ class FluentClientGenerator( .make_operation(&handle.conf) .await .map_err(#{SdkError}::construction_failure)?; - Ok(#{CustomizableOperation} { handle, operation }) + #{Ok}(#{CustomizableOperation} { handle, operation }) } // This function will go away in the near future. Do not rely on it. ##[doc(hidden)] - pub async fn send_middleware(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}>> + pub async fn send_middleware(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}>> #{send_bounds:W} { let op = self.inner.build().map_err(#{SdkError}::construction_failure)? .make_operation(&self.handle.conf) @@ -339,7 +345,7 @@ class FluentClientGenerator( /// By default, any retryable failures will be retried twice. Retry behavior /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be /// set when configuring the client. - pub async fn send(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}>> + pub async fn send(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}>> #{send_bounds:W} { self.send_middleware().await } @@ -350,6 +356,7 @@ class FluentClientGenerator( if (smithyRuntimeMode.generateOrchestrator) { val orchestratorScope = arrayOf( + *preludeScope, "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::orchestrator::HttpResponse"), "OperationError" to errorType, @@ -365,14 +372,14 @@ class FluentClientGenerator( rustTemplate( """ ##[doc(hidden)] - pub async fn send_orchestrator(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { - self.send_orchestrator_with_plugin(Option::>::None).await + pub async fn send_orchestrator(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + self.send_orchestrator_with_plugin(#{Option}::>::None).await } ##[doc(hidden)] // TODO(enableNewSmithyRuntime): Delete when unused /// Equivalent to [`Self::send_orchestrator`] but adds a final runtime plugin to shim missing behavior - pub async fn send_orchestrator_with_plugin(self, final_plugin: Option) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + pub async fn send_orchestrator_with_plugin(self, final_plugin: #{Option}) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { let mut runtime_plugins = #{RuntimePlugins}::new() .with_client_plugin(crate::config::ServiceRuntimePlugin::new(self.handle.clone())); if let Some(config_override) = self.config_override { @@ -393,7 +400,7 @@ class FluentClientGenerator( .unwrap() }) })?; - Ok(#{TypedBox}::<#{OperationOutput}>::assume_from(output).expect("correct output type").unwrap()) + #{Ok}(#{TypedBox}::<#{OperationOutput}>::assume_from(output).expect("correct output type").unwrap()) } """, *orchestratorScope, @@ -409,7 +416,7 @@ class FluentClientGenerator( /// By default, any retryable failures will be retried twice. Retry behavior /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be /// set when configuring the client. - pub async fn send(self) -> std::result::Result<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + pub async fn send(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { self.send_orchestrator().await } """, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt index 26d926a78d..8e17d5bd9d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/error/OperationErrorGenerator.kt @@ -22,10 +22,12 @@ 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.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.RuntimeType.Companion.errorMetadata +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.unhandledError import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.Section @@ -107,14 +109,16 @@ class OperationErrorGenerator( ) } writer.rustBlock("impl #T for ${errorSymbol.name}", createUnhandledError) { - rustBlock( + rustBlockTemplate( """ fn create_unhandled_error( - source: Box, - meta: std::option::Option<#T> + source: #{Box}, + meta: #{Option}<#{ErrorMeta}> ) -> Self """, - errorMetadata, + *preludeScope, + "StdError" to RuntimeType.StdError, + "ErrorMeta" to errorMetadata, ) { rust( """ @@ -129,7 +133,7 @@ class OperationErrorGenerator( } } writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.Display) { - rustBlock("fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result") { + rustBlock("fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result") { delegateToVariants(errors) { writable { rust("_inner.fmt(f)") } } @@ -152,21 +156,28 @@ class OperationErrorGenerator( "impl #T for ${errorSymbol.name}", RuntimeType.provideErrorKind(symbolProvider.config.runtimeConfig), ) { - rustBlock("fn code(&self) -> std::option::Option<&str>") { + rustBlockTemplate("fn code(&self) -> #{Option}<&str>", *preludeScope) { rust("#T::code(self)", RuntimeType.provideErrorMetadataTrait(runtimeConfig)) } - rustBlock("fn retryable_error_kind(&self) -> std::option::Option<#T>", retryErrorKindT) { + rustBlockTemplate( + "fn retryable_error_kind(&self) -> #{Option}<#{ErrorKind}>", + "ErrorKind" to retryErrorKindT, + *preludeScope, + ) { val retryableVariants = errors.filter { it.hasTrait() } if (retryableVariants.isEmpty()) { - rust("None") + rustTemplate("#{None}", *preludeScope) } else { rustBlock("match self") { retryableVariants.forEach { val errorVariantSymbol = symbolProvider.toSymbol(it) - rust("Self::${errorVariantSymbol.name}(inner) => Some(inner.retryable_error_kind()),") + rustTemplate( + "Self::${errorVariantSymbol.name}(inner) => #{Some}(inner.retryable_error_kind()),", + *preludeScope, + ) } - rust("_ => None") + rustTemplate("_ => #{None}", *preludeScope) } } } @@ -176,7 +187,7 @@ class OperationErrorGenerator( writer.rustTemplate( """ /// Creates the `${errorSymbol.name}::Unhandled` variant from any error type. - pub fn unhandled(err: impl Into>) -> Self { + pub fn unhandled(err: impl #{Into}<#{Box}>) -> Self { Self::Unhandled(#{Unhandled}::builder().source(err).build()) } @@ -185,8 +196,9 @@ class OperationErrorGenerator( Self::Unhandled(#{Unhandled}::builder().source(err.clone()).meta(err).build()) } """, + *preludeScope, "error_metadata" to errorMetadata, - "std_error" to RuntimeType.StdError, + "StdError" to RuntimeType.StdError, "Unhandled" to unhandledError(runtimeConfig), ) writer.docs( @@ -216,10 +228,14 @@ class OperationErrorGenerator( } writer.rustBlock("impl #T for ${errorSymbol.name}", RuntimeType.StdError) { - rustBlock("fn source(&self) -> std::option::Option<&(dyn #T + 'static)>", RuntimeType.StdError) { + rustBlockTemplate( + "fn source(&self) -> #{Option}<&(dyn #{StdError} + 'static)>", + *preludeScope, + "StdError" to RuntimeType.StdError, + ) { delegateToVariants(errors) { writable { - rust("Some(_inner)") + rustTemplate("#{Some}(_inner)", *preludeScope) } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGenerator.kt index 148a09d4bd..0a4e5fb09b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/http/RequestBindingGenerator.kt @@ -23,6 +23,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext 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.smithy.generators.OperationBuildError import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError @@ -71,6 +72,7 @@ class RequestBindingGenerator( private val encoder = RuntimeType.smithyTypes(runtimeConfig).resolve("primitive::Encoder") private val codegenScope = arrayOf( + *preludeScope, "BuildError" to runtimeConfig.operationBuildError(), "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, "Input" to symbolProvider.toSymbol(inputShape), @@ -90,11 +92,11 @@ class RequestBindingGenerator( fn update_http_builder( input: &#{Input}, builder: #{HttpRequestBuilder} - ) -> std::result::Result<#{HttpRequestBuilder}, #{BuildError}> + ) -> #{Result}<#{HttpRequestBuilder}, #{BuildError}> """, *codegenScope, ) { - write("let mut uri = String::new();") + rustTemplate("let mut uri = #{String}::new();", *preludeScope) write("uri_base(input, &mut uri)?;") if (hasQuery) { write("uri_query(input, &mut uri)?;") @@ -107,7 +109,7 @@ class RequestBindingGenerator( addHeadersFn, ) } - write("Ok(builder.method(${httpTrait.method.dq()}).uri(uri))") + rustTemplate("#{Ok}(builder.method(${httpTrait.method.dq()}).uri(uri))", *preludeScope) } } @@ -126,7 +128,7 @@ class RequestBindingGenerator( } val combinedArgs = listOf(formatString, *args.toTypedArray()) writer.rustBlockTemplate( - "fn uri_base(_input: &#{Input}, output: &mut String) -> std::result::Result<(), #{BuildError}>", + "fn uri_base(_input: &#{Input}, output: &mut #{String}) -> #{Result}<(), #{BuildError}>", *codegenScope, ) { rust("use #T as _;", RuntimeType.stdFmt.resolve("Write")) @@ -134,8 +136,8 @@ class RequestBindingGenerator( val member = inputShape.expectMember(label.content) serializeLabel(member, label, local(member)) } - rust("""write!(output, ${combinedArgs.joinToString(", ")}).expect("formatting should succeed");""") - rust("Ok(())") + rust("""::std::write!(output, ${combinedArgs.joinToString(", ")}).expect("formatting should succeed");""") + rustTemplate("#{Ok}(())", *codegenScope) } } @@ -165,7 +167,7 @@ class RequestBindingGenerator( } val preloadedParams = literalParams.keys + dynamicParams.map { it.locationName } writer.rustBlockTemplate( - "fn uri_query(_input: &#{Input}, mut output: &mut String) -> Result<(), #{BuildError}>", + "fn uri_query(_input: &#{Input}, mut output: &mut #{String}) -> #{Result}<(), #{BuildError}>", *codegenScope, ) { write("let mut query = #T::new(output);", RuntimeType.queryFormat(runtimeConfig, "Writer")) @@ -212,6 +214,7 @@ class RequestBindingGenerator( if (memberShape.isRequired) { val codegenScope = arrayOf( + *preludeScope, "BuildError" to OperationBuildError(runtimeConfig).missingField( memberName, "cannot be empty or unset", @@ -229,7 +232,7 @@ class RequestBindingGenerator( // Strings that aren't enums must be checked to see if they're empty if (target.isStringShape && !target.hasTrait()) { rustBlock("if $derefName.is_empty()") { - rustTemplate("return Err(#{BuildError:W});", *codegenScope) + rustTemplate("return #{Err}(#{BuildError:W});", *codegenScope) } } @@ -241,7 +244,7 @@ class RequestBindingGenerator( } } } - writer.rust("Ok(())") + writer.rustTemplate("#{Ok}(())", *codegenScope) } return true } @@ -329,9 +332,10 @@ class RequestBindingGenerator( rustTemplate( """ if $outputVar.is_empty() { - return Err(#{buildError:W}) + return #{Err}(#{buildError:W}) } """, + *preludeScope, "buildError" to buildError, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt index 5d206d47e8..8f2dcda292 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt @@ -20,6 +20,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext 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.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations @@ -56,13 +57,15 @@ open class MakeOperationGenerator( ?: codegenContext.serviceShape.id.getName(codegenContext.serviceShape) private val codegenScope = arrayOf( + *preludeScope, "config" to ClientRustModule.Config, "header_util" to RuntimeType.smithyHttp(runtimeConfig).resolve("header"), "http" to RuntimeType.Http, + "operation" to RuntimeType.operationModule(runtimeConfig), "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, "OpBuildError" to runtimeConfig.operationBuildError(), - "operation" to RuntimeType.operationModule(runtimeConfig), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), + "SharedPropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::SharedPropertyBag"), ) fun generateMakeOperation( @@ -73,7 +76,7 @@ open class MakeOperationGenerator( val operationName = symbolProvider.toSymbol(shape).name val baseReturnType = buildOperationType(implBlockWriter, shape, customizations) val returnType = - "std::result::Result<$baseReturnType, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" + "#{Result}<$baseReturnType, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" val outputSymbol = symbolProvider.toSymbol(shape) val takesOwnership = bodyGenerator.payloadMetadata(shape).takesOwnership @@ -99,7 +102,7 @@ open class MakeOperationGenerator( withBlock("let mut request = {", "};") { createHttpRequest(this, shape) } - rust("let mut properties = aws_smithy_http::property_bag::SharedPropertyBag::new();") + rustTemplate("let mut properties = #{SharedPropertyBag}::new();", *codegenScope) // When the payload is a `ByteStream`, `into_inner()` already returns an `SdkBody`, so we mute this // Clippy warning to make the codegen a little simpler in that case. @@ -116,7 +119,7 @@ open class MakeOperationGenerator( if (includeDefaultPayloadHeaders && needsContentLength(shape)) { rustTemplate( """ - if let Some(content_length) = body.content_length() { + if let #{Some}(content_length) = body.content_length() { request = #{header_util}::set_request_header_if_absent(request, #{http}::header::CONTENT_LENGTH, content_length); } """, @@ -140,7 +143,7 @@ open class MakeOperationGenerator( "OperationType" to symbolProvider.toSymbol(shape), ) writeCustomizations(customizations, OperationSection.FinalizeOperation(customizations, "op", "_config")) - rust("Ok(op)") + rustTemplate("#{Ok}(op)", *codegenScope) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index 5a1c4993b0..cafefc1179 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -351,7 +351,7 @@ class ProtocolTestGenerator( rustWriter.rustTemplate( """ // No body - #{AssertEq}(std::str::from_utf8(body).unwrap(), ""); + #{AssertEq}(::std::str::from_utf8(body).unwrap(), ""); """, *codegenScope, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index 83578b2ac5..ae83e78cfe 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -19,6 +19,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations @@ -61,6 +62,7 @@ open class HttpBoundProtocolTraitImplGenerator( private val parserGenerator = ProtocolParserGenerator(codegenContext, protocol) private val codegenScope = arrayOf( + *preludeScope, "ParseStrict" to RuntimeType.parseStrictResponse(runtimeConfig), "ParseResponse" to RuntimeType.parseHttpResponse(runtimeConfig), "http" to RuntimeType.Http, @@ -114,7 +116,7 @@ open class HttpBoundProtocolTraitImplGenerator( rustTemplate( """ impl #{ParseStrict} for $operationName { - type Output = std::result::Result<#{O}, #{E}>; + type Output = #{Result}<#{O}, #{E}>; fn parse(&self, response: &#{http}::Response<#{Bytes}>) -> Self::Output { let (success, status) = (response.status().is_success(), response.status().as_u16()); let headers = response.headers(); @@ -144,14 +146,14 @@ open class HttpBoundProtocolTraitImplGenerator( rustTemplate( """ impl #{ParseResponse} for $operationName { - type Output = std::result::Result<#{O}, #{E}>; - fn parse_unloaded(&self, response: &mut #{operation}::Response) -> Option { + type Output = #{Result}<#{O}, #{E}>; + fn parse_unloaded(&self, response: &mut #{operation}::Response) -> #{Option} { #{BeforeParseResponse} // This is an error, defer to the non-streaming parser if !response.http().status().is_success() && response.http().status().as_u16() != $successCode { - return None; + return #{None}; } - Some(#{parse_streaming_response}(response)) + #{Some}(#{parse_streaming_response}(response)) } fn parse_loaded(&self, response: &#{http}::Response<#{Bytes}>) -> Self::Output { // if streaming, we only hit this case if its an error @@ -180,7 +182,7 @@ open class HttpBoundProtocolTraitImplGenerator( return protocolFunctions.deserializeFn(operationShape, fnNameSuffix = "op_response") { fnName -> Attribute.AllowClippyUnnecessaryWraps.render(this) rustBlockTemplate( - "pub fn $fnName(op_response: &mut #{operation}::Response) -> std::result::Result<#{O}, #{E}>", + "pub fn $fnName(op_response: &mut #{operation}::Response) -> #{Result}<#{O}, #{E}>", *codegenScope, "O" to outputSymbol, "E" to errorSymbol, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/NamingObstacleCourseTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/NamingObstacleCourseTest.kt new file mode 100644 index 0000000000..aba0edc1a5 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/NamingObstacleCourseTest.kt @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeEnumVariantsModel +import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeEnumsModel +import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeOperationsModel +import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels.rustPreludeStructsModel + +class NamingObstacleCourseTest { + @Test + fun `test Rust prelude operation names compile`() { + clientIntegrationTest(rustPreludeOperationsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude structure names compile`() { + clientIntegrationTest(rustPreludeStructsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude enum names compile`() { + clientIntegrationTest(rustPreludeEnumsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude enum variant names compile`() { + clientIntegrationTest(rustPreludeEnumVariantsModel()) { _, _ -> } + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/StreamingShapeSymbolProviderTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/StreamingShapeSymbolProviderTest.kt index a2e233c719..61d97317f2 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/StreamingShapeSymbolProviderTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/StreamingShapeSymbolProviderTest.kt @@ -43,13 +43,13 @@ internal class StreamingShapeSymbolProviderTest { modelWithOperationTraits.lookup("test.synthetic#GenerateSpeechOutput\$data").also { shape -> symbolProvider.toSymbol(shape).also { symbol -> symbol.name shouldBe "data" - symbol.rustType() shouldBe RustType.Opaque("ByteStream", "aws_smithy_http::byte_stream") + symbol.rustType() shouldBe RustType.Opaque("ByteStream", "::aws_smithy_http::byte_stream") } } modelWithOperationTraits.lookup("test.synthetic#GenerateSpeechInput\$data").also { shape -> symbolProvider.toSymbol(shape).also { symbol -> symbol.name shouldBe "data" - symbol.rustType() shouldBe RustType.Opaque("ByteStream", "aws_smithy_http::byte_stream") + symbol.rustType() shouldBe RustType.Opaque("ByteStream", "::aws_smithy_http::byte_stream") } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index 2e372b4989..6976236d85 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -197,7 +197,7 @@ data class CargoDependency( } fun toType(): RuntimeType { - return RuntimeType(rustName, this) + return RuntimeType("::$rustName", this) } companion object { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustModule.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustModule.kt index b8c3237e41..78dee92dae 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustModule.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustModule.kt @@ -79,14 +79,19 @@ sealed class RustModule { } /** Creates a new public module */ - fun public(name: String, parent: RustModule = LibRs, documentationOverride: String? = null): LeafModule = - new( - name, - visibility = Visibility.PUBLIC, - inline = false, - parent = parent, - documentationOverride = documentationOverride, - ) + fun public( + name: String, + parent: RustModule = LibRs, + documentationOverride: String? = null, + additionalAttributes: List = emptyList(), + ): LeafModule = new( + name, + visibility = Visibility.PUBLIC, + inline = false, + parent = parent, + documentationOverride = documentationOverride, + additionalAttributes = additionalAttributes, + ) /** Creates a new private module */ fun private(name: String, parent: RustModule = LibRs): LeafModule = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index 79dde7de12..6d010089d2 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -199,7 +199,7 @@ fun RustType.qualifiedName(): String { /** Format this Rust type as an `impl Into` */ fun RustType.implInto(fullyQualified: Boolean = true): String { - return "impl Into<${this.render(fullyQualified)}>" + return "impl ${RuntimeType.Into.fullyQualifiedName()}<${this.render(fullyQualified)}>" } /** Format this Rust type so that it may be used as an argument type in a function definition */ @@ -498,6 +498,7 @@ class Attribute(val inner: Writable, val isDeriveHelper: Boolean = false) { val DenyMissingDocs = Attribute(deny("missing_docs")) val DocHidden = Attribute(doc("hidden")) val DocInline = Attribute(doc("inline")) + val NoImplicitPrelude = Attribute("no_implicit_prelude") fun shouldPanic(expectedMessage: String) = Attribute(macroWithArgs("should_panic", "expected = ${expectedMessage.dq()}")) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt index 61f219488a..007f5e24d3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt @@ -26,6 +26,7 @@ import software.amazon.smithy.model.traits.DocumentationTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.deprecated import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider 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.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.ValueExpression import software.amazon.smithy.rust.codegen.core.smithy.rustType @@ -142,6 +143,21 @@ fun > T.conditionalBlock( return this } +fun RustWriter.conditionalBlockTemplate( + textBeforeNewLine: String, + textAfterNewLine: String, + conditional: Boolean = true, + vararg args: Pair, + block: RustWriter.() -> Unit, +): RustWriter { + withTemplate(textBeforeNewLine.trim(), args) { beforeNewLine -> + withTemplate(textAfterNewLine.trim(), args) { afterNewLine -> + conditionalBlock(beforeNewLine, afterNewLine, conditional = conditional, block = block) + } + } + return this +} + /** * Convenience wrapper that tells Intellij that the contents of this block are Rust */ @@ -608,7 +624,7 @@ class RustWriter private constructor( when { member.isOptional() -> { val innerValue = ValueExpression.Reference(safeName("inner")) - rustBlock("if let Some(${innerValue.name}) = ${value.asRef()}") { + rustBlockTemplate("if let #{Some}(${innerValue.name}) = ${value.asRef()}", *preludeScope) { block(innerValue) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index f8c656c08e..6e5b5ee8ff 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -193,14 +193,67 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) * The companion object contains commonly used RuntimeTypes */ companion object { + /** + * Scope that contains all Rust prelude types, but not macros or functions. + * + * Prelude docs: https://doc.rust-lang.org/std/prelude/index.html#prelude-contents + */ + val preludeScope by lazy { + arrayOf( + // Rust 1.0 + "Copy" to std.resolve("marker::Copy"), + "Send" to Send, + "Sized" to std.resolve("marker::Sized"), + "Sync" to Sync, + "Unpin" to std.resolve("marker::Unpin"), + "Drop" to std.resolve("ops::Drop"), + "Fn" to std.resolve("ops::Fn"), + "FnMut" to std.resolve("ops::FnMut"), + "FnOnce" to std.resolve("ops::FnOnce"), + "Box" to Box, + "ToOwned" to std.resolve("borrow::ToOwned"), + "Clone" to Clone, + "PartialEq" to std.resolve("cmp::PartialEq"), + "PartialOrd" to std.resolve("cmp::PartialOrd"), + "Eq" to Eq, + "Ord" to Ord, + "AsRef" to AsRef, + "AsMut" to std.resolve("convert::AsMut"), + "Into" to Into, + "From" to From, + "Default" to Default, + "Iterator" to std.resolve("iter::Iterator"), + "Extend" to std.resolve("iter::Extend"), + "IntoIterator" to std.resolve("iter::IntoIterator"), + "DoubleEndedIterator" to std.resolve("iter::DoubleEndedIterator"), + "ExactSizeIterator" to std.resolve("iter::ExactSizeIterator"), + "Option" to Option, + "Some" to Option.resolve("Some"), + "None" to Option.resolve("None"), + "Result" to std.resolve("result::Result"), + "Ok" to std.resolve("result::Result::Ok"), + "Err" to std.resolve("result::Result::Err"), + "String" to String, + "ToString" to std.resolve("string::ToString"), + "Vec" to Vec, + + // 2021 Edition + "TryFrom" to std.resolve("convert::TryFrom"), + "TryInto" to std.resolve("convert::TryInto"), + "FromIterator" to std.resolve("iter::FromIterator"), + ) + } + // stdlib types - val std = RuntimeType("std") + val std = RuntimeType("::std") val stdCmp = std.resolve("cmp") val stdFmt = std.resolve("fmt") val stdConvert = std.resolve("convert") + val Arc = std.resolve("sync::Arc") val AsRef = stdConvert.resolve("AsRef") - val ByteSlab = std.resolve("vec::Vec") + val Bool = std.resolve("primitive::bool") val Box = std.resolve("boxed::Box") + val ByteSlab = std.resolve("vec::Vec") val Clone = std.resolve("clone::Clone") val Cow = std.resolve("borrow::Cow") val Debug = stdFmt.resolve("Debug") @@ -210,19 +263,18 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) val From = stdConvert.resolve("From") val Hash = std.resolve("hash::Hash") val HashMap = std.resolve("collections::HashMap") - val Ord = stdCmp.resolve("Ord") + val Into = stdConvert.resolve("Into") val Option = std.resolve("option::Option") + val Ord = stdCmp.resolve("Ord") val PartialEq = stdCmp.resolve("PartialEq") val PartialOrd = stdCmp.resolve("PartialOrd") val Phantom = std.resolve("marker::PhantomData") + val Send = std.resolve("marker::Send") val StdError = std.resolve("error::Error") val String = std.resolve("string::String") - val Bool = std.resolve("primitive::bool") + val Sync = std.resolve("marker::Sync") val TryFrom = stdConvert.resolve("TryFrom") val Vec = std.resolve("vec::Vec") - val Arc = std.resolve("sync::Arc") - val Send = std.resolve("marker::Send") - val Sync = std.resolve("marker::Sync") // external cargo dependency types val Bytes = CargoDependency.Bytes.toType().resolve("Bytes") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index 16cb28b803..da008a6bee 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -17,13 +17,14 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.asArgument import software.amazon.smithy.rust.codegen.core.rustlang.asOptional -import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock +import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.deprecatedShape 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.render import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.withBlock @@ -31,6 +32,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.Default import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig 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.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.canUseDefault import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization @@ -126,7 +128,6 @@ class BuilderGenerator( private val runtimeConfig = symbolProvider.config.runtimeConfig private val members: List = shape.allMembers.values.toList() private val structureSymbol = symbolProvider.toSymbol(shape) - private val builderSymbol = symbolProvider.symbolForBuilder(shape) private val metadata = structureSymbol.expectRustMetadata() // Filter out any derive that isn't Debug, PartialEq, or Clone. Then add a Default derive @@ -147,12 +148,12 @@ class BuilderGenerator( val fallibleBuilder = hasFallibleBuilder(shape, symbolProvider) val outputSymbol = symbolProvider.toSymbol(shape) val returnType = when (fallibleBuilder) { - true -> "Result<${implBlockWriter.format(outputSymbol)}, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" + true -> "#{Result}<${implBlockWriter.format(outputSymbol)}, ${implBlockWriter.format(runtimeConfig.operationBuildError())}>" false -> implBlockWriter.format(outputSymbol) } implBlockWriter.docs("Consumes the builder and constructs a #D.", outputSymbol) - implBlockWriter.rustBlock("pub fn build(self) -> $returnType") { - conditionalBlock("Ok(", ")", conditional = fallibleBuilder) { + implBlockWriter.rustBlockTemplate("pub fn build(self) -> $returnType", *preludeScope) { + conditionalBlockTemplate("#{Ok}(", ")", conditional = fallibleBuilder, *preludeScope) { // If a wrapper is specified, use the `::new` associated function to construct the wrapper coreBuilder(this) } @@ -182,7 +183,7 @@ class BuilderGenerator( writer.documentShape(member, model) writer.deprecatedShape(member) writer.rustBlock("pub fn $memberName(mut self, ${input.argument}) -> Self") { - write("self.$memberName = Some(${input.value});") + rustTemplate("self.$memberName = #{Some}(${input.value});", *preludeScope) write("self") } } @@ -273,13 +274,14 @@ class BuilderGenerator( val input = coreType.member.asArgument("input") rustBlock("pub fn $memberName(mut self, ${input.argument}) -> Self") { - rust( + rustTemplate( """ let mut v = self.$memberName.unwrap_or_default(); v.push(${input.value}); - self.$memberName = Some(v); + self.$memberName = #{Some}(v); self """, + *preludeScope, ) } } @@ -297,13 +299,14 @@ class BuilderGenerator( rustBlock( "pub fn $memberName(mut self, ${k.argument}, ${v.argument}) -> Self", ) { - rust( + rustTemplate( """ let mut hash_map = self.$memberName.unwrap_or_default(); hash_map.insert(${k.value}, ${v.value}); - self.$memberName = Some(hash_map); + self.$memberName = #{Some}(hash_map); self """, + *preludeScope, ) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt index 379a6982da..a84820ad26 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/EnumGenerator.kt @@ -27,6 +27,7 @@ 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.MaybeRenamed 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.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom @@ -208,14 +209,15 @@ open class EnumGenerator( } }, ) - rust( + rustTemplate( """ - impl AsRef for ${context.enumName} { + impl #{AsRef} for ${context.enumName} { fn as_ref(&self) -> &str { self.as_str() } } """, + *preludeScope, ) } @@ -238,8 +240,7 @@ open class EnumGenerator( } } """, - "From" to RuntimeType.From, - "AsRef" to RuntimeType.AsRef, + *preludeScope, ) } @@ -295,7 +296,7 @@ open class EnumGenerator( """ impl #{Debug} for ${context.enumName} { fn fmt(&self, f: &mut #{StdFmt}::Formatter<'_>) -> #{StdFmt}::Result { - write!(f, $REDACTION) + ::std::write!(f, $REDACTION) } } """, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt index e77f942a29..3a59bc1bcd 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt @@ -37,7 +37,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlock +import software.amazon.smithy.rust.codegen.core.rustlang.conditionalBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.escape import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock @@ -47,6 +47,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig 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.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section @@ -204,21 +205,23 @@ open class Instantiator( check(symbol.isOptional()) { "A null node was provided for $memberShape but the symbol was not optional. This is invalid input data." } - writer.rust("None") + writer.rustTemplate("#{None}", *preludeScope) } else { // Structure builder setters for structure shape members _always_ take in `Option`. // Other aggregate shapes' members are optional only when their symbol is. - writer.conditionalBlock( - "Some(", + writer.conditionalBlockTemplate( + "#{Some}(", ")", // The conditions are not commutative: note client builders always take in `Option`. conditional = symbol.isOptional() || (model.expectShape(memberShape.container) is StructureShape && builderKindBehavior.doesSetterTakeInOption(memberShape)), + *preludeScope, ) { - writer.conditionalBlock( - "Box::new(", + writer.conditionalBlockTemplate( + "#{Box}::new(", ")", conditional = symbol.rustType().stripOuter() is RustType.Box, + *preludeScope, ) { render( this, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt index 39b191ba96..3f7927d728 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/UnionGenerator.kt @@ -19,9 +19,11 @@ import software.amazon.smithy.rust.codegen.core.rustlang.documentShape import software.amazon.smithy.rust.codegen.core.rustlang.render import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget 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.smithy.expectRustMetadata import software.amazon.smithy.rust.codegen.core.smithy.renamedFrom import software.amazon.smithy.rust.codegen.core.smithy.rustType @@ -134,7 +136,7 @@ open class UnionGenerator( """ impl #{Debug} for ${unionSymbol.name} { fn fmt(&self, f: &mut #{StdFmt}::Formatter<'_>) -> #{StdFmt}::Result { - write!(f, $REDACTION) + ::std::write!(f, $REDACTION) } } """, @@ -197,8 +199,11 @@ private fun RustWriter.renderAsVariant( "/// Tries to convert the enum instance into [`$variantName`], extracting the inner `()`.", ) rust("/// Returns `Err(&Self)` if it can't be converted.") - rustBlock("pub fn as_$funcNamePart(&self) -> std::result::Result<(), &Self>") { - rust("if let ${unionSymbol.name}::$variantName = &self { Ok(()) } else { Err(self) }") + rustBlockTemplate("pub fn as_$funcNamePart(&self) -> #{Result}<(), &Self>", *preludeScope) { + rustTemplate( + "if let ${unionSymbol.name}::$variantName = &self { #{Ok}(()) } else { #{Err}(self) }", + *preludeScope, + ) } } else { val memberSymbol = symbolProvider.toSymbol(member) @@ -209,8 +214,11 @@ private fun RustWriter.renderAsVariant( targetSymbol, ) rust("/// Returns `Err(&Self)` if it can't be converted.") - rustBlock("pub fn as_$funcNamePart(&self) -> std::result::Result<&${memberSymbol.rustType().render()}, &Self>") { - rust("if let ${unionSymbol.name}::$variantName(val) = &self { Ok(val) } else { Err(self) }") + rustBlockTemplate("pub fn as_$funcNamePart(&self) -> #{Result}<&${memberSymbol.rustType().render()}, &Self>", *preludeScope) { + rustTemplate( + "if let ${unionSymbol.name}::$variantName(val) = &self { #{Ok}(val) } else { #{Err}(self) }", + *preludeScope, + ) } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt index 692bf32aab..049933bc45 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/error/ErrorImplGenerator.kt @@ -156,13 +156,13 @@ class ErrorImplGenerator( val errorDesc = symbol.name.letIf(symbol.name != shape.id.name) { symbolName -> "$symbolName [${shape.id.name}]" } - write("write!(f, ${errorDesc.dq()})?;") + write("::std::write!(f, ${errorDesc.dq()})?;") messageShape?.let { if (it.shouldRedact(model)) { - write("""write!(f, ": {}", $REDACTION)?;""") + write("""::std::write!(f, ": {}", $REDACTION)?;""") } else { ifSet(it, symbolProvider.toSymbol(it), ValueExpression.Reference("&self.message")) { field -> - write("""write!(f, ": {}", ${field.asRef()})?;""") + write("""::std::write!(f, ": {}", ${field.asRef()})?;""") } } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt index 6e9826f054..6b706a1e09 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/parse/EventStreamUnmarshallerGenerator.kt @@ -276,7 +276,7 @@ class EventStreamUnmarshallerGenerator( is StringShape -> { rustTemplate( """ - std::str::from_utf8(message.payload()) + ::std::str::from_utf8(message.payload()) .map_err(|_| #{Error}::unmarshalling("message payload is not valid UTF-8"))? .to_owned() """, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/NamingObstacleCourseTestModels.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/NamingObstacleCourseTestModels.kt new file mode 100644 index 0000000000..c45a7d0992 --- /dev/null +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/NamingObstacleCourseTestModels.kt @@ -0,0 +1,172 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.testutil + +import software.amazon.smithy.model.Model +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope + +object NamingObstacleCourseTestModels { + private val rustPrelude = preludeScope.map { pair -> pair.first } + + /** + * Test model that confounds the generation machinery by using operations named after every item + * in the Rust prelude. + */ + fun rustPreludeOperationsModel(): Model = StringBuilder().apply { + append( + """ + ${"$"}version: "2.0" + namespace crate + + use smithy.test#httpRequestTests + use smithy.test#httpResponseTests + use aws.protocols#awsJson1_1 + use aws.api#service + use smithy.framework#ValidationException + + structure InputAndOutput {} + + @awsJson1_1 + @service(sdkId: "Config") + service Config { + version: "2006-03-01", + rename: { "smithy.api#String": "PreludeString" }, + operations: [ + """, + ) + for (item in rustPrelude) { + append("$item,\n") + } + append( + """ + ] + } + """, + ) + for (item in rustPrelude) { + append("operation $item { input: InputAndOutput, output: InputAndOutput, errors: [ValidationException] }\n") + } + }.toString().asSmithyModel() + + fun rustPreludeStructsModel(): Model = StringBuilder().apply { + append( + """ + ${"$"}version: "2.0" + namespace crate + + use smithy.test#httpRequestTests + use smithy.test#httpResponseTests + use aws.protocols#awsJson1_1 + use aws.api#service + use smithy.framework#ValidationException + + structure InputAndOutput {} + + @awsJson1_1 + @service(sdkId: "Config") + service Config { + version: "2006-03-01", + rename: { "smithy.api#String": "PreludeString" }, + operations: [ + """, + ) + for (item in rustPrelude) { + append("Use$item,\n") + } + append( + """ + ] + } + """, + ) + for (item in rustPrelude) { + append("structure $item { $item: smithy.api#String }\n") + append("operation Use$item { input: $item, output: $item, errors: [ValidationException] }\n") + } + println(toString()) + }.toString().asSmithyModel() + + fun rustPreludeEnumsModel(): Model = StringBuilder().apply { + append( + """ + ${"$"}version: "2.0" + namespace crate + + use smithy.test#httpRequestTests + use smithy.test#httpResponseTests + use aws.protocols#awsJson1_1 + use aws.api#service + use smithy.framework#ValidationException + + structure InputAndOutput {} + + @awsJson1_1 + @service(sdkId: "Config") + service Config { + version: "2006-03-01", + rename: { "smithy.api#String": "PreludeString" }, + operations: [ + """, + ) + for (item in rustPrelude) { + append("Use$item,\n") + } + append( + """ + ] + } + """, + ) + for (item in rustPrelude) { + append("enum $item { $item }\n") + append("structure Struct$item { $item: $item }\n") + append("operation Use$item { input: Struct$item, output: Struct$item, errors: [ValidationException] }\n") + } + }.toString().asSmithyModel() + + fun rustPreludeEnumVariantsModel(): Model = StringBuilder().apply { + append( + """ + ${"$"}version: "2.0" + namespace crate + + use smithy.test#httpRequestTests + use smithy.test#httpResponseTests + use aws.protocols#awsJson1_1 + use aws.api#service + use smithy.framework#ValidationException + + @awsJson1_1 + @service(sdkId: "Config") + service Config { + version: "2006-03-01", + rename: { "smithy.api#String": "PreludeString" }, + operations: [EnumOp] + } + + operation EnumOp { + input: InputAndOutput, + output: InputAndOutput, + errors: [ValidationException], + } + + structure InputAndOutput { + the_enum: TheEnum, + } + + enum TheEnum { + """, + ) + for (item in rustPrelude) { + append("$item,\n") + } + append( + """ + } + """, + ) + }.toString().asSmithyModel() +} diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustTypeTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustTypeTest.kt index 7fa364dfaf..1c89f39058 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustTypeTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustTypeTest.kt @@ -54,14 +54,14 @@ internal class RustTypesTest { @Test fun `RustType_String_writable produces a template-compatible RuntimeType`() { - forInputExpectOutput(RustType.String.writable, "'std::string::String'") + forInputExpectOutput(RustType.String.writable, "'::std::string::String'") } @Test fun `RustType_Vec_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.Vec(RustType.String).writable, - "'std::vec::Vec'", + "'::std::vec::Vec<::std::string::String>'", ) } @@ -69,7 +69,7 @@ internal class RustTypesTest { fun `RustType_Slice_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.Slice(RustType.String).writable, - "'[std::string::String]'", + "'[::std::string::String]'", ) } @@ -77,7 +77,7 @@ internal class RustTypesTest { fun `RustType_HashMap_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.HashMap(RustType.String, RustType.String).writable, - "'std::collections::HashMap'", + "'::std::collections::HashMap<::std::string::String, ::std::string::String>'", ) } @@ -87,7 +87,7 @@ internal class RustTypesTest { RustType.HashSet(RustType.String).writable, // Rust doesn't guarantee that `HashSet`s are insertion ordered, so we use a `Vec` instead. // This is called out in a comment in the RustType.HashSet declaration - "'std::vec::Vec'", + "'::std::vec::Vec<::std::string::String>'", ) } @@ -95,15 +95,15 @@ internal class RustTypesTest { fun `RustType_Reference_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.Reference("&", RustType.String).writable, - "'&std::string::String'", + "'&::std::string::String'", ) forInputExpectOutput( RustType.Reference("&mut", RustType.String).writable, - "'&mut std::string::String'", + "'&mut ::std::string::String'", ) forInputExpectOutput( RustType.Reference("&'static", RustType.String).writable, - "&'static std::string::String'", + "&'static ::std::string::String'", ) } @@ -111,7 +111,7 @@ internal class RustTypesTest { fun `RustType_Option_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.Option(RustType.String).writable, - "'std::option::Option'", + "'::std::option::Option<::std::string::String>'", ) } @@ -119,7 +119,7 @@ internal class RustTypesTest { fun `RustType_Box_writable produces a template-compatible RuntimeType`() { forInputExpectOutput( RustType.Box(RustType.String).writable, - "'std::boxed::Box'", + "'::std::boxed::Box<::std::string::String>'", ) } @@ -147,7 +147,7 @@ internal class RustTypesTest { fun `types render properly`() { val type = RustType.Box(RustType.Option(RustType.Reference("a", RustType.Vec(RustType.String)))) type.render(false) shouldBe "Box>>" - type.render(true) shouldBe "std::boxed::Box>>" + type.render(true) shouldBe "::std::boxed::Box<::std::option::Option<&'a ::std::vec::Vec<::std::string::String>>>" } @Test @@ -211,7 +211,7 @@ internal class RustTypesTest { writable { attributeMacro.render(this) }, - "#[derive(std::clone::Clone, std::error::Error, std::fmt::Debug)]\n", + "#[derive(::std::clone::Clone, ::std::error::Error, ::std::fmt::Debug)]\n", ) } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt index ac14bcd20f..2bd5269cc2 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriterTest.kt @@ -143,7 +143,7 @@ class RustWriterTest { ) val sut = RustWriter.root() metadata.render(sut) - sut.toString().shouldContain("#[allow(deprecated)]\n#[derive(std::fmt::Debug)]\n#[foo]") + sut.toString().shouldContain("#[allow(deprecated)]\n#[derive(::std::fmt::Debug)]\n#[foo]") } @Test @@ -183,7 +183,7 @@ class RustWriterTest { "Inner" to inner, "http" to RuntimeType.Http.resolve("foo"), ) - sut.toString().shouldContain("inner: hello, regular: http::foo") + sut.toString().shouldContain("inner: hello, regular: ::http::foo") } @Test diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/WritableTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/WritableTest.kt index 7da0562451..04c5ff2f1a 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/WritableTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/WritableTest.kt @@ -33,7 +33,7 @@ internal class RustTypeParametersTest { @Test fun `rustTypeParameters accepts RuntimeType`() { val runtimeType = RuntimeType.String - forInputExpectOutput(runtimeType, "''") + forInputExpectOutput(runtimeType, "'<::std::string::String>'") } @Test @@ -60,7 +60,7 @@ internal class RustTypeParametersTest { writer.rustInlineTemplate("#{tps:W}", "tps" to tps) writer.rustInlineTemplate("'") - writer.toString() shouldContain "''" + writer.toString() shouldContain "''" } @Test diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt index 120fc5cb20..55b792218f 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customizations/SmithyTypesPubUseExtraTest.kt @@ -74,38 +74,38 @@ class SmithyTypesPubUseExtraTest { @Test fun `it re-exports Blob when a model uses blobs`() { - assertDoesntHaveType(typesWithEmptyModel(), "aws_smithy_types::Blob") - assertHasType(typesWithMember(inputMember = "foo: Blob"), "aws_smithy_types::Blob") - assertHasType(typesWithMember(outputMember = "foo: Blob"), "aws_smithy_types::Blob") + assertDoesntHaveType(typesWithEmptyModel(), "::aws_smithy_types::Blob") + assertHasType(typesWithMember(inputMember = "foo: Blob"), "::aws_smithy_types::Blob") + assertHasType(typesWithMember(outputMember = "foo: Blob"), "::aws_smithy_types::Blob") assertHasType( typesWithMember(inputMember = "foo: SomeUnion", unionMember = "foo: Blob"), - "aws_smithy_types::Blob", + "::aws_smithy_types::Blob", ) assertHasType( typesWithMember(outputMember = "foo: SomeUnion", unionMember = "foo: Blob"), - "aws_smithy_types::Blob", + "::aws_smithy_types::Blob", ) } @Test fun `it re-exports DateTime when a model uses timestamps`() { assertDoesntHaveType(typesWithEmptyModel(), "aws_smithy_types::DateTime") - assertHasType(typesWithMember(inputMember = "foo: Timestamp"), "aws_smithy_types::DateTime") - assertHasType(typesWithMember(outputMember = "foo: Timestamp"), "aws_smithy_types::DateTime") + assertHasType(typesWithMember(inputMember = "foo: Timestamp"), "::aws_smithy_types::DateTime") + assertHasType(typesWithMember(outputMember = "foo: Timestamp"), "::aws_smithy_types::DateTime") assertHasType( typesWithMember(inputMember = "foo: SomeUnion", unionMember = "foo: Timestamp"), - "aws_smithy_types::DateTime", + "::aws_smithy_types::DateTime", ) assertHasType( typesWithMember(outputMember = "foo: SomeUnion", unionMember = "foo: Timestamp"), - "aws_smithy_types::DateTime", + "::aws_smithy_types::DateTime", ) } @Test fun `it re-exports ByteStream and AggregatedBytes when a model has streaming`() { val streamingTypes = - listOf("aws_smithy_http::byte_stream::ByteStream", "aws_smithy_http::byte_stream::AggregatedBytes") + listOf("::aws_smithy_http::byte_stream::ByteStream", "::aws_smithy_http::byte_stream::AggregatedBytes") val streamingShape = "@streaming blob Streaming" assertDoesntHaveTypes(typesWithEmptyModel(), streamingTypes) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt index 42f878d6fc..8cdc481b39 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonType.kt @@ -111,6 +111,13 @@ sealed class PythonType { else -> it } } + // Most opaque types have a leading `::`, so strip that for Python as needed + .let { + when (it?.startsWith(".")) { + true -> it.substring(1) + else -> it + } + } } } diff --git a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerSymbolProviderTest.kt b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerSymbolProviderTest.kt index c7467b58ed..e3ab955a9f 100644 --- a/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerSymbolProviderTest.kt +++ b/codegen-server/python/src/test/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerSymbolProviderTest.kt @@ -16,8 +16,8 @@ import software.amazon.smithy.rust.codegen.server.smithy.testutil.ServerTestRust import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestRustSettings internal class PythonServerSymbolProviderTest { - private val pythonBlobType = RustType.Opaque("Blob", "aws_smithy_http_server_python::types") - private val pythonTimestampType = RustType.Opaque("DateTime", "aws_smithy_http_server_python::types") + private val pythonBlobType = RustType.Opaque("Blob", "::aws_smithy_http_server_python::types") + private val pythonTimestampType = RustType.Opaque("DateTime", "::aws_smithy_http_server_python::types") @Test fun `python symbol provider rewrites timestamp shape symbol`() { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGenerator.kt index 9b5775478e..f705e4af3a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGenerator.kt @@ -72,7 +72,7 @@ class ConstrainedCollectionGenerator( } val name = constrainedShapeSymbolProvider.toSymbol(shape).name - val inner = "std::vec::Vec<#{ValueMemberSymbol}>" + val inner = "::std::vec::Vec<#{ValueMemberSymbol}>" val constraintViolation = constraintViolationSymbolProvider.toSymbol(shape) val constrainedSymbol = symbolProvider.toSymbol(shape) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt index 44992f4a48..8e52d0d4da 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedTraitForEnumGenerator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.makeMaybeConstrained import software.amazon.smithy.rust.codegen.core.util.expectTrait @@ -30,7 +31,7 @@ class ConstrainedTraitForEnumGenerator( val symbol = symbolProvider.toSymbol(shape) val name = symbol.name - val unconstrainedType = "String" + val unconstrainedType = RuntimeType.String.fullyQualifiedName() writer.rustTemplate( """ @@ -38,12 +39,13 @@ class ConstrainedTraitForEnumGenerator( type Unconstrained = $unconstrainedType; } - impl From<$unconstrainedType> for #{MaybeConstrained} { + impl #{From}<$unconstrainedType> for #{MaybeConstrained} { fn from(value: $unconstrainedType) -> Self { Self::Unconstrained(value) } } """, + *preludeScope, "ConstrainedTrait" to RuntimeType.ConstrainedTrait, "MaybeConstrained" to symbol.makeMaybeConstrained(), ) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt index cc811b80f2..09a0d2d5cd 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerEnumGenerator.kt @@ -8,9 +8,11 @@ import software.amazon.smithy.model.shapes.StringShape 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.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumGeneratorContext import software.amazon.smithy.rust.codegen.core.smithy.generators.EnumType @@ -65,7 +67,7 @@ open class ConstrainedEnum( } rustBlock("impl #T<&str> for ${context.enumName}", RuntimeType.TryFrom) { rust("type Error = #T;", constraintViolationSymbol) - rustBlock("fn try_from(s: &str) -> Result>::Error>", RuntimeType.TryFrom) { + rustBlockTemplate("fn try_from(s: &str) -> #{Result}>::Error>", *preludeScope) { rustBlock("match s") { context.sortedMembers.forEach { member -> rust("${member.value.dq()} => Ok(${context.enumName}::${member.derivedName()}),") @@ -78,13 +80,12 @@ open class ConstrainedEnum( """ impl #{TryFrom}<#{String}> for ${context.enumName} { type Error = #{ConstraintViolation}; - fn try_from(s: #{String}) -> std::result::Result>::Error> { + fn try_from(s: #{String}) -> #{Result}>::Error> { s.as_str().try_into() } } """, - "String" to RuntimeType.String, - "TryFrom" to RuntimeType.TryFrom, + *preludeScope, "ConstraintViolation" to constraintViolationSymbol, ) } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/NamingObstacleCourseTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/NamingObstacleCourseTest.kt new file mode 100644 index 0000000000..b0ae1c3473 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/NamingObstacleCourseTest.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.testutil.NamingObstacleCourseTestModels +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest + +class NamingObstacleCourseTest { + @Test + fun `test Rust prelude operation names compile`() { + serverIntegrationTest(NamingObstacleCourseTestModels.rustPreludeOperationsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude structure names compile`() { + serverIntegrationTest(NamingObstacleCourseTestModels.rustPreludeStructsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude enum names compile`() { + serverIntegrationTest(NamingObstacleCourseTestModels.rustPreludeEnumsModel()) { _, _ -> } + } + + @Test + fun `test Rust prelude enum variant names compile`() { + serverIntegrationTest(NamingObstacleCourseTestModels.rustPreludeEnumVariantsModel()) { _, _ -> } + } +} diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt index 7c8efe9c17..5f47e10779 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/UnconstrainedShapeSymbolProviderTest.kt @@ -98,6 +98,6 @@ class UnconstrainedShapeSymbolProviderTest { val structureBShape = model.lookup("test#StructureB") unconstrainedShapeSymbolProvider.toSymbol(structureBShape).rustType().render() shouldBe "crate::model::StructureB" - unconstrainedShapeSymbolProvider.toSymbol(listAShape).rustType().render() shouldBe "std::vec::Vec" + unconstrainedShapeSymbolProvider.toSymbol(listAShape).rustType().render() shouldBe "::std::vec::Vec" } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGeneratorTest.kt index 060e0166a4..3a35120f83 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedBlobGeneratorTest.kt @@ -117,7 +117,7 @@ class ConstrainedBlobGeneratorTest { } @Test - fun `type should not be constructible without using a constructor`() { + fun `type should not be constructable without using a constructor`() { val model = """ namespace test @@ -139,6 +139,6 @@ class ConstrainedBlobGeneratorTest { ).render() // Check that the wrapped type is `pub(crate)`. - writer.toString() shouldContain "pub struct ConstrainedBlob(pub(crate) aws_smithy_types::Blob);" + writer.toString() shouldContain "pub struct ConstrainedBlob(pub(crate) ::aws_smithy_types::Blob);" } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGeneratorTest.kt index cce7da4aa6..3b9e0d4a7b 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedCollectionGeneratorTest.kt @@ -258,7 +258,7 @@ class ConstrainedCollectionGeneratorTest { } @Test - fun `type should not be constructible without using a constructor`() { + fun `type should not be constructable without using a constructor`() { val model = """ namespace test @@ -276,7 +276,7 @@ class ConstrainedCollectionGeneratorTest { render(codegenContext, writer, constrainedCollectionShape) // Check that the wrapped type is `pub(crate)`. - writer.toString() shouldContain "pub struct ConstrainedList(pub(crate) std::vec::Vec);" + writer.toString() shouldContain "pub struct ConstrainedList(pub(crate) ::std::vec::Vec<::std::string::String>);" } private fun render( diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt index 0eebb7e36b..cd83336124 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedMapGeneratorTest.kt @@ -128,7 +128,7 @@ class ConstrainedMapGeneratorTest { } @Test - fun `type should not be constructible without using a constructor`() { + fun `type should not be constructable without using a constructor`() { val model = """ namespace test @@ -146,7 +146,7 @@ class ConstrainedMapGeneratorTest { render(codegenContext, writer, constrainedMapShape) // Check that the wrapped type is `pub(crate)`. - writer.toString() shouldContain "pub struct ConstrainedMap(pub(crate) std::collections::HashMap);" + writer.toString() shouldContain "pub struct ConstrainedMap(pub(crate) ::std::collections::HashMap<::std::string::String, ::std::string::String>);" } private fun render( diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGeneratorTest.kt index 5a78574c93..3a34c7753c 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedNumberGeneratorTest.kt @@ -127,7 +127,7 @@ class ConstrainedNumberGeneratorTest { @ParameterizedTest @ArgumentsSource(NoStructuralConstructorTestProvider::class) - fun `type should not be constructible without using a constructor`(args: Triple) { + fun `type should not be constructable without using a constructor`(args: Triple) { val (smithyType, shapeName, rustType) = args val model = """ namespace test diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt index 62a7d061c3..eb0ba5ed34 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt @@ -131,7 +131,7 @@ class ConstrainedStringGeneratorTest { } @Test - fun `type should not be constructible without using a constructor`() { + fun `type should not be constructable without using a constructor`() { val model = """ namespace test @@ -153,7 +153,7 @@ class ConstrainedStringGeneratorTest { ).render() // Check that the wrapped type is `pub(crate)`. - writer.toString() shouldContain "pub struct ConstrainedString(pub(crate) std::string::String);" + writer.toString() shouldContain "pub struct ConstrainedString(pub(crate) ::std::string::String);" } @Test From 31c152d9af53afb9a5e6edf9df3def57931b9c1e Mon Sep 17 00:00:00 2001 From: Burak Date: Wed, 17 May 2023 11:51:00 +0100 Subject: [PATCH 095/253] Unify Pokemon model for Rust and Python servers (#2700) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation and Context Now we have the feature parity between Rust and Python servers (at least for the Pokémon service's needs) we can use the same model in both. Closes https://github.com/awslabs/smithy-rs/issues/1508 ## Testing ```bash $ cd smithy-rs/examples $ make test # test Rust servers $ cd python $ make test # test Python servers ``` ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --- codegen-client-test/build.gradle.kts | 4 +- .../common-test-models/pokemon-awsjson.smithy | 2 +- .../common-test-models/pokemon.smithy | 21 ++- codegen-server-test/build.gradle.kts | 4 +- codegen-server-test/python/build.gradle.kts | 6 +- .../python/model/pokemon-common.smithy | 1 - .../python/model/pokemon.smithy | 126 ------------------ examples/pokemon-service-common/Cargo.toml | 5 +- examples/pokemon-service-common/src/lib.rs | 37 ++++- examples/pokemon-service-lambda/src/main.rs | 3 +- examples/pokemon-service-tls/src/main.rs | 3 +- examples/pokemon-service/src/main.rs | 4 +- examples/python/Makefile | 5 +- examples/python/pokemon_service.py | 45 ++++++- 14 files changed, 116 insertions(+), 150 deletions(-) delete mode 120000 codegen-server-test/python/model/pokemon-common.smithy delete mode 100644 codegen-server-test/python/model/pokemon.smithy diff --git a/codegen-client-test/build.gradle.kts b/codegen-client-test/build.gradle.kts index f3796a6911..3709d4e594 100644 --- a/codegen-client-test/build.gradle.kts +++ b/codegen-client-test/build.gradle.kts @@ -93,8 +93,8 @@ val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> imports = listOf("$commonModels/naming-obstacle-course-structs.smithy"), ), CodegenTest("aws.protocoltests.json#TestService", "endpoint-rules"), - CodegenTest("com.aws.example.rust#PokemonService", "pokemon-service-client", imports = listOf("$commonModels/pokemon.smithy", "$commonModels/pokemon-common.smithy")), - CodegenTest("com.aws.example.rust#PokemonService", "pokemon-service-awsjson-client", imports = listOf("$commonModels/pokemon-awsjson.smithy", "$commonModels/pokemon-common.smithy")), + CodegenTest("com.aws.example#PokemonService", "pokemon-service-client", imports = listOf("$commonModels/pokemon.smithy", "$commonModels/pokemon-common.smithy")), + CodegenTest("com.aws.example#PokemonService", "pokemon-service-awsjson-client", imports = listOf("$commonModels/pokemon-awsjson.smithy", "$commonModels/pokemon-common.smithy")), ) } diff --git a/codegen-core/common-test-models/pokemon-awsjson.smithy b/codegen-core/common-test-models/pokemon-awsjson.smithy index 7d3a9ae021..12e455ecdd 100644 --- a/codegen-core/common-test-models/pokemon-awsjson.smithy +++ b/codegen-core/common-test-models/pokemon-awsjson.smithy @@ -4,7 +4,7 @@ $version: "1.0" // This is a temporary model to test AwsJson 1.0 with @streaming. // This model will be removed when protocol tests support @streaming. -namespace com.aws.example.rust +namespace com.aws.example use aws.protocols#awsJson1_0 use smithy.framework#ValidationException diff --git a/codegen-core/common-test-models/pokemon.smithy b/codegen-core/common-test-models/pokemon.smithy index 20e56e381a..745f51d93c 100644 --- a/codegen-core/common-test-models/pokemon.smithy +++ b/codegen-core/common-test-models/pokemon.smithy @@ -1,6 +1,6 @@ $version: "1.0" -namespace com.aws.example.rust +namespace com.aws.example use aws.protocols#restJson1 use smithy.framework#ValidationException @@ -20,7 +20,8 @@ service PokemonService { GetServerStatistics, DoNothing, CapturePokemon, - CheckHealth + CheckHealth, + StreamPokemonRadio ], } @@ -146,3 +147,19 @@ structure MasterBallUnsuccessful { @error("client") structure ThrottlingError {} + +/// Fetch a radio song from the database and stream it back as a playable audio. +@readonly +@http(uri: "/radio", method: "GET") +operation StreamPokemonRadio { + output: StreamPokemonRadioOutput +} + +@output +structure StreamPokemonRadioOutput { + @httpPayload + data: StreamingBlob +} + +@streaming +blob StreamingBlob \ No newline at end of file diff --git a/codegen-server-test/build.gradle.kts b/codegen-server-test/build.gradle.kts index 2dbeebca0a..c4b2f00dba 100644 --- a/codegen-server-test/build.gradle.kts +++ b/codegen-server-test/build.gradle.kts @@ -84,12 +84,12 @@ val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> CodegenTest("com.amazonaws.ebs#Ebs", "ebs", imports = listOf("$commonModels/ebs.json")), CodegenTest("com.amazonaws.s3#AmazonS3", "s3"), CodegenTest( - "com.aws.example.rust#PokemonService", + "com.aws.example#PokemonService", "pokemon-service-server-sdk", imports = listOf("$commonModels/pokemon.smithy", "$commonModels/pokemon-common.smithy"), ), CodegenTest( - "com.aws.example.rust#PokemonService", + "com.aws.example#PokemonService", "pokemon-service-awsjson-server-sdk", imports = listOf("$commonModels/pokemon-awsjson.smithy", "$commonModels/pokemon-common.smithy"), ), diff --git a/codegen-server-test/python/build.gradle.kts b/codegen-server-test/python/build.gradle.kts index e70e2b0e5d..423d8a4619 100644 --- a/codegen-server-test/python/build.gradle.kts +++ b/codegen-server-test/python/build.gradle.kts @@ -41,7 +41,11 @@ dependencies { val allCodegenTests = "../../codegen-core/common-test-models".let { commonModels -> listOf( CodegenTest("com.amazonaws.simple#SimpleService", "simple", imports = listOf("$commonModels/simple.smithy")), - CodegenTest("com.aws.example.python#PokemonService", "pokemon-service-server-sdk"), + CodegenTest( + "com.aws.example#PokemonService", + "pokemon-service-server-sdk", + imports = listOf("$commonModels/pokemon.smithy", "$commonModels/pokemon-common.smithy"), + ), CodegenTest( "com.amazonaws.ebs#Ebs", "ebs", imports = listOf("$commonModels/ebs.json"), diff --git a/codegen-server-test/python/model/pokemon-common.smithy b/codegen-server-test/python/model/pokemon-common.smithy deleted file mode 120000 index 31ad0d9f44..0000000000 --- a/codegen-server-test/python/model/pokemon-common.smithy +++ /dev/null @@ -1 +0,0 @@ -../../../codegen-core/common-test-models/pokemon-common.smithy \ No newline at end of file diff --git a/codegen-server-test/python/model/pokemon.smithy b/codegen-server-test/python/model/pokemon.smithy deleted file mode 100644 index b019d183a2..0000000000 --- a/codegen-server-test/python/model/pokemon.smithy +++ /dev/null @@ -1,126 +0,0 @@ -/// TODO(https://github.com/awslabs/smithy-rs/issues/1508) -/// Reconcile this model with the main one living inside codegen-server-test/model/pokemon.smithy -/// once the Python implementation supports Streaming and Union shapes. -$version: "1.0" - -namespace com.aws.example.python - -use aws.protocols#restJson1 -use com.aws.example#CheckHealth -use com.aws.example#DoNothing -use com.aws.example#GetServerStatistics -use com.aws.example#PokemonSpecies -use com.aws.example#Storage -use smithy.framework#ValidationException - -/// The Pokémon Service allows you to retrieve information about Pokémon species. -@title("Pokémon Service") -@restJson1 -service PokemonService { - version: "2021-12-01" - resources: [PokemonSpecies] - operations: [ - GetServerStatistics - DoNothing - CapturePokemon - CheckHealth - StreamPokemonRadio - ] -} - -/// Capture Pokémons via event streams. -@http(uri: "/capture-pokemon-event/{region}", method: "POST") -operation CapturePokemon { - input: CapturePokemonEventsInput - output: CapturePokemonEventsOutput - errors: [ - UnsupportedRegionError - ThrottlingError - ValidationException - ] -} - -@input -structure CapturePokemonEventsInput { - @httpPayload - events: AttemptCapturingPokemonEvent - @httpLabel - @required - region: String -} - -@output -structure CapturePokemonEventsOutput { - @httpPayload - events: CapturePokemonEvents -} - -@streaming -union AttemptCapturingPokemonEvent { - event: CapturingEvent - masterball_unsuccessful: MasterBallUnsuccessful -} - -structure CapturingEvent { - @eventPayload - payload: CapturingPayload -} - -structure CapturingPayload { - name: String - pokeball: String -} - -@streaming -union CapturePokemonEvents { - event: CaptureEvent - invalid_pokeball: InvalidPokeballError - throttlingError: ThrottlingError -} - -structure CaptureEvent { - @eventHeader - name: String - @eventHeader - captured: Boolean - @eventHeader - shiny: Boolean - @eventPayload - pokedex_update: Blob -} - -@error("server") -structure UnsupportedRegionError { - @required - region: String -} - -@error("client") -structure InvalidPokeballError { - @required - pokeball: String -} - -@error("server") -structure MasterBallUnsuccessful { - message: String -} - -@error("client") -structure ThrottlingError {} - -/// Fetch the radio song from the database and stream it back as a playable audio. -@readonly -@http(uri: "/radio", method: "GET") -operation StreamPokemonRadio { - output: StreamPokemonRadioOutput -} - -@output -structure StreamPokemonRadioOutput { - @httpPayload - data: StreamingBlob -} - -@streaming -blob StreamingBlob diff --git a/examples/pokemon-service-common/Cargo.toml b/examples/pokemon-service-common/Cargo.toml index 704055c813..d0012e178b 100644 --- a/examples/pokemon-service-common/Cargo.toml +++ b/examples/pokemon-service-common/Cargo.toml @@ -13,12 +13,11 @@ rand = "0.8" tracing = "0.1" tracing-subscriber = { version = "0.3.16", features = ["env-filter", "json"] } tokio = { version = "1", default-features = false, features = ["time"] } +tower = "0.4" # Local paths +aws-smithy-client = { path = "../../rust-runtime/aws-smithy-client" } aws-smithy-http = { path = "../../rust-runtime/aws-smithy-http" } aws-smithy-http-server = { path = "../../rust-runtime/aws-smithy-http-server" } pokemon-service-client = { path = "../pokemon-service-client" } pokemon-service-server-sdk = { path = "../pokemon-service-server-sdk" } - -[dev-dependencies] -tower = "0.4" diff --git a/examples/pokemon-service-common/src/lib.rs b/examples/pokemon-service-common/src/lib.rs index 0658151a05..d8618e0cce 100644 --- a/examples/pokemon-service-common/src/lib.rs +++ b/examples/pokemon-service-common/src/lib.rs @@ -15,7 +15,8 @@ use std::{ }; use async_stream::stream; -use aws_smithy_http::operation::Request; +use aws_smithy_client::{conns, hyper_ext::Adapter}; +use aws_smithy_http::{body::SdkBody, byte_stream::ByteStream, operation::Request}; use aws_smithy_http_server::Extension; use http::{ uri::{Authority, Scheme}, @@ -24,7 +25,8 @@ use http::{ use pokemon_service_server_sdk::{ error, input, model, model::CapturingPayload, output, types::Blob, }; -use rand::Rng; +use rand::{seq::SliceRandom, Rng}; +use tower::Service; use tracing_subscriber::{prelude::*, EnvFilter}; const PIKACHU_ENGLISH_FLAVOR_TEXT: &str = @@ -327,6 +329,37 @@ pub async fn check_health(_input: input::CheckHealthInput) -> output::CheckHealt output::CheckHealthOutput {} } +const RADIO_STREAMS: [&str; 2] = [ + "https://ia800107.us.archive.org/33/items/299SoundEffectCollection/102%20Palette%20Town%20Theme.mp3", + "https://ia600408.us.archive.org/29/items/PocketMonstersGreenBetaLavenderTownMusicwwwFlvtoCom/Pocket%20Monsters%20Green%20Beta-%20Lavender%20Town%20Music-%5Bwww_flvto_com%5D.mp3", +]; + +/// Streams a random Pokémon song. +pub async fn stream_pokemon_radio( + _input: input::StreamPokemonRadioInput, +) -> output::StreamPokemonRadioOutput { + let radio_stream_url = RADIO_STREAMS + .choose(&mut rand::thread_rng()) + .expect("`RADIO_STREAMS` is empty") + .parse::() + .expect("Invalid url in `RADIO_STREAMS`"); + + let mut connector = Adapter::builder().build(conns::https()); + let result = connector + .call( + http::Request::builder() + .uri(radio_stream_url) + .body(SdkBody::empty()) + .unwrap(), + ) + .await + .unwrap(); + + output::StreamPokemonRadioOutput { + data: ByteStream::new(result.into_body()), + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/examples/pokemon-service-lambda/src/main.rs b/examples/pokemon-service-lambda/src/main.rs index d81034d27e..2f08e072b5 100644 --- a/examples/pokemon-service-lambda/src/main.rs +++ b/examples/pokemon-service-lambda/src/main.rs @@ -9,7 +9,7 @@ use aws_smithy_http_server::{routing::LambdaHandler, AddExtensionLayer}; use pokemon_service_common::{ capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, - setup_tracing, State, + setup_tracing, stream_pokemon_radio, State, }; use pokemon_service_lambda::get_storage_lambda; use pokemon_service_server_sdk::PokemonService; @@ -28,6 +28,7 @@ pub async fn main() { .capture_pokemon(capture_pokemon) .do_nothing(do_nothing) .check_health(check_health) + .stream_pokemon_radio(stream_pokemon_radio) .build() .expect("failed to build an instance of PokemonService") // Set up shared state and middlewares. diff --git a/examples/pokemon-service-tls/src/main.rs b/examples/pokemon-service-tls/src/main.rs index 2a9ef679d2..a67d145042 100644 --- a/examples/pokemon-service-tls/src/main.rs +++ b/examples/pokemon-service-tls/src/main.rs @@ -34,7 +34,7 @@ use tokio_rustls::{ use pokemon_service_common::{ capture_pokemon, check_health, do_nothing, get_pokemon_species, get_server_statistics, - get_storage, setup_tracing, State, + get_storage, setup_tracing, stream_pokemon_radio, State, }; use pokemon_service_server_sdk::PokemonService; use pokemon_service_tls::{DEFAULT_ADDRESS, DEFAULT_PORT, DEFAULT_TEST_CERT, DEFAULT_TEST_KEY}; @@ -71,6 +71,7 @@ pub async fn main() { .capture_pokemon(capture_pokemon) .do_nothing(do_nothing) .check_health(check_health) + .stream_pokemon_radio(stream_pokemon_radio) .build() .expect("failed to build an instance of PokemonService") // Set up shared state and middlewares. diff --git a/examples/pokemon-service/src/main.rs b/examples/pokemon-service/src/main.rs index 7a865abfaf..db7878995b 100644 --- a/examples/pokemon-service/src/main.rs +++ b/examples/pokemon-service/src/main.rs @@ -23,7 +23,8 @@ use pokemon_service::{ do_nothing_but_log_request_ids, get_storage_with_local_approved, DEFAULT_ADDRESS, DEFAULT_PORT, }; use pokemon_service_common::{ - capture_pokemon, check_health, get_pokemon_species, get_server_statistics, setup_tracing, State, + capture_pokemon, check_health, get_pokemon_species, get_server_statistics, setup_tracing, + stream_pokemon_radio, State, }; use pokemon_service_server_sdk::PokemonService; @@ -67,6 +68,7 @@ pub async fn main() { .capture_pokemon(capture_pokemon) .do_nothing(do_nothing_but_log_request_ids) .check_health(check_health) + .stream_pokemon_radio(stream_pokemon_radio) .build() .expect("failed to build an instance of PokemonService"); diff --git a/examples/python/Makefile b/examples/python/Makefile index 56213fe289..74c63f05a8 100644 --- a/examples/python/Makefile +++ b/examples/python/Makefile @@ -45,7 +45,10 @@ release: codegen $(MAKE) generate-stubs $(MAKE) build-wheel-release -run: build +run: build install-wheel + python3 $(CUR_DIR)/pokemon_service.py + +run-release: release install-wheel python3 $(CUR_DIR)/pokemon_service.py py-check: install-wheel diff --git a/examples/python/pokemon_service.py b/examples/python/pokemon_service.py index 7ee4a7284f..eeb6445eb3 100644 --- a/examples/python/pokemon_service.py +++ b/examples/python/pokemon_service.py @@ -17,8 +17,10 @@ MasterBallUnsuccessful, ResourceNotFoundException, UnsupportedRegionError, + StorageAccessNotAuthorized, ) from pokemon_service_server_sdk.input import ( + GetStorageInput, CapturePokemonInput, CheckHealthInput, DoNothingInput, @@ -28,8 +30,14 @@ ) from pokemon_service_server_sdk.logging import TracingHandler from pokemon_service_server_sdk.middleware import MiddlewareException, Request, Response -from pokemon_service_server_sdk.model import CaptureEvent, CapturePokemonEvents, FlavorText, Language +from pokemon_service_server_sdk.model import ( + CaptureEvent, + CapturePokemonEvents, + FlavorText, + Language, +) from pokemon_service_server_sdk.output import ( + GetStorageOutput, CapturePokemonOutput, CheckHealthOutput, DoNothingOutput, @@ -164,7 +172,11 @@ async def check_content_type_header(request: Request, next: Next) -> Response: if content_type == "application/json": logging.debug("found valid `application/json` content type") else: - logging.warning("invalid content type %s, dumping headers: %s", content_type, request.headers.items()) + logging.warning( + "invalid content type %s, dumping headers: %s", + content_type, + request.headers.items(), + ) return await next(request) @@ -198,9 +210,24 @@ def do_nothing(_: DoNothingInput) -> DoNothingOutput: return DoNothingOutput() +# Retrieves the user's storage. +@app.get_storage +def get_storage(input: GetStorageInput) -> GetStorageOutput: + logging.debug("attempting to authenticate storage user") + + # We currently only support Ash and he has nothing stored + if input.user != "ash" or input.passcode != "pikachu123": + logging.debug("authentication failed") + raise StorageAccessNotAuthorized() + + return GetStorageOutput([]) + + # Get the translation of a Pokémon specie or an error. @app.get_pokemon_species -def get_pokemon_species(input: GetPokemonSpeciesInput, context: Context) -> GetPokemonSpeciesOutput: +def get_pokemon_species( + input: GetPokemonSpeciesInput, context: Context +) -> GetPokemonSpeciesOutput: if context.lambda_ctx is not None: logging.debug( "lambda Context: %s", @@ -218,7 +245,9 @@ def get_pokemon_species(input: GetPokemonSpeciesInput, context: Context) -> GetP if flavor_text_entries: logging.debug("total requests executed: %s", context.get_calls_count()) logging.info("found description for Pokémon %s", input.name) - return GetPokemonSpeciesOutput(name=input.name, flavor_text_entries=flavor_text_entries) + return GetPokemonSpeciesOutput( + name=input.name, flavor_text_entries=flavor_text_entries + ) else: logging.warning("description for Pokémon %s not in the database", input.name) raise ResourceNotFoundException("Requested Pokémon not available") @@ -226,7 +255,9 @@ def get_pokemon_species(input: GetPokemonSpeciesInput, context: Context) -> GetP # Get the number of requests served by this server. @app.get_server_statistics -def get_server_statistics(_: GetServerStatisticsInput, context: Context) -> GetServerStatisticsOutput: +def get_server_statistics( + _: GetServerStatisticsInput, context: Context +) -> GetServerStatisticsOutput: calls_count = context.get_calls_count() logging.debug("the service handled %d requests", calls_count) return GetServerStatisticsOutput(calls_count=calls_count) @@ -393,7 +424,9 @@ async def events(input: CapturePokemonInput) -> AsyncIterator[CapturePokemonEven # Stream a random Pokémon song. @app.stream_pokemon_radio -async def stream_pokemon_radio(_: StreamPokemonRadioInput, context: Context) -> StreamPokemonRadioOutput: +async def stream_pokemon_radio( + _: StreamPokemonRadioInput, context: Context +) -> StreamPokemonRadioOutput: import aiohttp radio_url = context.get_random_radio_stream() From 33e1a67b224cff5511999a478d177dc7ca394199 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Wed, 17 May 2023 11:30:45 -0500 Subject: [PATCH 096/253] add: request information header interceptors and plugins (#2641) ## Motivation and Context #1793 ## Description This adds a runtime plugin which provides support for a request info header. The plugin depends upon three interceptors: - `ServiceClockSkewInterceptor` - tracks the approximate latency between the client and server - `RequestAttemptsInterceptor` - tracks the number of attempts made for a single operation - `RequestInfoInterceptor` - adds request metadata to outgoing requests. Works by setting a header. ## Testing I wrote one test but need to implement several more ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-runtime/Cargo.toml | 1 + .../aws-runtime/external-types.toml | 4 + aws/rust-runtime/aws-runtime/src/lib.rs | 3 + .../aws-runtime/src/request_info.rs | 224 +++++++++++++ .../smithy/rustsdk/AwsCodegenDecorator.kt | 1 + .../rustsdk/RetryClassifierDecorator.kt | 4 +- .../RetryInformationHeaderDecorator.kt | 55 +++ .../integration-tests/aws-sdk-s3/Cargo.toml | 1 + .../benches/middleware_vs_orchestrator.rs | 5 +- .../slow-network-and-late-client-clock.json | 105 ++++++ .../three-retries_and-then-success.json | 312 ++++++++++++++++++ .../three-successful-attempts.json | 105 ++++++ .../tests/request_information_headers.rs | 257 +++++++++++++++ rust-runtime/aws-smithy-runtime/Cargo.toml | 2 +- .../src/client/orchestrator.rs | 1 + .../src/client/orchestrator/interceptors.rs | 10 + .../interceptors/request_attempts.rs | 68 ++++ .../interceptors/service_clock_skew.rs | 83 +++++ .../src/client/retries/strategy.rs | 4 + .../client/retries/strategy/fixed_delay.rs | 98 ++++++ .../aws-smithy-types/src/date_time/mod.rs | 8 +- 21 files changed, 1344 insertions(+), 7 deletions(-) create mode 100644 aws/rust-runtime/aws-runtime/src/request_info.rs create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt create mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/slow-network-and-late-client-clock.json create mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-retries_and-then-success.json create mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-successful-attempts.json create mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index ccb3d45542..cd0e4bc0cd 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -12,6 +12,7 @@ aws-credential-types = { path = "../aws-credential-types" } aws-http = { path = "../aws-http" } aws-sigv4 = { path = "../aws-sigv4" } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } +aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } aws-types = { path = "../aws-types" } diff --git a/aws/rust-runtime/aws-runtime/external-types.toml b/aws/rust-runtime/aws-runtime/external-types.toml index a38a0e0b57..0449ba09bb 100644 --- a/aws/rust-runtime/aws-runtime/external-types.toml +++ b/aws/rust-runtime/aws-runtime/external-types.toml @@ -5,6 +5,10 @@ allowed_external_types = [ "aws_smithy_types::*", "aws_smithy_runtime_api::*", "aws_types::*", + # TODO(audit-external-type-usage) We should newtype these or otherwise avoid exposing them + "http::header::name::HeaderName", + "http::header::value::HeaderValue", "http::request::Request", "http::response::Response", + "http::uri::Uri", ] diff --git a/aws/rust-runtime/aws-runtime/src/lib.rs b/aws/rust-runtime/aws-runtime/src/lib.rs index 9df20ae3bc..a86f74d056 100644 --- a/aws/rust-runtime/aws-runtime/src/lib.rs +++ b/aws/rust-runtime/aws-runtime/src/lib.rs @@ -30,3 +30,6 @@ pub mod retries; /// Supporting code for invocation ID headers in the AWS SDK. pub mod invocation_id; + +/// Supporting code for request metadata headers in the AWS SDK. +pub mod request_info; diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs new file mode 100644 index 0000000000..05a80e220b --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -0,0 +1,224 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime::client::orchestrator::interceptors::{RequestAttempts, ServiceClockSkew}; +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; +use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::date_time::Format; +use aws_smithy_types::retry::RetryConfig; +use aws_smithy_types::timeout::TimeoutConfig; +use aws_smithy_types::DateTime; +use http::{HeaderName, HeaderValue}; +use std::borrow::Cow; +use std::time::{Duration, SystemTime}; + +#[allow(clippy::declare_interior_mutable_const)] // we will never mutate this +const AMZ_SDK_REQUEST: HeaderName = HeaderName::from_static("amz-sdk-request"); + +/// Generates and attaches a request header that communicates request-related metadata. +/// Examples include: +/// +/// - When the client will time out this request. +/// - How many times the request has been retried. +/// - The maximum number of retries that the client will attempt. +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct RequestInfoInterceptor {} + +impl RequestInfoInterceptor { + /// Creates a new `RequestInfoInterceptor` + pub fn new() -> Self { + RequestInfoInterceptor {} + } +} + +impl RequestInfoInterceptor { + fn build_attempts_pair( + &self, + cfg: &ConfigBag, + ) -> Option<(Cow<'static, str>, Cow<'static, str>)> { + let request_attempts = cfg + .get::() + .map(|r_a| r_a.attempts()) + .unwrap_or(1); + let request_attempts = request_attempts.to_string(); + Some((Cow::Borrowed("attempt"), Cow::Owned(request_attempts))) + } + + fn build_max_attempts_pair( + &self, + cfg: &ConfigBag, + ) -> Option<(Cow<'static, str>, Cow<'static, str>)> { + // TODO(enableNewSmithyRuntime) What config will we actually store in the bag? Will it be a whole config or just the max_attempts part? + if let Some(retry_config) = cfg.get::() { + let max_attempts = retry_config.max_attempts().to_string(); + Some((Cow::Borrowed("max"), Cow::Owned(max_attempts))) + } else { + None + } + } + + fn build_ttl_pair(&self, cfg: &ConfigBag) -> Option<(Cow<'static, str>, Cow<'static, str>)> { + let timeout_config = cfg.get::()?; + let socket_read = timeout_config.read_timeout()?; + let estimated_skew: Duration = cfg.get::().cloned()?.into(); + let current_time = SystemTime::now(); + let ttl = current_time.checked_add(socket_read + estimated_skew)?; + let timestamp = DateTime::from(ttl); + let formatted_timestamp = timestamp + .fmt(Format::DateTime) + .expect("the resulting DateTime will always be valid"); + + Some((Cow::Borrowed("ttl"), Cow::Owned(formatted_timestamp))) + } +} + +impl Interceptor for RequestInfoInterceptor { + fn modify_before_transmit( + &self, + context: &mut InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let mut pairs = RequestPairs::new(); + if let Some(pair) = self.build_attempts_pair(cfg) { + pairs = pairs.with_pair(pair); + } + if let Some(pair) = self.build_max_attempts_pair(cfg) { + pairs = pairs.with_pair(pair); + } + if let Some(pair) = self.build_ttl_pair(cfg) { + pairs = pairs.with_pair(pair); + } + + let headers = context.request_mut().headers_mut(); + headers.insert(AMZ_SDK_REQUEST, pairs.try_into_header_value()?); + + Ok(()) + } +} + +/// A builder for creating a `RequestPairs` header value. `RequestPairs` is used to generate a +/// retry information header that is sent with every request. The information conveyed by this +/// header allows services to anticipate whether a client will time out or retry a request. +#[derive(Default, Debug)] +pub struct RequestPairs { + inner: Vec<(Cow<'static, str>, Cow<'static, str>)>, +} + +impl RequestPairs { + /// Creates a new `RequestPairs` builder. + pub fn new() -> Self { + Default::default() + } + + /// Adds a pair to the `RequestPairs` builder. + /// Only strings that can be converted to header values are considered valid. + pub fn with_pair( + mut self, + pair: (impl Into>, impl Into>), + ) -> Self { + let pair = (pair.0.into(), pair.1.into()); + self.inner.push(pair); + self + } + + /// Converts the `RequestPairs` builder into a `HeaderValue`. + pub fn try_into_header_value(self) -> Result { + self.try_into() + } +} + +impl TryFrom for HeaderValue { + type Error = BoxError; + + fn try_from(value: RequestPairs) -> Result { + let mut pairs = String::new(); + for (key, value) in value.inner { + if !pairs.is_empty() { + pairs.push_str("; "); + } + + pairs.push_str(&key); + pairs.push('='); + pairs.push_str(&value); + continue; + } + HeaderValue::from_str(&pairs).map_err(Into::into) + } +} + +#[cfg(test)] +mod tests { + use super::RequestInfoInterceptor; + use crate::request_info::RequestPairs; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime::client::orchestrator::interceptors::RequestAttempts; + use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; + use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; + use aws_smithy_runtime_api::config_bag::ConfigBag; + use aws_smithy_runtime_api::type_erasure::TypedBox; + use aws_smithy_types::retry::RetryConfig; + use aws_smithy_types::timeout::TimeoutConfig; + use http::HeaderValue; + use std::time::Duration; + + fn expect_header<'a>( + context: &'a InterceptorContext, + header_name: &str, + ) -> &'a str { + context + .request() + .headers() + .get(header_name) + .unwrap() + .to_str() + .unwrap() + } + + #[test] + fn test_request_pairs_for_initial_attempt() { + let context = InterceptorContext::<()>::new(TypedBox::new("doesntmatter").erase()); + let mut context = context.into_serialization_phase(); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + + let mut config = ConfigBag::base(); + config.put(RetryConfig::standard()); + config.put( + TimeoutConfig::builder() + .read_timeout(Duration::from_secs(30)) + .build(), + ); + config.put(RequestAttempts::new()); + + let _ = context.take_input(); + let mut context = context.into_before_transmit_phase(); + let interceptor = RequestInfoInterceptor::new(); + interceptor + .modify_before_transmit(&mut context, &mut config) + .unwrap(); + + assert_eq!( + expect_header(&context, "amz-sdk-request"), + "attempt=0; max=3" + ); + } + + #[test] + fn test_header_value_from_request_pairs_supports_all_valid_characters() { + // The list of valid characters is defined by an internal-only spec. + let rp = RequestPairs::new() + .with_pair(("allowed-symbols", "!#$&'*+-.^_`|~")) + .with_pair(("allowed-digits", "01234567890")) + .with_pair(( + "allowed-characters", + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + )) + .with_pair(("allowed-whitespace", " \t")); + let _header_value: HeaderValue = rp + .try_into() + .expect("request pairs can be converted into valid header value."); + } +} 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 6d445f271d..8959054d16 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 @@ -54,6 +54,7 @@ val DECORATORS: List = listOf( DisabledAuthDecorator(), RecursionDetectionDecorator(), InvocationIdDecorator(), + RetryInformationHeaderDecorator(), ), // Service specific decorators diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt index f3f1da9720..ad95b54e2b 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt @@ -57,7 +57,7 @@ class RetryClassifierFeature(private val runtimeConfig: RuntimeConfig) : Operati class OperationRetryClassifiersFeature( codegenContext: ClientCodegenContext, - operation: OperationShape, + operationShape: OperationShape, ) : OperationRuntimePluginCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) @@ -72,7 +72,7 @@ class OperationRetryClassifiersFeature( "RetryReason" to smithyRuntimeApi.resolve("client::retries::RetryReason"), "ClassifyRetry" to smithyRuntimeApi.resolve("client::retries::ClassifyRetry"), "RetryClassifiers" to smithyRuntimeApi.resolve("client::retries::RetryClassifiers"), - "OperationError" to codegenContext.symbolProvider.symbolForOperationError(operation), + "OperationError" to codegenContext.symbolProvider.symbolForOperationError(operationShape), "SdkError" to RuntimeType.smithyHttp(runtimeConfig).resolve("result::SdkError"), "ErasedError" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypeErasedError"), ) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt new file mode 100644 index 0000000000..d930693988 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt @@ -0,0 +1,55 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.util.letIf + +class RetryInformationHeaderDecorator : ClientCodegenDecorator { + override val name: String = "RetryInformationHeader" + override val order: Byte = 10 + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(AddRetryInformationHeaderInterceptors(codegenContext)) + } +} + +private class AddRetryInformationHeaderInterceptors(codegenContext: ClientCodegenContext) : + ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val smithyRuntime = RuntimeType.smithyRuntime(runtimeConfig) + private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.AdditionalConfig) { + // Track the latency between client and server. + section.registerInterceptor(runtimeConfig, this) { + rust("#T::new()", smithyRuntime.resolve("client::orchestrator::interceptors::ServiceClockSkewInterceptor")) + } + + // Track the number of request attempts made. + section.registerInterceptor(runtimeConfig, this) { + rust("#T::new()", smithyRuntime.resolve("client::orchestrator::interceptors::RequestAttemptsInterceptor")) + } + + // Add request metadata to outgoing requests. Sets a header. + section.registerInterceptor(runtimeConfig, this) { + rust("#T::new()", awsRuntime.resolve("request_info::RequestInfoInterceptor")) + } + } + } +} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml index 27998067f1..d93edb0e84 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml +++ b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml @@ -10,6 +10,7 @@ aws-http = { path = "../../../rust-runtime/aws-http" } aws-runtime = { path = "../../../rust-runtime/aws-runtime" } aws-sdk-s3 = { path = "../../build/sdk/aws-sdk-s3", features = ["test-util"] } aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client", features = ["test-util", "rustls"] } +aws-smithy-runtime = { path = "../../../../rust-runtime/aws-smithy-runtime" } aws-smithy-runtime-api = { path = "../../../../rust-runtime/aws-smithy-runtime-api" } aws-types = { path = "../../../rust-runtime/aws-types" } criterion = { version = "0.4", features = ["async_tokio"] } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs index c6659cb7c1..7c875e18ab 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs @@ -6,7 +6,7 @@ #[macro_use] extern crate criterion; use aws_sdk_s3 as s3; -use aws_smithy_runtime_api::client::interceptors::Interceptors; +use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::config_bag::ConfigBag; use criterion::{BenchmarkId, Criterion}; @@ -88,6 +88,7 @@ macro_rules! middleware_bench_fn { } async fn orchestrator(client: &s3::Client) { + #[derive(Debug)] struct FixupPlugin { region: String, } @@ -95,7 +96,7 @@ async fn orchestrator(client: &s3::Client) { fn configure( &self, cfg: &mut ConfigBag, - _interceptors: &mut Interceptors, + _interceptors: &mut InterceptorRegistrar, ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { let params_builder = s3::endpoint::Params::builder() .set_region(Some(self.region.clone())) diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/slow-network-and-late-client-clock.json b/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/slow-network-and-late-client-clock.json new file mode 100644 index 0000000000..692e5d23f2 --- /dev/null +++ b/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/slow-network-and-late-client-clock.json @@ -0,0 +1,105 @@ +{ + "events": [ + { + "connection_id": 0, + "action": { + "Request": { + "request": { + "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + "headers": { + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" + ], + "x-amz-date": [ + "20210618T170728Z" + ], + "x-amz-content-sha256": [ + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "amz-sdk-invocation-id": [ + "00000000-0000-4000-8000-000000000000" + ], + "user-agent": [ + "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" + ] + }, + "method": "GET" + } + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "x-amz-request-id": [ + "9X5E7C9EAB6AQEP2" + ], + "x-amz-id-2": [ + "gZsrBxajPyo1Q0DE2plGf7T6kAnxd4Xx7/S+8lq18GegL6kFbnVXLLh1LnBzpEpFiHN9XoNHkeA=" + ], + "content-type": [ + "application/xml" + ], + "transfer-encoding": [ + "chunked" + ], + "server": [ + "AmazonS3" + ], + "date": [ + "Wed, 26 Apr 2023 14:00:24 GMT" + ], + "x-amz-bucket-region": [ + "us-east-1" + ] + } + } + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "\n\n test-bucket\n prefix~\n 1\n 1000\n false\n \n some-file.file\n 2009-10-12T17:50:30.000Z\n 434234\n STANDARD\n \n" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + } + ], + "docs": "One SDK operation invocation. Client retries 3 times, successful response on 3rd attempt. Slow network, one way latency is 2 seconds. Server takes 1 second to generate response. Client clock is 10 minutes behind server clock. One second delay between retries.", + "version": "V0" +} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-retries_and-then-success.json b/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-retries_and-then-success.json new file mode 100644 index 0000000000..b0aee24861 --- /dev/null +++ b/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-retries_and-then-success.json @@ -0,0 +1,312 @@ +{ + "events": [ + { + "connection_id": 0, + "action": { + "Request": { + "request": { + "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + "headers": { + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" + ], + "x-amz-date": [ + "20190601T000000Z" + ], + "x-amz-content-sha256": [ + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "amz-sdk-invocation-id": [ + "00000000-0000-4000-8000-000000000000" + ], + "amz-sdk-request": [ + "attempt=1; max=3" + ], + "user-agent": [ + "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" + ] + }, + "method": "GET" + } + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Response": { + "response": { + "Ok": { + "status": 500, + "version": "HTTP/1.1", + "headers": { + "server": [ + "AmazonS3" + ], + "x-amz-request-id": [ + "foo-id" + ], + "x-amz-id-2": [ + "foo-id" + ], + "content-type": [ + "application/xml" + ], + "transfer-encoding": [ + "chunked" + ], + "server": [ + "AmazonS3" + ], + "date": [ + "Sat, 01 Jun 2019 00:00:00 GMT" + ] + } + } + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "\n \"\n Server\n InternalError\n We encountered an internal error. Please try again.\n foo-id\n\"" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Request": { + "request": { + "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + "headers": { + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" + ], + "x-amz-date": [ + "20190601T000001Z" + ], + "x-amz-content-sha256": [ + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "amz-sdk-invocation-id": [ + "00000000-0000-4000-8000-000000000000" + ], + "amz-sdk-request": [ + "ttl=20190601T000011Z; attempt=2; max=3" + ], + "user-agent": [ + "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" + ] + }, + "method": "GET" + } + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Response": { + "response": { + "Ok": { + "status": 500, + "version": "HTTP/1.1", + "headers": { + "server": [ + "AmazonS3" + ], + "x-amz-request-id": [ + "foo-id" + ], + "x-amz-id-2": [ + "foo-id" + ], + "content-type": [ + "application/xml" + ], + "transfer-encoding": [ + "chunked" + ], + "server": [ + "AmazonS3" + ], + "date": [ + "Sat, 01 Jun 2019 00:00:01 GMT" + ] + } + } + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "\n \"\n Server\n InternalError\n We encountered an internal error. Please try again.\n foo-id\n\"" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Request": { + "request": { + "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + "headers": { + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" + ], + "x-amz-date": [ + "20190601T000002Z" + ], + "x-amz-content-sha256": [ + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "amz-sdk-invocation-id": [ + "00000000-0000-4000-8000-000000000000" + ], + "amz-sdk-request": [ + "ttl=20190601T000012Z; attempt=3; max=3" + ], + "user-agent": [ + "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" + ] + }, + "method": "GET" + } + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "x-amz-request-id": [ + "9X5E7C9EAB6AQEP2" + ], + "x-amz-id-2": [ + "gZsrBxajPyo1Q0DE2plGf7T6kAnxd4Xx7/S+8lq18GegL6kFbnVXLLh1LnBzpEpFiHN9XoNHkeA=" + ], + "content-type": [ + "application/xml" + ], + "transfer-encoding": [ + "chunked" + ], + "server": [ + "AmazonS3" + ], + "date": [ + "Sat, 01 Jun 2019 00:00:02 GMT" + ], + "x-amz-bucket-region": [ + "us-east-1" + ] + } + } + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "\n\n test-bucket\n prefix~\n 1\n 1000\n false\n \n some-file.file\n 2009-10-12T17:50:30.000Z\n 434234\n STANDARD\n \n" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + } + ], + "docs": "One SDK operation invocation. Client retries 3 times, successful response on 3rd attempt. Fast network, latency + server time is less than one second. No clock skew. Client waits 1 second between retry attempts.", + "version": "V0" +} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-successful-attempts.json b/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-successful-attempts.json new file mode 100644 index 0000000000..180b54c5d7 --- /dev/null +++ b/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-successful-attempts.json @@ -0,0 +1,105 @@ +{ + "events": [ + { + "connection_id": 0, + "action": { + "Request": { + "request": { + "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", + "headers": { + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" + ], + "x-amz-date": [ + "20210618T170728Z" + ], + "x-amz-content-sha256": [ + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ], + "amz-sdk-invocation-id": [ + "00000000-0000-4000-8000-000000000000" + ], + "user-agent": [ + "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" + ] + }, + "method": "GET" + } + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "x-amz-request-id": [ + "9X5E7C9EAB6AQEP2" + ], + "x-amz-id-2": [ + "gZsrBxajPyo1Q0DE2plGf7T6kAnxd4Xx7/S+8lq18GegL6kFbnVXLLh1LnBzpEpFiHN9XoNHkeA=" + ], + "content-type": [ + "application/xml" + ], + "transfer-encoding": [ + "chunked" + ], + "server": [ + "AmazonS3" + ], + "date": [ + "Wed, 26 Apr 2023 14:00:24 GMT" + ], + "x-amz-bucket-region": [ + "us-east-1" + ] + } + } + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "\n\n test-bucket\n prefix~\n 1\n 1000\n false\n \n some-file.file\n 2009-10-12T17:50:30.000Z\n 434234\n STANDARD\n \n" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + } + ], + "docs": "Client makes 3 separate SDK operation invocations; All succeed on first attempt. Fast network, latency + server time is less than one second.", + "version": "V0" +} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs new file mode 100644 index 0000000000..af0ab416dd --- /dev/null +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs @@ -0,0 +1,257 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_http::user_agent::AwsUserAgent; +use aws_runtime::invocation_id::InvocationId; +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::endpoint::Params; +use aws_sdk_s3::Client; +use aws_smithy_client::dvr; +use aws_smithy_client::dvr::MediaType; +use aws_smithy_client::erase::DynConnector; +use aws_smithy_runtime::client::retries::strategy::FixedDelayRetryStrategy; +use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; +use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +#[derive(Debug)] +struct FixupPlugin { + client: Client, + timestamp: SystemTime, +} + +// # One SDK operation invocation. +// # Client retries 3 times, successful response on 3rd attempt. +// # Fast network, latency + server time is less than one second. +// # No clock skew +// # Client waits 1 second between retry attempts. +#[tokio::test] +async fn three_retries_and_then_success() { + tracing_subscriber::fmt::init(); + + impl RuntimePlugin for FixupPlugin { + fn configure( + &self, + cfg: &mut ConfigBag, + _interceptors: &mut InterceptorRegistrar, + ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { + let params_builder = Params::builder() + .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) + .bucket("test-bucket"); + + cfg.put(params_builder); + cfg.set_request_time(RequestTime::new(self.timestamp.clone())); + cfg.put(AwsUserAgent::for_tests()); + cfg.put(InvocationId::for_tests()); + cfg.set_retry_strategy(FixedDelayRetryStrategy::one_second_delay()); + Ok(()) + } + } + + let path = "test-data/request-information-headers/three-retries_and-then-success.json"; + let conn = dvr::ReplayingConnection::from_file(path).unwrap(); + let config = aws_sdk_s3::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .http_connector(DynConnector::new(conn.clone())) + .build(); + let client = Client::from_conf(config); + let fixup = FixupPlugin { + client: client.clone(), + timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), + }; + + let resp = dbg!( + client + .list_objects_v2() + .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) + .bucket("test-bucket") + .prefix("prefix~") + .send_orchestrator_with_plugin(Some(fixup)) + .await + ); + + let resp = resp.expect("valid e2e test"); + assert_eq!(resp.name(), Some("test-bucket")); + conn.full_validate(MediaType::Xml).await.expect("failed") +} +// +// // # Client makes 3 separate SDK operation invocations +// // # All succeed on first attempt. +// // # Fast network, latency + server time is less than one second. +// // - request: +// // time: 2019-06-01T00:00:00Z +// // headers: +// // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 +// // amz-sdk-request: attempt=1; max=3 +// // response: +// // status: 200 +// // time_received: 2019-06-01T00:00:00Z +// // headers: +// // Date: Sat, 01 Jun 2019 00:00:00 GMT +// // - request: +// // time: 2019-06-01T00:01:01Z +// // headers: +// // # Note the different invocation id because it's a new SDK +// // # invocation operation. +// // amz-sdk-invocation-id: 70370531-7b83-4b90-8b93-46975687ecf6 +// // amz-sdk-request: ttl=20190601T000011Z; attempt=1; max=3 +// // response: +// // status: 200 +// // time_received: 2019-06-01T00:00:01Z +// // headers: +// // Date: Sat, 01 Jun 2019 00:00:01 GMT +// // - request: +// // time: 2019-06-01T00:00:02Z +// // headers: +// // amz-sdk-invocation-id: 910bf450-6c90-43de-a508-3fa126a06b71 +// // amz-sdk-request: ttl=20190601T000012Z; attempt=1; max=3 +// // response: +// // status: 200 +// // time_received: 2019-06-01T00:00:02Z +// // headers: +// // Date: Sat, 01 Jun 2019 00:00:02 GMT +// const THREE_SUCCESSFUL_ATTEMPTS_PATH: &str = "test-data/request-information-headers/three-successful-attempts.json"; +// #[tokio::test] +// async fn three_successful_attempts() { +// tracing_subscriber::fmt::init(); +// +// impl RuntimePlugin for FixupPlugin { +// fn configure( +// &self, +// cfg: &mut ConfigBag, +// ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { +// let params_builder = Params::builder() +// .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) +// .bucket("test-bucket"); +// +// cfg.put(params_builder); +// cfg.set_request_time(RequestTime::new(self.timestamp.clone())); +// cfg.put(AwsUserAgent::for_tests()); +// cfg.put(InvocationId::for_tests()); +// Ok(()) +// } +// } +// +// let conn = dvr::ReplayingConnection::from_file(THREE_SUCCESSFUL_ATTEMPTS_PATH).unwrap(); +// let config = aws_sdk_s3::Config::builder() +// .credentials_provider(Credentials::for_tests()) +// .region(Region::new("us-east-1")) +// .http_connector(DynConnector::new(conn.clone())) +// .build(); +// let client = Client::from_conf(config); +// let fixup = FixupPlugin { +// client: client.clone(), +// timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), +// }; +// +// let resp = dbg!( +// client +// .list_objects_v2() +// .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) +// .bucket("test-bucket") +// .prefix("prefix~") +// .send_v2_with_plugin(Some(fixup)) +// .await +// ); +// +// let resp = resp.expect("valid e2e test"); +// assert_eq!(resp.name(), Some("test-bucket")); +// conn.full_validate(MediaType::Xml).await.expect("failed") +// } +// +// // # One SDK operation invocation. +// // # Client retries 3 times, successful response on 3rd attempt. +// // # Slow network, one way latency is 2 seconds. +// // # Server takes 1 second to generate response. +// // # Client clock is 10 minutes behind server clock. +// // # One second delay between retries. +// // - request: +// // time: 2019-06-01T00:00:00Z +// // headers: +// // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 +// // amz-sdk-request: attempt=1; max=3 +// // response: +// // status: 500 +// // time_received: 2019-06-01T00:00:05Z +// // headers: +// // Date: Sat, 01 Jun 2019 00:10:03 GMT +// // - request: +// // time: 2019-06-01T00:00:06Z +// // # The ttl is 00:00:16 with the client clock, +// // # but accounting for skew we have +// // # 00:10:03 - 00:00:05 = 00:09:58 +// // # ttl = 00:00:16 + 00:09:58 = 00:10:14 +// // headers: +// // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 +// // amz-sdk-request: ttl=20190601T001014Z; attempt=2; max=3 +// // response: +// // status: 500 +// // time_received: 2019-06-01T00:00:11Z +// // headers: +// // Date: Sat, 01 Jun 2019 00:10:09 GMT +// // - request: +// // time: 2019-06-01T00:00:12Z +// // headers: +// // # ttl = 00:00:12 + 20 = 00:00:22 +// // # skew is: +// // # 00:10:09 - 00:00:11 +// // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 +// // amz-sdk-request: ttl=20190601T001020Z; attempt=3; max=3 +// // response: +// // status: 200 +// // time_received: 2019-06-01T00:00:17Z +// // headers: +// // Date: Sat, 01 Jun 2019 00:10:15 GMT +// const SLOW_NETWORK_AND_LATE_CLIENT_CLOCK_PATH: &str = "test-data/request-information-headers/slow-network-and-late-client-clock.json"; +// #[tokio::test] +// async fn slow_network_and_late_client_clock() { +// tracing_subscriber::fmt::init(); +// +// impl RuntimePlugin for FixupPlugin { +// fn configure( +// &self, +// cfg: &mut ConfigBag, +// ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { +// let params_builder = Params::builder() +// .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) +// .bucket("test-bucket"); +// +// cfg.put(params_builder); +// cfg.set_request_time(RequestTime::new(self.timestamp.clone())); +// cfg.put(AwsUserAgent::for_tests()); +// cfg.put(InvocationId::for_tests()); +// Ok(()) +// } +// } +// +// let conn = dvr::ReplayingConnection::from_file(SLOW_NETWORK_AND_LATE_CLIENT_CLOCK_PATH).unwrap(); +// let config = aws_sdk_s3::Config::builder() +// .credentials_provider(Credentials::for_tests()) +// .region(Region::new("us-east-1")) +// .http_connector(DynConnector::new(conn.clone())) +// .build(); +// let client = Client::from_conf(config); +// let fixup = FixupPlugin { +// client: client.clone(), +// timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), +// }; +// +// let resp = dbg!( +// client +// .list_objects_v2() +// .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) +// .bucket("test-bucket") +// .prefix("prefix~") +// .send_v2_with_plugin(Some(fixup)) +// .await +// ); +// +// let resp = resp.expect("valid e2e test"); +// assert_eq!(resp.name(), Some("test-bucket")); +// conn.full_validate(MediaType::Xml).await.expect("failed") +// } diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 68b7f66319..496d50d84b 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -27,7 +27,7 @@ http-body = "0.4.5" pin-project-lite = "0.2.7" pin-utils = "0.1.0" tokio = { version = "1.25", features = [] } -tracing = "0.1" +tracing = "0.1.37" [dev-dependencies] aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio"] } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 4b291a87d4..e3be9839c3 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -23,6 +23,7 @@ mod auth; /// Defines types that implement a trait for endpoint resolution pub mod endpoints; mod http; +pub mod interceptors; #[doc(hidden)] #[macro_export] diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs new file mode 100644 index 0000000000..081b9b3bd7 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs @@ -0,0 +1,10 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +mod request_attempts; +mod service_clock_skew; + +pub use request_attempts::{RequestAttempts, RequestAttemptsInterceptor}; +pub use service_clock_skew::{ServiceClockSkew, ServiceClockSkewInterceptor}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs new file mode 100644 index 0000000000..c69137b022 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs @@ -0,0 +1,68 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; +use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::config_bag::ConfigBag; + +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct RequestAttempts { + attempts: u32, +} + +impl RequestAttempts { + pub fn new() -> Self { + Self::default() + } + + // There is no legitimate reason to set this unless you're testing things. + // Therefore, this is only available for tests. + #[cfg(test)] + pub fn new_with_attempts(attempts: u32) -> Self { + Self { attempts } + } + + pub fn attempts(&self) -> u32 { + self.attempts + } + + fn increment(mut self) -> Self { + self.attempts += 1; + self + } +} + +#[derive(Debug, Default)] +#[non_exhaustive] +pub struct RequestAttemptsInterceptor {} + +impl RequestAttemptsInterceptor { + pub fn new() -> Self { + Self::default() + } +} + +impl Interceptor for RequestAttemptsInterceptor { + fn modify_before_retry_loop( + &self, + _ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + cfg.put(RequestAttempts::new()); + Ok(()) + } + + fn modify_before_transmit( + &self, + _ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + if let Some(request_attempts) = cfg.get::().cloned() { + cfg.put(request_attempts.increment()); + } + Ok(()) + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs new file mode 100644 index 0000000000..579ecbb484 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs @@ -0,0 +1,83 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeDeserialization; +use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::date_time::Format; +use aws_smithy_types::DateTime; +use std::time::{Duration, SystemTime}; + +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct ServiceClockSkew { + inner: Duration, +} + +impl ServiceClockSkew { + fn new(inner: Duration) -> Self { + Self { inner } + } + + pub fn skew(&self) -> Duration { + self.inner + } +} + +impl From for Duration { + fn from(skew: ServiceClockSkew) -> Duration { + skew.inner + } +} + +#[derive(Debug, Default)] +#[non_exhaustive] +pub struct ServiceClockSkewInterceptor {} + +impl ServiceClockSkewInterceptor { + pub fn new() -> Self { + Self::default() + } +} + +fn calculate_skew(time_sent: DateTime, time_received: DateTime) -> Duration { + let skew = (time_sent.as_secs_f64() - time_received.as_secs_f64()).max(0.0); + Duration::from_secs_f64(skew) +} + +fn extract_time_sent_from_response( + ctx: &mut InterceptorContext, +) -> Result { + let date_header = ctx + .response() + .headers() + .get("date") + .ok_or("Response from server does not include a `date` header")? + .to_str()?; + DateTime::from_str(date_header, Format::HttpDate).map_err(Into::into) +} + +impl Interceptor for ServiceClockSkewInterceptor { + fn modify_before_deserialization( + &self, + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let time_received = DateTime::from(SystemTime::now()); + let time_sent = match extract_time_sent_from_response(ctx) { + Ok(time_sent) => time_sent, + Err(e) => { + // We don't want to fail a request for this because 1xx and 5xx responses and + // responses from servers with no clock may omit this header. We still log it at the + // trace level to aid in debugging. + tracing::trace!("failed to calculate clock skew of service from response: {e}. Ignoring this error...",); + return Ok(()); + } + }; + let skew = ServiceClockSkew::new(calculate_skew(time_sent, time_received)); + cfg.put(skew); + Ok(()) + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs index 6b7854fc2c..6f1e71d39e 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs @@ -3,6 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +#[cfg(feature = "test-util")] +mod fixed_delay; mod never; +#[cfg(feature = "test-util")] +pub use fixed_delay::FixedDelayRetryStrategy; pub use never::NeverRetryStrategy; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs new file mode 100644 index 0000000000..1ee985c22e --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -0,0 +1,98 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::client::orchestrator::interceptors::RequestAttempts; +use aws_smithy_runtime_api::client::interceptors::context::phase::AfterDeserialization; +use aws_smithy_runtime_api::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::BoxError; +use aws_smithy_runtime_api::client::retries::{ + ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, ShouldAttempt, +}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use std::time::Duration; + +// A retry policy used in tests. This relies on an error classifier already present in the config bag. +// If a server response is retryable, it will be retried after a fixed delay. +#[derive(Debug, Clone)] +pub struct FixedDelayRetryStrategy { + fixed_delay: Duration, + max_attempts: u32, +} + +impl FixedDelayRetryStrategy { + pub fn new(fixed_delay: Duration) -> Self { + Self { + fixed_delay, + max_attempts: 4, + } + } + + pub fn one_second_delay() -> Self { + Self::new(Duration::from_secs(1)) + } +} + +impl RetryStrategy for FixedDelayRetryStrategy { + fn should_attempt_initial_request(&self, _cfg: &ConfigBag) -> Result { + Ok(ShouldAttempt::Yes) + } + + fn should_attempt_retry( + &self, + ctx: &InterceptorContext, + cfg: &ConfigBag, + ) -> Result { + // Look a the result. If it's OK then we're done; No retry required. Otherwise, we need to inspect it + let error = match ctx.output_or_error() { + Ok(_) => { + tracing::trace!("request succeeded, no retry necessary"); + return Ok(ShouldAttempt::No); + } + Err(err) => err, + }; + + let request_attempts: &RequestAttempts = cfg + .get() + .expect("at least one request attempt is made before any retry is attempted"); + if request_attempts.attempts() == self.max_attempts { + tracing::trace!( + attempts = request_attempts.attempts(), + max_attempts = self.max_attempts, + "not retrying because we are out of attempts" + ); + return Ok(ShouldAttempt::No); + } + + let retry_classifiers = cfg + .get::() + .expect("a retry classifier is set"); + let retry_reason = retry_classifiers.classify_retry(error); + + let backoff = match retry_reason { + Some(RetryReason::Explicit(_)) => self.fixed_delay, + Some(RetryReason::Error(_)) => self.fixed_delay, + Some(_) => { + unreachable!("RetryReason is non-exhaustive. Therefore, we need to cover this unreachable case.") + } + None => { + tracing::trace!( + attempts = request_attempts.attempts(), + max_attempts = self.max_attempts, + "encountered unretryable error" + ); + return Ok(ShouldAttempt::No); + } + }; + + tracing::debug!( + "attempt {} failed with {:?}; retrying after {:?}", + request_attempts.attempts(), + retry_reason, + backoff + ); + + Ok(ShouldAttempt::YesAfterDelay(backoff)) + } +} diff --git a/rust-runtime/aws-smithy-types/src/date_time/mod.rs b/rust-runtime/aws-smithy-types/src/date_time/mod.rs index 6bc099d25a..fd9555b5e6 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/mod.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs @@ -330,16 +330,20 @@ impl fmt::Display for ConversionError { /// Formats for representing a `DateTime` in the Smithy protocols. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Format { - /// RFC-3339 Date Time. If the date time has an offset, an error will be returned + /// RFC-3339 Date Time. If the date time has an offset, an error will be returned. + /// e.g. `2019-12-16T23:48:18Z` DateTime, - /// RFC-3339 Date Time. Offsets are supported + /// RFC-3339 Date Time. Offsets are supported. + /// e.g. `2019-12-16T23:48:18+01:00` DateTimeWithOffset, /// Date format used by the HTTP `Date` header, specified in RFC-7231. + /// e.g. `Mon, 16 Dec 2019 23:48:18 GMT` HttpDate, /// Number of seconds since the Unix epoch formatted as a floating point. + /// e.g. `1576540098.52` EpochSeconds, } From 3b95b9706ce52d3b691a804a1732c1c43ec2a5ad Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Wed, 17 May 2023 16:31:23 -0500 Subject: [PATCH 097/253] Port customizable operation to orchestrator (#2706) ## Motivation and Context Port [Customizable Operation](https://github.com/awslabs/smithy-rs/pull/1647) to orchestrator ## Description This PR implements `CustomizableOperation` in the orchestrator. Just like the counterpart in the middleware, it is created when the `customize` method (in the orchestrator mode) on a fluent builder is called. The `customize` method in the orchestrator could technically be made a synchronous method because there is no need to create an operation, which requires `async`, therefore making the `customize` method in the middleware `async`. However, during the transition from the middleware to the orchestrator, the integration tests ([example](https://github.com/awslabs/smithy-rs/blob/31c152d9af53afb9a5e6edf9df3def57931b9c1e/aws/sdk/integration-tests/s3/tests/signing-it.rs#L36)) need to be able to run in both modes. For this reason, the `customize` method in the orchestrator is temporarily marked as `async`. Regarding methods defined on the new `CustomizableOperation`, they include `mutate_request` and `map_request` from the counterpart in the middleware. However, it did not port `map_operation` because there is no operation to map on. Most use cases for `map_operation` is put things in a property bag. The new `CustomizableOperation` provides an `interceptor` method to accomplish the same, i.e putting things in a config bag. Finally, for integration tests to run in both modes, the code gen emits the implementation of the `customize` method differently depending on the active Smithy runtime mode, similar to what the implementation of `send` method does. ## Testing Added one `sra-test` for mutating a request. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito Co-authored-by: John DiSanti --- .../aws-sdk-s3/tests/interceptors.rs | 58 ++++++- .../tests/request_information_headers.rs | 7 +- .../client/CustomizableOperationGenerator.kt | 143 +++++++++++++++++- .../client/FluentClientGenerator.kt | 65 ++++---- rust-runtime/aws-smithy-runtime/src/client.rs | 3 + .../src/client/interceptor.rs | 80 ++++++++++ 6 files changed, 319 insertions(+), 37 deletions(-) create mode 100644 rust-runtime/aws-smithy-runtime/src/client/interceptor.rs diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs index 208bd8aa8b..f33ef94ff8 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs @@ -40,11 +40,12 @@ async fn operation_interceptor_test() { let resp = dbg!( client .list_objects_v2() - .config_override( - aws_sdk_s3::Config::builder().interceptor(util::TestUserAgentInterceptor) - ) .bucket("test-bucket") .prefix("prefix~") + .customize() + .await + .unwrap() + .interceptor(util::TestUserAgentInterceptor) .send_orchestrator_with_plugin(Some(fixup)) .await ); @@ -106,11 +107,56 @@ async fn interceptor_priority() { let resp = dbg!( client .list_objects_v2() - .config_override(aws_sdk_s3::Config::builder().interceptor( - RequestTimeAdvanceInterceptor(Duration::from_secs(1624036048)) - )) .bucket("test-bucket") .prefix("prefix~") + .customize() + .await + .unwrap() + .interceptor(RequestTimeAdvanceInterceptor(Duration::from_secs( + 1624036048 + ))) + .send_orchestrator_with_plugin(Some(fixup)) + .await + ); + let resp = resp.expect("valid e2e test"); + assert_eq!(resp.name(), Some("test-bucket")); + conn.full_validate(MediaType::Xml).await.expect("success") +} + +#[tokio::test] +async fn set_test_user_agent_through_request_mutation() { + let conn = dvr::ReplayingConnection::from_file(LIST_BUCKETS_PATH).unwrap(); + + let config = aws_sdk_s3::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .http_connector(DynConnector::new(conn.clone())) + .build(); + let client = Client::from_conf(config); + let fixup = util::FixupPlugin { + timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), + }; + + let resp = dbg!( + client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .customize() + .await + .unwrap() + .mutate_request(|request| { + request.headers_mut() + .insert( + http::HeaderName::from_static("user-agent"), + http::HeaderValue::from_str("aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0").unwrap(), + ); + request.headers_mut() + .insert( + http::HeaderName::from_static("x-amz-user-agent"), + http::HeaderValue::from_str("aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0").unwrap(), + ); + }) .send_orchestrator_with_plugin(Some(fixup)) .await ); diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs index af0ab416dd..a5138f40de 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs @@ -68,9 +68,12 @@ async fn three_retries_and_then_success() { let resp = dbg!( client .list_objects_v2() - .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) .bucket("test-bucket") .prefix("prefix~") + .customize() + .await + .unwrap() + .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) .send_orchestrator_with_plugin(Some(fixup)) .await ); @@ -152,7 +155,6 @@ async fn three_retries_and_then_success() { // let resp = dbg!( // client // .list_objects_v2() -// .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) // .bucket("test-bucket") // .prefix("prefix~") // .send_v2_with_plugin(Some(fixup)) @@ -244,7 +246,6 @@ async fn three_retries_and_then_success() { // let resp = dbg!( // client // .list_objects_v2() -// .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) // .bucket("test-bucket") // .prefix("prefix~") // .send_v2_with_plugin(Some(fixup)) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index d16d927d7f..3bfd660a75 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.client +import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency @@ -16,13 +17,14 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig 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.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.util.outputShape /** * Generates the code required to add the `.customize()` function to the * fluent client builders. */ class CustomizableOperationGenerator( - codegenContext: ClientCodegenContext, + private val codegenContext: ClientCodegenContext, private val generics: FluentClientGenerics, ) { private val runtimeConfig = codegenContext.runtimeConfig @@ -126,6 +128,145 @@ class CustomizableOperationGenerator( *codegenScope, ) } + + fun renderForOrchestrator(writer: RustWriter, operation: OperationShape) { + val symbolProvider = codegenContext.symbolProvider + val model = codegenContext.model + + val builderName = operation.fluentBuilderType(symbolProvider).name + val outputType = symbolProvider.toSymbol(operation.outputShape(model)) + val errorType = symbolProvider.symbolForOperationError(operation) + + val codegenScope = arrayOf( + *preludeScope, + "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::HttpResponse"), + "Interceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::interceptors::Interceptor"), + "MapRequestInterceptor" to RuntimeType.smithyRuntime(runtimeConfig) + .resolve("client::interceptor::MapRequestInterceptor"), + "MutateRequestInterceptor" to RuntimeType.smithyRuntime(runtimeConfig) + .resolve("client::interceptor::MutateRequestInterceptor"), + "OperationError" to errorType, + "OperationOutput" to outputType, + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "SdkBody" to RuntimeType.sdkBody(runtimeConfig), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::interceptors::SharedInterceptor"), + ) + + writer.rustTemplate( + """ + /// A wrapper type for [`$builderName`]($builderName) that allows for configuring a single + /// operation invocation. + pub struct CustomizableOperation { + pub(crate) fluent_builder: $builderName, + pub(crate) config_override: #{Option}, + pub(crate) interceptors: Vec<#{SharedInterceptor}>, + } + + impl CustomizableOperation { + /// Adds an [`Interceptor`](#{Interceptor}) that runs at specific stages of the request execution pipeline. + /// + /// Note that interceptors can also be added to `CustomizableOperation` by `config_override`, + /// `map_request`, and `mutate_request` (the last two are implemented via interceptors under the hood). + /// The order in which those user-specified operation interceptors are invoked should not be relied upon + /// as it is an implementation detail. + pub fn interceptor(mut self, interceptor: impl #{Interceptor} + #{Send} + #{Sync} + 'static) -> Self { + self.interceptors.push(#{SharedInterceptor}::new(interceptor)); + self + } + + /// Allows for customizing the operation's request. + pub fn map_request(mut self, f: F) -> Self + where + F: #{Fn}(&mut http::Request<#{SdkBody}>) -> #{Result}<(), E> + + #{Send} + + #{Sync} + + 'static, + E: ::std::error::Error + #{Send} + #{Sync} + 'static, + { + self.interceptors.push( + #{SharedInterceptor}::new( + #{MapRequestInterceptor}::new(f), + ), + ); + self + } + + /// Convenience for `map_request` where infallible direct mutation of request is acceptable. + pub fn mutate_request(mut self, f: F) -> Self + where + F: #{Fn}(&mut http::Request<#{SdkBody}>) + #{Send} + #{Sync} + 'static, + { + self.interceptors.push( + #{SharedInterceptor}::new( + #{MutateRequestInterceptor}::new(f), + ), + ); + self + } + + /// Overrides config for a single operation invocation. + /// + /// `config_override` is applied to the operation configuration level. + /// The fields in the builder that are `Some` override those applied to the service + /// configuration level. For instance, + /// + /// Config A overridden by Config B == Config C + /// field_1: None, field_1: Some(v2), field_1: Some(v2), + /// field_2: Some(v1), field_2: Some(v2), field_2: Some(v2), + /// field_3: Some(v1), field_3: None, field_3: Some(v1), + pub fn config_override( + mut self, + config_override: impl #{Into}, + ) -> Self { + self.config_override = Some(config_override.into()); + self + } + + /// Sends the request and returns the response. + pub async fn send( + self + ) -> #{Result}< + #{OperationOutput}, + #{SdkError}< + #{OperationError}, + #{HttpResponse} + > + > { + self.send_orchestrator_with_plugin(#{Option}::<#{Box}>::None) + .await + } + + ##[doc(hidden)] + // TODO(enableNewSmithyRuntime): Delete when unused + /// Equivalent to [`Self::send`] but adds a final runtime plugin to shim missing behavior + pub async fn send_orchestrator_with_plugin( + self, + final_plugin: #{Option} + ) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + let mut config_override = if let Some(config_override) = self.config_override { + config_override + } else { + crate::config::Builder::new() + }; + + self.interceptors.into_iter().for_each(|interceptor| { + config_override.add_interceptor(interceptor); + }); + + self.fluent_builder + .config_override(config_override) + .send_orchestrator_with_plugin(final_plugin) + .await + } + } + """, + *codegenScope, + ) + } } fun renderCustomizableOperationSend(runtimeConfig: RuntimeConfig, generics: FluentClientGenerics, writer: RustWriter) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 10a4f0c02f..53c17b8d61 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -84,13 +84,17 @@ class FluentClientGenerator( fun render(crate: RustCrate) { renderFluentClient(crate) + val customizableOperationGenerator = CustomizableOperationGenerator(codegenContext, generics) operations.forEach { operation -> crate.withModule(symbolProvider.moduleForBuilder(operation)) { renderFluentBuilder(operation) + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + customizableOperationGenerator.renderForOrchestrator(this, operation) + } } } - CustomizableOperationGenerator(codegenContext, generics).render(crate) + customizableOperationGenerator.render(crate) } private fun renderFluentClient(crate: RustCrate) { @@ -307,9 +311,9 @@ class FluentClientGenerator( ) rustTemplate( """ - /// Consume this builder, creating a customizable operation that can be modified before being - /// sent. The operation's inner [http::Request] can be modified as well. - pub async fn customize(self) -> #{Result}< + // This function will go away in the near future. Do not rely on it. + ##[doc(hidden)] + pub async fn customize_middleware(self) -> #{Result}< #{CustomizableOperation}#{customizable_op_type_params:W}, #{SdkError}<#{OperationError}> > #{send_bounds:W} { @@ -349,6 +353,15 @@ class FluentClientGenerator( #{send_bounds:W} { self.send_middleware().await } + + /// Consumes this builder, creating a customizable operation that can be modified before being + /// sent. The operation's inner [http::Request] can be modified as well. + pub async fn customize(self) -> #{Result}< + #{CustomizableOperation}#{customizable_op_type_params:W}, + #{SdkError}<#{OperationError}> + > #{send_bounds:W} { + self.customize_middleware().await + } """, *middlewareScope, ) @@ -357,6 +370,8 @@ class FluentClientGenerator( if (smithyRuntimeMode.generateOrchestrator) { val orchestratorScope = arrayOf( *preludeScope, + "CustomizableOperation" to symbolProvider.moduleForBuilder(operation).toType() + .resolve("CustomizableOperation"), "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::orchestrator::HttpResponse"), "OperationError" to errorType, @@ -382,10 +397,10 @@ class FluentClientGenerator( pub async fn send_orchestrator_with_plugin(self, final_plugin: #{Option}) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { let mut runtime_plugins = #{RuntimePlugins}::new() .with_client_plugin(crate::config::ServiceRuntimePlugin::new(self.handle.clone())); + runtime_plugins = runtime_plugins.with_operation_plugin(#{Operation}::new()); if let Some(config_override) = self.config_override { runtime_plugins = runtime_plugins.with_operation_plugin(config_override); } - runtime_plugins = runtime_plugins.with_operation_plugin(#{Operation}::new()); if let Some(final_plugin) = final_plugin { runtime_plugins = runtime_plugins.with_client_plugin(final_plugin); } @@ -402,6 +417,12 @@ class FluentClientGenerator( })?; #{Ok}(#{TypedBox}::<#{OperationOutput}>::assume_from(output).expect("correct output type").unwrap()) } + + ##[doc(hidden)] + // TODO(enableNewSmithyRuntime): Remove `async` once we switch to orchestrator + pub async fn customize_orchestrator(self) -> #{CustomizableOperation} { + #{CustomizableOperation} { fluent_builder: self, config_override: None, interceptors: vec![] } + } """, *orchestratorScope, ) @@ -419,6 +440,16 @@ class FluentClientGenerator( pub async fn send(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { self.send_orchestrator().await } + + /// Consumes this builder, creating a customizable operation that can be modified before being + /// sent. + // TODO(enableNewSmithyRuntime): Remove `async` and `Result` once we switch to orchestrator + pub async fn customize(self) -> #{Result}< + #{CustomizableOperation}, + #{SdkError}<#{OperationError}> + > { + #{Ok}(self.customize_orchestrator().await) + } """, *orchestratorScope, ) @@ -426,17 +457,7 @@ class FluentClientGenerator( rustTemplate( """ - /// Sets the `config_override` for the builder. - /// - /// `config_override` is applied to the operation configuration level. - /// The fields in the builder that are `Some` override those applied to the service - /// configuration level. For instance, - /// - /// Config A overridden by Config B == Config C - /// field_1: None, field_1: Some(v2), field_1: Some(v2), - /// field_2: Some(v1), field_2: Some(v2), field_2: Some(v2), - /// field_3: Some(v1), field_3: None, field_3: Some(v1), - pub fn config_override( + pub(crate) fn config_override( mut self, config_override: impl Into, ) -> Self { @@ -444,17 +465,7 @@ class FluentClientGenerator( self } - /// Sets the `config_override` for the builder. - /// - /// `config_override` is applied to the operation configuration level. - /// The fields in the builder that are `Some` override those applied to the service - /// configuration level. For instance, - /// - /// Config A overridden by Config B == Config C - /// field_1: None, field_1: Some(v2), field_1: Some(v2), - /// field_2: Some(v1), field_2: Some(v2), field_2: Some(v2), - /// field_3: Some(v1), field_3: None, field_3: Some(v1), - pub fn set_config_override( + pub(crate) fn set_config_override( &mut self, config_override: Option, ) -> &mut Self { diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index c2edaced8b..1a6a106da3 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -29,3 +29,6 @@ pub mod runtime_plugin; /// Smithy identity used by auth and signing. pub mod identity; + +/// Interceptors for Smithy clients. +pub mod interceptor; diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs new file mode 100644 index 0000000000..4e5de36b63 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs @@ -0,0 +1,80 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::body::SdkBody; +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; +use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use std::fmt; +use std::marker::PhantomData; + +pub struct MapRequestInterceptor { + f: F, + _phantom: PhantomData, +} + +impl fmt::Debug for MapRequestInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MapRequestInterceptor") + } +} + +impl MapRequestInterceptor { + pub fn new(f: F) -> Self { + Self { + f, + _phantom: PhantomData, + } + } +} + +impl Interceptor for MapRequestInterceptor +where + F: Fn(&mut http::Request) -> Result<(), E> + Send + Sync + 'static, + E: std::error::Error + Send + Sync + 'static, +{ + fn modify_before_signing( + &self, + context: &mut InterceptorContext, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let request = context.request_mut(); + (self.f)(request)?; + + Ok(()) + } +} + +pub struct MutateRequestInterceptor { + f: F, +} + +impl fmt::Debug for MutateRequestInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MutateRequestInterceptor") + } +} + +impl MutateRequestInterceptor { + pub fn new(f: F) -> Self { + Self { f } + } +} + +impl Interceptor for MutateRequestInterceptor +where + F: Fn(&mut http::Request) + Send + Sync + 'static, +{ + fn modify_before_signing( + &self, + context: &mut InterceptorContext, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let request = context.request_mut(); + (self.f)(request); + + Ok(()) + } +} From a514793fa8f93907d601d7e77074cd8d3edf7d35 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 17 May 2023 16:31:35 -0700 Subject: [PATCH 098/253] Port API Gateway customizations to the orchestrator (#2705) ## Motivation and Context This PR ports the API Gateway `Accept` header customization into the orchestrator implementation. The original implementation of this customization in #287 didn't include a test, so this PR also adds an integration test for it. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-inlineable/Cargo.toml | 1 + .../src/apigateway_interceptors.rs | 30 + aws/rust-runtime/aws-inlineable/src/lib.rs | 3 + .../customize/ServiceSpecificDecorator.kt | 8 + .../apigateway/ApiGatewayDecorator.kt | 45 +- aws/sdk/aws-models/apigateway.json | 12865 ++++++++++++++++ aws/sdk/integration-tests/Cargo.toml | 1 + .../integration-tests/apigateway/Cargo.toml | 17 + .../apigateway/tests/accept_header.rs | 32 + rust-runtime/aws-smithy-runtime/src/client.rs | 5 +- 10 files changed, 13003 insertions(+), 4 deletions(-) create mode 100644 aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs create mode 100644 aws/sdk/aws-models/apigateway.json create mode 100644 aws/sdk/integration-tests/apigateway/Cargo.toml create mode 100644 aws/sdk/integration-tests/apigateway/tests/accept_header.rs diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml index e98507ef63..f471975d82 100644 --- a/aws/rust-runtime/aws-inlineable/Cargo.toml +++ b/aws/rust-runtime/aws-inlineable/Cargo.toml @@ -20,6 +20,7 @@ aws-smithy-checksums = { path = "../../../rust-runtime/aws-smithy-checksums" } aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client" } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } aws-smithy-http-tower= { path = "../../../rust-runtime/aws-smithy-http-tower" } +aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } aws-types = { path = "../aws-types" } bytes = "1" diff --git a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs new file mode 100644 index 0000000000..0ba3c1fa10 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs @@ -0,0 +1,30 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; +use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use http::header::ACCEPT; +use http::HeaderValue; + +/// Interceptor that adds an Accept header to API Gateway requests. +#[derive(Debug, Default)] +pub(crate) struct AcceptHeaderInterceptor; + +impl Interceptor for AcceptHeaderInterceptor { + fn modify_before_signing( + &self, + context: &mut InterceptorContext, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + context + .request_mut() + .headers_mut() + .insert(ACCEPT, HeaderValue::from_static("application/json")); + Ok(()) + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index ed582f0e54..c2414b6b5f 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -19,6 +19,9 @@ unreachable_pub )] +/// Interceptors for API Gateway +pub mod apigateway_interceptors; + /// Stub credentials provider for use when no credentials provider is used. pub mod no_credentials; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index 8e957b3f59..921990fbd6 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientProtocolMap import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization import software.amazon.smithy.rust.codegen.core.smithy.RustCrate @@ -130,4 +131,11 @@ class ServiceSpecificDecorator( model.maybeApply(service) { delegateTo.transformModel(service, model) } + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.serviceRuntimePluginCustomizations(codegenContext, baseCustomizations) + } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt index 5959918ef7..9d5a8400cb 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt @@ -8,25 +8,44 @@ package software.amazon.smithy.rustsdk.customize.apigateway import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency 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.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.util.letIf +import software.amazon.smithy.rustsdk.InlineAwsDependency class ApiGatewayDecorator : ClientCodegenDecorator { override val name: String = "ApiGateway" override val order: Byte = 0 + // TODO(enableNewSmithyRuntime): Delete when cleaning up middleware override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = baseCustomizations + ApiGatewayAddAcceptHeader() + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateMiddleware) { + it + ApiGatewayAddAcceptHeader() + } + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + ApiGatewayAcceptHeaderInterceptorCustomization(codegenContext) + } } -class ApiGatewayAddAcceptHeader : OperationCustomization() { +// TODO(enableNewSmithyRuntime): Delete when cleaning up middleware +private class ApiGatewayAddAcceptHeader : OperationCustomization() { override fun section(section: OperationSection): Writable = when (section) { is OperationSection.FinalizeOperation -> emptySection is OperationSection.OperationImplBlock -> emptySection @@ -39,6 +58,28 @@ class ApiGatewayAddAcceptHeader : OperationCustomization() { RuntimeType.Http, ) } + else -> emptySection } } + +private class ApiGatewayAcceptHeaderInterceptorCustomization(private val codegenContext: ClientCodegenContext) : + ServiceRuntimePluginCustomization() { + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.AdditionalConfig) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rustTemplate( + "#{Interceptor}::default()", + "Interceptor" to RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "apigateway_interceptors", + additionalDependency = arrayOf( + CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig), + ), + ), + ).resolve("AcceptHeaderInterceptor"), + ) + } + } + } +} diff --git a/aws/sdk/aws-models/apigateway.json b/aws/sdk/aws-models/apigateway.json new file mode 100644 index 0000000000..dbd9d381df --- /dev/null +++ b/aws/sdk/aws-models/apigateway.json @@ -0,0 +1,12865 @@ +{ + "smithy": "2.0", + "metadata": { + "suppressions": [ + { + "id": "HttpMethodSemantics", + "namespace": "*" + }, + { + "id": "HttpResponseCodeSemantics", + "namespace": "*" + }, + { + "id": "PaginatedTrait", + "namespace": "*" + }, + { + "id": "HttpHeaderTrait", + "namespace": "*" + }, + { + "id": "HttpUriConflict", + "namespace": "*" + }, + { + "id": "Service", + "namespace": "*" + } + ] + }, + "shapes": { + "com.amazonaws.apigateway#AccessLogSettings": { + "type": "structure", + "members": { + "format": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A single line format of the access logs of data, as specified by selected $context variables. The format must include at least $context.requestId.

" + } + }, + "destinationArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the CloudWatch Logs log group or Kinesis Data Firehose delivery stream to receive access logs. If you specify a Kinesis Data Firehose delivery stream, the stream name must begin with amazon-apigateway-.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Access log settings, including the access log format and access log destination ARN.

" + } + }, + "com.amazonaws.apigateway#Account": { + "type": "structure", + "members": { + "cloudwatchRoleArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ARN of an Amazon CloudWatch role for the current Account.

" + } + }, + "throttleSettings": { + "target": "com.amazonaws.apigateway#ThrottleSettings", + "traits": { + "smithy.api#documentation": "

Specifies the API request limits configured for the current Account.

" + } + }, + "features": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of features supported for the account. When usage plans are enabled, the features list will include an entry of \"UsagePlans\".

" + } + }, + "apiKeyVersion": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The version of the API keys used for the account.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents an AWS account that is associated with API Gateway.

" + } + }, + "com.amazonaws.apigateway#ApiKey": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the API Key.

" + } + }, + "value": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The value of the API Key.

" + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the API Key.

" + } + }, + "customerId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

An AWS Marketplace customer identifier , when integrating with the AWS SaaS Marketplace.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the API Key.

" + } + }, + "enabled": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether the API Key can be used by callers.

" + } + }, + "createdDate": { + "target": "com.amazonaws.apigateway#Timestamp", + "traits": { + "smithy.api#documentation": "

The timestamp when the API Key was created.

" + } + }, + "lastUpdatedDate": { + "target": "com.amazonaws.apigateway#Timestamp", + "traits": { + "smithy.api#documentation": "

The timestamp when the API Key was last updated.

" + } + }, + "stageKeys": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of Stage resources that are associated with the ApiKey resource.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A resource that can be distributed to callers for executing Method resources that require an API key. API keys can be mapped to any Stage on any RestApi, which indicates that the callers with the API key can make requests to that stage.

" + } + }, + "com.amazonaws.apigateway#ApiKeyIds": { + "type": "structure", + "members": { + "ids": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of all the ApiKey identifiers.

" + } + }, + "warnings": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of warning messages.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The identifier of an ApiKey used in a UsagePlan.

" + } + }, + "com.amazonaws.apigateway#ApiKeySourceType": { + "type": "enum", + "members": { + "HEADER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "HEADER" + } + }, + "AUTHORIZER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AUTHORIZER" + } + } + } + }, + "com.amazonaws.apigateway#ApiKeys": { + "type": "structure", + "members": { + "warnings": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of warning messages logged during the import of API keys when the failOnWarnings option is set to true.

" + } + }, + "items": { + "target": "com.amazonaws.apigateway#ListOfApiKey", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a collection of API keys as represented by an ApiKeys resource.

" + } + }, + "com.amazonaws.apigateway#ApiKeysFormat": { + "type": "enum", + "members": { + "csv": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "csv" + } + } + } + }, + "com.amazonaws.apigateway#ApiStage": { + "type": "structure", + "members": { + "apiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

API Id of the associated API stage in a usage plan.

" + } + }, + "stage": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

API stage name of the associated API stage in a usage plan.

" + } + }, + "throttle": { + "target": "com.amazonaws.apigateway#MapOfApiStageThrottleSettings", + "traits": { + "smithy.api#documentation": "

Map containing method level throttling information for API stage in a usage plan.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

API stage name of the associated API stage in a usage plan.

" + } + }, + "com.amazonaws.apigateway#Authorizer": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier for the authorizer resource.

" + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the authorizer.

" + } + }, + "type": { + "target": "com.amazonaws.apigateway#AuthorizerType", + "traits": { + "smithy.api#documentation": "

The authorizer type. Valid values are TOKEN for a Lambda function using a single authorization token submitted in a custom header, REQUEST for a Lambda function using incoming request parameters, and COGNITO_USER_POOLS for using an Amazon Cognito user pool.

" + } + }, + "providerARNs": { + "target": "com.amazonaws.apigateway#ListOfARNs", + "traits": { + "smithy.api#documentation": "

A list of the Amazon Cognito user pool ARNs for the COGNITO_USER_POOLS authorizer. Each element is of this format: arn:aws:cognito-idp:{region}:{account_id}:userpool/{user_pool_id}. For a TOKEN or REQUEST authorizer, this is not defined.

" + } + }, + "authType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Optional customer-defined field, used in OpenAPI imports and exports without functional impact.

" + } + }, + "authorizerUri": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the authorizer's Uniform Resource Identifier (URI). For TOKEN or REQUEST authorizers, this must be a well-formed Lambda function URI, for example, arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:{account_id}:function:{lambda_function_name}/invocations. In general, the URI has this form arn:aws:apigateway:{region}:lambda:path/{service_api}, where {region} is the same as the region hosting the Lambda function, path indicates that the remaining substring in the URI should be treated as the path to the resource, including the initial /. For Lambda functions, this is usually of the form /2015-03-31/functions/[FunctionARN]/invocations.

" + } + }, + "authorizerCredentials": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the required credentials as an IAM role for API Gateway to invoke the authorizer. To specify an IAM role for API Gateway to assume, use the role's Amazon Resource Name (ARN). To use resource-based permissions on the Lambda function, specify null.

" + } + }, + "identitySource": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identity source for which authorization is requested. For a TOKEN or\n COGNITO_USER_POOLS authorizer, this is required and specifies the request\n header mapping expression for the custom header holding the authorization token submitted by\n the client. For example, if the token header name is Auth, the header mapping expression is\n method.request.header.Auth. For the REQUEST authorizer, this is required when authorization\n caching is enabled. The value is a comma-separated string of one or more mapping expressions\n of the specified request parameters. For example, if an Auth header, a Name query string\n parameter are defined as identity sources, this value is method.request.header.Auth,\n method.request.querystring.Name. These parameters will be used to derive the authorization\n caching key and to perform runtime validation of the REQUEST authorizer by verifying all of\n the identity-related request parameters are present, not null and non-empty. Only when this is\n true does the authorizer invoke the authorizer Lambda function, otherwise, it returns a 401\n Unauthorized response without calling the Lambda function. The valid value is a string of\n comma-separated mapping expressions of the specified request parameters. When the\n authorization caching is not enabled, this property is optional.

" + } + }, + "identityValidationExpression": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A validation expression for the incoming identity token. For TOKEN authorizers, this value is a regular expression. For COGNITO_USER_POOLS authorizers, API Gateway will match the aud field of the incoming token from the client against the specified regular expression. It will invoke the authorizer's Lambda function when there is a match. Otherwise, it will return a 401 Unauthorized response without calling the Lambda function. The validation expression does not apply to the REQUEST authorizer.

" + } + }, + "authorizerResultTtlInSeconds": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The TTL in seconds of cached authorizer results. If it equals 0, authorization caching is disabled. If it is greater than 0, API Gateway will cache authorizer responses. If this field is not set, the default value is 300. The maximum value is 3600, or 1 hour.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents an authorization layer for methods. If enabled on a method, API Gateway will activate the authorizer when a client calls the method.

" + } + }, + "com.amazonaws.apigateway#AuthorizerType": { + "type": "enum", + "members": { + "TOKEN": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "TOKEN" + } + }, + "REQUEST": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "REQUEST" + } + }, + "COGNITO_USER_POOLS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "COGNITO_USER_POOLS" + } + } + }, + "traits": { + "smithy.api#documentation": "

The authorizer type. Valid values are TOKEN for a Lambda function using a single authorization token submitted in a custom header, REQUEST for a Lambda function using incoming request parameters, and COGNITO_USER_POOLS for using an Amazon Cognito user pool.

" + } + }, + "com.amazonaws.apigateway#Authorizers": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfAuthorizer", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a collection of Authorizer resources.

" + } + }, + "com.amazonaws.apigateway#BackplaneControlService": { + "type": "service", + "version": "2015-07-09", + "operations": [ + { + "target": "com.amazonaws.apigateway#CreateApiKey" + }, + { + "target": "com.amazonaws.apigateway#CreateAuthorizer" + }, + { + "target": "com.amazonaws.apigateway#CreateBasePathMapping" + }, + { + "target": "com.amazonaws.apigateway#CreateDeployment" + }, + { + "target": "com.amazonaws.apigateway#CreateDocumentationPart" + }, + { + "target": "com.amazonaws.apigateway#CreateDocumentationVersion" + }, + { + "target": "com.amazonaws.apigateway#CreateDomainName" + }, + { + "target": "com.amazonaws.apigateway#CreateModel" + }, + { + "target": "com.amazonaws.apigateway#CreateRequestValidator" + }, + { + "target": "com.amazonaws.apigateway#CreateResource" + }, + { + "target": "com.amazonaws.apigateway#CreateRestApi" + }, + { + "target": "com.amazonaws.apigateway#CreateStage" + }, + { + "target": "com.amazonaws.apigateway#CreateUsagePlan" + }, + { + "target": "com.amazonaws.apigateway#CreateUsagePlanKey" + }, + { + "target": "com.amazonaws.apigateway#CreateVpcLink" + }, + { + "target": "com.amazonaws.apigateway#DeleteApiKey" + }, + { + "target": "com.amazonaws.apigateway#DeleteAuthorizer" + }, + { + "target": "com.amazonaws.apigateway#DeleteBasePathMapping" + }, + { + "target": "com.amazonaws.apigateway#DeleteClientCertificate" + }, + { + "target": "com.amazonaws.apigateway#DeleteDeployment" + }, + { + "target": "com.amazonaws.apigateway#DeleteDocumentationPart" + }, + { + "target": "com.amazonaws.apigateway#DeleteDocumentationVersion" + }, + { + "target": "com.amazonaws.apigateway#DeleteDomainName" + }, + { + "target": "com.amazonaws.apigateway#DeleteGatewayResponse" + }, + { + "target": "com.amazonaws.apigateway#DeleteIntegration" + }, + { + "target": "com.amazonaws.apigateway#DeleteIntegrationResponse" + }, + { + "target": "com.amazonaws.apigateway#DeleteMethod" + }, + { + "target": "com.amazonaws.apigateway#DeleteMethodResponse" + }, + { + "target": "com.amazonaws.apigateway#DeleteModel" + }, + { + "target": "com.amazonaws.apigateway#DeleteRequestValidator" + }, + { + "target": "com.amazonaws.apigateway#DeleteResource" + }, + { + "target": "com.amazonaws.apigateway#DeleteRestApi" + }, + { + "target": "com.amazonaws.apigateway#DeleteStage" + }, + { + "target": "com.amazonaws.apigateway#DeleteUsagePlan" + }, + { + "target": "com.amazonaws.apigateway#DeleteUsagePlanKey" + }, + { + "target": "com.amazonaws.apigateway#DeleteVpcLink" + }, + { + "target": "com.amazonaws.apigateway#FlushStageAuthorizersCache" + }, + { + "target": "com.amazonaws.apigateway#FlushStageCache" + }, + { + "target": "com.amazonaws.apigateway#GenerateClientCertificate" + }, + { + "target": "com.amazonaws.apigateway#GetAccount" + }, + { + "target": "com.amazonaws.apigateway#GetApiKey" + }, + { + "target": "com.amazonaws.apigateway#GetApiKeys" + }, + { + "target": "com.amazonaws.apigateway#GetAuthorizer" + }, + { + "target": "com.amazonaws.apigateway#GetAuthorizers" + }, + { + "target": "com.amazonaws.apigateway#GetBasePathMapping" + }, + { + "target": "com.amazonaws.apigateway#GetBasePathMappings" + }, + { + "target": "com.amazonaws.apigateway#GetClientCertificate" + }, + { + "target": "com.amazonaws.apigateway#GetClientCertificates" + }, + { + "target": "com.amazonaws.apigateway#GetDeployment" + }, + { + "target": "com.amazonaws.apigateway#GetDeployments" + }, + { + "target": "com.amazonaws.apigateway#GetDocumentationPart" + }, + { + "target": "com.amazonaws.apigateway#GetDocumentationParts" + }, + { + "target": "com.amazonaws.apigateway#GetDocumentationVersion" + }, + { + "target": "com.amazonaws.apigateway#GetDocumentationVersions" + }, + { + "target": "com.amazonaws.apigateway#GetDomainName" + }, + { + "target": "com.amazonaws.apigateway#GetDomainNames" + }, + { + "target": "com.amazonaws.apigateway#GetExport" + }, + { + "target": "com.amazonaws.apigateway#GetGatewayResponse" + }, + { + "target": "com.amazonaws.apigateway#GetGatewayResponses" + }, + { + "target": "com.amazonaws.apigateway#GetIntegration" + }, + { + "target": "com.amazonaws.apigateway#GetIntegrationResponse" + }, + { + "target": "com.amazonaws.apigateway#GetMethod" + }, + { + "target": "com.amazonaws.apigateway#GetMethodResponse" + }, + { + "target": "com.amazonaws.apigateway#GetModel" + }, + { + "target": "com.amazonaws.apigateway#GetModels" + }, + { + "target": "com.amazonaws.apigateway#GetModelTemplate" + }, + { + "target": "com.amazonaws.apigateway#GetRequestValidator" + }, + { + "target": "com.amazonaws.apigateway#GetRequestValidators" + }, + { + "target": "com.amazonaws.apigateway#GetResource" + }, + { + "target": "com.amazonaws.apigateway#GetResources" + }, + { + "target": "com.amazonaws.apigateway#GetRestApi" + }, + { + "target": "com.amazonaws.apigateway#GetRestApis" + }, + { + "target": "com.amazonaws.apigateway#GetSdk" + }, + { + "target": "com.amazonaws.apigateway#GetSdkType" + }, + { + "target": "com.amazonaws.apigateway#GetSdkTypes" + }, + { + "target": "com.amazonaws.apigateway#GetStage" + }, + { + "target": "com.amazonaws.apigateway#GetStages" + }, + { + "target": "com.amazonaws.apigateway#GetTags" + }, + { + "target": "com.amazonaws.apigateway#GetUsage" + }, + { + "target": "com.amazonaws.apigateway#GetUsagePlan" + }, + { + "target": "com.amazonaws.apigateway#GetUsagePlanKey" + }, + { + "target": "com.amazonaws.apigateway#GetUsagePlanKeys" + }, + { + "target": "com.amazonaws.apigateway#GetUsagePlans" + }, + { + "target": "com.amazonaws.apigateway#GetVpcLink" + }, + { + "target": "com.amazonaws.apigateway#GetVpcLinks" + }, + { + "target": "com.amazonaws.apigateway#ImportApiKeys" + }, + { + "target": "com.amazonaws.apigateway#ImportDocumentationParts" + }, + { + "target": "com.amazonaws.apigateway#ImportRestApi" + }, + { + "target": "com.amazonaws.apigateway#PutGatewayResponse" + }, + { + "target": "com.amazonaws.apigateway#PutIntegration" + }, + { + "target": "com.amazonaws.apigateway#PutIntegrationResponse" + }, + { + "target": "com.amazonaws.apigateway#PutMethod" + }, + { + "target": "com.amazonaws.apigateway#PutMethodResponse" + }, + { + "target": "com.amazonaws.apigateway#PutRestApi" + }, + { + "target": "com.amazonaws.apigateway#TagResource" + }, + { + "target": "com.amazonaws.apigateway#TestInvokeAuthorizer" + }, + { + "target": "com.amazonaws.apigateway#TestInvokeMethod" + }, + { + "target": "com.amazonaws.apigateway#UntagResource" + }, + { + "target": "com.amazonaws.apigateway#UpdateAccount" + }, + { + "target": "com.amazonaws.apigateway#UpdateApiKey" + }, + { + "target": "com.amazonaws.apigateway#UpdateAuthorizer" + }, + { + "target": "com.amazonaws.apigateway#UpdateBasePathMapping" + }, + { + "target": "com.amazonaws.apigateway#UpdateClientCertificate" + }, + { + "target": "com.amazonaws.apigateway#UpdateDeployment" + }, + { + "target": "com.amazonaws.apigateway#UpdateDocumentationPart" + }, + { + "target": "com.amazonaws.apigateway#UpdateDocumentationVersion" + }, + { + "target": "com.amazonaws.apigateway#UpdateDomainName" + }, + { + "target": "com.amazonaws.apigateway#UpdateGatewayResponse" + }, + { + "target": "com.amazonaws.apigateway#UpdateIntegration" + }, + { + "target": "com.amazonaws.apigateway#UpdateIntegrationResponse" + }, + { + "target": "com.amazonaws.apigateway#UpdateMethod" + }, + { + "target": "com.amazonaws.apigateway#UpdateMethodResponse" + }, + { + "target": "com.amazonaws.apigateway#UpdateModel" + }, + { + "target": "com.amazonaws.apigateway#UpdateRequestValidator" + }, + { + "target": "com.amazonaws.apigateway#UpdateResource" + }, + { + "target": "com.amazonaws.apigateway#UpdateRestApi" + }, + { + "target": "com.amazonaws.apigateway#UpdateStage" + }, + { + "target": "com.amazonaws.apigateway#UpdateUsage" + }, + { + "target": "com.amazonaws.apigateway#UpdateUsagePlan" + }, + { + "target": "com.amazonaws.apigateway#UpdateVpcLink" + } + ], + "traits": { + "aws.api#service": { + "sdkId": "API Gateway", + "arnNamespace": "apigateway", + "cloudFormationName": "ApiGateway", + "cloudTrailEventSource": "apigateway.amazonaws.com", + "endpointPrefix": "apigateway" + }, + "aws.auth#sigv4": { + "name": "apigateway" + }, + "aws.protocols#restJson1": {}, + "smithy.api#documentation": "Amazon API Gateway\n

Amazon API Gateway helps developers deliver robust, secure, and scalable mobile and web application back ends. API Gateway allows developers to securely connect mobile and web applications to APIs that run on AWS Lambda, Amazon EC2, or other publicly addressable web services that are hosted outside of AWS.

", + "smithy.api#title": "Amazon API Gateway", + "smithy.rules#endpointRuleSet": { + "version": "1.0", + "parameters": { + "Region": { + "builtIn": "AWS::Region", + "required": false, + "documentation": "The AWS region used to dispatch the request.", + "type": "String" + }, + "UseDualStack": { + "builtIn": "AWS::UseDualStack", + "required": true, + "default": false, + "documentation": "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.", + "type": "Boolean" + }, + "UseFIPS": { + "builtIn": "AWS::UseFIPS", + "required": true, + "default": false, + "documentation": "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.", + "type": "Boolean" + }, + "Endpoint": { + "builtIn": "SDK::Endpoint", + "required": false, + "documentation": "Override the endpoint used to send this request", + "type": "String" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "endpoint": { + "url": { + "ref": "Endpoint" + }, + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Region" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + }, + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://apigateway-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://apigateway-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://apigateway.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://apigateway.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + } + ] + }, + { + "conditions": [], + "error": "Invalid Configuration: Missing Region", + "type": "error" + } + ] + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ + { + "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.af-south-1.amazonaws.com" + } + }, + "params": { + "Region": "af-south-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.ap-east-1.amazonaws.com" + } + }, + "params": { + "Region": "ap-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.ap-northeast-1.amazonaws.com" + } + }, + "params": { + "Region": "ap-northeast-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.ap-northeast-2.amazonaws.com" + } + }, + "params": { + "Region": "ap-northeast-2", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.ap-northeast-3.amazonaws.com" + } + }, + "params": { + "Region": "ap-northeast-3", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.ap-south-1.amazonaws.com" + } + }, + "params": { + "Region": "ap-south-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.ap-southeast-1.amazonaws.com" + } + }, + "params": { + "Region": "ap-southeast-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.ap-southeast-2.amazonaws.com" + } + }, + "params": { + "Region": "ap-southeast-2", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.ca-central-1.amazonaws.com" + } + }, + "params": { + "Region": "ca-central-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.eu-central-1.amazonaws.com" + } + }, + "params": { + "Region": "eu-central-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.eu-north-1.amazonaws.com" + } + }, + "params": { + "Region": "eu-north-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.eu-south-1.amazonaws.com" + } + }, + "params": { + "Region": "eu-south-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.eu-west-1.amazonaws.com" + } + }, + "params": { + "Region": "eu-west-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.eu-west-2.amazonaws.com" + } + }, + "params": { + "Region": "eu-west-2", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.eu-west-3.amazonaws.com" + } + }, + "params": { + "Region": "eu-west-3", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.me-south-1.amazonaws.com" + } + }, + "params": { + "Region": "me-south-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.sa-east-1.amazonaws.com" + } + }, + "params": { + "Region": "sa-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.us-east-2.amazonaws.com" + } + }, + "params": { + "Region": "us-east-2", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.us-west-1.amazonaws.com" + } + }, + "params": { + "Region": "us-west-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.us-west-2.amazonaws.com" + } + }, + "params": { + "Region": "us-west-2", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://apigateway-fips.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway-fips.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://apigateway.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.cn-northwest-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-northwest-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://apigateway-fips.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway-fips.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://apigateway.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.us-gov-west-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-west-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://apigateway-fips.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway-fips.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://apigateway.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway-fips.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway-fips.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://apigateway.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "UseFIPS": false, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips enabled and dualstack disabled", + "expect": { + "error": "Invalid Configuration: FIPS and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips disabled and dualstack enabled", + "expect": { + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } + } + ], + "version": "1.0" + } + } + }, + "com.amazonaws.apigateway#BadRequestException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.apigateway#String" + } + }, + "traits": { + "smithy.api#documentation": "

The submitted request is not valid, for example, the input is incomplete or incorrect. See the accompanying error message for details.

", + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + }, + "com.amazonaws.apigateway#BasePathMapping": { + "type": "structure", + "members": { + "basePath": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The base path name that callers of the API must provide as part of the URL after the domain name.

" + } + }, + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

" + } + }, + "stage": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the associated stage.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents the base path that callers of the API must provide as part of the URL after the domain name.

" + } + }, + "com.amazonaws.apigateway#BasePathMappings": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfBasePathMapping", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a collection of BasePathMapping resources.

" + } + }, + "com.amazonaws.apigateway#Blob": { + "type": "blob" + }, + "com.amazonaws.apigateway#Boolean": { + "type": "boolean", + "traits": { + "smithy.api#default": false + } + }, + "com.amazonaws.apigateway#CacheClusterSize": { + "type": "enum", + "members": { + "SIZE_0_POINT_5_GB": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "0.5" + } + }, + "SIZE_1_POINT_6_GB": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "1.6" + } + }, + "SIZE_6_POINT_1_GB": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "6.1" + } + }, + "SIZE_13_POINT_5_GB": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "13.5" + } + }, + "SIZE_28_POINT_4_GB": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "28.4" + } + }, + "SIZE_58_POINT_2_GB": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "58.2" + } + }, + "SIZE_118_GB": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "118" + } + }, + "SIZE_237_GB": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "237" + } + } + }, + "traits": { + "smithy.api#documentation": "

Returns the size of the CacheCluster.

" + } + }, + "com.amazonaws.apigateway#CacheClusterStatus": { + "type": "enum", + "members": { + "CREATE_IN_PROGRESS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CREATE_IN_PROGRESS" + } + }, + "AVAILABLE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AVAILABLE" + } + }, + "DELETE_IN_PROGRESS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DELETE_IN_PROGRESS" + } + }, + "NOT_AVAILABLE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NOT_AVAILABLE" + } + }, + "FLUSH_IN_PROGRESS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "FLUSH_IN_PROGRESS" + } + } + }, + "traits": { + "smithy.api#documentation": "

Returns the status of the CacheCluster.

" + } + }, + "com.amazonaws.apigateway#CanarySettings": { + "type": "structure", + "members": { + "percentTraffic": { + "target": "com.amazonaws.apigateway#Double", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The percent (0-100) of traffic diverted to a canary deployment.

" + } + }, + "deploymentId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ID of the canary deployment.

" + } + }, + "stageVariableOverrides": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Stage variables overridden for a canary release deployment, including new stage variables introduced in the canary. These stage variables are represented as a string-to-string map between stage variable names and their values.

" + } + }, + "useStageCache": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A Boolean flag to indicate whether the canary deployment uses the stage cache or not.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration settings of a canary deployment.

" + } + }, + "com.amazonaws.apigateway#ClientCertificate": { + "type": "structure", + "members": { + "clientCertificateId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the client certificate.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the client certificate.

" + } + }, + "pemEncodedCertificate": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The PEM-encoded public key of the client certificate, which can be used to configure certificate authentication in the integration endpoint .

" + } + }, + "createdDate": { + "target": "com.amazonaws.apigateway#Timestamp", + "traits": { + "smithy.api#documentation": "

The timestamp when the client certificate was created.

" + } + }, + "expirationDate": { + "target": "com.amazonaws.apigateway#Timestamp", + "traits": { + "smithy.api#documentation": "

The timestamp when the client certificate will expire.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a client certificate used to configure client-side SSL authentication while sending requests to the integration endpoint.

" + } + }, + "com.amazonaws.apigateway#ClientCertificates": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfClientCertificate", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a collection of ClientCertificate resources.

" + } + }, + "com.amazonaws.apigateway#ConflictException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.apigateway#String" + } + }, + "traits": { + "smithy.api#documentation": "

The request configuration has conflicts. For details, see the accompanying error message.

", + "smithy.api#error": "client", + "smithy.api#httpError": 409 + } + }, + "com.amazonaws.apigateway#ConnectionType": { + "type": "enum", + "members": { + "INTERNET": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INTERNET" + } + }, + "VPC_LINK": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "VPC_LINK" + } + } + } + }, + "com.amazonaws.apigateway#ContentHandlingStrategy": { + "type": "enum", + "members": { + "CONVERT_TO_BINARY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CONVERT_TO_BINARY" + } + }, + "CONVERT_TO_TEXT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CONVERT_TO_TEXT" + } + } + } + }, + "com.amazonaws.apigateway#CreateApiKey": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateApiKeyRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#ApiKey" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Create an ApiKey resource.

", + "smithy.api#http": { + "method": "POST", + "uri": "/apikeys", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateApiKeyRequest": { + "type": "structure", + "members": { + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the ApiKey.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the ApiKey.

" + } + }, + "enabled": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether the ApiKey can be used by callers.

" + } + }, + "generateDistinctId": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether (true) or not (false) the key identifier is distinct from the created API key value. This parameter is deprecated and should not be used.

" + } + }, + "value": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a value of the API key.

" + } + }, + "stageKeys": { + "target": "com.amazonaws.apigateway#ListOfStageKeys", + "traits": { + "smithy.api#documentation": "

DEPRECATED FOR USAGE PLANS - Specifies stages associated with the API key.

" + } + }, + "customerId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

An AWS Marketplace customer identifier , when integrating with the AWS SaaS Marketplace.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to create an ApiKey resource.

" + } + }, + "com.amazonaws.apigateway#CreateAuthorizer": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateAuthorizerRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Authorizer" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Adds a new Authorizer resource to an existing RestApi resource.

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis/{restApiId}/authorizers", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateAuthorizerRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the authorizer.

", + "smithy.api#required": {} + } + }, + "type": { + "target": "com.amazonaws.apigateway#AuthorizerType", + "traits": { + "smithy.api#documentation": "

The authorizer type. Valid values are TOKEN for a Lambda function using a single authorization token submitted in a custom header, REQUEST for a Lambda function using incoming request parameters, and COGNITO_USER_POOLS for using an Amazon Cognito user pool.

", + "smithy.api#required": {} + } + }, + "providerARNs": { + "target": "com.amazonaws.apigateway#ListOfARNs", + "traits": { + "smithy.api#documentation": "

A list of the Amazon Cognito user pool ARNs for the COGNITO_USER_POOLS authorizer. Each element is of this format: arn:aws:cognito-idp:{region}:{account_id}:userpool/{user_pool_id}. For a TOKEN or REQUEST authorizer, this is not defined.

" + } + }, + "authType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Optional customer-defined field, used in OpenAPI imports and exports without functional impact.

" + } + }, + "authorizerUri": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the authorizer's Uniform Resource Identifier (URI). For TOKEN or REQUEST authorizers, this must be a well-formed Lambda function URI, for example, arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:{account_id}:function:{lambda_function_name}/invocations. In general, the URI has this form arn:aws:apigateway:{region}:lambda:path/{service_api}, where {region} is the same as the region hosting the Lambda function, path indicates that the remaining substring in the URI should be treated as the path to the resource, including the initial /. For Lambda functions, this is usually of the form /2015-03-31/functions/[FunctionARN]/invocations.

" + } + }, + "authorizerCredentials": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the required credentials as an IAM role for API Gateway to invoke the authorizer. To specify an IAM role for API Gateway to assume, use the role's Amazon Resource Name (ARN). To use resource-based permissions on the Lambda function, specify null.

" + } + }, + "identitySource": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identity source for which authorization is requested. For a TOKEN or\n COGNITO_USER_POOLS authorizer, this is required and specifies the request\n header mapping expression for the custom header holding the authorization token submitted by\n the client. For example, if the token header name is Auth, the header mapping\n expression is method.request.header.Auth. For the REQUEST\n authorizer, this is required when authorization caching is enabled. The value is a\n comma-separated string of one or more mapping expressions of the specified request parameters.\n For example, if an Auth header, a Name query string parameter are\n defined as identity sources, this value is method.request.header.Auth,\n method.request.querystring.Name. These parameters will be used to derive the\n authorization caching key and to perform runtime validation of the REQUEST\n authorizer by verifying all of the identity-related request parameters are present, not null\n and non-empty. Only when this is true does the authorizer invoke the authorizer Lambda\n function, otherwise, it returns a 401 Unauthorized response without calling the Lambda\n function. The valid value is a string of comma-separated mapping expressions of the specified\n request parameters. When the authorization caching is not enabled, this property is\n optional.

" + } + }, + "identityValidationExpression": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A validation expression for the incoming identity token. For TOKEN authorizers, this value is a regular expression. For COGNITO_USER_POOLS authorizers, API Gateway will match the aud field of the incoming token from the client against the specified regular expression. It will invoke the authorizer's Lambda function when there is a match. Otherwise, it will return a 401 Unauthorized response without calling the Lambda function. The validation expression does not apply to the REQUEST authorizer.

" + } + }, + "authorizerResultTtlInSeconds": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The TTL in seconds of cached authorizer results. If it equals 0, authorization caching is disabled. If it is greater than 0, API Gateway will cache authorizer responses. If this field is not set, the default value is 300. The maximum value is 3600, or 1 hour.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to add a new Authorizer to an existing RestApi resource.

" + } + }, + "com.amazonaws.apigateway#CreateBasePathMapping": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateBasePathMappingRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#BasePathMapping" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a new BasePathMapping resource.

", + "smithy.api#http": { + "method": "POST", + "uri": "/domainnames/{domainName}/basepathmappings", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateBasePathMappingRequest": { + "type": "structure", + "members": { + "domainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The domain name of the BasePathMapping resource to create.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "basePath": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The base path name that callers of the API must provide as part of the URL after the domain name. This value must be unique for all of the mappings across a single API. Specify '(none)' if you do not want callers to specify a base path name after the domain name.

" + } + }, + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#required": {} + } + }, + "stage": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the API's stage that you want to use for this mapping. Specify '(none)' if you want callers to explicitly specify the stage name after any base path name.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to create a new BasePathMapping resource.

" + } + }, + "com.amazonaws.apigateway#CreateDeployment": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateDeploymentRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Deployment" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a Deployment resource, which makes a specified RestApi callable over the internet.

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis/{restApiId}/deployments", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateDeploymentRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the Stage resource for the Deployment resource to create.

" + } + }, + "stageDescription": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the Stage resource for the Deployment resource to create.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description for the Deployment resource to create.

" + } + }, + "cacheClusterEnabled": { + "target": "com.amazonaws.apigateway#NullableBoolean", + "traits": { + "smithy.api#documentation": "

Enables a cache cluster for the Stage resource specified in the input.

" + } + }, + "cacheClusterSize": { + "target": "com.amazonaws.apigateway#CacheClusterSize", + "traits": { + "smithy.api#documentation": "

The stage's cache capacity in GB. For more information about choosing a cache size, see Enabling API caching to enhance responsiveness.

" + } + }, + "variables": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A map that defines the stage variables for the Stage resource that is associated\n with the new deployment. Variable names can have alphanumeric and underscore characters, and the values\n must match [A-Za-z0-9-._~:/?#&=,]+.

" + } + }, + "canarySettings": { + "target": "com.amazonaws.apigateway#DeploymentCanarySettings", + "traits": { + "smithy.api#documentation": "

The input configuration for the canary deployment when the deployment is a canary release deployment.

" + } + }, + "tracingEnabled": { + "target": "com.amazonaws.apigateway#NullableBoolean", + "traits": { + "smithy.api#documentation": "

Specifies whether active tracing with X-ray is enabled for the Stage.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to create a Deployment resource.

" + } + }, + "com.amazonaws.apigateway#CreateDocumentationPart": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateDocumentationPartRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DocumentationPart" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a documentation part.

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis/{restApiId}/documentation/parts", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateDocumentationPartRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "location": { + "target": "com.amazonaws.apigateway#DocumentationPartLocation", + "traits": { + "smithy.api#documentation": "

The location of the targeted API entity of the to-be-created documentation part.

", + "smithy.api#required": {} + } + }, + "properties": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The new documentation content map of the targeted API entity. Enclosed key-value pairs are API-specific, but only OpenAPI-compliant key-value pairs can be exported and, hence, published.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Creates a new documentation part of a given API.

" + } + }, + "com.amazonaws.apigateway#CreateDocumentationVersion": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateDocumentationVersionRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DocumentationVersion" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a documentation version

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis/{restApiId}/documentation/versions", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateDocumentationVersionRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "documentationVersion": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The version identifier of the new snapshot.

", + "smithy.api#required": {} + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The stage name to be associated with the new documentation snapshot.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A description about the new documentation snapshot.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Creates a new documentation version of a given API.

" + } + }, + "com.amazonaws.apigateway#CreateDomainName": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateDomainNameRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DomainName" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a new domain name.

", + "smithy.api#http": { + "method": "POST", + "uri": "/domainnames", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateDomainNameRequest": { + "type": "structure", + "members": { + "domainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the DomainName resource.

", + "smithy.api#required": {} + } + }, + "certificateName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The user-friendly name of the certificate that will be used by edge-optimized endpoint for this domain name.

" + } + }, + "certificateBody": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

[Deprecated] The body of the server certificate that will be used by edge-optimized endpoint for this domain name provided by your certificate authority.

" + } + }, + "certificatePrivateKey": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

[Deprecated] Your edge-optimized endpoint's domain name certificate's private key.

" + } + }, + "certificateChain": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

[Deprecated] The intermediate certificates and optionally the root certificate, one after the other without any blank lines, used by an edge-optimized endpoint for this domain name. If you include the root certificate, your certificate chain must start with intermediate certificates and end with the root certificate. Use the intermediate certificates that were provided by your certificate authority. Do not include any intermediaries that are not in the chain of trust path.

" + } + }, + "certificateArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The reference to an AWS-managed certificate that will be used by edge-optimized endpoint for this domain name. AWS Certificate Manager is the only supported source.

" + } + }, + "regionalCertificateName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The user-friendly name of the certificate that will be used by regional endpoint for this domain name.

" + } + }, + "regionalCertificateArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The reference to an AWS-managed certificate that will be used by regional endpoint for this domain name. AWS Certificate Manager is the only supported source.

" + } + }, + "endpointConfiguration": { + "target": "com.amazonaws.apigateway#EndpointConfiguration", + "traits": { + "smithy.api#documentation": "

The endpoint configuration of this DomainName showing the endpoint types of the domain name.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" + } + }, + "securityPolicy": { + "target": "com.amazonaws.apigateway#SecurityPolicy", + "traits": { + "smithy.api#documentation": "

The Transport Layer Security (TLS) version + cipher suite for this DomainName. The valid values are TLS_1_0 and TLS_1_2.

" + } + }, + "mutualTlsAuthentication": { + "target": "com.amazonaws.apigateway#MutualTlsAuthenticationInput" + }, + "ownershipVerificationCertificateArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ARN of the public certificate issued by ACM to validate ownership of your custom\n domain. Only required when configuring mutual TLS and using an ACM imported or private CA\n certificate ARN as the regionalCertificateArn.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to create a new domain name.

" + } + }, + "com.amazonaws.apigateway#CreateModel": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateModelRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Model" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Adds a new Model resource to an existing RestApi resource.

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis/{restApiId}/models", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateModelRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The RestApi identifier under which the Model will be created.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the model. Must be alphanumeric.

", + "smithy.api#required": {} + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the model.

" + } + }, + "schema": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The schema for the model. For application/json models, this should be JSON schema draft 4 model.

" + } + }, + "contentType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The content-type for the model.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to add a new Model to an existing RestApi resource.

" + } + }, + "com.amazonaws.apigateway#CreateRequestValidator": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateRequestValidatorRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#RequestValidator" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a RequestValidator of a given RestApi.

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis/{restApiId}/requestvalidators", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateRequestValidatorRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the to-be-created RequestValidator.

" + } + }, + "validateRequestBody": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A Boolean flag to indicate whether to validate request body according to the configured model schema for the method (true) or not (false).

" + } + }, + "validateRequestParameters": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A Boolean flag to indicate whether to validate request parameters, true, or not false.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Creates a RequestValidator of a given RestApi.

" + } + }, + "com.amazonaws.apigateway#CreateResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateResourceRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Resource" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a Resource resource.

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis/{restApiId}/resources/{parentId}", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateResourceRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "parentId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The parent resource's identifier.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "pathPart": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The last path segment for this resource.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to create a Resource resource.

" + } + }, + "com.amazonaws.apigateway#CreateRestApi": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateRestApiRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#RestApi" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a new RestApi resource.

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateRestApiRequest": { + "type": "structure", + "members": { + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the RestApi.

", + "smithy.api#required": {} + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the RestApi.

" + } + }, + "version": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A version identifier for the API.

" + } + }, + "cloneFrom": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ID of the RestApi that you want to clone from.

" + } + }, + "binaryMediaTypes": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

The list of binary media types supported by the RestApi. By default, the RestApi supports only UTF-8-encoded text payloads.

" + } + }, + "minimumCompressionSize": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

A nullable integer that is used to enable compression (with non-negative between 0 and 10485760 (10M) bytes, inclusive) or disable compression (with a null value) on an API. When compression is enabled, compression or decompression is not applied on the payload if the payload size is smaller than this value. Setting it to zero allows compression for any payload size.

" + } + }, + "apiKeySource": { + "target": "com.amazonaws.apigateway#ApiKeySourceType", + "traits": { + "smithy.api#documentation": "

The source of the API key for metering requests according to a usage plan. Valid values\n are: >HEADER to read the API key from the X-API-Key header of a\n request. AUTHORIZER to read the API key from the UsageIdentifierKey\n from a custom authorizer.

" + } + }, + "endpointConfiguration": { + "target": "com.amazonaws.apigateway#EndpointConfiguration", + "traits": { + "smithy.api#documentation": "

The endpoint configuration of this RestApi showing the endpoint types of the API.

" + } + }, + "policy": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A stringified JSON policy document that applies to this RestApi regardless of the caller and Method configuration.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" + } + }, + "disableExecuteApiEndpoint": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether clients can invoke your API by using the default execute-api endpoint.\n By default, clients can invoke your API with the default\n https://{api_id}.execute-api.{region}.amazonaws.com endpoint. To require that clients use a\n custom domain name to invoke your API, disable the default endpoint

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The POST Request to add a new RestApi resource to your collection.

" + } + }, + "com.amazonaws.apigateway#CreateStage": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateStageRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Stage" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a new Stage resource that references a pre-existing Deployment for the API.

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis/{restApiId}/stages", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateStageRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name for the Stage resource. Stage names can only contain alphanumeric characters, hyphens, and underscores. Maximum length is 128 characters.

", + "smithy.api#required": {} + } + }, + "deploymentId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the Deployment resource for the Stage resource.

", + "smithy.api#required": {} + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the Stage resource.

" + } + }, + "cacheClusterEnabled": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Whether cache clustering is enabled for the stage.

" + } + }, + "cacheClusterSize": { + "target": "com.amazonaws.apigateway#CacheClusterSize", + "traits": { + "smithy.api#documentation": "

The stage's cache capacity in GB. For more information about choosing a cache size, see Enabling API caching to enhance responsiveness.

" + } + }, + "variables": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A map that defines the stage variables for the new Stage resource. Variable names\n can have alphanumeric and underscore characters, and the values must match\n [A-Za-z0-9-._~:/?#&=,]+.

" + } + }, + "documentationVersion": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The version of the associated API documentation.

" + } + }, + "canarySettings": { + "target": "com.amazonaws.apigateway#CanarySettings", + "traits": { + "smithy.api#documentation": "

The canary deployment settings of this stage.

" + } + }, + "tracingEnabled": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether active tracing with X-ray is enabled for the Stage.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to create a Stage resource.

" + } + }, + "com.amazonaws.apigateway#CreateUsagePlan": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateUsagePlanRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#UsagePlan" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a usage plan with the throttle and quota limits, as well as the associated API stages, specified in the payload.

", + "smithy.api#http": { + "method": "POST", + "uri": "/usageplans", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateUsagePlanKey": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateUsagePlanKeyRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#UsagePlanKey" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a usage plan key for adding an existing API key to a usage plan.

", + "smithy.api#http": { + "method": "POST", + "uri": "/usageplans/{usagePlanId}/keys", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#CreateUsagePlanKeyRequest": { + "type": "structure", + "members": { + "usagePlanId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Id of the UsagePlan resource representing the usage plan containing the to-be-created UsagePlanKey resource representing a plan customer.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "keyId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of a UsagePlanKey resource for a plan customer.

", + "smithy.api#required": {} + } + }, + "keyType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The type of a UsagePlanKey resource for a plan customer.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The POST request to create a usage plan key for adding an existing API key to a usage plan.

" + } + }, + "com.amazonaws.apigateway#CreateUsagePlanRequest": { + "type": "structure", + "members": { + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the usage plan.

", + "smithy.api#required": {} + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the usage plan.

" + } + }, + "apiStages": { + "target": "com.amazonaws.apigateway#ListOfApiStage", + "traits": { + "smithy.api#documentation": "

The associated API stages of the usage plan.

" + } + }, + "throttle": { + "target": "com.amazonaws.apigateway#ThrottleSettings", + "traits": { + "smithy.api#documentation": "

The throttling limits of the usage plan.

" + } + }, + "quota": { + "target": "com.amazonaws.apigateway#QuotaSettings", + "traits": { + "smithy.api#documentation": "

The quota of the usage plan.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The POST request to create a usage plan with the name, description, throttle limits and quota limits, as well as the associated API stages, specified in the payload.

" + } + }, + "com.amazonaws.apigateway#CreateVpcLink": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#CreateVpcLinkRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#VpcLink" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a VPC link, under the caller's account in a selected region, in an asynchronous operation that typically takes 2-4 minutes to complete and become operational. The caller must have permissions to create and update VPC Endpoint services.

", + "smithy.api#http": { + "method": "POST", + "uri": "/vpclinks", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#CreateVpcLinkRequest": { + "type": "structure", + "members": { + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name used to label and identify the VPC link.

", + "smithy.api#required": {} + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the VPC link.

" + } + }, + "targetArns": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

The ARN of the network load balancer of the VPC targeted by the VPC link. The network load balancer must be owned by the same AWS account of the API owner.

", + "smithy.api#required": {} + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Creates a VPC link, under the caller's account in a selected region, in an asynchronous operation that typically takes 2-4 minutes to complete and become operational. The caller must have permissions to create and update VPC Endpoint services.

" + } + }, + "com.amazonaws.apigateway#DeleteApiKey": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteApiKeyRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes the ApiKey resource.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/apikeys/{apiKey}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteApiKeyRequest": { + "type": "structure", + "members": { + "apiKey": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the ApiKey resource to be deleted.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to delete the ApiKey resource.

" + } + }, + "com.amazonaws.apigateway#DeleteAuthorizer": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteAuthorizerRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes an existing Authorizer resource.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/authorizers/{authorizerId}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteAuthorizerRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "authorizerId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the Authorizer resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to delete an existing Authorizer resource.

" + } + }, + "com.amazonaws.apigateway#DeleteBasePathMapping": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteBasePathMappingRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes the BasePathMapping resource.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/domainnames/{domainName}/basepathmappings/{basePath}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteBasePathMappingRequest": { + "type": "structure", + "members": { + "domainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The domain name of the BasePathMapping resource to delete.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "basePath": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The base path name of the BasePathMapping resource to delete.

\n

To specify an empty base path, set this parameter to '(none)'.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to delete the BasePathMapping resource.

" + } + }, + "com.amazonaws.apigateway#DeleteClientCertificate": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteClientCertificateRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes the ClientCertificate resource.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/clientcertificates/{clientCertificateId}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteClientCertificateRequest": { + "type": "structure", + "members": { + "clientCertificateId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the ClientCertificate resource to be deleted.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to delete the ClientCertificate resource.

" + } + }, + "com.amazonaws.apigateway#DeleteDeployment": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteDeploymentRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes a Deployment resource. Deleting a deployment will only succeed if there are no Stage resources associated with it.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/deployments/{deploymentId}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteDeploymentRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "deploymentId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the Deployment resource to delete.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to delete a Deployment resource.

" + } + }, + "com.amazonaws.apigateway#DeleteDocumentationPart": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteDocumentationPartRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes a documentation part

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/documentation/parts/{documentationPartId}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteDocumentationPartRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "documentationPartId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the to-be-deleted documentation part.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Deletes an existing documentation part of an API.

" + } + }, + "com.amazonaws.apigateway#DeleteDocumentationVersion": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteDocumentationVersionRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes a documentation version.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/documentation/versions/{documentationVersion}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteDocumentationVersionRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "documentationVersion": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The version identifier of a to-be-deleted documentation snapshot.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Deletes an existing documentation version of an API.

" + } + }, + "com.amazonaws.apigateway#DeleteDomainName": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteDomainNameRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes the DomainName resource.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/domainnames/{domainName}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteDomainNameRequest": { + "type": "structure", + "members": { + "domainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the DomainName resource to be deleted.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to delete the DomainName resource.

" + } + }, + "com.amazonaws.apigateway#DeleteGatewayResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteGatewayResponseRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Clears any customization of a GatewayResponse of a specified response type on the given RestApi and resets it with the default settings.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/gatewayresponses/{responseType}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteGatewayResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "responseType": { + "target": "com.amazonaws.apigateway#GatewayResponseType", + "traits": { + "smithy.api#documentation": "

The response type of the associated GatewayResponse.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Clears any customization of a GatewayResponse of a specified response type on the given RestApi and resets it with the default settings.

" + } + }, + "com.amazonaws.apigateway#DeleteIntegration": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteIntegrationRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Represents a delete integration.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration", + "code": 204 + } + } + }, + "com.amazonaws.apigateway#DeleteIntegrationRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a delete integration request's resource identifier.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a delete integration request's HTTP method.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a delete integration request.

" + } + }, + "com.amazonaws.apigateway#DeleteIntegrationResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteIntegrationResponseRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Represents a delete integration response.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration/responses/{statusCode}", + "code": 204 + } + } + }, + "com.amazonaws.apigateway#DeleteIntegrationResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a delete integration response request's resource identifier.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a delete integration response request's HTTP method.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

Specifies a delete integration response request's status code.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a delete integration response request.

" + } + }, + "com.amazonaws.apigateway#DeleteMethod": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteMethodRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes an existing Method resource.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}", + "code": 204 + } + } + }, + "com.amazonaws.apigateway#DeleteMethodRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Resource identifier for the Method resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The HTTP verb of the Method resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to delete an existing Method resource.

" + } + }, + "com.amazonaws.apigateway#DeleteMethodResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteMethodResponseRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes an existing MethodResponse resource.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/responses/{statusCode}", + "code": 204 + } + } + }, + "com.amazonaws.apigateway#DeleteMethodResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Resource identifier for the MethodResponse resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The HTTP verb of the Method resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

The status code identifier for the MethodResponse resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to delete an existing MethodResponse resource.

" + } + }, + "com.amazonaws.apigateway#DeleteModel": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteModelRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes a model.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/models/{modelName}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteModelRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "modelName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the model to delete.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to delete an existing model in an existing RestApi resource.

" + } + }, + "com.amazonaws.apigateway#DeleteRequestValidator": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteRequestValidatorRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes a RequestValidator of a given RestApi.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/requestvalidators/{requestValidatorId}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteRequestValidatorRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "requestValidatorId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the RequestValidator to be deleted.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Deletes a specified RequestValidator of a given RestApi.

" + } + }, + "com.amazonaws.apigateway#DeleteResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteResourceRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes a Resource resource.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/resources/{resourceId}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteResourceRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the Resource resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to delete a Resource.

" + } + }, + "com.amazonaws.apigateway#DeleteRestApi": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteRestApiRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes the specified API.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteRestApiRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to delete the specified API from your collection.

" + } + }, + "com.amazonaws.apigateway#DeleteStage": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteStageRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes a Stage resource.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/stages/{stageName}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteStageRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the Stage resource to delete.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to delete a Stage resource.

" + } + }, + "com.amazonaws.apigateway#DeleteUsagePlan": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteUsagePlanRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes a usage plan of a given plan Id.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/usageplans/{usagePlanId}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteUsagePlanKey": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteUsagePlanKeyRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes a usage plan key and remove the underlying API key from the associated usage plan.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/usageplans/{usagePlanId}/keys/{keyId}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteUsagePlanKeyRequest": { + "type": "structure", + "members": { + "usagePlanId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Id of the UsagePlan resource representing the usage plan containing the to-be-deleted UsagePlanKey resource representing a plan customer.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "keyId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Id of the UsagePlanKey resource to be deleted.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The DELETE request to delete a usage plan key and remove the underlying API key from the associated usage plan.

" + } + }, + "com.amazonaws.apigateway#DeleteUsagePlanRequest": { + "type": "structure", + "members": { + "usagePlanId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Id of the to-be-deleted usage plan.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The DELETE request to delete a usage plan of a given plan Id.

" + } + }, + "com.amazonaws.apigateway#DeleteVpcLink": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#DeleteVpcLinkRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Deletes an existing VpcLink of a specified identifier.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/vpclinks/{vpcLinkId}", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#DeleteVpcLinkRequest": { + "type": "structure", + "members": { + "vpcLinkId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the VpcLink. It is used in an Integration to reference this VpcLink.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Deletes an existing VpcLink of a specified identifier.

" + } + }, + "com.amazonaws.apigateway#Deployment": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier for the deployment resource.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description for the deployment resource.

" + } + }, + "createdDate": { + "target": "com.amazonaws.apigateway#Timestamp", + "traits": { + "smithy.api#documentation": "

The date and time that the deployment resource was created.

" + } + }, + "apiSummary": { + "target": "com.amazonaws.apigateway#PathToMapOfMethodSnapshot", + "traits": { + "smithy.api#documentation": "

A summary of the RestApi at the date and time that the deployment resource was created.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

An immutable representation of a RestApi resource that can be called by users using Stages. A deployment must be associated with a Stage for it to be callable over the Internet.

" + } + }, + "com.amazonaws.apigateway#DeploymentCanarySettings": { + "type": "structure", + "members": { + "percentTraffic": { + "target": "com.amazonaws.apigateway#Double", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The percentage (0.0-100.0) of traffic routed to the canary deployment.

" + } + }, + "stageVariableOverrides": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A stage variable overrides used for the canary release deployment. They can override existing stage variables or add new stage variables for the canary release deployment. These stage variables are represented as a string-to-string map between stage variable names and their values.

" + } + }, + "useStageCache": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A Boolean flag to indicate whether the canary release deployment uses the stage cache or not.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The input configuration for a canary deployment.

" + } + }, + "com.amazonaws.apigateway#Deployments": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfDeployment", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a collection resource that contains zero or more references to your existing deployments, and links that guide you on how to interact with your collection. The collection offers a paginated view of the contained deployments.

" + } + }, + "com.amazonaws.apigateway#DocumentationPart": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The DocumentationPart identifier, generated by API Gateway when the DocumentationPart is created.

" + } + }, + "location": { + "target": "com.amazonaws.apigateway#DocumentationPartLocation", + "traits": { + "smithy.api#documentation": "

The location of the API entity to which the documentation applies. Valid fields depend on the targeted API entity type. All the valid location fields are not required. If not explicitly specified, a valid location field is treated as a wildcard and associated documentation content may be inherited by matching entities, unless overridden.

" + } + }, + "properties": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A content map of API-specific key-value pairs describing the targeted API entity. The map must be encoded as a JSON string, e.g., \"{ \\\"description\\\": \\\"The API does ...\\\" }\". Only OpenAPI-compliant documentation-related fields from the properties map are exported and, hence, published as part of the API entity definitions, while the original documentation parts are exported in a OpenAPI extension of x-amazon-apigateway-documentation.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A documentation part for a targeted API entity.

" + } + }, + "com.amazonaws.apigateway#DocumentationPartIds": { + "type": "structure", + "members": { + "ids": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of the returned documentation part identifiers.

" + } + }, + "warnings": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of warning messages reported during import of documentation parts.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A collection of the imported DocumentationPart identifiers.

" + } + }, + "com.amazonaws.apigateway#DocumentationPartLocation": { + "type": "structure", + "members": { + "type": { + "target": "com.amazonaws.apigateway#DocumentationPartType", + "traits": { + "smithy.api#documentation": "

The type of API entity to which the documentation content applies. Valid values are API, AUTHORIZER, MODEL, RESOURCE, METHOD, PATH_PARAMETER, QUERY_PARAMETER, REQUEST_HEADER, REQUEST_BODY, RESPONSE, RESPONSE_HEADER, and RESPONSE_BODY. Content inheritance does not apply to any entity of the API, AUTHORIZER, METHOD, MODEL, REQUEST_BODY, or RESOURCE type.

", + "smithy.api#required": {} + } + }, + "path": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The URL path of the target. It is a valid field for the API entity types of RESOURCE, METHOD, PATH_PARAMETER, QUERY_PARAMETER, REQUEST_HEADER, REQUEST_BODY, RESPONSE, RESPONSE_HEADER, and RESPONSE_BODY. The default value is / for the root resource. When an applicable child entity inherits the content of another entity of the same type with more general specifications of the other location attributes, the child entity's path attribute must match that of the parent entity as a prefix.

" + } + }, + "method": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The HTTP verb of a method. It is a valid field for the API entity types of METHOD, PATH_PARAMETER, QUERY_PARAMETER, REQUEST_HEADER, REQUEST_BODY, RESPONSE, RESPONSE_HEADER, and RESPONSE_BODY. The default value is * for any method. When an applicable child entity inherits the content of an entity of the same type with more general specifications of the other location attributes, the child entity's method attribute must match that of the parent entity exactly.

" + } + }, + "statusCode": { + "target": "com.amazonaws.apigateway#DocumentationPartLocationStatusCode", + "traits": { + "smithy.api#documentation": "

The HTTP status code of a response. It is a valid field for the API entity types of RESPONSE, RESPONSE_HEADER, and RESPONSE_BODY. The default value is * for any status code. When an applicable child entity inherits the content of an entity of the same type with more general specifications of the other location attributes, the child entity's statusCode attribute must match that of the parent entity exactly.

" + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the targeted API entity. It is a valid and required field for the API entity types of AUTHORIZER, MODEL, PATH_PARAMETER, QUERY_PARAMETER, REQUEST_HEADER, REQUEST_BODY and RESPONSE_HEADER. It is an invalid field for any other entity type.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies the target API entity to which the documentation applies.

" + } + }, + "com.amazonaws.apigateway#DocumentationPartLocationStatusCode": { + "type": "string", + "traits": { + "smithy.api#pattern": "^([1-5]\\d\\d|\\*|\\s*)$" + } + }, + "com.amazonaws.apigateway#DocumentationPartType": { + "type": "enum", + "members": { + "API": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "API" + } + }, + "AUTHORIZER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AUTHORIZER" + } + }, + "MODEL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MODEL" + } + }, + "RESOURCE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RESOURCE" + } + }, + "METHOD": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "METHOD" + } + }, + "PATH_PARAMETER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PATH_PARAMETER" + } + }, + "QUERY_PARAMETER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "QUERY_PARAMETER" + } + }, + "REQUEST_HEADER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "REQUEST_HEADER" + } + }, + "REQUEST_BODY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "REQUEST_BODY" + } + }, + "RESPONSE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RESPONSE" + } + }, + "RESPONSE_HEADER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RESPONSE_HEADER" + } + }, + "RESPONSE_BODY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RESPONSE_BODY" + } + } + } + }, + "com.amazonaws.apigateway#DocumentationParts": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfDocumentationPart", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

The collection of documentation parts of an API.

" + } + }, + "com.amazonaws.apigateway#DocumentationVersion": { + "type": "structure", + "members": { + "version": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The version identifier of the API documentation snapshot.

" + } + }, + "createdDate": { + "target": "com.amazonaws.apigateway#Timestamp", + "traits": { + "smithy.api#documentation": "

The date when the API documentation snapshot is created.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the API documentation snapshot.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A snapshot of the documentation of an API.

" + } + }, + "com.amazonaws.apigateway#DocumentationVersions": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfDocumentationVersion", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

The collection of documentation snapshots of an API.

" + } + }, + "com.amazonaws.apigateway#DomainName": { + "type": "structure", + "members": { + "domainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The custom domain name as an API host name, for example, my-api.example.com.

" + } + }, + "certificateName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the certificate that will be used by edge-optimized endpoint for this domain name.

" + } + }, + "certificateArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The reference to an AWS-managed certificate that will be used by edge-optimized endpoint for this domain name. AWS Certificate Manager is the only supported source.

" + } + }, + "certificateUploadDate": { + "target": "com.amazonaws.apigateway#Timestamp", + "traits": { + "smithy.api#documentation": "

The timestamp when the certificate that was used by edge-optimized endpoint for this domain name was uploaded.

" + } + }, + "regionalDomainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The domain name associated with the regional endpoint for this custom domain name. You set up this association by adding a DNS record that points the custom domain name to this regional domain name. The regional domain name is returned by API Gateway when you create a regional endpoint.

" + } + }, + "regionalHostedZoneId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The region-specific Amazon Route 53 Hosted Zone ID of the regional endpoint. For more information, see Set up a Regional Custom Domain Name and AWS Regions and Endpoints for API Gateway.

" + } + }, + "regionalCertificateName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the certificate that will be used for validating the regional domain name.

" + } + }, + "regionalCertificateArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The reference to an AWS-managed certificate that will be used for validating the regional domain name. AWS Certificate Manager is the only supported source.

" + } + }, + "distributionDomainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The domain name of the Amazon CloudFront distribution associated with this custom domain name for an edge-optimized endpoint. You set up this association when adding a DNS record pointing the custom domain name to this distribution name. For more information about CloudFront distributions, see the Amazon CloudFront documentation.

" + } + }, + "distributionHostedZoneId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The region-agnostic Amazon Route 53 Hosted Zone ID of the edge-optimized endpoint. The valid value is Z2FDTNDATAQYW2 for all the regions. For more information, see Set up a Regional Custom Domain Name and AWS Regions and Endpoints for API Gateway.

" + } + }, + "endpointConfiguration": { + "target": "com.amazonaws.apigateway#EndpointConfiguration", + "traits": { + "smithy.api#documentation": "

The endpoint configuration of this DomainName showing the endpoint types of the domain name.

" + } + }, + "domainNameStatus": { + "target": "com.amazonaws.apigateway#DomainNameStatus", + "traits": { + "smithy.api#documentation": "

The status of the DomainName migration. The valid values are AVAILABLE and UPDATING. If the status is UPDATING, the domain cannot be modified further until the existing operation is complete. If it is AVAILABLE, the domain can be updated.

" + } + }, + "domainNameStatusMessage": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

An optional text message containing detailed information about status of the DomainName migration.

" + } + }, + "securityPolicy": { + "target": "com.amazonaws.apigateway#SecurityPolicy", + "traits": { + "smithy.api#documentation": "

The Transport Layer Security (TLS) version + cipher suite for this DomainName. The valid values are TLS_1_0 and TLS_1_2.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" + } + }, + "mutualTlsAuthentication": { + "target": "com.amazonaws.apigateway#MutualTlsAuthentication", + "traits": { + "smithy.api#documentation": "

The mutual TLS authentication configuration for a custom domain name. If specified, API Gateway\n performs two-way authentication between the client and the server. Clients must present a\n trusted certificate to access your API.

" + } + }, + "ownershipVerificationCertificateArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ARN of the public certificate issued by ACM to validate ownership of your custom\n domain. Only required when configuring mutual TLS and using an ACM imported or private CA\n certificate ARN as the regionalCertificateArn.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a custom domain name as a user-friendly host name of an API (RestApi).

" + } + }, + "com.amazonaws.apigateway#DomainNameStatus": { + "type": "enum", + "members": { + "AVAILABLE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AVAILABLE" + } + }, + "UPDATING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "UPDATING" + } + }, + "PENDING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PENDING" + } + }, + "PENDING_CERTIFICATE_REIMPORT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PENDING_CERTIFICATE_REIMPORT" + } + }, + "PENDING_OWNERSHIP_VERIFICATION": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PENDING_OWNERSHIP_VERIFICATION" + } + } + } + }, + "com.amazonaws.apigateway#DomainNames": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfDomainName", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a collection of DomainName resources.

" + } + }, + "com.amazonaws.apigateway#Double": { + "type": "double", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.apigateway#EndpointConfiguration": { + "type": "structure", + "members": { + "types": { + "target": "com.amazonaws.apigateway#ListOfEndpointType", + "traits": { + "smithy.api#documentation": "

A list of endpoint types of an API (RestApi) or its custom domain name (DomainName). For an edge-optimized API and its custom domain name, the endpoint type is \"EDGE\". For a regional API and its custom domain name, the endpoint type is REGIONAL. For a private API, the endpoint type is PRIVATE.

" + } + }, + "vpcEndpointIds": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of VpcEndpointIds of an API (RestApi) against which to create Route53 ALIASes. It is only supported for PRIVATE endpoint type.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The endpoint configuration to indicate the types of endpoints an API (RestApi) or its custom domain name (DomainName) has.

" + } + }, + "com.amazonaws.apigateway#EndpointType": { + "type": "enum", + "members": { + "REGIONAL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "REGIONAL" + } + }, + "EDGE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "EDGE" + } + }, + "PRIVATE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PRIVATE" + } + } + }, + "traits": { + "smithy.api#documentation": "

The endpoint type. The valid values are EDGE for edge-optimized API setup, most suitable for mobile applications; REGIONAL for regional API endpoint setup, most suitable for calling from AWS Region; and PRIVATE for private APIs.

" + } + }, + "com.amazonaws.apigateway#ExportResponse": { + "type": "structure", + "members": { + "contentType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The content-type header value in the HTTP response. This will correspond to a valid 'accept' type in the request.

", + "smithy.api#httpHeader": "Content-Type" + } + }, + "contentDisposition": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The content-disposition header value in the HTTP response.

", + "smithy.api#httpHeader": "Content-Disposition" + } + }, + "body": { + "target": "com.amazonaws.apigateway#Blob", + "traits": { + "smithy.api#documentation": "

The binary blob response to GetExport, which contains the export.

", + "smithy.api#httpPayload": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The binary blob response to GetExport, which contains the generated SDK.

" + } + }, + "com.amazonaws.apigateway#FlushStageAuthorizersCache": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#FlushStageAuthorizersCacheRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Flushes all authorizer cache entries on a stage.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/stages/{stageName}/cache/authorizers", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#FlushStageAuthorizersCacheRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the stage to flush.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to flush authorizer cache entries on a specified stage.

" + } + }, + "com.amazonaws.apigateway#FlushStageCache": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#FlushStageCacheRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Flushes a stage's cache.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/restapis/{restApiId}/stages/{stageName}/cache/data", + "code": 202 + } + } + }, + "com.amazonaws.apigateway#FlushStageCacheRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the stage to flush its cache.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to flush a stage's cache.

" + } + }, + "com.amazonaws.apigateway#GatewayResponse": { + "type": "structure", + "members": { + "responseType": { + "target": "com.amazonaws.apigateway#GatewayResponseType", + "traits": { + "smithy.api#documentation": "

The response type of the associated GatewayResponse.

" + } + }, + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

The HTTP status code for this GatewayResponse.

" + } + }, + "responseParameters": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Response parameters (paths, query strings and headers) of the GatewayResponse as a\n string-to-string map of key-value pairs.

" + } + }, + "responseTemplates": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Response templates of the GatewayResponse as a string-to-string map of key-value pairs.

" + } + }, + "defaultResponse": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A Boolean flag to indicate whether this GatewayResponse is the default gateway response (true) or not (false). A default gateway response is one generated by API Gateway without any customization by an API developer.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A gateway response of a given response type and status code, with optional response parameters and mapping templates.

" + } + }, + "com.amazonaws.apigateway#GatewayResponseType": { + "type": "enum", + "members": { + "DEFAULT_4XX": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DEFAULT_4XX" + } + }, + "DEFAULT_5XX": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DEFAULT_5XX" + } + }, + "RESOURCE_NOT_FOUND": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RESOURCE_NOT_FOUND" + } + }, + "UNAUTHORIZED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "UNAUTHORIZED" + } + }, + "INVALID_API_KEY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INVALID_API_KEY" + } + }, + "ACCESS_DENIED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ACCESS_DENIED" + } + }, + "AUTHORIZER_FAILURE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AUTHORIZER_FAILURE" + } + }, + "AUTHORIZER_CONFIGURATION_ERROR": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AUTHORIZER_CONFIGURATION_ERROR" + } + }, + "INVALID_SIGNATURE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INVALID_SIGNATURE" + } + }, + "EXPIRED_TOKEN": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "EXPIRED_TOKEN" + } + }, + "MISSING_AUTHENTICATION_TOKEN": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MISSING_AUTHENTICATION_TOKEN" + } + }, + "INTEGRATION_FAILURE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INTEGRATION_FAILURE" + } + }, + "INTEGRATION_TIMEOUT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INTEGRATION_TIMEOUT" + } + }, + "API_CONFIGURATION_ERROR": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "API_CONFIGURATION_ERROR" + } + }, + "UNSUPPORTED_MEDIA_TYPE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "UNSUPPORTED_MEDIA_TYPE" + } + }, + "BAD_REQUEST_PARAMETERS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BAD_REQUEST_PARAMETERS" + } + }, + "BAD_REQUEST_BODY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BAD_REQUEST_BODY" + } + }, + "REQUEST_TOO_LARGE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "REQUEST_TOO_LARGE" + } + }, + "THROTTLED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "THROTTLED" + } + }, + "QUOTA_EXCEEDED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "QUOTA_EXCEEDED" + } + }, + "WAF_FILTERED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "WAF_FILTERED" + } + } + } + }, + "com.amazonaws.apigateway#GatewayResponses": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfGatewayResponse", + "traits": { + "smithy.api#documentation": "

Returns the entire collection, because of no pagination support.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set. The GatewayResponse collection does not support pagination and the position does not apply here.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

The collection of the GatewayResponse instances of a RestApi as a responseType-to-GatewayResponse object map of key-value pairs. As such, pagination is not supported for querying this collection.

" + } + }, + "com.amazonaws.apigateway#GenerateClientCertificate": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GenerateClientCertificateRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#ClientCertificate" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Generates a ClientCertificate resource.

", + "smithy.api#http": { + "method": "POST", + "uri": "/clientcertificates", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#GenerateClientCertificateRequest": { + "type": "structure", + "members": { + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the ClientCertificate.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to generate a ClientCertificate resource.

" + } + }, + "com.amazonaws.apigateway#GetAccount": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetAccountRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Account" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets information about the current Account resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/account", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetAccountRequest": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to get information about the current Account resource.

" + } + }, + "com.amazonaws.apigateway#GetApiKey": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetApiKeyRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#ApiKey" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets information about the current ApiKey resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/apikeys/{apiKey}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetApiKeyRequest": { + "type": "structure", + "members": { + "apiKey": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the ApiKey resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "includeValue": { + "target": "com.amazonaws.apigateway#NullableBoolean", + "traits": { + "smithy.api#documentation": "

A boolean flag to specify whether (true) or not (false) the result contains the key value.

", + "smithy.api#httpQuery": "includeValue" + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to get information about the current ApiKey resource.

" + } + }, + "com.amazonaws.apigateway#GetApiKeys": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetApiKeysRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#ApiKeys" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets information about the current ApiKeys resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/apikeys", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetApiKeysRequest": { + "type": "structure", + "members": { + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + }, + "nameQuery": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of queried API keys.

", + "smithy.api#httpQuery": "name" + } + }, + "customerId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of a customer in AWS Marketplace or an external system, such as a developer portal.

", + "smithy.api#httpQuery": "customerId" + } + }, + "includeValues": { + "target": "com.amazonaws.apigateway#NullableBoolean", + "traits": { + "smithy.api#documentation": "

A boolean flag to specify whether (true) or not (false) the result contains key values.

", + "smithy.api#httpQuery": "includeValues" + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to get information about the current ApiKeys resource.

" + } + }, + "com.amazonaws.apigateway#GetAuthorizer": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetAuthorizerRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Authorizer" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Describe an existing Authorizer resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/authorizers/{authorizerId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetAuthorizerRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "authorizerId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the Authorizer resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to describe an existing Authorizer resource.

" + } + }, + "com.amazonaws.apigateway#GetAuthorizers": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetAuthorizersRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Authorizers" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Describe an existing Authorizers resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/authorizers", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetAuthorizersRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to describe an existing Authorizers resource.

" + } + }, + "com.amazonaws.apigateway#GetBasePathMapping": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetBasePathMappingRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#BasePathMapping" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Describe a BasePathMapping resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/domainnames/{domainName}/basepathmappings/{basePath}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetBasePathMappingRequest": { + "type": "structure", + "members": { + "domainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The domain name of the BasePathMapping resource to be described.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "basePath": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The base path name that callers of the API must provide as part of the URL after the domain name. This value must be unique for all of the mappings across a single API. Specify '(none)' if you do not want callers to specify any base path name after the domain name.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to describe a BasePathMapping resource.

" + } + }, + "com.amazonaws.apigateway#GetBasePathMappings": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetBasePathMappingsRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#BasePathMappings" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Represents a collection of BasePathMapping resources.

", + "smithy.api#http": { + "method": "GET", + "uri": "/domainnames/{domainName}/basepathmappings", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetBasePathMappingsRequest": { + "type": "structure", + "members": { + "domainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The domain name of a BasePathMapping resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to get information about a collection of BasePathMapping resources.

" + } + }, + "com.amazonaws.apigateway#GetClientCertificate": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetClientCertificateRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#ClientCertificate" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets information about the current ClientCertificate resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/clientcertificates/{clientCertificateId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetClientCertificateRequest": { + "type": "structure", + "members": { + "clientCertificateId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the ClientCertificate resource to be described.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to get information about the current ClientCertificate resource.

" + } + }, + "com.amazonaws.apigateway#GetClientCertificates": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetClientCertificatesRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#ClientCertificates" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets a collection of ClientCertificate resources.

", + "smithy.api#http": { + "method": "GET", + "uri": "/clientcertificates", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetClientCertificatesRequest": { + "type": "structure", + "members": { + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to get information about a collection of ClientCertificate resources.

" + } + }, + "com.amazonaws.apigateway#GetDeployment": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetDeploymentRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Deployment" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets information about a Deployment resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/deployments/{deploymentId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetDeploymentRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "deploymentId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the Deployment resource to get information about.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "embed": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A query parameter to retrieve the specified embedded resources of the returned Deployment resource in the response. In a REST API call, this embed parameter value is a list of comma-separated strings, as in GET /restapis/{restapi_id}/deployments/{deployment_id}?embed=var1,var2. The SDK and other platform-dependent libraries might use a different format for the list. Currently, this request supports only retrieval of the embedded API summary this way. Hence, the parameter value must be a single-valued list containing only the \"apisummary\" string. For example, GET /restapis/{restapi_id}/deployments/{deployment_id}?embed=apisummary.

", + "smithy.api#httpQuery": "embed" + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to get information about a Deployment resource.

" + } + }, + "com.amazonaws.apigateway#GetDeployments": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetDeploymentsRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Deployments" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets information about a Deployments collection.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/deployments", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetDeploymentsRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to get information about a Deployments collection.

" + } + }, + "com.amazonaws.apigateway#GetDocumentationPart": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetDocumentationPartRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DocumentationPart" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets a documentation part.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/documentation/parts/{documentationPartId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetDocumentationPartRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "documentationPartId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Gets a specified documentation part of a given API.

" + } + }, + "com.amazonaws.apigateway#GetDocumentationParts": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetDocumentationPartsRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DocumentationParts" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets documentation parts.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/documentation/parts", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetDocumentationPartsRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "type": { + "target": "com.amazonaws.apigateway#DocumentationPartType", + "traits": { + "smithy.api#documentation": "

The type of API entities of the to-be-retrieved documentation parts.

", + "smithy.api#httpQuery": "type" + } + }, + "nameQuery": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of API entities of the to-be-retrieved documentation parts.

", + "smithy.api#httpQuery": "name" + } + }, + "path": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The path of API entities of the to-be-retrieved documentation parts.

", + "smithy.api#httpQuery": "path" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + }, + "locationStatus": { + "target": "com.amazonaws.apigateway#LocationStatusType", + "traits": { + "smithy.api#documentation": "

The status of the API documentation parts to retrieve. Valid values are DOCUMENTED for retrieving DocumentationPart resources with content and UNDOCUMENTED for DocumentationPart resources without content.

", + "smithy.api#httpQuery": "locationStatus" + } + } + }, + "traits": { + "smithy.api#documentation": "

Gets the documentation parts of an API. The result may be filtered by the type, name, or path of API entities (targets).

" + } + }, + "com.amazonaws.apigateway#GetDocumentationVersion": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetDocumentationVersionRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DocumentationVersion" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets a documentation version.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/documentation/versions/{documentationVersion}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetDocumentationVersionRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "documentationVersion": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The version identifier of the to-be-retrieved documentation snapshot.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Gets a documentation snapshot of an API.

" + } + }, + "com.amazonaws.apigateway#GetDocumentationVersions": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetDocumentationVersionsRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DocumentationVersions" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets documentation versions.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/documentation/versions", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetDocumentationVersionsRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

Gets the documentation versions of an API.

" + } + }, + "com.amazonaws.apigateway#GetDomainName": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetDomainNameRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DomainName" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Represents a domain name that is contained in a simpler, more intuitive URL that can be called.

", + "smithy.api#http": { + "method": "GET", + "uri": "/domainnames/{domainName}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetDomainNameRequest": { + "type": "structure", + "members": { + "domainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the DomainName resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to get the name of a DomainName resource.

" + } + }, + "com.amazonaws.apigateway#GetDomainNames": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetDomainNamesRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DomainNames" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Represents a collection of DomainName resources.

", + "smithy.api#http": { + "method": "GET", + "uri": "/domainnames", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetDomainNamesRequest": { + "type": "structure", + "members": { + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to describe a collection of DomainName resources.

" + } + }, + "com.amazonaws.apigateway#GetExport": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetExportRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#ExportResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Exports a deployed version of a RestApi in a specified format.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/stages/{stageName}/exports/{exportType}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetExportRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the Stage that will be exported.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "exportType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The type of export. Acceptable values are 'oas30' for OpenAPI 3.0.x and 'swagger' for Swagger/OpenAPI 2.0.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "parameters": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map of query string parameters that specify properties of the export, depending on the requested exportType. For exportType\n oas30 and swagger, any combination of the following parameters are supported: extensions='integrations' or extensions='apigateway' will export the API with x-amazon-apigateway-integration extensions. extensions='authorizers' will export the API with x-amazon-apigateway-authorizer extensions. postman will export the API with Postman extensions, allowing for import to the Postman tool

", + "smithy.api#httpQueryParams": {} + } + }, + "accepts": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The content-type of the export, for example application/json. Currently application/json and application/yaml are supported for exportType ofoas30 and swagger. This should be specified in the Accept header for direct API requests.

", + "smithy.api#httpHeader": "Accept" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request a new export of a RestApi for a particular Stage.

" + } + }, + "com.amazonaws.apigateway#GetGatewayResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetGatewayResponseRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#GatewayResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets a GatewayResponse of a specified response type on the given RestApi.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/gatewayresponses/{responseType}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetGatewayResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "responseType": { + "target": "com.amazonaws.apigateway#GatewayResponseType", + "traits": { + "smithy.api#documentation": "

The response type of the associated GatewayResponse.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Gets a GatewayResponse of a specified response type on the given RestApi.

" + } + }, + "com.amazonaws.apigateway#GetGatewayResponses": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetGatewayResponsesRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#GatewayResponses" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets the GatewayResponses collection on the given RestApi. If an API developer has not added any definitions for gateway responses, the result will be the API Gateway-generated default GatewayResponses collection for the supported response types.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/gatewayresponses", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetGatewayResponsesRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set. The GatewayResponse collection does not support pagination and the position does not apply here.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500. The GatewayResponses collection does not support pagination and the limit does not apply here.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

Gets the GatewayResponses collection on the given RestApi. If an API developer has not added any definitions for gateway responses, the result will be the API Gateway-generated default GatewayResponses collection for the supported response types.

" + } + }, + "com.amazonaws.apigateway#GetIntegration": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetIntegrationRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Integration" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Get the integration settings.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetIntegrationRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a get integration request's resource identifier

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a get integration request's HTTP method.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a request to get the integration configuration.

" + } + }, + "com.amazonaws.apigateway#GetIntegrationResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetIntegrationResponseRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#IntegrationResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Represents a get integration response.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration/responses/{statusCode}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetIntegrationResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a get integration response request's resource identifier.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a get integration response request's HTTP method.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

Specifies a get integration response request's status code.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a get integration response request.

" + } + }, + "com.amazonaws.apigateway#GetMethod": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetMethodRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Method" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Describe an existing Method resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetMethodRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Resource identifier for the Method resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the method request's HTTP method type.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to describe an existing Method resource.

" + } + }, + "com.amazonaws.apigateway#GetMethodResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetMethodResponseRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#MethodResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Describes a MethodResponse resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/responses/{statusCode}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetMethodResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Resource identifier for the MethodResponse resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The HTTP verb of the Method resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

The status code for the MethodResponse resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to describe a MethodResponse resource.

" + } + }, + "com.amazonaws.apigateway#GetModel": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetModelRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Model" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Describes an existing model defined for a RestApi resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/models/{modelName}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetModelRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The RestApi identifier under which the Model exists.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "modelName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the model as an identifier.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "flatten": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A query parameter of a Boolean value to resolve (true) all external model references and returns a flattened model schema or not (false) The default is false.

", + "smithy.api#httpQuery": "flatten" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to list information about a model in an existing RestApi resource.

" + } + }, + "com.amazonaws.apigateway#GetModelTemplate": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetModelTemplateRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Template" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Generates a sample mapping template that can be used to transform a payload into the structure of a model.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/models/{modelName}/default_template", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetModelTemplateRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "modelName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the model for which to generate a template.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to generate a sample mapping template used to transform the payload.

" + } + }, + "com.amazonaws.apigateway#GetModels": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetModelsRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Models" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Describes existing Models defined for a RestApi resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/models", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetModelsRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to list existing Models defined for a RestApi resource.

" + } + }, + "com.amazonaws.apigateway#GetRequestValidator": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetRequestValidatorRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#RequestValidator" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets a RequestValidator of a given RestApi.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/requestvalidators/{requestValidatorId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetRequestValidatorRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "requestValidatorId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the RequestValidator to be retrieved.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Gets a RequestValidator of a given RestApi.

" + } + }, + "com.amazonaws.apigateway#GetRequestValidators": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetRequestValidatorsRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#RequestValidators" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets the RequestValidators collection of a given RestApi.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/requestvalidators", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetRequestValidatorsRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

Gets the RequestValidators collection of a given RestApi.

" + } + }, + "com.amazonaws.apigateway#GetResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetResourceRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Resource" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Lists information about a resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/resources/{resourceId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetResourceRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier for the Resource resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "embed": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A query parameter to retrieve the specified resources embedded in the returned Resource representation in the response. This embed parameter value is a list of comma-separated strings. Currently, the request supports only retrieval of the embedded Method resources this way. The query parameter value must be a single-valued list and contain the \"methods\" string. For example, GET /restapis/{restapi_id}/resources/{resource_id}?embed=methods.

", + "smithy.api#httpQuery": "embed" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to list information about a resource.

" + } + }, + "com.amazonaws.apigateway#GetResources": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetResourcesRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Resources" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Lists information about a collection of Resource resources.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/resources", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetResourcesRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + }, + "embed": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A query parameter used to retrieve the specified resources embedded in the returned Resources resource in the response. This embed parameter value is a list of comma-separated strings. Currently, the request supports only retrieval of the embedded Method resources this way. The query parameter value must be a single-valued list and contain the \"methods\" string. For example, GET /restapis/{restapi_id}/resources?embed=methods.

", + "smithy.api#httpQuery": "embed" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to list information about a collection of resources.

" + } + }, + "com.amazonaws.apigateway#GetRestApi": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetRestApiRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#RestApi" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Lists the RestApi resource in the collection.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetRestApiRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The GET request to list an existing RestApi defined for your collection.

" + } + }, + "com.amazonaws.apigateway#GetRestApis": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetRestApisRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#RestApis" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Lists the RestApis resources for your collection.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetRestApisRequest": { + "type": "structure", + "members": { + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

The GET request to list existing RestApis defined for your collection.

" + } + }, + "com.amazonaws.apigateway#GetSdk": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetSdkRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#SdkResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Generates a client SDK for a RestApi and Stage.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/stages/{stageName}/sdks/{sdkType}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetSdkRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the Stage that the SDK will use.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "sdkType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The language for the generated SDK. Currently java, javascript, android, objectivec (for iOS), swift (for iOS), and ruby are supported.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "parameters": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A string-to-string key-value map of query parameters sdkType-dependent properties of the SDK. For sdkType of objectivec or swift, a parameter named classPrefix is required. For sdkType of android, parameters named groupId, artifactId, artifactVersion, and invokerPackage are required. For sdkType of java, parameters named serviceName and javaPackageName are required.

", + "smithy.api#httpQueryParams": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Request a new generated client SDK for a RestApi and Stage.

" + } + }, + "com.amazonaws.apigateway#GetSdkType": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetSdkTypeRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#SdkType" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets an SDK type.

", + "smithy.api#http": { + "method": "GET", + "uri": "/sdktypes/{id}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetSdkTypeRequest": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the queried SdkType instance.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Get an SdkType instance.

" + } + }, + "com.amazonaws.apigateway#GetSdkTypes": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetSdkTypesRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#SdkTypes" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets SDK types

", + "smithy.api#http": { + "method": "GET", + "uri": "/sdktypes", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetSdkTypesRequest": { + "type": "structure", + "members": { + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

Get the SdkTypes collection.

" + } + }, + "com.amazonaws.apigateway#GetStage": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetStageRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Stage" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets information about a Stage resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/stages/{stageName}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetStageRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the Stage resource to get information about.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to get information about a Stage resource.

" + } + }, + "com.amazonaws.apigateway#GetStages": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetStagesRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Stages" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets information about one or more Stage resources.

", + "smithy.api#http": { + "method": "GET", + "uri": "/restapis/{restApiId}/stages", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetStagesRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "deploymentId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The stages' deployment identifiers.

", + "smithy.api#httpQuery": "deploymentId" + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to get information about one or more Stage resources.

" + } + }, + "com.amazonaws.apigateway#GetTags": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetTagsRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Tags" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets the Tags collection for a given resource.

", + "smithy.api#http": { + "method": "GET", + "uri": "/tags/{resourceArn}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetTagsRequest": { + "type": "structure", + "members": { + "resourceArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ARN of a resource that can be tagged.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

(Not currently supported) The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

(Not currently supported) The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

Gets the Tags collection for a given resource.

" + } + }, + "com.amazonaws.apigateway#GetUsage": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetUsageRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Usage" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets the usage data of a usage plan in a specified time interval.

", + "smithy.api#http": { + "method": "GET", + "uri": "/usageplans/{usagePlanId}/usage", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetUsagePlan": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetUsagePlanRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#UsagePlan" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets a usage plan of a given plan identifier.

", + "smithy.api#http": { + "method": "GET", + "uri": "/usageplans/{usagePlanId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetUsagePlanKey": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetUsagePlanKeyRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#UsagePlanKey" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets a usage plan key of a given key identifier.

", + "smithy.api#http": { + "method": "GET", + "uri": "/usageplans/{usagePlanId}/keys/{keyId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetUsagePlanKeyRequest": { + "type": "structure", + "members": { + "usagePlanId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Id of the UsagePlan resource representing the usage plan containing the to-be-retrieved UsagePlanKey resource representing a plan customer.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "keyId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The key Id of the to-be-retrieved UsagePlanKey resource representing a plan customer.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The GET request to get a usage plan key of a given key identifier.

" + } + }, + "com.amazonaws.apigateway#GetUsagePlanKeys": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetUsagePlanKeysRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#UsagePlanKeys" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets all the usage plan keys representing the API keys added to a specified usage plan.

", + "smithy.api#http": { + "method": "GET", + "uri": "/usageplans/{usagePlanId}/keys", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetUsagePlanKeysRequest": { + "type": "structure", + "members": { + "usagePlanId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Id of the UsagePlan resource representing the usage plan containing the to-be-retrieved UsagePlanKey resource representing a plan customer.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + }, + "nameQuery": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A query parameter specifying the name of the to-be-returned usage plan keys.

", + "smithy.api#httpQuery": "name" + } + } + }, + "traits": { + "smithy.api#documentation": "

The GET request to get all the usage plan keys representing the API keys added to a specified usage plan.

" + } + }, + "com.amazonaws.apigateway#GetUsagePlanRequest": { + "type": "structure", + "members": { + "usagePlanId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the UsagePlan resource to be retrieved.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The GET request to get a usage plan of a given plan identifier.

" + } + }, + "com.amazonaws.apigateway#GetUsagePlans": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetUsagePlansRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#UsagePlans" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets all the usage plans of the caller's account.

", + "smithy.api#http": { + "method": "GET", + "uri": "/usageplans", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetUsagePlansRequest": { + "type": "structure", + "members": { + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "keyId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the API key associated with the usage plans.

", + "smithy.api#httpQuery": "keyId" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

The GET request to get all the usage plans of the caller's account.

" + } + }, + "com.amazonaws.apigateway#GetUsageRequest": { + "type": "structure", + "members": { + "usagePlanId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Id of the usage plan associated with the usage data.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "keyId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Id of the API key associated with the resultant usage data.

", + "smithy.api#httpQuery": "keyId" + } + }, + "startDate": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The starting date (e.g., 2016-01-01) of the usage data.

", + "smithy.api#httpQuery": "startDate", + "smithy.api#required": {} + } + }, + "endDate": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ending date (e.g., 2016-12-31) of the usage data.

", + "smithy.api#httpQuery": "endDate", + "smithy.api#required": {} + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

The GET request to get the usage data of a usage plan in a specified time interval.

" + } + }, + "com.amazonaws.apigateway#GetVpcLink": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetVpcLinkRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#VpcLink" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets a specified VPC link under the caller's account in a region.

", + "smithy.api#http": { + "method": "GET", + "uri": "/vpclinks/{vpcLinkId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#GetVpcLinkRequest": { + "type": "structure", + "members": { + "vpcLinkId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the VpcLink. It is used in an Integration to reference this VpcLink.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Gets a specified VPC link under the caller's account in a region.

" + } + }, + "com.amazonaws.apigateway#GetVpcLinks": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#GetVpcLinksRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#VpcLinks" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Gets the VpcLinks collection under the caller's account in a selected region.

", + "smithy.api#http": { + "method": "GET", + "uri": "/vpclinks", + "code": 200 + }, + "smithy.api#paginated": { + "inputToken": "position", + "outputToken": "position", + "items": "items", + "pageSize": "limit" + } + } + }, + "com.amazonaws.apigateway#GetVpcLinksRequest": { + "type": "structure", + "members": { + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + }, + "limit": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", + "smithy.api#httpQuery": "limit" + } + } + }, + "traits": { + "smithy.api#documentation": "

Gets the VpcLinks collection under the caller's account in a selected region.

" + } + }, + "com.amazonaws.apigateway#ImportApiKeys": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#ImportApiKeysRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#ApiKeyIds" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Import API keys from an external source, such as a CSV-formatted file.

", + "smithy.api#http": { + "method": "POST", + "uri": "/apikeys?mode=import", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#ImportApiKeysRequest": { + "type": "structure", + "members": { + "body": { + "target": "com.amazonaws.apigateway#Blob", + "traits": { + "smithy.api#documentation": "

The payload of the POST request to import API keys. For the payload format, see API Key File Format.

", + "smithy.api#httpPayload": {}, + "smithy.api#required": {} + } + }, + "format": { + "target": "com.amazonaws.apigateway#ApiKeysFormat", + "traits": { + "smithy.api#documentation": "

A query parameter to specify the input format to imported API keys. Currently, only the csv format is supported.

", + "smithy.api#httpQuery": "format", + "smithy.api#required": {} + } + }, + "failOnWarnings": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A query parameter to indicate whether to rollback ApiKey importation (true) or not (false) when error is encountered.

", + "smithy.api#httpQuery": "failonwarnings" + } + } + }, + "traits": { + "smithy.api#documentation": "

The POST request to import API keys from an external source, such as a CSV-formatted file.

" + } + }, + "com.amazonaws.apigateway#ImportDocumentationParts": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#ImportDocumentationPartsRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DocumentationPartIds" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Imports documentation parts

", + "smithy.api#http": { + "method": "PUT", + "uri": "/restapis/{restApiId}/documentation/parts", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#ImportDocumentationPartsRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "mode": { + "target": "com.amazonaws.apigateway#PutMode", + "traits": { + "smithy.api#documentation": "

A query parameter to indicate whether to overwrite (OVERWRITE) any existing DocumentationParts definition or to merge (MERGE) the new definition into the existing one. The default value is MERGE.

", + "smithy.api#httpQuery": "mode" + } + }, + "failOnWarnings": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A query parameter to specify whether to rollback the documentation importation (true) or not (false) when a warning is encountered. The default value is false.

", + "smithy.api#httpQuery": "failonwarnings" + } + }, + "body": { + "target": "com.amazonaws.apigateway#Blob", + "traits": { + "smithy.api#documentation": "

Raw byte array representing the to-be-imported documentation parts. To import from an OpenAPI file, this is a JSON object.

", + "smithy.api#httpPayload": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Import documentation parts from an external (e.g., OpenAPI) definition file.

" + } + }, + "com.amazonaws.apigateway#ImportRestApi": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#ImportRestApiRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#RestApi" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

A feature of the API Gateway control service for creating a new API from an external API definition file.

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis?mode=import", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#ImportRestApiRequest": { + "type": "structure", + "members": { + "failOnWarnings": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A query parameter to indicate whether to rollback the API creation (true) or not (false)\n when a warning is encountered. The default value is false.

", + "smithy.api#httpQuery": "failonwarnings" + } + }, + "parameters": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map of context-specific query string parameters specifying the behavior of different API importing operations. The following shows operation-specific parameters and their supported values.

\n

To exclude DocumentationParts from the import, set parameters as ignore=documentation.

\n

To configure the endpoint type, set parameters as endpointConfigurationTypes=EDGE, endpointConfigurationTypes=REGIONAL, or endpointConfigurationTypes=PRIVATE. The default endpoint type is EDGE.

\n

To handle imported basepath, set parameters as basepath=ignore, basepath=prepend or basepath=split.

\n

For example, the AWS CLI command to exclude documentation from the imported API is:

\n

The AWS CLI command to set the regional endpoint on the imported API is:

", + "smithy.api#httpQueryParams": {} + } + }, + "body": { + "target": "com.amazonaws.apigateway#Blob", + "traits": { + "smithy.api#documentation": "

The POST request body containing external API definitions. Currently, only OpenAPI definition JSON/YAML files are supported. The maximum size of the API definition file is 6MB.

", + "smithy.api#httpPayload": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A POST request to import an API to API Gateway using an input of an API definition file.

" + } + }, + "com.amazonaws.apigateway#Integer": { + "type": "integer", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.apigateway#Integration": { + "type": "structure", + "members": { + "type": { + "target": "com.amazonaws.apigateway#IntegrationType", + "traits": { + "smithy.api#documentation": "

Specifies an API method integration type. The valid value is one of the following:

\n

For the HTTP and HTTP proxy integrations, each integration can specify a protocol (http/https), port and path. Standard 80 and 443 ports are supported as well as custom ports above 1024. An HTTP or HTTP proxy integration with a connectionType of VPC_LINK is referred to as a private integration and uses a VpcLink to connect API Gateway to a network load balancer of a VPC.

" + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the integration's HTTP method type.

" + } + }, + "uri": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies Uniform Resource Identifier (URI) of the integration endpoint.

\n

For HTTP or HTTP_PROXY integrations, the URI must be a fully formed, encoded HTTP(S) URL\n according to the RFC-3986 specification, for either standard integration, where connectionType\n is not VPC_LINK, or private integration, where connectionType is VPC_LINK. For a private HTTP\n integration, the URI is not used for routing. For AWS or AWS_PROXY integrations, the URI is of\n the form arn:aws:apigateway:{region}:{subdomain.service|service}:path|action/{service_api}.\n Here, {Region} is the API Gateway region (e.g., us-east-1); {service} is the name of the\n integrated Amazon Web Services service (e.g., s3); and {subdomain} is a designated subdomain supported by\n certain Amazon Web Services service for fast host-name lookup. action can be used for an Amazon Web Services service\n action-based API, using an Action={name}&{p1}={v1}&p2={v2}... query string. The ensuing\n {service_api} refers to a supported action {name} plus any required input parameters.\n Alternatively, path can be used for an AWS service path-based API. The ensuing service_api\n refers to the path to an Amazon Web Services service resource, including the region of the integrated Amazon Web Services \n service, if applicable. For example, for integration with the S3 API of GetObject, the uri can\n be either arn:aws:apigateway:us-west-2:s3:action/GetObject&Bucket={bucket}&Key={key} or\n arn:aws:apigateway:us-west-2:s3:path/{bucket}/{key}\n

" + } + }, + "connectionType": { + "target": "com.amazonaws.apigateway#ConnectionType", + "traits": { + "smithy.api#documentation": "

The type of the network connection to the integration endpoint. The valid value is INTERNET for connections through the public routable internet or VPC_LINK for private connections between API Gateway and a network load balancer in a VPC. The default value is INTERNET.

" + } + }, + "connectionId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ID of the VpcLink used for the integration when connectionType=VPC_LINK and undefined, otherwise.

" + } + }, + "credentials": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the credentials required for the integration, if any. For AWS integrations, three options are available. To specify an IAM Role for API Gateway to assume, use the role's Amazon Resource Name (ARN). To require that the caller's identity be passed through from the request, specify the string arn:aws:iam::\\*:user/\\*. To use resource-based permissions on supported AWS services, specify null.

" + } + }, + "requestParameters": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map specifying request parameters that are passed from the method request to the back end. The key is an integration request parameter name and the associated value is a method request parameter value or static value that must be enclosed within single quotes and pre-encoded as required by the back end. The method request parameter value must match the pattern of method.request.{location}.{name}, where location is querystring, path, or header and name must be a valid and unique method request parameter name.

" + } + }, + "requestTemplates": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Represents a map of Velocity templates that are applied on the request payload based on the value of the Content-Type header sent by the client. The content type value is the key in this map, and the template (as a String) is the value.

" + } + }, + "passthroughBehavior": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies how the method request body of an unmapped content type will be passed through\n the integration request to the back end without transformation. A content type is unmapped if\n no mapping template is defined in the integration or the content type does not match any of\n the mapped content types, as specified in requestTemplates. The valid value is one of the\n following: WHEN_NO_MATCH: passes the method request body through the integration request to\n the back end without transformation when the method request content type does not match any\n content type associated with the mapping templates defined in the integration request.\n WHEN_NO_TEMPLATES: passes the method request body through the integration request to the back\n end without transformation when no mapping template is defined in the integration request. If\n a template is defined when this option is selected, the method request of an unmapped\n content-type will be rejected with an HTTP 415 Unsupported Media Type response. NEVER: rejects\n the method request with an HTTP 415 Unsupported Media Type response when either the method\n request content type does not match any content type associated with the mapping templates\n defined in the integration request or no mapping template is defined in the integration\n request.

" + } + }, + "contentHandling": { + "target": "com.amazonaws.apigateway#ContentHandlingStrategy", + "traits": { + "smithy.api#documentation": "

Specifies how to handle request payload content type conversions. Supported values are CONVERT_TO_BINARY and CONVERT_TO_TEXT, with the following behaviors:

\n

If this property is not defined, the request payload will be passed through from the method request to integration request without modification, provided that the passthroughBehavior is configured to support payload pass-through.

" + } + }, + "timeoutInMillis": { + "target": "com.amazonaws.apigateway#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Custom timeout between 50 and 29,000 milliseconds. The default value is 29,000 milliseconds or 29 seconds.

" + } + }, + "cacheNamespace": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a group of related cached parameters. By default, API Gateway uses the resource ID as the cacheNamespace. You can specify the same cacheNamespace across resources to return the same cached data for requests to different resources.

" + } + }, + "cacheKeyParameters": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of request parameters whose values API Gateway caches. To be valid values for cacheKeyParameters, these parameters must also be specified for Method requestParameters.

" + } + }, + "integrationResponses": { + "target": "com.amazonaws.apigateway#MapOfIntegrationResponse", + "traits": { + "smithy.api#documentation": "

Specifies the integration's responses.

" + } + }, + "tlsConfig": { + "target": "com.amazonaws.apigateway#TlsConfig", + "traits": { + "smithy.api#documentation": "

Specifies the TLS configuration for an integration.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents an HTTP, HTTP_PROXY, AWS, AWS_PROXY, or Mock integration.

" + } + }, + "com.amazonaws.apigateway#IntegrationResponse": { + "type": "structure", + "members": { + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

Specifies the status code that is used to map the integration response to an existing MethodResponse.

" + } + }, + "selectionPattern": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the regular expression (regex) pattern used to choose an integration response based on the response from the back end. For example, if the success response returns nothing and the error response returns some string, you could use the .+ regex to match error response. However, make sure that the error response does not contain any newline (\\n) character in such cases. If the back end is an AWS Lambda function, the AWS Lambda function error header is matched. For all other HTTP and AWS back ends, the HTTP status code is matched.

" + } + }, + "responseParameters": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map specifying response parameters that are passed to the method response from the back end.\n The key is a method response header parameter name and the mapped value is an integration response header value, a static value enclosed within a pair of single quotes, or a JSON expression from the integration response body. The mapping key must match the pattern of method.response.header.{name}, where name is a valid and unique header name. The mapped non-static value must match the pattern of integration.response.header.{name} or integration.response.body.{JSON-expression}, where name is a valid and unique response header name and JSON-expression is a valid JSON expression without the $ prefix.

" + } + }, + "responseTemplates": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Specifies the templates used to transform the integration response body. Response templates are represented as a key/value map, with a content-type as the key and a template as the value.

" + } + }, + "contentHandling": { + "target": "com.amazonaws.apigateway#ContentHandlingStrategy", + "traits": { + "smithy.api#documentation": "

Specifies how to handle response payload content type conversions. Supported values are CONVERT_TO_BINARY and CONVERT_TO_TEXT, with the following behaviors:

\n

If this property is not defined, the response payload will be passed through from the integration response to the method response without modification.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents an integration response. The status code must map to an existing MethodResponse, and parameters and templates can be used to transform the back-end response.

" + } + }, + "com.amazonaws.apigateway#IntegrationType": { + "type": "enum", + "members": { + "HTTP": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "HTTP" + } + }, + "AWS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS" + } + }, + "MOCK": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MOCK" + } + }, + "HTTP_PROXY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "HTTP_PROXY" + } + }, + "AWS_PROXY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS_PROXY" + } + } + }, + "traits": { + "smithy.api#documentation": "

The integration type. The valid value is HTTP for integrating an API method with an HTTP backend; AWS with any AWS service endpoints; MOCK for testing without actually invoking the backend; HTTP_PROXY for integrating with the HTTP proxy integration; AWS_PROXY for integrating with the Lambda proxy integration.

" + } + }, + "com.amazonaws.apigateway#LimitExceededException": { + "type": "structure", + "members": { + "retryAfterSeconds": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#httpHeader": "Retry-After" + } + }, + "message": { + "target": "com.amazonaws.apigateway#String" + } + }, + "traits": { + "smithy.api#documentation": "

The request exceeded the rate limit. Retry after the specified time period.

", + "smithy.api#error": "client", + "smithy.api#httpError": 429 + } + }, + "com.amazonaws.apigateway#ListOfARNs": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#ProviderARN" + } + }, + "com.amazonaws.apigateway#ListOfApiKey": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#ApiKey" + } + }, + "com.amazonaws.apigateway#ListOfApiStage": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#ApiStage" + } + }, + "com.amazonaws.apigateway#ListOfAuthorizer": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#Authorizer" + } + }, + "com.amazonaws.apigateway#ListOfBasePathMapping": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#BasePathMapping" + } + }, + "com.amazonaws.apigateway#ListOfClientCertificate": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#ClientCertificate" + } + }, + "com.amazonaws.apigateway#ListOfDeployment": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#Deployment" + } + }, + "com.amazonaws.apigateway#ListOfDocumentationPart": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#DocumentationPart" + } + }, + "com.amazonaws.apigateway#ListOfDocumentationVersion": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#DocumentationVersion" + } + }, + "com.amazonaws.apigateway#ListOfDomainName": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#DomainName" + } + }, + "com.amazonaws.apigateway#ListOfEndpointType": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#EndpointType" + } + }, + "com.amazonaws.apigateway#ListOfGatewayResponse": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#GatewayResponse" + } + }, + "com.amazonaws.apigateway#ListOfLong": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#Long" + } + }, + "com.amazonaws.apigateway#ListOfModel": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#Model" + } + }, + "com.amazonaws.apigateway#ListOfPatchOperation": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#PatchOperation" + }, + "traits": { + "smithy.api#documentation": "

A list of operations describing the updates to apply to the specified resource. The patches are applied\n in the order specified in the list.

" + } + }, + "com.amazonaws.apigateway#ListOfRequestValidator": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#RequestValidator" + } + }, + "com.amazonaws.apigateway#ListOfResource": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#Resource" + } + }, + "com.amazonaws.apigateway#ListOfRestApi": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#RestApi" + } + }, + "com.amazonaws.apigateway#ListOfSdkConfigurationProperty": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#SdkConfigurationProperty" + } + }, + "com.amazonaws.apigateway#ListOfSdkType": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#SdkType" + } + }, + "com.amazonaws.apigateway#ListOfStage": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#Stage" + } + }, + "com.amazonaws.apigateway#ListOfStageKeys": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#StageKey" + } + }, + "com.amazonaws.apigateway#ListOfString": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#String" + } + }, + "com.amazonaws.apigateway#ListOfUsage": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#ListOfLong" + } + }, + "com.amazonaws.apigateway#ListOfUsagePlan": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#UsagePlan" + } + }, + "com.amazonaws.apigateway#ListOfUsagePlanKey": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#UsagePlanKey" + } + }, + "com.amazonaws.apigateway#ListOfVpcLink": { + "type": "list", + "member": { + "target": "com.amazonaws.apigateway#VpcLink" + } + }, + "com.amazonaws.apigateway#LocationStatusType": { + "type": "enum", + "members": { + "DOCUMENTED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DOCUMENTED" + } + }, + "UNDOCUMENTED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "UNDOCUMENTED" + } + } + } + }, + "com.amazonaws.apigateway#Long": { + "type": "long", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.apigateway#MapOfApiStageThrottleSettings": { + "type": "map", + "key": { + "target": "com.amazonaws.apigateway#String" + }, + "value": { + "target": "com.amazonaws.apigateway#ThrottleSettings" + } + }, + "com.amazonaws.apigateway#MapOfIntegrationResponse": { + "type": "map", + "key": { + "target": "com.amazonaws.apigateway#String" + }, + "value": { + "target": "com.amazonaws.apigateway#IntegrationResponse" + } + }, + "com.amazonaws.apigateway#MapOfKeyUsages": { + "type": "map", + "key": { + "target": "com.amazonaws.apigateway#String" + }, + "value": { + "target": "com.amazonaws.apigateway#ListOfUsage" + } + }, + "com.amazonaws.apigateway#MapOfMethod": { + "type": "map", + "key": { + "target": "com.amazonaws.apigateway#String" + }, + "value": { + "target": "com.amazonaws.apigateway#Method" + } + }, + "com.amazonaws.apigateway#MapOfMethodResponse": { + "type": "map", + "key": { + "target": "com.amazonaws.apigateway#String" + }, + "value": { + "target": "com.amazonaws.apigateway#MethodResponse" + } + }, + "com.amazonaws.apigateway#MapOfMethodSettings": { + "type": "map", + "key": { + "target": "com.amazonaws.apigateway#String" + }, + "value": { + "target": "com.amazonaws.apigateway#MethodSetting" + } + }, + "com.amazonaws.apigateway#MapOfMethodSnapshot": { + "type": "map", + "key": { + "target": "com.amazonaws.apigateway#String" + }, + "value": { + "target": "com.amazonaws.apigateway#MethodSnapshot" + } + }, + "com.amazonaws.apigateway#MapOfStringToBoolean": { + "type": "map", + "key": { + "target": "com.amazonaws.apigateway#String" + }, + "value": { + "target": "com.amazonaws.apigateway#NullableBoolean" + } + }, + "com.amazonaws.apigateway#MapOfStringToList": { + "type": "map", + "key": { + "target": "com.amazonaws.apigateway#String" + }, + "value": { + "target": "com.amazonaws.apigateway#ListOfString" + } + }, + "com.amazonaws.apigateway#MapOfStringToString": { + "type": "map", + "key": { + "target": "com.amazonaws.apigateway#String" + }, + "value": { + "target": "com.amazonaws.apigateway#String" + } + }, + "com.amazonaws.apigateway#Method": { + "type": "structure", + "members": { + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The method's HTTP verb.

" + } + }, + "authorizationType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The method's authorization type. Valid values are NONE for open access, AWS_IAM for using AWS IAM permissions, CUSTOM for using a custom authorizer, or COGNITO_USER_POOLS for using a Cognito user pool.

" + } + }, + "authorizerId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of an Authorizer to use on this method. The authorizationType must be CUSTOM.

" + } + }, + "apiKeyRequired": { + "target": "com.amazonaws.apigateway#NullableBoolean", + "traits": { + "smithy.api#documentation": "

A boolean flag specifying whether a valid ApiKey is required to invoke this method.

" + } + }, + "requestValidatorId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of a RequestValidator for request validation.

" + } + }, + "operationName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A human-friendly operation identifier for the method. For example, you can assign the operationName of ListPets for the GET /pets method in the PetStore example.

" + } + }, + "requestParameters": { + "target": "com.amazonaws.apigateway#MapOfStringToBoolean", + "traits": { + "smithy.api#documentation": "

A key-value map defining required or optional method request parameters that can be accepted by API Gateway. A key is a method request parameter name matching the pattern of method.request.{location}.{name}, where location is querystring, path, or header and name is a valid and unique parameter name. The value associated with the key is a Boolean flag indicating whether the parameter is required (true) or optional (false). The method request parameter names defined here are available in Integration to be mapped to integration request parameters or templates.

" + } + }, + "requestModels": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map specifying data schemas, represented by Model resources, (as the mapped value) of the request payloads of given content types (as the mapping key).

" + } + }, + "methodResponses": { + "target": "com.amazonaws.apigateway#MapOfMethodResponse", + "traits": { + "smithy.api#documentation": "

Gets a method response associated with a given HTTP status code.

" + } + }, + "methodIntegration": { + "target": "com.amazonaws.apigateway#Integration", + "traits": { + "smithy.api#documentation": "

Gets the method's integration responsible for passing the client-submitted request to the back end and performing necessary transformations to make the request compliant with the back end.

" + } + }, + "authorizationScopes": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of authorization scopes configured on the method. The scopes are used with a COGNITO_USER_POOLS authorizer to authorize the method invocation. The authorization works by matching the method scopes against the scopes parsed from the access token in the incoming request. The method invocation is authorized if any method scopes matches a claimed scope in the access token. Otherwise, the invocation is not authorized. When the method scope is configured, the client must provide an access token instead of an identity token for authorization purposes.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

\n Represents a client-facing interface by which the client calls the API to access back-end resources. A Method resource is\n integrated with an Integration resource. Both consist of a request and one or more responses. The method request takes\n the client input that is passed to the back end through the integration request. A method response returns the output from\n the back end to the client through an integration response. A method request is embodied in a Method resource, whereas\n an integration request is embodied in an Integration resource. On the other hand, a method response is represented\n by a MethodResponse resource, whereas an integration response is represented by an IntegrationResponse resource.\n

" + } + }, + "com.amazonaws.apigateway#MethodResponse": { + "type": "structure", + "members": { + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

The method response's status code.

" + } + }, + "responseParameters": { + "target": "com.amazonaws.apigateway#MapOfStringToBoolean", + "traits": { + "smithy.api#documentation": "

A key-value map specifying required or optional response parameters that API Gateway can send back to the caller. A key defines a method response header and the value specifies whether the associated method response header is required or not. The expression of the key must match the pattern method.response.header.{name}, where name is a valid and unique header name. API Gateway passes certain integration response data to the method response headers specified here according to the mapping you prescribe in the API's IntegrationResponse. The integration response data that can be mapped include an integration response header expressed in integration.response.header.{name}, a static value enclosed within a pair of single quotes (e.g., 'application/json'), or a JSON expression from the back-end response payload in the form of integration.response.body.{JSON-expression}, where JSON-expression is a valid JSON expression without the $ prefix.)

" + } + }, + "responseModels": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Specifies the Model resources used for the response's content-type. Response models are represented as a key/value map, with a content-type as the key and a Model name as the value.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a method response of a given HTTP status code returned to the client. The method response is passed from the back end through the associated integration response that can be transformed using a mapping template.

" + } + }, + "com.amazonaws.apigateway#MethodSetting": { + "type": "structure", + "members": { + "metricsEnabled": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether Amazon CloudWatch metrics are enabled for this method. The PATCH path for this setting is /{method_setting_key}/metrics/enabled, and the value is a Boolean.

" + } + }, + "loggingLevel": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the logging level for this method, which affects the log entries pushed to Amazon CloudWatch Logs. The PATCH path for this setting is /{method_setting_key}/logging/loglevel, and the available levels are OFF, ERROR, and INFO. Choose ERROR to write only error-level entries to CloudWatch Logs, or choose INFO to include all ERROR events as well as extra informational events.

" + } + }, + "dataTraceEnabled": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether data trace logging is enabled for this method, which affects the log entries pushed to Amazon CloudWatch Logs. The PATCH path for this setting is /{method_setting_key}/logging/dataTrace, and the value is a Boolean.

" + } + }, + "throttlingBurstLimit": { + "target": "com.amazonaws.apigateway#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Specifies the throttling burst limit. The PATCH path for this setting is /{method_setting_key}/throttling/burstLimit, and the value is an integer.

" + } + }, + "throttlingRateLimit": { + "target": "com.amazonaws.apigateway#Double", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Specifies the throttling rate limit. The PATCH path for this setting is /{method_setting_key}/throttling/rateLimit, and the value is a double.

" + } + }, + "cachingEnabled": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether responses should be cached and returned for requests. A cache cluster must be enabled on the stage for responses to be cached. The PATCH path for this setting is /{method_setting_key}/caching/enabled, and the value is a Boolean.

" + } + }, + "cacheTtlInSeconds": { + "target": "com.amazonaws.apigateway#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Specifies the time to live (TTL), in seconds, for cached responses. The higher the TTL, the longer the response will be cached. The PATCH path for this setting is /{method_setting_key}/caching/ttlInSeconds, and the value is an integer.

" + } + }, + "cacheDataEncrypted": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether the cached responses are encrypted. The PATCH path for this setting is /{method_setting_key}/caching/dataEncrypted, and the value is a Boolean.

" + } + }, + "requireAuthorizationForCacheControl": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether authorization is required for a cache invalidation request. The PATCH path for this setting is /{method_setting_key}/caching/requireAuthorizationForCacheControl, and the value is a Boolean.

" + } + }, + "unauthorizedCacheControlHeaderStrategy": { + "target": "com.amazonaws.apigateway#UnauthorizedCacheControlHeaderStrategy", + "traits": { + "smithy.api#documentation": "

Specifies how to handle unauthorized requests for cache invalidation. The PATCH path for this setting is /{method_setting_key}/caching/unauthorizedCacheControlHeaderStrategy, and the available values are FAIL_WITH_403, SUCCEED_WITH_RESPONSE_HEADER, SUCCEED_WITHOUT_RESPONSE_HEADER.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies the method setting properties.

" + } + }, + "com.amazonaws.apigateway#MethodSnapshot": { + "type": "structure", + "members": { + "authorizationType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The method's authorization type. Valid values are NONE for open access, AWS_IAM for using AWS IAM permissions, CUSTOM for using a custom authorizer, or COGNITO_USER_POOLS for using a Cognito user pool.

" + } + }, + "apiKeyRequired": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether the method requires a valid ApiKey.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a summary of a Method resource, given a particular date and time.

" + } + }, + "com.amazonaws.apigateway#Model": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier for the model resource.

" + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the model. Must be an alphanumeric string.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the model.

" + } + }, + "schema": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The schema for the model. For application/json models, this should be JSON schema draft 4 model. Do not include \"\\*/\" characters in the description of any properties because such \"\\*/\" characters may be interpreted as the closing marker for comments in some languages, such as Java or JavaScript, causing the installation of your API's SDK generated by API Gateway to fail.

" + } + }, + "contentType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The content-type for the model.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents the data structure of a method's request or response payload.

" + } + }, + "com.amazonaws.apigateway#Models": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfModel", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a collection of Model resources.

" + } + }, + "com.amazonaws.apigateway#MutualTlsAuthentication": { + "type": "structure", + "members": { + "truststoreUri": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

An Amazon S3 URL that specifies the truststore for mutual TLS authentication, for example\n s3://bucket-name/key-name. The truststore can contain certificates from public or private\n certificate authorities. To update the truststore, upload a new version to S3, and then update\n your custom domain name to use the new version. To update the truststore, you must have\n permissions to access the S3 object.

" + } + }, + "truststoreVersion": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The version of the S3 object that contains your truststore. To specify a version, you must have versioning enabled for the S3 bucket.

" + } + }, + "truststoreWarnings": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of warnings that API Gateway returns while processing your truststore. Invalid\n certificates produce warnings. Mutual TLS is still enabled, but some clients might not be able\n to access your API. To resolve warnings, upload a new truststore to S3, and then update you\n domain name to use the new version.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The mutual TLS authentication configuration for a custom domain name. If specified, API Gateway\n performs two-way authentication between the client and the server. Clients must present a\n trusted certificate to access your API.

" + } + }, + "com.amazonaws.apigateway#MutualTlsAuthenticationInput": { + "type": "structure", + "members": { + "truststoreUri": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

An Amazon S3 URL that specifies the truststore for mutual TLS authentication, for example\n s3://bucket-name/key-name. The truststore can contain certificates from public or private\n certificate authorities. To update the truststore, upload a new version to S3, and then update\n your custom domain name to use the new version. To update the truststore, you must have\n permissions to access the S3 object.

" + } + }, + "truststoreVersion": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The version of the S3 object that contains your truststore. To specify a version, you must have versioning enabled for the S3 bucket

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The mutual TLS authentication configuration for a custom domain name. If specified, API Gateway\n performs two-way authentication between the client and the server. Clients must present a\n trusted certificate to access your API.

" + } + }, + "com.amazonaws.apigateway#NotFoundException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.apigateway#String" + } + }, + "traits": { + "smithy.api#documentation": "

The requested resource is not found. Make sure that the request URI is correct.

", + "smithy.api#error": "client", + "smithy.api#httpError": 404 + } + }, + "com.amazonaws.apigateway#NullableBoolean": { + "type": "boolean" + }, + "com.amazonaws.apigateway#NullableInteger": { + "type": "integer" + }, + "com.amazonaws.apigateway#Op": { + "type": "enum", + "members": { + "add": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "add" + } + }, + "remove": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "remove" + } + }, + "replace": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "replace" + } + }, + "move": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "move" + } + }, + "copy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "copy" + } + }, + "test": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "test" + } + } + } + }, + "com.amazonaws.apigateway#PatchOperation": { + "type": "structure", + "members": { + "op": { + "target": "com.amazonaws.apigateway#Op", + "traits": { + "smithy.api#documentation": "

An update operation to be performed with this PATCH request. The valid value can be\n add, remove, replace or copy. Not all valid operations are supported for a given\n resource. Support of the operations depends on specific operational contexts. Attempts\n to apply an unsupported operation on a resource will return an error message..

" + } + }, + "path": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The op operation's target, as identified by a JSON Pointer value that references a\n location within the targeted resource. For example, if the target resource has an\n updateable property of {\"name\":\"value\"}, the path for this property is /name. If the\n name property value is a JSON object (e.g., {\"name\": {\"child/name\": \"child-value\"}}),\n the path for the child/name property will be /name/child~1name. Any slash (\"/\")\n character appearing in path names must be escaped with \"~1\", as shown in the example\n above. Each op operation can have only one path associated with it.

" + } + }, + "value": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The new target value of the update operation. It is applicable for the add or replace\n operation. When using AWS CLI to update a property of a JSON value, enclose the JSON\n object with a pair of single quotes in a Linux shell, e.g., '{\"a\": ...}'.

" + } + }, + "from": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The copy update operation's source as identified by a JSON-Pointer value referencing\n the location within the targeted resource to copy the value from. For example, to\n promote a canary deployment, you copy the canary deployment ID to the affiliated\n deployment ID by calling a PATCH request on a Stage resource with \"op\":\"copy\",\n \"from\":\"/canarySettings/deploymentId\" and \"path\":\"/deploymentId\".

" + } + } + }, + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + }, + "com.amazonaws.apigateway#PathToMapOfMethodSnapshot": { + "type": "map", + "key": { + "target": "com.amazonaws.apigateway#String" + }, + "value": { + "target": "com.amazonaws.apigateway#MapOfMethodSnapshot" + } + }, + "com.amazonaws.apigateway#ProviderARN": { + "type": "string" + }, + "com.amazonaws.apigateway#PutGatewayResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#PutGatewayResponseRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#GatewayResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Creates a customization of a GatewayResponse of a specified response type and status code on the given RestApi.

", + "smithy.api#http": { + "method": "PUT", + "uri": "/restapis/{restApiId}/gatewayresponses/{responseType}", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#PutGatewayResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "responseType": { + "target": "com.amazonaws.apigateway#GatewayResponseType", + "traits": { + "smithy.api#documentation": "

The response type of the associated GatewayResponse

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

The HTTP status code of the GatewayResponse.

" + } + }, + "responseParameters": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Response parameters (paths, query strings and headers) of the GatewayResponse as a string-to-string map of key-value pairs.

" + } + }, + "responseTemplates": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Response templates of the GatewayResponse as a string-to-string map of key-value pairs.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Creates a customization of a GatewayResponse of a specified response type and status code on the given RestApi.

" + } + }, + "com.amazonaws.apigateway#PutIntegration": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#PutIntegrationRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Integration" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Sets up a method's integration.

", + "smithy.api#http": { + "method": "PUT", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#PutIntegrationRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a put integration request's resource ID.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the HTTP method for the integration.

", + "smithy.api#httpLabel": {}, + "smithy.api#jsonName": "requestHttpMethod", + "smithy.api#required": {} + } + }, + "type": { + "target": "com.amazonaws.apigateway#IntegrationType", + "traits": { + "smithy.api#documentation": "

Specifies a put integration input's type.

", + "smithy.api#required": {} + } + }, + "integrationHttpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The HTTP method for the integration.

", + "smithy.api#jsonName": "httpMethod" + } + }, + "uri": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies Uniform Resource Identifier (URI) of the integration endpoint. For HTTP or\n HTTP_PROXY integrations, the URI must be a fully formed, encoded HTTP(S) URL according to the\n RFC-3986 specification, for either standard integration, where connectionType is not VPC_LINK,\n or private integration, where connectionType is VPC_LINK. For a private HTTP integration, the\n URI is not used for routing. For AWS or AWS_PROXY integrations, the URI is of the form\n arn:aws:apigateway:{region}:{subdomain.service|service}:path|action/{service_api}. Here,\n {Region} is the API Gateway region (e.g., us-east-1); {service} is the name of the integrated\n Amazon Web Services service (e.g., s3); and {subdomain} is a designated subdomain supported by certain Amazon Web Services\n service for fast host-name lookup. action can be used for an Amazon Web Services service action-based API,\n using an Action={name}&{p1}={v1}&p2={v2}... query string. The ensuing {service_api} refers to\n a supported action {name} plus any required input parameters. Alternatively, path can be used\n for an Amazon Web Services service path-based API. The ensuing service_api refers to the path to an Amazon Web Services\n service resource, including the region of the integrated Amazon Web Services service, if applicable. For\n example, for integration with the S3 API of GetObject, the uri can be either\n arn:aws:apigateway:us-west-2:s3:action/GetObject&Bucket={bucket}&Key={key} or\n arn:aws:apigateway:us-west-2:s3:path/{bucket}/{key}.

" + } + }, + "connectionType": { + "target": "com.amazonaws.apigateway#ConnectionType", + "traits": { + "smithy.api#documentation": "

The type of the network connection to the integration endpoint. The valid value is INTERNET for connections through the public routable internet or VPC_LINK for private connections between API Gateway and a network load balancer in a VPC. The default value is INTERNET.

" + } + }, + "connectionId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ID of the VpcLink used for the integration. Specify this value only if you specify VPC_LINK as the connection type.

" + } + }, + "credentials": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies whether credentials are required for a put integration.

" + } + }, + "requestParameters": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map specifying request parameters that are passed from the method request to the back end. The key is an integration request parameter name and the associated value is a method request parameter value or static value that must be enclosed within single quotes and pre-encoded as required by the back end. The method request parameter value must match the pattern of method.request.{location}.{name}, where location is querystring, path, or header and name must be a valid and unique method request parameter name.

" + } + }, + "requestTemplates": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Represents a map of Velocity templates that are applied on the request payload based on the value of the Content-Type header sent by the client. The content type value is the key in this map, and the template (as a String) is the value.

" + } + }, + "passthroughBehavior": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the pass-through behavior for incoming requests based on the Content-Type header in the request, and the available mapping templates specified as the requestTemplates property on the Integration resource. There are three valid values: WHEN_NO_MATCH, WHEN_NO_TEMPLATES, and NEVER.\n

" + } + }, + "cacheNamespace": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a group of related cached parameters. By default, API Gateway uses the resource ID as the cacheNamespace. You can specify the same cacheNamespace across resources to return the same cached data for requests to different resources.

" + } + }, + "cacheKeyParameters": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of request parameters whose values API Gateway caches. To be valid values for cacheKeyParameters, these parameters must also be specified for Method requestParameters.

" + } + }, + "contentHandling": { + "target": "com.amazonaws.apigateway#ContentHandlingStrategy", + "traits": { + "smithy.api#documentation": "

Specifies how to handle request payload content type conversions. Supported values are CONVERT_TO_BINARY and CONVERT_TO_TEXT, with the following behaviors:

\n

If this property is not defined, the request payload will be passed through from the method request to integration request without modification, provided that the passthroughBehavior is configured to support payload pass-through.

" + } + }, + "timeoutInMillis": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

Custom timeout between 50 and 29,000 milliseconds. The default value is 29,000 milliseconds or 29 seconds.

" + } + }, + "tlsConfig": { + "target": "com.amazonaws.apigateway#TlsConfig" + } + }, + "traits": { + "smithy.api#documentation": "

Sets up a method's integration.

" + } + }, + "com.amazonaws.apigateway#PutIntegrationResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#PutIntegrationResponseRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#IntegrationResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Represents a put integration.

", + "smithy.api#http": { + "method": "PUT", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration/responses/{statusCode}", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#PutIntegrationResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a put integration response request's resource identifier.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a put integration response request's HTTP method.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

Specifies the status code that is used to map the integration response to an existing MethodResponse.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "selectionPattern": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the selection pattern of a put integration response.

" + } + }, + "responseParameters": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map specifying response parameters that are passed to the method response from the back end.\n The key is a method response header parameter name and the mapped value is an integration response header value, a static value enclosed within a pair of single quotes, or a JSON expression from the integration response body. The mapping key must match the pattern of method.response.header.{name}, where name is a valid and unique header name. The mapped non-static value must match the pattern of integration.response.header.{name} or integration.response.body.{JSON-expression}, where name must be a valid and unique response header name and JSON-expression a valid JSON expression without the $ prefix.

" + } + }, + "responseTemplates": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Specifies a put integration response's templates.

" + } + }, + "contentHandling": { + "target": "com.amazonaws.apigateway#ContentHandlingStrategy", + "traits": { + "smithy.api#documentation": "

Specifies how to handle response payload content type conversions. Supported values are CONVERT_TO_BINARY and CONVERT_TO_TEXT, with the following behaviors:

\n

If this property is not defined, the response payload will be passed through from the integration response to the method response without modification.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a put integration response request.

" + } + }, + "com.amazonaws.apigateway#PutMethod": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#PutMethodRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Method" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Add a method to an existing Resource resource.

", + "smithy.api#http": { + "method": "PUT", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#PutMethodRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Resource identifier for the new Method resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the method request's HTTP method type.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "authorizationType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The method's authorization type. Valid values are NONE for open access, AWS_IAM for using AWS IAM permissions, CUSTOM for using a custom authorizer, or COGNITO_USER_POOLS for using a Cognito user pool.

", + "smithy.api#required": {} + } + }, + "authorizerId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies the identifier of an Authorizer to use on this Method, if the type is CUSTOM or COGNITO_USER_POOLS. The authorizer identifier is generated by API Gateway when you created the authorizer.

" + } + }, + "apiKeyRequired": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether the method required a valid ApiKey.

" + } + }, + "operationName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A human-friendly operation identifier for the method. For example, you can assign the operationName of ListPets for the GET /pets method in the PetStore example.

" + } + }, + "requestParameters": { + "target": "com.amazonaws.apigateway#MapOfStringToBoolean", + "traits": { + "smithy.api#documentation": "

A key-value map defining required or optional method request parameters that can be accepted by API Gateway. A key defines a method request parameter name matching the pattern of method.request.{location}.{name}, where location is querystring, path, or header and name is a valid and unique parameter name. The value associated with the key is a Boolean flag indicating whether the parameter is required (true) or optional (false). The method request parameter names defined here are available in Integration to be mapped to integration request parameters or body-mapping templates.

" + } + }, + "requestModels": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Specifies the Model resources used for the request's content type. Request models are represented as a key/value map, with a content type as the key and a Model name as the value.

" + } + }, + "requestValidatorId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of a RequestValidator for validating the method request.

" + } + }, + "authorizationScopes": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

A list of authorization scopes configured on the method. The scopes are used with a COGNITO_USER_POOLS authorizer to authorize the method invocation. The authorization works by matching the method scopes against the scopes parsed from the access token in the incoming request. The method invocation is authorized if any method scopes matches a claimed scope in the access token. Otherwise, the invocation is not authorized. When the method scope is configured, the client must provide an access token instead of an identity token for authorization purposes.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to add a method to an existing Resource resource.

" + } + }, + "com.amazonaws.apigateway#PutMethodResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#PutMethodResponseRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#MethodResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Adds a MethodResponse to an existing Method resource.

", + "smithy.api#http": { + "method": "PUT", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/responses/{statusCode}", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#PutMethodResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Resource identifier for the Method resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The HTTP verb of the Method resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

The method response's status code.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "responseParameters": { + "target": "com.amazonaws.apigateway#MapOfStringToBoolean", + "traits": { + "smithy.api#documentation": "

A key-value map specifying required or optional response parameters that API Gateway can send back to the caller. A key defines a method response header name and the associated value is a Boolean flag indicating whether the method response parameter is required or not. The method response header names must match the pattern of method.response.header.{name}, where name is a valid and unique header name. The response parameter names defined here are available in the integration response to be mapped from an integration response header expressed in integration.response.header.{name}, a static value enclosed within a pair of single quotes (e.g., 'application/json'), or a JSON expression from the back-end response payload in the form of integration.response.body.{JSON-expression}, where JSON-expression is a valid JSON expression without the $ prefix.)

" + } + }, + "responseModels": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Specifies the Model resources used for the response's content type. Response models are represented as a key/value map, with a content type as the key and a Model name as the value.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to add a MethodResponse to an existing Method resource.

" + } + }, + "com.amazonaws.apigateway#PutMode": { + "type": "enum", + "members": { + "Merge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "merge" + } + }, + "Overwrite": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "overwrite" + } + } + } + }, + "com.amazonaws.apigateway#PutRestApi": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#PutRestApiRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#RestApi" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

A feature of the API Gateway control service for updating an existing API with an input of external API definitions.\n The update can take the form of merging the supplied definition into the existing API or overwriting the existing API.

", + "smithy.api#http": { + "method": "PUT", + "uri": "/restapis/{restApiId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#PutRestApiRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "mode": { + "target": "com.amazonaws.apigateway#PutMode", + "traits": { + "smithy.api#documentation": "

The mode query parameter to specify the update mode. Valid values are \"merge\" and \"overwrite\". By default,\n the update mode is \"merge\".

", + "smithy.api#httpQuery": "mode" + } + }, + "failOnWarnings": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A query parameter to indicate whether to rollback the API update (true) or not (false)\n when a warning is encountered. The default value is false.

", + "smithy.api#httpQuery": "failonwarnings" + } + }, + "parameters": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

Custom header parameters as part of the request. For example, to exclude DocumentationParts from an imported API, set ignore=documentation as a parameters value, as in the AWS CLI command of aws apigateway import-rest-api --parameters ignore=documentation --body 'file:///path/to/imported-api-body.json'.

", + "smithy.api#httpQueryParams": {} + } + }, + "body": { + "target": "com.amazonaws.apigateway#Blob", + "traits": { + "smithy.api#documentation": "

The PUT request body containing external API definitions. Currently, only OpenAPI definition JSON/YAML files are supported. The maximum size of the API definition file is 6MB.

", + "smithy.api#httpPayload": {}, + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A PUT request to update an existing API, with external API definitions specified as the request body.

" + } + }, + "com.amazonaws.apigateway#QuotaPeriodType": { + "type": "enum", + "members": { + "DAY": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DAY" + } + }, + "WEEK": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "WEEK" + } + }, + "MONTH": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MONTH" + } + } + } + }, + "com.amazonaws.apigateway#QuotaSettings": { + "type": "structure", + "members": { + "limit": { + "target": "com.amazonaws.apigateway#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The target maximum number of requests that can be made in a given time period.

" + } + }, + "offset": { + "target": "com.amazonaws.apigateway#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The number of requests subtracted from the given limit in the initial time period.

" + } + }, + "period": { + "target": "com.amazonaws.apigateway#QuotaPeriodType", + "traits": { + "smithy.api#documentation": "

The time period in which the limit applies. Valid values are \"DAY\", \"WEEK\" or \"MONTH\".

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Quotas configured for a usage plan.

" + } + }, + "com.amazonaws.apigateway#RequestValidator": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of this RequestValidator.

" + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of this RequestValidator

" + } + }, + "validateRequestBody": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A Boolean flag to indicate whether to validate a request body according to the configured Model schema.

" + } + }, + "validateRequestParameters": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A Boolean flag to indicate whether to validate request parameters (true) or not (false).

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A set of validation rules for incoming Method requests.

" + } + }, + "com.amazonaws.apigateway#RequestValidators": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfRequestValidator", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

A collection of RequestValidator resources of a given RestApi.

" + } + }, + "com.amazonaws.apigateway#Resource": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The resource's identifier.

" + } + }, + "parentId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The parent resource's identifier.

" + } + }, + "pathPart": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The last path segment for this resource.

" + } + }, + "path": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The full path for this resource.

" + } + }, + "resourceMethods": { + "target": "com.amazonaws.apigateway#MapOfMethod", + "traits": { + "smithy.api#documentation": "

Gets an API resource's method of a given HTTP verb.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents an API resource.

" + } + }, + "com.amazonaws.apigateway#Resources": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfResource", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a collection of Resource resources.

" + } + }, + "com.amazonaws.apigateway#RestApi": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The API's identifier. This identifier is unique across all of your APIs in API Gateway.

" + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The API's name.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The API's description.

" + } + }, + "createdDate": { + "target": "com.amazonaws.apigateway#Timestamp", + "traits": { + "smithy.api#documentation": "

The timestamp when the API was created.

" + } + }, + "version": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A version identifier for the API.

" + } + }, + "warnings": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

The warning messages reported when failonwarnings is turned on during API import.

" + } + }, + "binaryMediaTypes": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

The list of binary media types supported by the RestApi. By default, the RestApi supports only UTF-8-encoded text payloads.

" + } + }, + "minimumCompressionSize": { + "target": "com.amazonaws.apigateway#NullableInteger", + "traits": { + "smithy.api#documentation": "

A nullable integer that is used to enable compression (with non-negative between 0 and 10485760 (10M) bytes, inclusive) or disable compression (with a null value) on an API. When compression is enabled, compression or decompression is not applied on the payload if the payload size is smaller than this value. Setting it to zero allows compression for any payload size.

" + } + }, + "apiKeySource": { + "target": "com.amazonaws.apigateway#ApiKeySourceType", + "traits": { + "smithy.api#documentation": "

The source of the API key for metering requests according to a usage plan. Valid values\n are: >HEADER to read the API key from the X-API-Key header of a\n request. AUTHORIZER to read the API key from the UsageIdentifierKey\n from a custom authorizer.

" + } + }, + "endpointConfiguration": { + "target": "com.amazonaws.apigateway#EndpointConfiguration", + "traits": { + "smithy.api#documentation": "

The endpoint configuration of this RestApi showing the endpoint types of the API.

" + } + }, + "policy": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A stringified JSON policy document that applies to this RestApi regardless of the caller and Method configuration.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" + } + }, + "disableExecuteApiEndpoint": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether clients can invoke your API by using the default execute-api endpoint.\n By default, clients can invoke your API with the default\n https://{api_id}.execute-api.{region}.amazonaws.com endpoint. To require that clients use a\n custom domain name to invoke your API, disable the default endpoint.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a REST API.

" + } + }, + "com.amazonaws.apigateway#RestApis": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfRestApi", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains references to your APIs and links that guide you in how to interact with your collection. A collection offers a paginated view of your APIs.

" + } + }, + "com.amazonaws.apigateway#SdkConfigurationProperty": { + "type": "structure", + "members": { + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of a an SdkType configuration property.

" + } + }, + "friendlyName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The user-friendly name of an SdkType configuration property.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of an SdkType configuration property.

" + } + }, + "required": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

A boolean flag of an SdkType configuration property to indicate if the associated SDK configuration property is required (true) or not (false).

" + } + }, + "defaultValue": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The default value of an SdkType configuration property.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A configuration property of an SDK type.

" + } + }, + "com.amazonaws.apigateway#SdkResponse": { + "type": "structure", + "members": { + "contentType": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The content-type header value in the HTTP response.

", + "smithy.api#httpHeader": "Content-Type" + } + }, + "contentDisposition": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The content-disposition header value in the HTTP response.

", + "smithy.api#httpHeader": "Content-Disposition" + } + }, + "body": { + "target": "com.amazonaws.apigateway#Blob", + "traits": { + "smithy.api#documentation": "

The binary blob response to GetSdk, which contains the generated SDK.

", + "smithy.api#httpPayload": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The binary blob response to GetSdk, which contains the generated SDK.

" + } + }, + "com.amazonaws.apigateway#SdkType": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of an SdkType instance.

" + } + }, + "friendlyName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The user-friendly name of an SdkType instance.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of an SdkType.

" + } + }, + "configurationProperties": { + "target": "com.amazonaws.apigateway#ListOfSdkConfigurationProperty", + "traits": { + "smithy.api#documentation": "

A list of configuration properties of an SdkType.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A type of SDK that API Gateway can generate.

" + } + }, + "com.amazonaws.apigateway#SdkTypes": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfSdkType", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + } + }, + "traits": { + "smithy.api#documentation": "

The collection of SdkType instances.

" + } + }, + "com.amazonaws.apigateway#SecurityPolicy": { + "type": "enum", + "members": { + "TLS_1_0": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "TLS_1_0" + } + }, + "TLS_1_2": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "TLS_1_2" + } + } + } + }, + "com.amazonaws.apigateway#ServiceUnavailableException": { + "type": "structure", + "members": { + "retryAfterSeconds": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#httpHeader": "Retry-After" + } + }, + "message": { + "target": "com.amazonaws.apigateway#String" + } + }, + "traits": { + "smithy.api#documentation": "

The requested service is not available. For details see the accompanying error message. Retry after the specified time period.

", + "smithy.api#error": "server", + "smithy.api#httpError": 503 + } + }, + "com.amazonaws.apigateway#Stage": { + "type": "structure", + "members": { + "deploymentId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the Deployment that the stage points to.

" + } + }, + "clientCertificateId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of a client certificate for an API stage.

" + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the stage is the first path segment in the Uniform Resource Identifier (URI) of a call to API Gateway. Stage names can only contain alphanumeric characters, hyphens, and underscores. Maximum length is 128 characters.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The stage's description.

" + } + }, + "cacheClusterEnabled": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether a cache cluster is enabled for the stage.

" + } + }, + "cacheClusterSize": { + "target": "com.amazonaws.apigateway#CacheClusterSize", + "traits": { + "smithy.api#documentation": "

The stage's cache capacity in GB. For more information about choosing a cache size, see Enabling API caching to enhance responsiveness.

" + } + }, + "cacheClusterStatus": { + "target": "com.amazonaws.apigateway#CacheClusterStatus", + "traits": { + "smithy.api#documentation": "

The status of the cache cluster for the stage, if enabled.

" + } + }, + "methodSettings": { + "target": "com.amazonaws.apigateway#MapOfMethodSettings", + "traits": { + "smithy.api#documentation": "

A map that defines the method settings for a Stage resource. Keys (designated as /{method_setting_key below) are method paths defined as {resource_path}/{http_method} for an individual method override, or /\\*/\\* for overriding all methods in the stage.

" + } + }, + "variables": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A map that defines the stage variables for a Stage resource. Variable names can\n have alphanumeric and underscore characters, and the values must match [A-Za-z0-9-._~:/?#&=,]+.

" + } + }, + "documentationVersion": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The version of the associated API documentation.

" + } + }, + "accessLogSettings": { + "target": "com.amazonaws.apigateway#AccessLogSettings", + "traits": { + "smithy.api#documentation": "

Settings for logging access in this stage.

" + } + }, + "canarySettings": { + "target": "com.amazonaws.apigateway#CanarySettings", + "traits": { + "smithy.api#documentation": "

Settings for the canary deployment in this stage.

" + } + }, + "tracingEnabled": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether active tracing with X-ray is enabled for the Stage.

" + } + }, + "webAclArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ARN of the WebAcl associated with the Stage.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" + } + }, + "createdDate": { + "target": "com.amazonaws.apigateway#Timestamp", + "traits": { + "smithy.api#documentation": "

The timestamp when the stage was created.

" + } + }, + "lastUpdatedDate": { + "target": "com.amazonaws.apigateway#Timestamp", + "traits": { + "smithy.api#documentation": "

The timestamp when the stage last updated.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a unique identifier for a version of a deployed RestApi that is callable by users.

" + } + }, + "com.amazonaws.apigateway#StageKey": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

" + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The stage name associated with the stage key.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A reference to a unique stage identified in the format {restApiId}/{stage}.

" + } + }, + "com.amazonaws.apigateway#Stages": { + "type": "structure", + "members": { + "item": { + "target": "com.amazonaws.apigateway#ListOfStage", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A list of Stage resources that are associated with the ApiKey resource.

" + } + }, + "com.amazonaws.apigateway#StatusCode": { + "type": "string", + "traits": { + "smithy.api#documentation": "

The status code.

", + "smithy.api#pattern": "^[1-5]\\d\\d$" + } + }, + "com.amazonaws.apigateway#String": { + "type": "string" + }, + "com.amazonaws.apigateway#TagResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#TagResourceRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Adds or updates a tag on a given resource.

", + "smithy.api#http": { + "method": "PUT", + "uri": "/tags/{resourceArn}", + "code": 204 + } + } + }, + "com.amazonaws.apigateway#TagResourceRequest": { + "type": "structure", + "members": { + "resourceArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ARN of a resource that can be tagged.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Adds or updates a tag on a given resource.

" + } + }, + "com.amazonaws.apigateway#Tags": { + "type": "structure", + "members": { + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" + } + }, + "com.amazonaws.apigateway#Template": { + "type": "structure", + "members": { + "value": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Apache Velocity Template Language (VTL) template content used for the template resource.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a mapping template used to transform a payload.

" + } + }, + "com.amazonaws.apigateway#TestInvokeAuthorizer": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#TestInvokeAuthorizerRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#TestInvokeAuthorizerResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Simulate the execution of an Authorizer in your RestApi with headers, parameters, and an incoming request body.

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis/{restApiId}/authorizers/{authorizerId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#TestInvokeAuthorizerRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "authorizerId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a test invoke authorizer request's Authorizer ID.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "headers": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map of headers to simulate an incoming invocation request. This is where the incoming authorization token, or identity source, should be specified.

" + } + }, + "multiValueHeaders": { + "target": "com.amazonaws.apigateway#MapOfStringToList", + "traits": { + "smithy.api#documentation": "

The headers as a map from string to list of values to simulate an incoming invocation request. This is where the incoming authorization token, or identity source, may be specified.

" + } + }, + "pathWithQueryString": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The URI path, including query string, of the simulated invocation request. Use this to specify path parameters and query string parameters.

" + } + }, + "body": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The simulated request body of an incoming invocation request.

" + } + }, + "stageVariables": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map of stage variables to simulate an invocation on a deployed Stage.

" + } + }, + "additionalContext": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map of additional context variables.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Make a request to simulate the invocation of an Authorizer.

" + } + }, + "com.amazonaws.apigateway#TestInvokeAuthorizerResponse": { + "type": "structure", + "members": { + "clientStatus": { + "target": "com.amazonaws.apigateway#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The HTTP status code that the client would have received. Value is 0 if the authorizer succeeded.

" + } + }, + "log": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The API Gateway execution log for the test authorizer request.

" + } + }, + "latency": { + "target": "com.amazonaws.apigateway#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The execution latency of the test authorizer request.

" + } + }, + "principalId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The principal identity returned by the Authorizer

" + } + }, + "policy": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The JSON policy document returned by the Authorizer

" + } + }, + "authorization": { + "target": "com.amazonaws.apigateway#MapOfStringToList", + "traits": { + "smithy.api#documentation": "

The authorization response.

" + } + }, + "claims": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The open identity claims, with any supported custom attributes, returned from the Cognito Your User Pool configured for the API.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents the response of the test invoke request for a custom Authorizer

" + } + }, + "com.amazonaws.apigateway#TestInvokeMethod": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#TestInvokeMethodRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#TestInvokeMethodResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Simulate the invocation of a Method in your RestApi with headers, parameters, and an incoming request body.

", + "smithy.api#http": { + "method": "POST", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#TestInvokeMethodRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a test invoke method request's resource ID.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies a test invoke method request's HTTP method.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "pathWithQueryString": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The URI path, including query string, of the simulated invocation request. Use this to specify path parameters and query string parameters.

" + } + }, + "body": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The simulated request body of an incoming invocation request.

" + } + }, + "headers": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map of headers to simulate an incoming invocation request.

" + } + }, + "multiValueHeaders": { + "target": "com.amazonaws.apigateway#MapOfStringToList", + "traits": { + "smithy.api#documentation": "

The headers as a map from string to list of values to simulate an incoming invocation request.

" + } + }, + "clientCertificateId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A ClientCertificate identifier to use in the test invocation. API Gateway will use the certificate when making the HTTPS request to the defined back-end endpoint.

" + } + }, + "stageVariables": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

A key-value map of stage variables to simulate an invocation on a deployed Stage.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Make a request to simulate the invocation of a Method.

" + } + }, + "com.amazonaws.apigateway#TestInvokeMethodResponse": { + "type": "structure", + "members": { + "status": { + "target": "com.amazonaws.apigateway#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The HTTP status code.

" + } + }, + "body": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The body of the HTTP response.

" + } + }, + "headers": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The headers of the HTTP response.

" + } + }, + "multiValueHeaders": { + "target": "com.amazonaws.apigateway#MapOfStringToList", + "traits": { + "smithy.api#documentation": "

The headers of the HTTP response as a map from string to list of values.

" + } + }, + "log": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The API Gateway execution log for the test invoke request.

" + } + }, + "latency": { + "target": "com.amazonaws.apigateway#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The execution latency of the test invoke request.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents the response of the test invoke request in the HTTP method.

" + } + }, + "com.amazonaws.apigateway#ThrottleSettings": { + "type": "structure", + "members": { + "burstLimit": { + "target": "com.amazonaws.apigateway#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The API target request burst rate limit. This allows more requests through for a period of time than the target rate limit.

" + } + }, + "rateLimit": { + "target": "com.amazonaws.apigateway#Double", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The API target request rate limit.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The API request rate limits.

" + } + }, + "com.amazonaws.apigateway#Timestamp": { + "type": "timestamp" + }, + "com.amazonaws.apigateway#TlsConfig": { + "type": "structure", + "members": { + "insecureSkipVerification": { + "target": "com.amazonaws.apigateway#Boolean", + "traits": { + "smithy.api#default": false, + "smithy.api#documentation": "

Specifies whether or not API Gateway skips verification that the certificate for an integration endpoint is\n issued by a supported certificate authority. This isn’t recommended, but it enables you to\n use certificates that are signed by private certificate authorities, or certificates\n that are self-signed. If enabled, API Gateway still performs basic certificate\n validation, which includes checking the certificate's expiration date, hostname, and\n presence of a root certificate authority. Supported only for HTTP and\n HTTP_PROXY integrations.

\n \n

Enabling insecureSkipVerification isn't recommended, especially for integrations with public\n HTTPS endpoints. If you enable insecureSkipVerification, you increase the risk of man-in-the-middle attacks.

\n
" + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies the TLS configuration for an integration.

" + } + }, + "com.amazonaws.apigateway#TooManyRequestsException": { + "type": "structure", + "members": { + "retryAfterSeconds": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#httpHeader": "Retry-After" + } + }, + "message": { + "target": "com.amazonaws.apigateway#String" + } + }, + "traits": { + "smithy.api#documentation": "

The request has reached its throttling limit. Retry after the specified time period.

", + "smithy.api#error": "client", + "smithy.api#httpError": 429 + } + }, + "com.amazonaws.apigateway#UnauthorizedCacheControlHeaderStrategy": { + "type": "enum", + "members": { + "FAIL_WITH_403": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "FAIL_WITH_403" + } + }, + "SUCCEED_WITH_RESPONSE_HEADER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SUCCEED_WITH_RESPONSE_HEADER" + } + }, + "SUCCEED_WITHOUT_RESPONSE_HEADER": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SUCCEED_WITHOUT_RESPONSE_HEADER" + } + } + } + }, + "com.amazonaws.apigateway#UnauthorizedException": { + "type": "structure", + "members": { + "message": { + "target": "com.amazonaws.apigateway#String" + } + }, + "traits": { + "smithy.api#documentation": "

The request is denied because the caller has insufficient permissions.

", + "smithy.api#error": "client", + "smithy.api#httpError": 401 + } + }, + "com.amazonaws.apigateway#UntagResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UntagResourceRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Removes a tag from a given resource.

", + "smithy.api#http": { + "method": "DELETE", + "uri": "/tags/{resourceArn}", + "code": 204 + } + } + }, + "com.amazonaws.apigateway#UntagResourceRequest": { + "type": "structure", + "members": { + "resourceArn": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ARN of a resource that can be tagged.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "tagKeys": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

The Tag keys to delete.

", + "smithy.api#httpQuery": "tagKeys", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Removes a tag from a given resource.

" + } + }, + "com.amazonaws.apigateway#UpdateAccount": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateAccountRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Account" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Changes information about the current Account resource.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/account", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateAccountRequest": { + "type": "structure", + "members": { + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to change information about the current Account resource.

" + } + }, + "com.amazonaws.apigateway#UpdateApiKey": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateApiKeyRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#ApiKey" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Changes information about an ApiKey resource.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/apikeys/{apiKey}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateApiKeyRequest": { + "type": "structure", + "members": { + "apiKey": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the ApiKey resource to be updated.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to change information about an ApiKey resource.

" + } + }, + "com.amazonaws.apigateway#UpdateAuthorizer": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateAuthorizerRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Authorizer" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Updates an existing Authorizer resource.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/authorizers/{authorizerId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateAuthorizerRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "authorizerId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the Authorizer resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to update an existing Authorizer resource.

" + } + }, + "com.amazonaws.apigateway#UpdateBasePathMapping": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateBasePathMappingRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#BasePathMapping" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Changes information about the BasePathMapping resource.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/domainnames/{domainName}/basepathmappings/{basePath}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateBasePathMappingRequest": { + "type": "structure", + "members": { + "domainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The domain name of the BasePathMapping resource to change.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "basePath": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The base path of the BasePathMapping resource to change.

\n

To specify an empty base path, set this parameter to '(none)'.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to change information about the BasePathMapping resource.

" + } + }, + "com.amazonaws.apigateway#UpdateClientCertificate": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateClientCertificateRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#ClientCertificate" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Changes information about an ClientCertificate resource.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/clientcertificates/{clientCertificateId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateClientCertificateRequest": { + "type": "structure", + "members": { + "clientCertificateId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the ClientCertificate resource to be updated.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to change information about an ClientCertificate resource.

" + } + }, + "com.amazonaws.apigateway#UpdateDeployment": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateDeploymentRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Deployment" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#ServiceUnavailableException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Changes information about a Deployment resource.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/deployments/{deploymentId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateDeploymentRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "deploymentId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The replacement identifier for the Deployment resource to change information about.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to change information about a Deployment resource.

" + } + }, + "com.amazonaws.apigateway#UpdateDocumentationPart": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateDocumentationPartRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DocumentationPart" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Updates a documentation part.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/documentation/parts/{documentationPartId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateDocumentationPartRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "documentationPartId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the to-be-updated documentation part.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Updates an existing documentation part of a given API.

" + } + }, + "com.amazonaws.apigateway#UpdateDocumentationVersion": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateDocumentationVersionRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DocumentationVersion" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Updates a documentation version.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/documentation/versions/{documentationVersion}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateDocumentationVersionRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi..

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "documentationVersion": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The version identifier of the to-be-updated documentation version.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Updates an existing documentation version of an API.

" + } + }, + "com.amazonaws.apigateway#UpdateDomainName": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateDomainNameRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#DomainName" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Changes information about the DomainName resource.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/domainnames/{domainName}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateDomainNameRequest": { + "type": "structure", + "members": { + "domainName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the DomainName resource to be changed.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to change information about the DomainName resource.

" + } + }, + "com.amazonaws.apigateway#UpdateGatewayResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateGatewayResponseRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#GatewayResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Updates a GatewayResponse of a specified response type on the given RestApi.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/gatewayresponses/{responseType}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateGatewayResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "responseType": { + "target": "com.amazonaws.apigateway#GatewayResponseType", + "traits": { + "smithy.api#documentation": "

The response type of the associated GatewayResponse.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Updates a GatewayResponse of a specified response type on the given RestApi.

" + } + }, + "com.amazonaws.apigateway#UpdateIntegration": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateIntegrationRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Integration" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Represents an update integration.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateIntegrationRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Represents an update integration request's resource identifier.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Represents an update integration request's HTTP method.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents an update integration request.

" + } + }, + "com.amazonaws.apigateway#UpdateIntegrationResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateIntegrationResponseRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#IntegrationResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Represents an update integration response.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration/responses/{statusCode}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateIntegrationResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies an update integration response request's resource identifier.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

Specifies an update integration response request's HTTP method.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

Specifies an update integration response request's status code.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents an update integration response request.

" + } + }, + "com.amazonaws.apigateway#UpdateMethod": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateMethodRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Method" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Updates an existing Method resource.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateMethodRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Resource identifier for the Method resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The HTTP verb of the Method resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to update an existing Method resource.

" + } + }, + "com.amazonaws.apigateway#UpdateMethodResponse": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateMethodResponseRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#MethodResponse" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Updates an existing MethodResponse resource.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/responses/{statusCode}", + "code": 201 + } + } + }, + "com.amazonaws.apigateway#UpdateMethodResponseRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Resource identifier for the MethodResponse resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "httpMethod": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The HTTP verb of the Method resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "statusCode": { + "target": "com.amazonaws.apigateway#StatusCode", + "traits": { + "smithy.api#documentation": "

The status code for the MethodResponse resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A request to update an existing MethodResponse resource.

" + } + }, + "com.amazonaws.apigateway#UpdateModel": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateModelRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Model" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Changes information about a model.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/models/{modelName}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateModelRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "modelName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the model to update.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to update an existing model in an existing RestApi resource.

" + } + }, + "com.amazonaws.apigateway#UpdateRequestValidator": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateRequestValidatorRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#RequestValidator" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Updates a RequestValidator of a given RestApi.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/requestvalidators/{requestValidatorId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateRequestValidatorRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "requestValidatorId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of RequestValidator to be updated.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Updates a RequestValidator of a given RestApi.

" + } + }, + "com.amazonaws.apigateway#UpdateResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateResourceRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Resource" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Changes information about a Resource resource.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/resources/{resourceId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateResourceRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "resourceId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the Resource resource.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to change information about a Resource resource.

" + } + }, + "com.amazonaws.apigateway#UpdateRestApi": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateRestApiRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#RestApi" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Changes information about the specified API.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateRestApiRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Request to update an existing RestApi resource in your collection.

" + } + }, + "com.amazonaws.apigateway#UpdateStage": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateStageRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Stage" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Changes information about a Stage resource.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/restapis/{restApiId}/stages/{stageName}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateStageRequest": { + "type": "structure", + "members": { + "restApiId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The string identifier of the associated RestApi.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "stageName": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of the Stage resource to change information about.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Requests API Gateway to change information about a Stage resource.

" + } + }, + "com.amazonaws.apigateway#UpdateUsage": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateUsageRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#Usage" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Grants a temporary extension to the remaining quota of a usage plan associated with a specified API key.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/usageplans/{usagePlanId}/keys/{keyId}/usage", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateUsagePlan": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateUsagePlanRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#UsagePlan" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Updates a usage plan of a given plan Id.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/usageplans/{usagePlanId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateUsagePlanRequest": { + "type": "structure", + "members": { + "usagePlanId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Id of the to-be-updated usage plan.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The PATCH request to update a usage plan of a given plan Id.

" + } + }, + "com.amazonaws.apigateway#UpdateUsageRequest": { + "type": "structure", + "members": { + "usagePlanId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Id of the usage plan associated with the usage data.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "keyId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the API key associated with the usage plan in which a temporary extension is granted to the remaining quota.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The PATCH request to grant a temporary extension to the remaining quota of a usage plan associated with a specified API key.

" + } + }, + "com.amazonaws.apigateway#UpdateVpcLink": { + "type": "operation", + "input": { + "target": "com.amazonaws.apigateway#UpdateVpcLinkRequest" + }, + "output": { + "target": "com.amazonaws.apigateway#VpcLink" + }, + "errors": [ + { + "target": "com.amazonaws.apigateway#BadRequestException" + }, + { + "target": "com.amazonaws.apigateway#ConflictException" + }, + { + "target": "com.amazonaws.apigateway#LimitExceededException" + }, + { + "target": "com.amazonaws.apigateway#NotFoundException" + }, + { + "target": "com.amazonaws.apigateway#TooManyRequestsException" + }, + { + "target": "com.amazonaws.apigateway#UnauthorizedException" + } + ], + "traits": { + "smithy.api#documentation": "

Updates an existing VpcLink of a specified identifier.

", + "smithy.api#http": { + "method": "PATCH", + "uri": "/vpclinks/{vpcLinkId}", + "code": 200 + } + } + }, + "com.amazonaws.apigateway#UpdateVpcLinkRequest": { + "type": "structure", + "members": { + "vpcLinkId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the VpcLink. It is used in an Integration to reference this VpcLink.

", + "smithy.api#httpLabel": {}, + "smithy.api#required": {} + } + }, + "patchOperations": { + "target": "com.amazonaws.apigateway#ListOfPatchOperation", + "traits": { + "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Updates an existing VpcLink of a specified identifier.

" + } + }, + "com.amazonaws.apigateway#Usage": { + "type": "structure", + "members": { + "usagePlanId": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The plan Id associated with this usage data.

" + } + }, + "startDate": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The starting date of the usage data.

" + } + }, + "endDate": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The ending date of the usage data.

" + } + }, + "items": { + "target": "com.amazonaws.apigateway#MapOfKeyUsages", + "traits": { + "smithy.api#documentation": "

The usage data, as daily logs of used and remaining quotas, over the specified time interval indexed over the API keys in a usage plan. For example, {..., \"values\" : { \"{api_key}\" : [ [0, 100], [10, 90], [100, 10]]}, where {api_key} stands for an API key value and the daily log entry is of the format [used quota, remaining quota].

", + "smithy.api#jsonName": "values" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents the usage data of a usage plan.

" + } + }, + "com.amazonaws.apigateway#UsagePlan": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of a UsagePlan resource.

" + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of a usage plan.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of a usage plan.

" + } + }, + "apiStages": { + "target": "com.amazonaws.apigateway#ListOfApiStage", + "traits": { + "smithy.api#documentation": "

The associated API stages of a usage plan.

" + } + }, + "throttle": { + "target": "com.amazonaws.apigateway#ThrottleSettings", + "traits": { + "smithy.api#documentation": "

A map containing method level throttling information for API stage in a usage plan.

" + } + }, + "quota": { + "target": "com.amazonaws.apigateway#QuotaSettings", + "traits": { + "smithy.api#documentation": "

The target maximum number of permitted requests per a given unit time interval.

" + } + }, + "productCode": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The AWS Markeplace product identifier to associate with the usage plan as a SaaS product on AWS Marketplace.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a usage plan used to specify who can assess associated API stages. Optionally, target request rate and quota limits can be set. \n In some cases clients can exceed the targets that you set. Don’t rely on usage plans to control costs. \n Consider using Amazon Web Services Budgets to monitor costs \n and WAF to manage API requests.

" + } + }, + "com.amazonaws.apigateway#UsagePlanKey": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The Id of a usage plan key.

" + } + }, + "type": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The type of a usage plan key. Currently, the valid key type is API_KEY.

" + } + }, + "value": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The value of a usage plan key.

" + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name of a usage plan key.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a usage plan key to identify a plan customer.

" + } + }, + "com.amazonaws.apigateway#UsagePlanKeys": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfUsagePlanKey", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents the collection of usage plan keys added to usage plans for the associated API keys and, possibly, other types of keys.

" + } + }, + "com.amazonaws.apigateway#UsagePlans": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfUsagePlan", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a collection of usage plans for an AWS account.

" + } + }, + "com.amazonaws.apigateway#VpcLink": { + "type": "structure", + "members": { + "id": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The identifier of the VpcLink. It is used in an Integration to reference this VpcLink.

" + } + }, + "name": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The name used to label and identify the VPC link.

" + } + }, + "description": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The description of the VPC link.

" + } + }, + "targetArns": { + "target": "com.amazonaws.apigateway#ListOfString", + "traits": { + "smithy.api#documentation": "

The ARN of the network load balancer of the VPC targeted by the VPC link. The network load balancer must be owned by the same AWS account of the API owner.

" + } + }, + "status": { + "target": "com.amazonaws.apigateway#VpcLinkStatus", + "traits": { + "smithy.api#documentation": "

The status of the VPC link. The valid values are AVAILABLE, PENDING, DELETING, or FAILED. Deploying an API will wait if the status is PENDING and will fail if the status is DELETING.

" + } + }, + "statusMessage": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

A description about the VPC link status.

" + } + }, + "tags": { + "target": "com.amazonaws.apigateway#MapOfStringToString", + "traits": { + "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

An API Gateway VPC link for a RestApi to access resources in an Amazon Virtual Private Cloud (VPC).

" + } + }, + "com.amazonaws.apigateway#VpcLinkStatus": { + "type": "enum", + "members": { + "AVAILABLE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AVAILABLE" + } + }, + "PENDING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PENDING" + } + }, + "DELETING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DELETING" + } + }, + "FAILED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "FAILED" + } + } + } + }, + "com.amazonaws.apigateway#VpcLinks": { + "type": "structure", + "members": { + "items": { + "target": "com.amazonaws.apigateway#ListOfVpcLink", + "traits": { + "smithy.api#documentation": "

The current page of elements from this collection.

", + "smithy.api#jsonName": "item" + } + }, + "position": { + "target": "com.amazonaws.apigateway#String", + "traits": { + "smithy.api#documentation": "

The current pagination position in the paged result set.

", + "smithy.api#httpQuery": "position" + } + } + }, + "traits": { + "smithy.api#documentation": "

The collection of VPC links under the caller's account in a region.

" + } + } + } +} diff --git a/aws/sdk/integration-tests/Cargo.toml b/aws/sdk/integration-tests/Cargo.toml index 7410b5f824..fd0ab55dcf 100644 --- a/aws/sdk/integration-tests/Cargo.toml +++ b/aws/sdk/integration-tests/Cargo.toml @@ -2,6 +2,7 @@ # `./gradlew -Paws.fullsdk=true :aws:sdk:assemble` these tests are copied into their respective Service crates. [workspace] members = [ + "apigateway", "dynamodb", "ec2", "glacier", diff --git a/aws/sdk/integration-tests/apigateway/Cargo.toml b/aws/sdk/integration-tests/apigateway/Cargo.toml new file mode 100644 index 0000000000..a920367880 --- /dev/null +++ b/aws/sdk/integration-tests/apigateway/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "apigateway-tests" +version = "0.1.0" +authors = ["AWS Rust SDK Team "] +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } +aws-sdk-apigateway = { path = "../../build/aws-sdk/sdk/apigateway" } +aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } +aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test"} +tokio = { version = "1.23.1", features = ["full", "test-util"]} diff --git a/aws/sdk/integration-tests/apigateway/tests/accept_header.rs b/aws/sdk/integration-tests/apigateway/tests/accept_header.rs new file mode 100644 index 0000000000..9a4a087d38 --- /dev/null +++ b/aws/sdk/integration-tests/apigateway/tests/accept_header.rs @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_sdk_apigateway::config::{Credentials, Region}; +use aws_sdk_apigateway::{Client, Config}; +use aws_smithy_client::test_connection::capture_request; +use aws_smithy_protocol_test::{assert_ok, validate_headers}; + +#[tokio::test] +async fn accept_header_is_application_json() { + let (conn, handler) = capture_request(None); + let conf = Config::builder() + .region(Region::new("us-east-1")) + .credentials_provider(Credentials::for_tests()) + .http_connector(conn) + .build(); + + let client = Client::from_conf(conf); + let _result = client + .delete_resource() + .rest_api_id("some-rest-api-id") + .resource_id("some-resource-id") + .send() + .await; + let request = handler.expect_request(); + assert_ok(validate_headers( + request.headers(), + [("accept", "application/json")], + )); +} diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index 1a6a106da3..1558db8bfe 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -5,11 +5,12 @@ pub mod auth; -pub mod orchestrator; - /// Smithy connector runtime plugins pub mod connections; +/// The client orchestrator implementation +pub mod orchestrator; + /// Smithy code related to retry handling and token buckets. /// /// This code defines when and how failed requests should be retried. It also defines the behavior From a9e22ce17a8e6c4b2cfbcd0e5a3b5e6d74bb3c05 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Thu, 18 May 2023 09:59:36 -0500 Subject: [PATCH 099/253] Fix: orchestrator flow (#2699) ## Motivation and Context Necessary before we can implement retries. ## Description I noticed that we weren't handling the flow quite right when errors occurred. This PR fixes that and adds interceptor-based tests to ensure things are working right. I still think we could use more tests but the PR is already quite large. ## Testing This PR contains tests ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../src/apigateway_interceptors.rs | 7 +- .../aws-runtime/src/invocation_id.rs | 25 +- .../aws-runtime/src/recursion_detection.rs | 17 +- .../aws-runtime/src/request_info.rs | 22 +- .../aws-runtime/src/user_agent.rs | 34 +- .../aws-sdk-s3/tests/interceptors.rs | 10 +- .../aws-sdk-s3/tests/util.rs | 6 +- .../EndpointParamsInterceptorGenerator.kt | 7 +- .../protocol/ResponseDeserializerGenerator.kt | 10 +- .../src/client/interceptors.rs | 491 ++++++----- .../src/client/interceptors/context.rs | 784 ++++++------------ .../src/client/interceptors/context/phase.rs | 53 ++ .../client/interceptors/context/wrappers.rs | 253 ++++++ .../src/client/interceptors/error.rs | 5 + .../src/client/orchestrator.rs | 15 +- .../src/client/orchestrator/error.rs | 117 +++ .../src/client/retries.rs | 9 +- .../src/type_erasure.rs | 34 +- .../src/client/interceptor.rs | 9 +- .../src/client/orchestrator.rs | 621 ++++++++++---- .../src/client/orchestrator/auth.rs | 109 +-- .../src/client/orchestrator/endpoints.rs | 3 +- .../interceptors/request_attempts.rs | 9 +- .../interceptors/service_clock_skew.rs | 9 +- .../client/retries/strategy/fixed_delay.rs | 3 +- .../src/client/retries/strategy/never.rs | 3 +- .../src/client/test_util/deserializer.rs | 13 +- 27 files changed, 1616 insertions(+), 1062 deletions(-) create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/phase.rs create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs diff --git a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs index 0ba3c1fa10..b6e56890de 100644 --- a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs @@ -5,8 +5,9 @@ #![allow(dead_code)] -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; -use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeTransmitInterceptorContextMut, BoxError, Interceptor, +}; use aws_smithy_runtime_api::config_bag::ConfigBag; use http::header::ACCEPT; use http::HeaderValue; @@ -18,7 +19,7 @@ pub(crate) struct AcceptHeaderInterceptor; impl Interceptor for AcceptHeaderInterceptor { fn modify_before_signing( &self, - context: &mut InterceptorContext, + context: &mut BeforeTransmitInterceptorContextMut<'_>, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { context diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 98d5405ecb..b5234264eb 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -3,9 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; use aws_smithy_runtime_api::client::interceptors::error::BoxError; -use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeTransmitInterceptorContextMut, Interceptor, +}; use aws_smithy_runtime_api::config_bag::ConfigBag; use http::{HeaderName, HeaderValue}; use uuid::Uuid; @@ -38,7 +39,7 @@ impl Default for InvocationIdInterceptor { impl Interceptor for InvocationIdInterceptor { fn modify_before_retry_loop( &self, - context: &mut InterceptorContext, + context: &mut BeforeTransmitInterceptorContextMut<'_>, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let headers = context.request_mut().headers_mut(); @@ -73,31 +74,31 @@ impl InvocationId { mod tests { use crate::invocation_id::InvocationIdInterceptor; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_runtime_api::type_erasure::TypedBox; use http::HeaderValue; - fn expect_header<'a>( - context: &'a InterceptorContext, - header_name: &str, - ) -> &'a HeaderValue { + fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a HeaderValue { context.request().headers().get(header_name).unwrap() } #[test] fn test_id_is_generated_and_set() { - let mut context = InterceptorContext::<()>::new(TypedBox::new("doesntmatter").erase()) - .into_serialization_phase(); + let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + context.enter_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let _ = context.take_input(); - let mut context = context.into_before_transmit_phase(); + context.enter_before_transmit_phase(); let mut config = ConfigBag::base(); let interceptor = InvocationIdInterceptor::new(); + let mut ctx = Into::into(&mut context); interceptor - .modify_before_retry_loop(&mut context, &mut config) + .modify_before_signing(&mut ctx, &mut config) + .unwrap(); + interceptor + .modify_before_retry_loop(&mut ctx, &mut config) .unwrap(); let header = expect_header(&context, "amz-sdk-invocation-id"); diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs index 38d2cf2725..4c0d9f10b0 100644 --- a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; -use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeTransmitInterceptorContextMut, BoxError, Interceptor, +}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_types::os_shim_internal::Env; use http::HeaderValue; @@ -40,7 +41,7 @@ impl RecursionDetectionInterceptor { impl Interceptor for RecursionDetectionInterceptor { fn modify_before_signing( &self, - context: &mut InterceptorContext, + context: &mut BeforeTransmitInterceptorContextMut<'_>, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let request = context.request_mut(); @@ -73,6 +74,7 @@ mod tests { use super::*; use aws_smithy_http::body::SdkBody; use aws_smithy_protocol_test::{assert_ok, validate_headers}; + use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::type_erasure::TypedBox; use aws_types::os_shim_internal::Env; use http::HeaderValue; @@ -146,15 +148,16 @@ mod tests { request = request.header(name, value); } let request = request.body(SdkBody::empty()).expect("must be valid"); - let mut context = InterceptorContext::<()>::new(TypedBox::new("doesntmatter").erase()) - .into_serialization_phase(); + let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + context.enter_serialization_phase(); context.set_request(request); let _ = context.take_input(); - let mut context = context.into_before_transmit_phase(); + context.enter_before_transmit_phase(); let mut config = ConfigBag::base(); + let mut ctx = Into::into(&mut context); RecursionDetectionInterceptor { env } - .modify_before_signing(&mut context, &mut config) + .modify_before_signing(&mut ctx, &mut config) .expect("interceptor must succeed"); let mutated_request = context.request(); for name in mutated_request.headers().keys() { diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index 05a80e220b..3896654ef6 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -4,8 +4,9 @@ */ use aws_smithy_runtime::client::orchestrator::interceptors::{RequestAttempts, ServiceClockSkew}; -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; -use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeTransmitInterceptorContextMut, BoxError, Interceptor, +}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_types::date_time::Format; use aws_smithy_types::retry::RetryConfig; @@ -79,7 +80,7 @@ impl RequestInfoInterceptor { impl Interceptor for RequestInfoInterceptor { fn modify_before_transmit( &self, - context: &mut InterceptorContext, + context: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let mut pairs = RequestPairs::new(); @@ -156,7 +157,6 @@ mod tests { use crate::request_info::RequestPairs; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime::client::orchestrator::interceptors::RequestAttempts; - use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_runtime_api::type_erasure::TypedBox; @@ -165,10 +165,7 @@ mod tests { use http::HeaderValue; use std::time::Duration; - fn expect_header<'a>( - context: &'a InterceptorContext, - header_name: &str, - ) -> &'a str { + fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a str { context .request() .headers() @@ -180,8 +177,8 @@ mod tests { #[test] fn test_request_pairs_for_initial_attempt() { - let context = InterceptorContext::<()>::new(TypedBox::new("doesntmatter").erase()); - let mut context = context.into_serialization_phase(); + let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + context.enter_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let mut config = ConfigBag::base(); @@ -194,10 +191,11 @@ mod tests { config.put(RequestAttempts::new()); let _ = context.take_input(); - let mut context = context.into_before_transmit_phase(); + context.enter_before_transmit_phase(); let interceptor = RequestInfoInterceptor::new(); + let mut ctx = (&mut context).into(); interceptor - .modify_before_transmit(&mut context, &mut config) + .modify_before_transmit(&mut ctx, &mut config) .unwrap(); assert_eq!( diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index cdd3003da6..326cc5ccae 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -4,9 +4,10 @@ */ use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; use aws_smithy_runtime_api::client::interceptors::error::BoxError; -use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeTransmitInterceptorContextMut, Interceptor, +}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_types::app_name::AppName; use aws_types::os_shim_internal::Env; @@ -73,7 +74,7 @@ fn header_values( impl Interceptor for UserAgentInterceptor { fn modify_before_signing( &self, - context: &mut InterceptorContext, + context: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let api_metadata = cfg @@ -113,10 +114,7 @@ mod tests { use aws_smithy_runtime_api::type_erasure::TypedBox; use aws_smithy_types::error::display::DisplayErrorContext; - fn expect_header<'a>( - context: &'a InterceptorContext, - header_name: &str, - ) -> &'a str { + fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a str { context .request() .headers() @@ -126,12 +124,13 @@ mod tests { .unwrap() } - fn context() -> InterceptorContext { - let mut context = InterceptorContext::<()>::new(TypedBox::new("doesntmatter").erase()) - .into_serialization_phase(); + fn context() -> InterceptorContext { + let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + context.enter_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let _ = context.take_input(); - context.into_before_transmit_phase() + context.enter_before_transmit_phase(); + context } #[test] @@ -143,8 +142,9 @@ mod tests { config.put(ApiMetadata::new("unused", "unused")); let interceptor = UserAgentInterceptor::new(); + let mut ctx = Into::into(&mut context); interceptor - .modify_before_signing(&mut context, &mut config) + .modify_before_signing(&mut ctx, &mut config) .unwrap(); let header = expect_header(&context, "user-agent"); @@ -166,8 +166,9 @@ mod tests { config.put(api_metadata.clone()); let interceptor = UserAgentInterceptor::new(); + let mut ctx = Into::into(&mut context); interceptor - .modify_before_signing(&mut context, &mut config) + .modify_before_signing(&mut ctx, &mut config) .unwrap(); let expected_ua = AwsUserAgent::new_from_environment(Env::real(), api_metadata); @@ -195,8 +196,9 @@ mod tests { config.put(AppName::new("my_awesome_app").unwrap()); let interceptor = UserAgentInterceptor::new(); + let mut ctx = Into::into(&mut context); interceptor - .modify_before_signing(&mut context, &mut config) + .modify_before_signing(&mut ctx, &mut config) .unwrap(); let app_value = "app/my_awesome_app"; @@ -219,11 +221,13 @@ mod tests { let mut config = ConfigBag::base(); let interceptor = UserAgentInterceptor::new(); + let mut ctx = Into::into(&mut context); + let error = format!( "{}", DisplayErrorContext( &*interceptor - .modify_before_signing(&mut context, &mut config) + .modify_before_signing(&mut ctx, &mut config) .expect_err("it should error") ) ); diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs index f33ef94ff8..1f061f7f9d 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs @@ -10,8 +10,10 @@ use aws_sdk_s3::Client; use aws_smithy_client::dvr; use aws_smithy_client::dvr::MediaType; use aws_smithy_client::erase::DynConnector; -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; -use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output}; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeTransmitInterceptorContextMut, Interceptor, +}; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::orchestrator::RequestTime; use aws_smithy_runtime_api::config_bag::ConfigBag; @@ -59,7 +61,7 @@ struct RequestTimeResetInterceptor; impl Interceptor for RequestTimeResetInterceptor { fn modify_before_signing( &self, - _context: &mut InterceptorContext, + _context: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { cfg.set_request_time(RequestTime::new(UNIX_EPOCH)); @@ -73,7 +75,7 @@ struct RequestTimeAdvanceInterceptor(Duration); impl Interceptor for RequestTimeAdvanceInterceptor { fn modify_before_signing( &self, - _context: &mut InterceptorContext, + _context: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { let request_time = cfg.request_time().unwrap(); diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs index af3d53094f..4f2997f5c6 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs @@ -5,9 +5,9 @@ use aws_http::user_agent::AwsUserAgent; use aws_runtime::invocation_id::InvocationId; -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; +use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output}; use aws_smithy_runtime_api::client::interceptors::{ - Interceptor, InterceptorContext, InterceptorRegistrar, + BeforeTransmitInterceptorContextMut, Interceptor, InterceptorRegistrar, }; use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; @@ -39,7 +39,7 @@ pub struct TestUserAgentInterceptor; impl Interceptor for TestUserAgentInterceptor { fn modify_before_signing( &self, - context: &mut InterceptorContext, + context: &mut BeforeTransmitInterceptorContextMut<'_>, _cfg: &mut ConfigBag, ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { let headers = context.request_mut().headers_mut(); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index ee1975d65d..04eb32ca5a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -49,7 +49,10 @@ class EndpointParamsInterceptorGenerator( "HttpResponse" to orchestrator.resolve("HttpResponse"), "Interceptor" to interceptors.resolve("Interceptor"), "InterceptorContext" to interceptors.resolve("InterceptorContext"), - "BeforeSerializationPhase" to interceptors.resolve("context::phase::BeforeSerialization"), + "BeforeSerializationInterceptorContextRef" to interceptors.resolve("context::wrappers::BeforeSerializationInterceptorContextRef"), + "Input" to interceptors.resolve("context::Input"), + "Output" to interceptors.resolve("context::Output"), + "Error" to interceptors.resolve("context::Error"), "InterceptorError" to interceptors.resolve("error::InterceptorError"), "Params" to endpointTypesGenerator.paramsStruct(), ) @@ -67,7 +70,7 @@ class EndpointParamsInterceptorGenerator( impl #{Interceptor} for $interceptorName { fn read_before_execution( &self, - context: &#{InterceptorContext}<#{BeforeSerializationPhase}>, + context: &#{BeforeSerializationInterceptorContextRef}<'_, #{Input}, #{Output}, #{Error}>, cfg: &mut #{ConfigBag}, ) -> Result<(), #{BoxError}> { let _input = context.input() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index 1df6e33c6a..83bc4dff58 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -41,6 +41,7 @@ class ResponseDeserializerGenerator( "Instrument" to CargoDependency.Tracing.toType().resolve("Instrument"), "Output" to interceptorContext.resolve("Output"), "OutputOrError" to interceptorContext.resolve("OutputOrError"), + "OrchestratorError" to orchestrator.resolve("OrchestratorError"), "ResponseDeserializer" to orchestrator.resolve("ResponseDeserializer"), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), "SdkError" to RuntimeType.sdkError(runtimeConfig), @@ -98,7 +99,7 @@ class ResponseDeserializerGenerator( if !response.status().is_success() && response.status().as_u16() != $successCode { return None; } - Some(#{type_erase_result}(#{parse_streaming_response}(response))) + Some(#{type_erase_result}(#{parse_streaming_response}(response)).into()) } """, *codegenScope, @@ -117,7 +118,7 @@ class ResponseDeserializerGenerator( """ // For streaming operations, we only hit this case if its an error let body = response.body().bytes().expect("body loaded"); - #{type_erase_result}(#{parse_error}(response.status().as_u16(), response.headers(), body)) + #{type_erase_result}(#{parse_error}(response.status().as_u16(), response.headers(), body)).into() """, *codegenScope, "parse_error" to parserGenerator.parseErrorFn(operationShape, customizations), @@ -140,7 +141,7 @@ class ResponseDeserializerGenerator( } else { #{parse_response}(status, headers, body) }; - #{type_erase_result}(parse_result) + #{type_erase_result}(parse_result).into() """, *codegenScope, "parse_error" to parserGenerator.parseErrorFn(operationShape, customizations), @@ -154,13 +155,14 @@ class ResponseDeserializerGenerator( private fun typeEraseResult(): RuntimeType = ProtocolFunctions.crossOperationFn("type_erase_result") { fnName -> rustTemplate( """ - pub(crate) fn $fnName(result: Result) -> Result<#{Output}, #{Error}> + pub(crate) fn $fnName(result: Result) -> Result<#{Output}, #{OrchestratorError}<#{Error}>> where O: std::fmt::Debug + Send + Sync + 'static, E: std::error::Error + std::fmt::Debug + Send + Sync + 'static, { result.map(|output| #{TypedBox}::new(output).erase()) .map_err(|error| #{TypedBox}::new(error).erase_error()) + .map_err(Into::into) } """, *codegenScope, diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index ce4195d68c..ec322badf7 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -6,12 +6,21 @@ pub mod context; pub mod error; -use crate::client::interceptors::context::phase::{ - AfterDeserialization, BeforeDeserialization, BeforeSerialization, BeforeTransmit, +use crate::client::interceptors::context::wrappers::{ + FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, }; use crate::config_bag::ConfigBag; use aws_smithy_types::error::display::DisplayErrorContext; -pub use context::InterceptorContext; +pub use context::{ + wrappers::{ + AfterDeserializationInterceptorContextMut, AfterDeserializationInterceptorContextRef, + BeforeDeserializationInterceptorContextMut, BeforeDeserializationInterceptorContextRef, + BeforeSerializationInterceptorContextMut, BeforeSerializationInterceptorContextRef, + BeforeTransmitInterceptorContextMut, BeforeTransmitInterceptorContextRef, + }, + InterceptorContext, +}; +use context::{Error, Input, Output}; pub use error::{BoxError, InterceptorError}; use std::ops::Deref; use std::sync::Arc; @@ -19,11 +28,7 @@ use std::sync::Arc; macro_rules! interceptor_trait_fn { ($name:ident, $phase:ident, $docs:tt) => { #[doc = $docs] - fn $name( - &self, - context: &InterceptorContext<$phase>, - cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { + fn $name(&self, context: &$phase<'_>, cfg: &mut ConfigBag) -> Result<(), BoxError> { let _ctx = context; let _cfg = cfg; Ok(()) @@ -31,11 +36,7 @@ macro_rules! interceptor_trait_fn { }; (mut $name:ident, $phase:ident, $docs:tt) => { #[doc = $docs] - fn $name( - &self, - context: &mut InterceptorContext<$phase>, - cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { + fn $name(&self, context: &mut $phase<'_>, cfg: &mut ConfigBag) -> Result<(), BoxError> { let _ctx = context; let _cfg = cfg; Ok(()) @@ -56,7 +57,7 @@ macro_rules! interceptor_trait_fn { pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_execution, - BeforeSerialization, + BeforeSerializationInterceptorContextRef, " A hook called at the start of an execution, before the SDK does anything else. @@ -80,7 +81,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_serialization, - BeforeSerialization, + BeforeSerializationInterceptorContextMut, " A hook called before the input message is marshalled into a transport message. @@ -108,7 +109,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_serialization, - BeforeSerialization, + BeforeSerializationInterceptorContextRef, " A hook called before the input message is marshalled into a transport @@ -130,7 +131,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_after_serialization, - BeforeTransmit, + BeforeTransmitInterceptorContextRef, " /// A hook called after the input message is marshalled into /// a transport message. @@ -152,7 +153,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_retry_loop, - BeforeTransmit, + BeforeTransmitInterceptorContextMut, " A hook called before the retry loop is entered. This method has the ability to modify and return a new transport request @@ -174,7 +175,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_attempt, - BeforeTransmit, + BeforeTransmitInterceptorContextRef, " A hook called before each attempt at sending the transmission request message to the service. @@ -201,7 +202,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_signing, - BeforeTransmit, + BeforeTransmitInterceptorContextMut, " A hook called before the transport request message is signed. This method has the ability to modify and return a new transport @@ -233,7 +234,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_signing, - BeforeTransmit, + BeforeTransmitInterceptorContextRef, " A hook called before the transport request message is signed. @@ -257,7 +258,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_after_signing, - BeforeTransmit, + BeforeTransmitInterceptorContextRef, " A hook called after the transport request message is signed. @@ -281,7 +282,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_transmit, - BeforeTransmit, + BeforeTransmitInterceptorContextMut, " /// A hook called before the transport request message is sent to the /// service. This method has the ability to modify and return @@ -313,7 +314,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_transmit, - BeforeTransmit, + BeforeTransmitInterceptorContextRef, " A hook called before the transport request message is sent to the service. @@ -341,7 +342,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_after_transmit, - BeforeDeserialization, + BeforeDeserializationInterceptorContextRef, " A hook called after the transport request message is sent to the service and a transport response message is received. @@ -369,7 +370,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( mut modify_before_deserialization, - BeforeDeserialization, + BeforeDeserializationInterceptorContextMut, " A hook called before the transport response message is unmarshalled. This method has the ability to modify and return a new transport @@ -401,7 +402,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_before_deserialization, - BeforeDeserialization, + BeforeDeserializationInterceptorContextRef, " A hook called before the transport response message is unmarshalled @@ -428,7 +429,7 @@ pub trait Interceptor: std::fmt::Debug { interceptor_trait_fn!( read_after_deserialization, - AfterDeserialization, + AfterDeserializationInterceptorContextRef, " A hook called after the transport response message is unmarshalled. @@ -453,115 +454,127 @@ pub trait Interceptor: std::fmt::Debug { " ); - interceptor_trait_fn!( - mut modify_before_attempt_completion, - AfterDeserialization, - " - A hook called when an attempt is completed. This method has the - ability to modify and return a new output message or error - matching the currently-executing operation. - - **When:** This will **ALWAYS** be called once per attempt, except when a - failure occurs before `before_attempt`. This method may - be called multiple times in the event of retries. - - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()], - [InterceptorContext::response()] and - [InterceptorContext::output_or_error()] are **ALWAYS** available. In the event - of retries, the `InterceptorContext` will not include changes made - in previous attempts (e.g. by request signers or other interceptors). - - **Error Behavior:** If errors are raised by this - hook, execution will jump to `after_attempt` with - the raised error as the [InterceptorContext::output_or_error()]. - - **Return Constraints:** Any output message returned by this - hook MUST match the operation being invoked. Any error type can be - returned, replacing the response currently in the context. - " - ); - - interceptor_trait_fn!( - read_after_attempt, - AfterDeserialization, - " - A hook called when an attempt is completed. - - **When:** This will **ALWAYS** be called once per attempt, as long as - `before_attempt` has been executed. - - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()] and - [InterceptorContext::output_or_error()] are **ALWAYS** available. - The [InterceptorContext::response()] is available if a - response was received by the service for this attempt. - In the event of retries, the `InterceptorContext` will not include - changes made in previous attempts (e.g. by request signers or other - interceptors). - - **Error Behavior:** Errors raised by this hook will be stored - until all interceptors have had their `after_attempt` invoked. - If multiple `after_execution` methods raise errors, the latest - will be used and earlier ones will be logged and dropped. If the - retry strategy determines that the execution is retryable, - execution will then jump to `before_attempt`. Otherwise, - execution will jump to `modify_before_attempt_completion` with the - raised error as the [InterceptorContext::output_or_error()]. - " - ); - - interceptor_trait_fn!( - mut modify_before_completion, - AfterDeserialization, - " - A hook called when an execution is completed. - This method has the ability to modify and return a new - output message or error matching the currently - executing - operation. - - **When:** This will **ALWAYS** be called once per execution. - - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::output_or_error()] are **ALWAYS** available. The - [InterceptorContext::request()] - and [InterceptorContext::response()] are available if the - execution proceeded far enough for them to be generated. - - **Error Behavior:** If errors are raised by this - hook , execution will jump to `after_attempt` with - the raised error as the [InterceptorContext::output_or_error()]. - - **Return Constraints:** Any output message returned by this - hook MUST match the operation being invoked. Any error type can be - returned , replacing the response currently in the context. - " - ); - - interceptor_trait_fn!( - read_after_execution, - AfterDeserialization, - " - A hook called when an execution is completed. + /// A hook called when an attempt is completed. This method has the + /// ability to modify and return a new output message or error + /// matching the currently-executing operation. + /// + /// **When:** This will **ALWAYS** be called once per attempt, except when a + /// failure occurs before `before_attempt`. This method may + /// be called multiple times in the event of retries. + /// + /// **Available Information:** The [InterceptorContext::input()], + /// [InterceptorContext::request()], + /// [InterceptorContext::response()] and + /// [InterceptorContext::output_or_error()] are **ALWAYS** available. In the event + /// of retries, the `InterceptorContext` will not include changes made + /// in previous attempts (e.g. by request signers or other interceptors). + /// + /// **Error Behavior:** If errors are raised by this + /// hook, execution will jump to `after_attempt` with + /// the raised error as the [InterceptorContext::output_or_error()]. + /// + /// **Return Constraints:** Any output message returned by this + /// hook MUST match the operation being invoked. Any error type can be + /// returned, replacing the response currently in the context. + fn modify_before_attempt_completion( + &self, + context: &mut FinalizerInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let _ctx = context; + let _cfg = cfg; + Ok(()) + } - **When:** This will **ALWAYS** be called once per execution. The duration - between invocation of this hook and `before_execution` is very - close to the full duration of the execution. + /// A hook called when an attempt is completed. + /// + /// **When:** This will **ALWAYS** be called once per attempt, as long as + /// `before_attempt` has been executed. + /// + /// **Available Information:** The [InterceptorContext::input()], + /// [InterceptorContext::request()] and + /// [InterceptorContext::output_or_error()] are **ALWAYS** available. + /// The [InterceptorContext::response()] is available if a + /// response was received by the service for this attempt. + /// In the event of retries, the `InterceptorContext` will not include + /// changes made in previous attempts (e.g. by request signers or other + /// interceptors). + /// + /// **Error Behavior:** Errors raised by this hook will be stored + /// until all interceptors have had their `after_attempt` invoked. + /// If multiple `after_execution` methods raise errors, the latest + /// will be used and earlier ones will be logged and dropped. If the + /// retry strategy determines that the execution is retryable, + /// execution will then jump to `before_attempt`. Otherwise, + /// execution will jump to `modify_before_attempt_completion` with the + /// raised error as the [InterceptorContext::output_or_error()]. + fn read_after_attempt( + &self, + context: &FinalizerInterceptorContextRef<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let _ctx = context; + let _cfg = cfg; + Ok(()) + } - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::output_or_error()] are **ALWAYS** available. The - [InterceptorContext::request()] and - [InterceptorContext::response()] are available if the - execution proceeded far enough for them to be generated. + /// A hook called when an execution is completed. + /// This method has the ability to modify and return a new + /// output message or error matching the currently - executing + /// operation. + /// + /// **When:** This will **ALWAYS** be called once per execution. + /// + /// **Available Information:** The [InterceptorContext::input()] + /// and [InterceptorContext::output_or_error()] are **ALWAYS** available. The + /// [InterceptorContext::request()] + /// and [InterceptorContext::response()] are available if the + /// execution proceeded far enough for them to be generated. + /// + /// **Error Behavior:** If errors are raised by this + /// hook , execution will jump to `after_attempt` with + /// the raised error as the [InterceptorContext::output_or_error()]. + /// + /// **Return Constraints:** Any output message returned by this + /// hook MUST match the operation being invoked. Any error type can be + /// returned , replacing the response currently in the context. + fn modify_before_completion( + &self, + context: &mut FinalizerInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let _ctx = context; + let _cfg = cfg; + Ok(()) + } - **Error Behavior:** Errors raised by this hook will be stored - until all interceptors have had their `after_execution` invoked. - The error will then be treated as the - [InterceptorContext::output_or_error()] to the customer. If multiple - `after_execution` methods raise errors , the latest will be - used and earlier ones will be logged and dropped. - " - ); + /// A hook called when an execution is completed. + /// + /// **When:** This will **ALWAYS** be called once per execution. The duration + /// between invocation of this hook and `before_execution` is very + /// close to the full duration of the execution. + /// + /// **Available Information:** The [InterceptorContext::input()] + /// and [InterceptorContext::output_or_error()] are **ALWAYS** available. The + /// [InterceptorContext::request()] and + /// [InterceptorContext::response()] are available if the + /// execution proceeded far enough for them to be generated. + /// + /// **Error Behavior:** Errors raised by this hook will be stored + /// until all interceptors have had their `after_execution` invoked. + /// The error will then be treated as the + /// [InterceptorContext::output_or_error()] to the customer. If multiple + /// `after_execution` methods raise errors , the latest will be + /// used and earlier ones will be logged and dropped. + fn read_after_execution( + &self, + context: &FinalizerInterceptorContextRef<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let _ctx = context; + let _cfg = cfg; + Ok(()) + } } /// Interceptor wrapper that may be shared @@ -619,40 +632,42 @@ pub struct Interceptors { } macro_rules! interceptor_impl_fn { - (context, $name:ident, $phase:ident) => { - interceptor_impl_fn!(context, $name, $name, $phase); - }; - (mut context, $name:ident, $phase:ident) => { - interceptor_impl_fn!(mut context, $name, $name, $phase); - }; - (context, $outer_name:ident, $inner_name:ident, $phase:ident) => { - interceptor_impl_fn!( - $outer_name, - $inner_name(context: &InterceptorContext<$phase>) - ); - }; - (mut context, $outer_name:ident, $inner_name:ident, $phase:ident) => { - interceptor_impl_fn!( - $outer_name, - $inner_name(context: &mut InterceptorContext<$phase>) - ); + (mut $interceptor:ident) => { + pub fn $interceptor( + &self, + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let mut result: Result<(), BoxError> = Ok(()); + let mut ctx = ctx.into(); + for interceptor in self.interceptors() { + if let Err(new_error) = interceptor.$interceptor(&mut ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + result.map_err(InterceptorError::$interceptor) + } }; - ($outer_name:ident, $inner_name:ident ($context:ident : $context_ty:ty)) => { - pub fn $outer_name( + (ref $interceptor:ident) => { + pub fn $interceptor( &self, - $context: $context_ty, + ctx: &InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { let mut result: Result<(), BoxError> = Ok(()); + let ctx = ctx.into(); for interceptor in self.interceptors() { - if let Err(new_error) = interceptor.$inner_name($context, cfg) { + if let Err(new_error) = interceptor.$interceptor(&ctx, cfg) { if let Err(last_error) = result { tracing::debug!("{}", DisplayErrorContext(&*last_error)); } result = Err(new_error); } } - result.map_err(InterceptorError::$inner_name) + result.map_err(InterceptorError::$interceptor) } }; } @@ -679,48 +694,128 @@ impl Interceptors { &mut self.operation_interceptors } - interceptor_impl_fn!( - context, - client_read_before_execution, - read_before_execution, - BeforeSerialization - ); - interceptor_impl_fn!( - context, - operation_read_before_execution, - read_before_execution, - BeforeSerialization - ); - interceptor_impl_fn!( - mut context, - modify_before_serialization, - BeforeSerialization - ); - interceptor_impl_fn!(context, read_before_serialization, BeforeSerialization); - interceptor_impl_fn!(context, read_after_serialization, BeforeTransmit); - interceptor_impl_fn!(mut context, modify_before_retry_loop, BeforeTransmit); - interceptor_impl_fn!(context, read_before_attempt, BeforeTransmit); - interceptor_impl_fn!(mut context, modify_before_signing, BeforeTransmit); - interceptor_impl_fn!(context, read_before_signing, BeforeTransmit); - interceptor_impl_fn!(context, read_after_signing, BeforeTransmit); - interceptor_impl_fn!(mut context, modify_before_transmit, BeforeTransmit); - interceptor_impl_fn!(context, read_before_transmit, BeforeTransmit); - interceptor_impl_fn!(context, read_after_transmit, BeforeDeserialization); - interceptor_impl_fn!( - mut context, - modify_before_deserialization, - BeforeDeserialization - ); - interceptor_impl_fn!(context, read_before_deserialization, BeforeDeserialization); - interceptor_impl_fn!(context, read_after_deserialization, AfterDeserialization); - interceptor_impl_fn!( - mut context, - modify_before_attempt_completion, - AfterDeserialization - ); - interceptor_impl_fn!(context, read_after_attempt, AfterDeserialization); - interceptor_impl_fn!(mut context, modify_before_completion, AfterDeserialization); - interceptor_impl_fn!(context, read_after_execution, AfterDeserialization); + pub fn client_read_before_execution( + &self, + ctx: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let mut result: Result<(), BoxError> = Ok(()); + let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); + for interceptor in self.client_interceptors.0.iter() { + if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + result.map_err(InterceptorError::read_before_execution) + } + + pub fn operation_read_before_execution( + &self, + ctx: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let mut result: Result<(), BoxError> = Ok(()); + let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); + for interceptor in self.operation_interceptors.0.iter() { + if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + result.map_err(InterceptorError::read_before_execution) + } + + interceptor_impl_fn!(mut modify_before_serialization); + interceptor_impl_fn!(ref read_before_serialization); + interceptor_impl_fn!(ref read_after_serialization); + interceptor_impl_fn!(mut modify_before_retry_loop); + interceptor_impl_fn!(ref read_before_attempt); + interceptor_impl_fn!(mut modify_before_signing); + interceptor_impl_fn!(ref read_before_signing); + interceptor_impl_fn!(ref read_after_signing); + interceptor_impl_fn!(mut modify_before_transmit); + interceptor_impl_fn!(ref read_before_transmit); + interceptor_impl_fn!(ref read_after_transmit); + interceptor_impl_fn!(mut modify_before_deserialization); + interceptor_impl_fn!(ref read_before_deserialization); + interceptor_impl_fn!(ref read_after_deserialization); + + pub fn modify_before_attempt_completion( + &self, + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let mut result: Result<(), BoxError> = Ok(()); + let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); + for interceptor in self.interceptors() { + if let Err(new_error) = interceptor.modify_before_attempt_completion(&mut ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + result.map_err(InterceptorError::modify_before_attempt_completion) + } + + pub fn read_after_attempt( + &self, + ctx: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let mut result: Result<(), BoxError> = Ok(()); + let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); + for interceptor in self.interceptors() { + if let Err(new_error) = interceptor.read_after_attempt(&ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + result.map_err(InterceptorError::read_after_attempt) + } + + pub fn modify_before_completion( + &self, + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let mut result: Result<(), BoxError> = Ok(()); + let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); + for interceptor in self.interceptors() { + if let Err(new_error) = interceptor.modify_before_completion(&mut ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + result.map_err(InterceptorError::modify_before_completion) + } + + pub fn read_after_execution( + &self, + ctx: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let mut result: Result<(), BoxError> = Ok(()); + let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); + for interceptor in self.interceptors() { + if let Err(new_error) = interceptor.read_after_execution(&ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + result.map_err(InterceptorError::read_after_execution) + } } #[cfg(test)] diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 309b1ca9d9..82481f437d 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -7,184 +7,85 @@ //! //! Interceptors have access to varying pieces of context during the course of an operation. //! -//! An operation is composed of multiple phases. The initial phase is [`phase::BeforeSerialization`], which -//! has the original input as context. The next phase is [`phase::BeforeTransmit`], which has the serialized +//! An operation is composed of multiple phases. The initial phase is [`Phase::BeforeSerialization`], which +//! has the original input as context. The next phase is [`Phase::BeforeTransmit`], which has the serialized //! request as context. Depending on which hook is being called with the dispatch context, //! the serialized request may or may not be signed (which should be apparent from the hook name). -//! Following the [`phase::BeforeTransmit`] phase is the [`phase::BeforeDeserialization`] phase, which has -//! the raw response available as context. Finally, the [`phase::AfterDeserialization`] phase +//! Following the [`Phase::BeforeTransmit`] phase is the [`Phase::BeforeDeserialization`] phase, which has +//! the raw response available as context. Finally, the [`Phase::AfterDeserialization`] phase //! has both the raw and parsed response available. //! //! To summarize: -//! 1. [`phase::BeforeSerialization`]: Only has the operation input. -//! 2. [`phase::BeforeTransmit`]: Only has the serialized request. -//! 3. [`phase::BeforeDeserialization`]: Has the raw response. -//! 3. [`phase::AfterDeserialization`]: Has the raw response and the parsed response. +//! 1. [`Phase::BeforeSerialization`]: Only has the operation input. +//! 2. [`Phase::BeforeTransmit`]: Only has the serialized request. +//! 3. [`Phase::BeforeDeserialization`]: Has the raw response. +//! 3. [`Phase::AfterDeserialization`]: Has the raw response and the parsed response. //! //! When implementing hooks, if information from a previous phase is required, then implement //! an earlier hook to examine that context, and save off any necessary information into the -//! [`crate::config_bag::ConfigBag`] for later hooks to examine. Interior mutability is **NOT** +//! [`ConfigBag`] for later hooks to examine. Interior mutability is **NOT** //! recommended for storing request-specific information in your interceptor implementation. -//! Use the [`crate::config_bag::ConfigBag`] instead. +//! Use the [`ConfigBag`] instead. -use crate::client::interceptors::BoxError; -use crate::client::orchestrator::{HttpRequest, HttpResponse}; +/// Operation phases. +pub mod phase; +pub mod wrappers; + +use crate::client::orchestrator::{HttpRequest, HttpResponse, OrchestratorError}; use crate::config_bag::ConfigBag; use crate::type_erasure::{TypeErasedBox, TypeErasedError}; use aws_smithy_http::result::SdkError; +use phase::Phase; +use std::fmt::Debug; +use std::mem; +use tracing::{error, trace}; pub type Input = TypeErasedBox; pub type Output = TypeErasedBox; pub type Error = TypeErasedError; -pub type OutputOrError = Result; +pub type OutputOrError = Result>; type Request = HttpRequest; type Response = HttpResponse; -/// Operation phases. -pub mod phase { - use crate::client::interceptors::context::{Error, Output}; - use crate::client::interceptors::BoxError; - use crate::client::orchestrator::HttpResponse; - use aws_smithy_http::result::{ConnectorError, SdkError}; - - macro_rules! impl_phase { - ($phase:ty, $convert_err:ident) => { - impl Phase for $phase { - fn convert_error( - &self, - error: BoxError, - output_or_error: Option>, - response: Option, - ) -> SdkError { - $convert_err(error, output_or_error, response) - } - } - }; - } - - #[doc(hidden)] - pub trait Phase { - fn convert_error( - &self, - error: BoxError, - output_or_error: Option>, - response: Option, - ) -> SdkError; - } - - fn convert_construction_failure( - error: BoxError, - _: Option>, - _: Option, - ) -> SdkError { - SdkError::construction_failure(error) - } - - fn convert_dispatch_error( - error: BoxError, - _: Option>, - response: Option, - ) -> SdkError { - let error = match error.downcast::() { - Ok(connector_error) => { - return SdkError::dispatch_failure(*connector_error); - } - Err(e) => e, - }; - if let Some(response) = response { - SdkError::response_error(error, response) - } else { - SdkError::dispatch_failure(ConnectorError::other(error, None)) - } - } - - fn convert_response_handling_error( - error: BoxError, - output_or_error: Option>, - response: Option, - ) -> SdkError { - match (response, output_or_error) { - (Some(response), Some(Err(error))) => SdkError::service_error(error, response), - (Some(response), _) => SdkError::response_error(error, response), - _ => unreachable!("phase has a response"), - } - } - - /// Represents the phase of an operation prior to serialization. - #[derive(Default, Debug)] - #[non_exhaustive] - pub struct BeforeSerialization; - impl_phase!(BeforeSerialization, convert_construction_failure); - - #[doc(hidden)] // This one isn't exposed in the interceptors, but is used internally - #[derive(Default, Debug)] - #[non_exhaustive] - pub struct Serialization; - impl_phase!(Serialization, convert_construction_failure); - - /// Represents the phase of an operation prior to transmitting a request over the network. - #[derive(Default, Debug)] - #[non_exhaustive] - pub struct BeforeTransmit; - impl_phase!(BeforeTransmit, convert_dispatch_error); - - #[doc(hidden)] // This one isn't exposed in the interceptors, but is used internally - #[derive(Default, Debug)] - #[non_exhaustive] - pub struct Transmit; - impl_phase!(Transmit, convert_dispatch_error); - - /// Represents the phase of an operation after receiving a response, but before parsing that response. - #[derive(Default, Debug)] - #[non_exhaustive] - pub struct BeforeDeserialization; - impl_phase!(BeforeDeserialization, convert_response_handling_error); - - #[doc(hidden)] // This one isn't exposed in the interceptors, but is used internally - #[derive(Default, Debug)] - #[non_exhaustive] - pub struct Deserialization; - impl_phase!(Deserialization, convert_response_handling_error); - - /// Represents the phase of an operation after parsing a response. - #[derive(Default, Debug)] - #[non_exhaustive] - pub struct AfterDeserialization; - impl_phase!(AfterDeserialization, convert_response_handling_error); -} - /// A container for the data currently available to an interceptor. /// /// Different context is available based on which phase the operation is currently in. For example, -/// context in the [`phase::BeforeSerialization`] phase won't have a `request` yet since the input hasn't been -/// serialized at that point. But once it gets into the [`phase::BeforeTransmit`] phase, the `request` will be set. -pub struct InterceptorContext { - input: Option, - output_or_error: Option>, - request: Option, - response: Option, +/// context in the [`Phase::BeforeSerialization`] phase won't have a `request` yet since the input hasn't been +/// serialized at that point. But once it gets into the [`Phase::BeforeTransmit`] phase, the `request` will be set. +#[derive(Debug)] +pub struct InterceptorContext +where + E: Debug, +{ + pub(crate) input: Option, + pub(crate) output_or_error: Option>>, + pub(crate) request: Option, + pub(crate) response: Option, phase: Phase, + tainted: bool, + request_checkpoint: Option, } -// -// All phases -// -impl InterceptorContext<(), Input, Output, Error> { - /// Creates a new interceptor context in the [`phase::BeforeSerialization`] phase. - pub fn new( - input: Input, - ) -> InterceptorContext { +impl InterceptorContext { + /// Creates a new interceptor context in the [`Phase::BeforeSerialization`] phase. + pub fn new(input: Input) -> InterceptorContext { InterceptorContext { input: Some(input), output_or_error: None, request: None, response: None, - phase: Default::default(), + phase: Phase::BeforeSerialization, + tainted: false, + request_checkpoint: None, } } } -impl InterceptorContext { + +impl InterceptorContext +where + E: Debug, +{ /// Decomposes the context into its constituent parts. #[doc(hidden)] #[allow(clippy::type_complexity)] @@ -192,114 +93,60 @@ impl InterceptorContext { self, ) -> ( Option, - Option>, + Option>>, Option, Option, - Phase, ) { ( self.input, self.output_or_error, self.request, self.response, - self.phase, ) } -} -// -// BeforeSerialization phase methods -// -impl InterceptorContext { + pub fn finalize(self) -> Result> { + let Self { + output_or_error, + response, + phase, + .. + } = self; + output_or_error + .expect("output_or_error must always beset before finalize is called.") + .map_err(|error| OrchestratorError::into_sdk_error(error, &phase, response)) + } + /// Retrieve the input for the operation being invoked. pub fn input(&self) -> &I { self.input .as_ref() - .expect("input is present in phase::BeforeSerialization") + .expect("input is present in 'before serialization'") } /// Retrieve the input for the operation being invoked. pub fn input_mut(&mut self) -> &mut I { self.input .as_mut() - .expect("input is present in phase::BeforeSerialization") - } - - /// Advance to the next phase. - #[doc(hidden)] - pub fn into_serialization_phase(self) -> InterceptorContext { - InterceptorContext { - input: self.input, - output_or_error: self.output_or_error, - request: self.request, - response: self.response, - phase: phase::Serialization::default(), - } + .expect("input is present in 'before serialization'") } -} -// -// Serialization phase methods -// -impl InterceptorContext { /// Takes ownership of the input. pub fn take_input(&mut self) -> Option { self.input.take() } + /// Set the request for the operation being invoked. pub fn set_request(&mut self, request: Request) { - debug_assert!( - self.request.is_none(), - "called set_request but a request was already set" - ); self.request = Some(request); } - /// Advance to the next phase. - #[doc(hidden)] - pub fn into_before_transmit_phase(self) -> InterceptorContext { - debug_assert!( - self.input.is_none(), - "input must be taken before going into phase::BeforeTransmit" - ); - debug_assert!( - self.request.is_some(), - "request must be set before going into phase::BeforeTransmit" - ); - InterceptorContext { - input: self.input, - output_or_error: self.output_or_error, - request: self.request, - response: self.response, - phase: Default::default(), - } - } -} - -// -// BeforeTransmit phase methods -// -impl InterceptorContext { - /// Creates a new interceptor context in the [`phase::BeforeTransmit`] phase. - pub fn new( - input: Option, - request: HttpRequest, - ) -> InterceptorContext { - InterceptorContext { - input, - output_or_error: None, - request: Some(request), - response: None, - phase: Default::default(), - } - } - /// Retrieve the transmittable request for the operation being invoked. /// This will only be available once request marshalling has completed. pub fn request(&self) -> &Request { self.request .as_ref() - .expect("request populated in phase::BeforeTransmit") + .expect("request populated in 'before transmit'") } /// Retrieve the transmittable request for the operation being invoked. @@ -307,266 +154,135 @@ impl InterceptorContext { pub fn request_mut(&mut self) -> &mut Request { self.request .as_mut() - .expect("request populated in phase::BeforeTransmit") + .expect("request populated in 'before transmit'") } - #[doc(hidden)] - pub fn into_transmit_phase(self) -> InterceptorContext { - InterceptorContext { - input: self.input, - output_or_error: self.output_or_error, - request: self.request, - response: self.response, - phase: Default::default(), - } - } -} - -// -// Transmit phase methods -// -impl InterceptorContext { /// Takes ownership of the request. - #[doc(hidden)] pub fn take_request(&mut self) -> Request { - debug_assert!(self.request.is_some()); self.request .take() - .expect("take request once during transmit") + .expect("take request once during 'transmit'") } - #[doc(hidden)] + /// Set the response for the operation being invoked. pub fn set_response(&mut self, response: Response) { - debug_assert!( - self.response.is_none(), - "called set_response but a response was already set" - ); self.response = Some(response); } - #[doc(hidden)] - pub fn into_before_deserialization_phase( - self, - ) -> InterceptorContext { - debug_assert!( - self.request.is_none(), - "request must be taken before going into phase::BeforeDeserialization" - ); - debug_assert!( - self.response.is_some(), - "response must be set to before going into phase::BeforeDeserialization" - ); - InterceptorContext { - input: self.input, - output_or_error: self.output_or_error, - request: self.request, - response: self.response, - phase: Default::default(), - } - } -} - -impl InterceptorContext { - /// Returns the response. - pub fn response(&self) -> &Response { - self.response - .as_ref() - .expect("response set in phase::BeforeDeserialization") - } - - /// Returns a mutable reference to the response. - pub fn response_mut(&mut self) -> &mut Response { - self.response - .as_mut() - .expect("response set in phase::BeforeDeserialization") - } - - #[doc(hidden)] - pub fn into_deserialization_phase(self) -> InterceptorContext { - InterceptorContext { - input: self.input, - output_or_error: self.output_or_error, - request: self.request, - response: self.response, - phase: Default::default(), - } - } -} - -impl InterceptorContext { /// Returns the response. pub fn response(&self) -> &Response { - self.response - .as_ref() - .expect("response set in phase::Deserialization") + self.response.as_ref().expect( + "response set in 'before deserialization' and available in the phases following it", + ) } /// Returns a mutable reference to the response. pub fn response_mut(&mut self) -> &mut Response { - self.response - .as_mut() - .expect("response set in phase::Deserialization") + self.response.as_mut().expect( + "response is set in 'before deserialization' and available in the following phases", + ) } - #[doc(hidden)] - pub fn set_output_or_error(&mut self, output: Result) { - debug_assert!(self.output_or_error.is_none()); + /// Set the output or error for the operation being invoked. + pub fn set_output_or_error(&mut self, output: Result>) { self.output_or_error = Some(output); } - #[doc(hidden)] - pub fn into_after_deserialization_phase( - self, - ) -> InterceptorContext { - debug_assert!( - self.output_or_error.is_some(), - "output must be set to before going into phase::AfterDeserialization" - ); - InterceptorContext { - input: self.input, - output_or_error: self.output_or_error, - request: self.request, - response: self.response, - phase: Default::default(), - } - } -} - -impl InterceptorContext { - /// Returns the response. - pub fn response(&self) -> &Response { - self.response - .as_ref() - .expect("response set in phase::BeforeDeserialization") - } - - /// Returns a mutable reference to the response. - pub fn response_mut(&mut self) -> &mut Response { - self.response - .as_mut() - .expect("response set in phase::BeforeDeserialization") - } - /// Returns the deserialized output or error. - pub fn output_or_error(&self) -> Result<&O, &E> { + pub fn output_or_error(&self) -> Result<&O, &OrchestratorError> { self.output_or_error .as_ref() - .expect("output set in phase::AfterDeserialization") + .expect("output set in Phase::AfterDeserialization") .as_ref() } /// Returns the mutable reference to the deserialized output or error. - pub fn output_or_error_mut(&mut self) -> &mut Result { + pub fn output_or_error_mut(&mut self) -> &mut Result> { self.output_or_error .as_mut() - .expect("output set in phase::AfterDeserialization") + .expect("output set in 'after deserialization'") } + /// Advance to the Serialization phase. #[doc(hidden)] - pub fn finalize(self) -> Result> { - self.output_or_error - .expect("output already populated in the response handling phase") - .map_err(|error| { - SdkError::service_error( - error, - self.response - .expect("raw response already populated in the response handling phase"), - ) - }) - } -} - -// This isn't great since it relies on a lot of runtime checking, but the -// compiler doesn't exactly make it easy to handle phase changes in a `loop`. -#[doc(hidden)] -pub struct AttemptCheckpoint { - tainted: bool, - checkpointed_request: Option, - before_transmit: Option>, - transmit: Option>, - before_deserialization: Option>, - deserialization: Option>, - after_deserialization: Option>, -} - -impl AttemptCheckpoint { - pub fn new(before_transmit: InterceptorContext) -> Self { - Self { - tainted: false, - checkpointed_request: Self::try_clone(before_transmit.request()), - before_transmit: Some(before_transmit), - transmit: None, - before_deserialization: None, - deserialization: None, - after_deserialization: None, - } + pub fn enter_serialization_phase(&mut self) { + debug_assert!( + self.phase.is_before_serialization(), + "called enter_serialization_phase but phase is not before 'serialization'" + ); + self.phase = Phase::Serialization; } - pub fn before_transmit(&mut self) -> &mut InterceptorContext { + /// Advance to the BeforeTransmit phase. + #[doc(hidden)] + pub fn enter_before_transmit_phase(&mut self) { + debug_assert!( + self.phase.is_serialization(), + "called enter_before_transmit_phase but phase is not 'serialization'" + ); + debug_assert!( + self.input.is_none(), + "input must be taken before calling enter_before_transmit_phase" + ); + debug_assert!( + self.request.is_some(), + "request must be set before calling enter_before_transmit_phase" + ); + self.request_checkpoint = try_clone(self.request()); self.tainted = true; - self.before_transmit - .as_mut() - .expect("must be in the before transmit phase") + self.phase = Phase::BeforeTransmit; } - pub fn transmit(&mut self) -> &mut InterceptorContext { - self.transmit - .as_mut() - .expect("must be in the transmit phase") - } - - pub fn before_deser(&mut self) -> &mut InterceptorContext { - self.before_deserialization - .as_mut() - .expect("must be in the before deserialization phase") - } - - pub fn deser(&mut self) -> &mut InterceptorContext { - self.deserialization - .as_mut() - .expect("must be in the deserialization phase") - } - - pub fn after_deser(&mut self) -> &mut InterceptorContext { - self.after_deserialization - .as_mut() - .expect("must be in the after deserialization phase") - } - - pub fn transition_to_transmit(&mut self) { - self.transmit = Some( - self.before_transmit - .take() - .expect("must be in the before transmit phase") - .into_transmit_phase(), + /// Advance to the Transmit phase. + #[doc(hidden)] + pub fn enter_transmit_phase(&mut self) { + debug_assert!( + self.phase.is_before_transmit(), + "called enter_transmit_phase but phase is not before transmit" ); + self.phase = Phase::Transmit; } - pub fn transition_to_deserialization(&mut self) { - self.deserialization = Some( - self.before_deserialization - .take() - .expect("must be in the before deserialization phase") - .into_deserialization_phase(), - ) + /// Advance to the BeforeDeserialization phase. + #[doc(hidden)] + pub fn enter_before_deserialization_phase(&mut self) { + debug_assert!( + self.phase.is_transmit(), + "called enter_before_deserialization_phase but phase is not 'transmit'" + ); + debug_assert!( + self.request.is_none(), + "request must be taken before entering the 'before deserialization' phase" + ); + debug_assert!( + self.response.is_some(), + "response must be set to before entering the 'before deserialization' phase" + ); + self.phase = Phase::BeforeDeserialization; } - pub fn transition_to_before_deserialization(&mut self) { - self.before_deserialization = Some( - self.transmit - .take() - .expect("must be in the transmit phase") - .into_before_deserialization_phase(), - ) + /// Advance to the Deserialization phase. + #[doc(hidden)] + pub fn enter_deserialization_phase(&mut self) { + debug_assert!( + self.phase.is_before_deserialization(), + "called enter_deserialization_phase but phase is not 'before deserialization'" + ); + self.phase = Phase::Deserialization; } - pub fn transition_to_after_deserialization(&mut self) { - self.after_deserialization = Some( - self.deserialization - .take() - .expect("must be in the deserialization phase") - .into_after_deserialization_phase(), - ) + /// Advance to the AfterDeserialization phase. + #[doc(hidden)] + pub fn enter_after_deserialization_phase(&mut self) { + debug_assert!( + self.phase.is_deserialization(), + "called enter_after_deserialization_phase but phase is not 'deserialization'" + ); + debug_assert!( + self.output_or_error.is_some(), + "output must be set to before entering the 'after deserialization' phase" + ); + self.phase = Phase::AfterDeserialization; } // Returns false if rewinding isn't possible @@ -575,73 +291,55 @@ impl AttemptCheckpoint { if !self.tainted { return true; } - // If checkpointed_request was never set, then this is not a retryable request - if self.checkpointed_request.is_none() { + // If request_checkpoint was never set, then this is not a retryable request + if self.request_checkpoint.is_none() { return false; } // Otherwise, rewind back to the beginning of BeforeTransmit // TODO(enableNewSmithyRuntime): Also rewind the ConfigBag - fn into_input(context: InterceptorContext

) -> Option { - context.into_parts().0 - } - // Take the input from the current phase - let input = None - .or(self.before_transmit.take().map(into_input)) - .or(self.transmit.take().map(into_input)) - .or(self.before_deserialization.take().map(into_input)) - .or(self.deserialization.take().map(into_input)) - .or(self.after_deserialization.take().map(into_input)) - .expect("at least one phase must be in progress"); - let fresh_request = - Self::try_clone(self.checkpointed_request.as_ref().expect("checked above")) - .expect("cloneable request"); - self.before_transmit = Some(InterceptorContext::::new( - input, - fresh_request, - )); + self.phase = Phase::BeforeTransmit; + self.request = try_clone(self.request_checkpoint.as_ref().expect("checked above")); + self.response = None; + self.output_or_error = None; true } - pub fn into_error(self, reason: BoxError) -> SdkError { - fn err( - context: InterceptorContext

, - ) -> Box SdkError> { - Box::new(move |reason| { - let (_input, output_or_error, _request, response, phase) = context.into_parts(); - phase.convert_error(reason, output_or_error, response) - }) + /// Mark this context as failed due to errors during the operation. Any errors already contained + /// by the context will be replaced by the given error. + pub fn fail(&mut self, error: OrchestratorError) { + if !self.is_failed() { + trace!( + "orchestrator is transitioning to the 'failure' phase from the '{:?}' phase", + self.phase + ); + } + if let Some(Err(existing_err)) = mem::replace(&mut self.output_or_error, Some(Err(error))) { + error!("orchestrator context received an error but one was already present; Throwing away previous error: {:?}", existing_err); } - // Convert the current phase into an error - (None - .or(self.before_transmit.map(err)) - .or(self.transmit.map(err)) - .or(self.before_deserialization.map(err)) - .or(self.deserialization.map(err)) - .or(self.after_deserialization.map(err)) - .expect("at least one phase must be in progress"))(reason) } - pub fn finalize(self) -> Result> { - self.after_deserialization - .expect("must be in the after deserialization phase") - .finalize() + /// Return `true` if this context's `output_or_error` is an error. Otherwise, return `false`. + pub fn is_failed(&self) -> bool { + self.output_or_error + .as_ref() + .map(Result::is_err) + .unwrap_or_default() } +} - pub fn try_clone(request: &HttpRequest) -> Option { - let cloned_body = request.body().try_clone()?; - let mut cloned_request = ::http::Request::builder() - .uri(request.uri().clone()) - .method(request.method()); - *cloned_request - .headers_mut() - .expect("builder has not been modified, headers must be valid") = - request.headers().clone(); - Some( - cloned_request - .body(cloned_body) - .expect("a clone of a valid request should be a valid request"), - ) - } +fn try_clone(request: &HttpRequest) -> Option { + let cloned_body = request.body().try_clone()?; + let mut cloned_request = ::http::Request::builder() + .uri(request.uri().clone()) + .method(request.method()); + *cloned_request + .headers_mut() + .expect("builder has not been modified, headers must be valid") = request.headers().clone(); + Some( + cloned_request + .body(cloned_body) + .expect("a clone of a valid request should be a valid request"), + ) } #[cfg(test)] @@ -657,40 +355,38 @@ mod tests { let input = TypedBox::new("input".to_string()).erase(); let output = TypedBox::new("output".to_string()).erase(); - let mut context = InterceptorContext::<()>::new(input); + let mut context = InterceptorContext::new(input); assert_eq!("input", context.input().downcast_ref::().unwrap()); context.input_mut(); - let mut context = context.into_serialization_phase(); + context.enter_serialization_phase(); let _ = context.take_input(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); - let mut checkpoint = AttemptCheckpoint::new(context.into_before_transmit_phase()); - checkpoint.before_transmit().request(); - checkpoint.before_transmit().request_mut(); + context.enter_before_transmit_phase(); + context.request(); + context.request_mut(); - checkpoint.transition_to_transmit(); - let _ = checkpoint.transmit().take_request(); - checkpoint - .transmit() - .set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); + context.enter_transmit_phase(); + let _ = context.take_request(); + context.set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); - checkpoint.transition_to_before_deserialization(); - checkpoint.before_deser().response(); - checkpoint.before_deser().response_mut(); + context.enter_before_deserialization_phase(); + context.response(); + context.response_mut(); - checkpoint.transition_to_deserialization(); - checkpoint.deser().response(); - checkpoint.deser().response_mut(); - checkpoint.deser().set_output_or_error(Ok(output)); + context.enter_deserialization_phase(); + context.response(); + context.response_mut(); + context.set_output_or_error(Ok(output)); - checkpoint.transition_to_after_deserialization(); - checkpoint.after_deser().response(); - checkpoint.after_deser().response_mut(); - let _ = checkpoint.after_deser().output_or_error(); - let _ = checkpoint.after_deser().output_or_error_mut(); + context.enter_after_deserialization_phase(); + context.response(); + context.response_mut(); + let _ = context.output_or_error(); + let _ = context.output_or_error_mut(); - let output = checkpoint.finalize().expect("success"); + let output = context.output_or_error.unwrap().expect("success"); assert_eq!("output", output.downcast_ref::().unwrap()); } @@ -701,7 +397,7 @@ mod tests { struct Error; impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("dontcare") + f.write_str("don't care") } } impl std::error::Error for Error {} @@ -711,90 +407,72 @@ mod tests { let output = TypedBox::new("output".to_string()).erase(); let error = TypedBox::new(Error).erase_error(); - let context = InterceptorContext::<()>::new(input); + let mut context = InterceptorContext::new(input); assert_eq!("input", context.input().downcast_ref::().unwrap()); - let mut context = context.into_serialization_phase(); + context.enter_serialization_phase(); let _ = context.take_input(); context.set_request( http::Request::builder() - .header("test", "the-original-unmutated-request") + .header("test", "the-original-un-mutated-request") .body(SdkBody::empty()) .unwrap(), ); - - let mut checkpoint = AttemptCheckpoint::new(context.into_before_transmit_phase()); + context.enter_before_transmit_phase(); // Modify the test header post-checkpoint to simulate modifying the request for signing or a mutating interceptor - checkpoint - .before_transmit() - .request_mut() - .headers_mut() - .remove("test"); - checkpoint - .before_transmit() - .request_mut() - .headers_mut() - .insert( - "test", - HeaderValue::from_static("request-modified-after-signing"), - ); + context.request_mut().headers_mut().remove("test"); + context.request_mut().headers_mut().insert( + "test", + HeaderValue::from_static("request-modified-after-signing"), + ); - checkpoint.transition_to_transmit(); - let request = checkpoint.transmit().take_request(); + context.enter_transmit_phase(); + let request = context.take_request(); assert_eq!( "request-modified-after-signing", request.headers().get("test").unwrap() ); - checkpoint - .transmit() - .set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); + context.set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); - checkpoint.transition_to_before_deserialization(); - checkpoint.transition_to_deserialization(); - checkpoint.deser().set_output_or_error(Err(error)); + context.enter_before_deserialization_phase(); + context.enter_deserialization_phase(); + context.set_output_or_error(Err(OrchestratorError::operation(error))); - assert!(checkpoint.rewind(&mut cfg)); + assert!(context.rewind(&mut cfg)); // Now after rewinding, the test header should be its original value assert_eq!( - "the-original-unmutated-request", - checkpoint - .before_transmit() - .request() - .headers() - .get("test") - .unwrap() + "the-original-un-mutated-request", + context.request().headers().get("test").unwrap() ); - checkpoint.transition_to_transmit(); - let _ = checkpoint.transmit().take_request(); - checkpoint - .transmit() - .set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); + context.enter_transmit_phase(); + let _ = context.take_request(); + context.set_response(http::Response::builder().body(SdkBody::empty()).unwrap()); - checkpoint.transition_to_before_deserialization(); - checkpoint.transition_to_deserialization(); - checkpoint.deser().set_output_or_error(Ok(output)); + context.enter_before_deserialization_phase(); + context.enter_deserialization_phase(); + context.set_output_or_error(Ok(output)); - checkpoint.transition_to_after_deserialization(); + context.enter_after_deserialization_phase(); - let output = checkpoint.finalize().expect("success"); + let output = context.output_or_error.unwrap().expect("success"); assert_eq!("output", output.downcast_ref::().unwrap()); } #[test] fn try_clone_clones_all_data() { let request = ::http::Request::builder() - .uri(Uri::from_static("http://www.amazon.com")) + .uri(Uri::from_static("https://www.amazon.com")) .method("POST") .header(CONTENT_LENGTH, 456) .header(AUTHORIZATION, "Token: hello") .body(SdkBody::from("hello world!")) .expect("valid request"); - let cloned = AttemptCheckpoint::try_clone(&request).expect("request is cloneable"); + let cloned = try_clone(&request).expect("request is cloneable"); - assert_eq!(&Uri::from_static("http://www.amazon.com"), cloned.uri()); + assert_eq!(&Uri::from_static("https://www.amazon.com"), cloned.uri()); assert_eq!("POST", cloned.method()); assert_eq!(2, cloned.headers().len()); assert_eq!("Token: hello", cloned.headers().get(AUTHORIZATION).unwrap(),); diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/phase.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/phase.rs new file mode 100644 index 0000000000..01c7f2f12d --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/phase.rs @@ -0,0 +1,53 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[derive(Debug)] +#[non_exhaustive] +pub enum Phase { + /// Represents the phase of an operation prior to serialization. + BeforeSerialization, + /// Represents the phase of an operation where the request is serialized. + Serialization, + /// Represents the phase of an operation prior to transmitting a request over the network. + BeforeTransmit, + /// Represents the phase of an operation where the request is transmitted over the network. + Transmit, + /// Represents the phase of an operation prior to parsing a response. + BeforeDeserialization, + /// Represents the phase of an operation where the response is parsed. + Deserialization, + /// Represents the phase of an operation after parsing a response. + AfterDeserialization, +} + +impl Phase { + pub fn is_before_serialization(&self) -> bool { + matches!(self, Self::BeforeSerialization) + } + + pub fn is_serialization(&self) -> bool { + matches!(self, Self::Serialization) + } + + pub fn is_before_transmit(&self) -> bool { + matches!(self, Self::BeforeTransmit) + } + + pub fn is_transmit(&self) -> bool { + matches!(self, Self::Transmit) + } + + pub fn is_before_deserialization(&self) -> bool { + matches!(self, Self::BeforeDeserialization) + } + + pub fn is_deserialization(&self) -> bool { + matches!(self, Self::Deserialization) + } + + pub fn is_after_deserialization(&self) -> bool { + matches!(self, Self::AfterDeserialization) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs new file mode 100644 index 0000000000..b9db1e4042 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs @@ -0,0 +1,253 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::{Error, Input, InterceptorContext, Output}; +use crate::client::interceptors::context::{Request, Response}; +use crate::client::orchestrator::OrchestratorError; +use std::fmt::Debug; + +macro_rules! output { + (&Option>) => { + Option> + }; + (&Option<$ty:ty>) => { + Option<&$ty> + }; + (&mut Option<$ty:ty>) => { + Option<&mut $ty> + }; + (&Result<$o_ty:ty, $e_ty:ty>) => { + Result<&$o_ty, &$e_ty> + }; + (&$($tt:tt)+) => { + &$($tt)+ + }; + (&mut $($tt:tt)+) => { + &mut $($tt)+ + }; +} + +macro_rules! declare_method { + (&mut $name:ident, $inner_name:ident, $doc:literal, Option<$ty:ty>) => { + #[doc=$doc] + pub fn $name(&mut self) -> Option<&mut $ty> { + self.inner.$inner_name.as_ref() + } + }; + (&$name:ident, $inner_name:ident, $doc:literal, Option<$ty:ty>) => { + #[doc=$doc] + pub fn $name(&self) -> Option<$ty> { + self.inner.$inner_name.as_mut() + } + }; + (&mut $name:ident, $doc:literal, $($tt:tt)+) => { + #[doc=$doc] + pub fn $name(&mut self) -> output!(&mut $($tt)+) { + self.inner.$name() + } + }; + (&$name:ident, $doc:literal, $($tt:tt)+) => { + #[doc=$doc] + pub fn $name(&self) -> output!(&$($tt)+) { + self.inner.$name() + } + }; +} + +macro_rules! declare_known_method { + (output_or_error: &mut $($tt:tt)+) => { + declare_method!(&mut output_or_error_mut, "Returns a mutable reference to the deserialized output or error.", $($tt)+); + }; + (output_or_error: &$($tt:tt)+) => { + declare_method!(&output_or_error, "Returns a reference to the deserialized output or error.", $($tt)+); + }; + (input: &mut $($tt:tt)+) => { + declare_method!(&mut input_mut, "Returns a mutable reference to the input.", $($tt)+); + }; + (input: &$($tt:tt)+) => { + declare_method!(&input, "Returns a reference to the input.", $($tt)+); + }; + (request: &mut $($tt:tt)+) => { + declare_method!(&mut request_mut, "Returns a mutable reference to the transmittable request for the operation being invoked.", $($tt)+); + }; + (request: &$($tt:tt)+) => { + declare_method!(&request, "Returns a reference to the transmittable request for the operation being invoked.", $($tt)+); + }; + (response: &mut $($tt:tt)+) => { + declare_method!(&mut response_mut, "Returns a mutable reference to the response.", $($tt)+); + }; + (response: &$($tt:tt)+) => { + declare_method!(&response, "Returns a reference to the response.", $($tt)+); + }; +} + +macro_rules! declare_wrapper { + (($ref_struct_name:ident $mut_struct_name:ident)$($tt:tt)+) => { + pub struct $ref_struct_name<'a, I = Input, O = Output, E = Error> + where E: Debug { + inner: &'a InterceptorContext, + } + + impl<'a, I, O, E: Debug> From<&'a InterceptorContext> for $ref_struct_name<'a, I, O, E> + { + fn from(inner: &'a InterceptorContext) -> Self { + Self { inner } + } + } + + impl<'a, I, O, E: Debug> $ref_struct_name<'a, I, O, E> { + declare_ref_wrapper_methods!($($tt)+); + } + + pub struct $mut_struct_name<'a, I = Input, O = Output, E = Error> + where E: Debug { + inner: &'a mut InterceptorContext, + } + + impl<'a, I, O, E: Debug> From<&'a mut InterceptorContext> for $mut_struct_name<'a, I, O, E> + { + fn from(inner: &'a mut InterceptorContext) -> Self { + Self { inner } + } + } + + impl<'a, I, O, E: Debug> $mut_struct_name<'a, I, O, E> { + declare_ref_wrapper_methods!($($tt)+); + declare_mut_wrapper_methods!($($tt)+); + } + }; +} + +macro_rules! declare_ref_wrapper_methods { + (($field:ident: $($head:tt)+)$($tail:tt)+) => { + declare_known_method!($field: &$($head)+); + declare_ref_wrapper_methods!($($tail)+); + }; + (($field:ident: $($tt:tt)+)) => { + declare_known_method!($field: &$($tt)+); + }; +} + +macro_rules! declare_mut_wrapper_methods { + (($field:ident: $($head:tt)+)$($tail:tt)+) => { + declare_known_method!($field: &mut $($head)+); + declare_mut_wrapper_methods!($($tail)+); + }; + (($field:ident: $($tt:tt)+)) => { + declare_known_method!($field: &mut $($tt)+); + }; +} + +declare_wrapper!( + (BeforeSerializationInterceptorContextRef BeforeSerializationInterceptorContextMut) + (input: I) +); + +declare_wrapper!( + (BeforeTransmitInterceptorContextRef BeforeTransmitInterceptorContextMut) + (input: I) + (request: Request) +); + +declare_wrapper!( + (BeforeDeserializationInterceptorContextRef BeforeDeserializationInterceptorContextMut) + (input: I) + (request: Request) + (response: Response) +); + +declare_wrapper!( + (AfterDeserializationInterceptorContextRef AfterDeserializationInterceptorContextMut) + (input: I) + (request: Request) + (response: Response) + (output_or_error: Result>) +); + +// Why are all the rest of these defined with a macro but these last two aren't? I simply ran out of +// time. Consider updating the macros to support these last two if you're looking for a challenge. +// - Zelda + +pub struct FinalizerInterceptorContextRef<'a, I = Input, O = Output, E = Error> +where + E: Debug, +{ + inner: &'a InterceptorContext, +} + +impl<'a, I, O, E: Debug> From<&'a InterceptorContext> + for FinalizerInterceptorContextRef<'a, I, O, E> +{ + fn from(inner: &'a InterceptorContext) -> Self { + Self { inner } + } +} + +impl<'a, I, O, E: Debug> FinalizerInterceptorContextRef<'a, I, O, E> { + pub fn input(&self) -> Option<&I> { + self.inner.input.as_ref() + } + + pub fn request(&self) -> Option<&Request> { + self.inner.request.as_ref() + } + + pub fn response(&self) -> Option<&Response> { + self.inner.response.as_ref() + } + + pub fn output_or_error(&self) -> Option>> { + self.inner.output_or_error.as_ref().map(|o| o.as_ref()) + } +} + +pub struct FinalizerInterceptorContextMut<'a, I = Input, O = Output, E = Error> +where + E: Debug, +{ + inner: &'a mut InterceptorContext, +} + +impl<'a, I, O, E: Debug> From<&'a mut InterceptorContext> + for FinalizerInterceptorContextMut<'a, I, O, E> +{ + fn from(inner: &'a mut InterceptorContext) -> Self { + Self { inner } + } +} + +impl<'a, I, O, E: Debug> FinalizerInterceptorContextMut<'a, I, O, E> { + pub fn input(&self) -> Option<&I> { + self.inner.input.as_ref() + } + + pub fn request(&self) -> Option<&Request> { + self.inner.request.as_ref() + } + + pub fn response(&self) -> Option<&Response> { + self.inner.response.as_ref() + } + + pub fn output_or_error(&self) -> Option>> { + self.inner.output_or_error.as_ref().map(|o| o.as_ref()) + } + + pub fn input_mut(&mut self) -> Option<&mut I> { + self.inner.input.as_mut() + } + + pub fn request_mut(&mut self) -> Option<&mut Request> { + self.inner.request.as_mut() + } + + pub fn response_mut(&mut self) -> Option<&mut Response> { + self.inner.response.as_mut() + } + + pub fn output_or_error_mut(&mut self) -> Option<&mut Result>> { + self.inner.output_or_error.as_mut() + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs index 8d8b828971..4026269779 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs @@ -61,6 +61,11 @@ impl InterceptorError { interceptor_error_fn!(modify_before_completion => ModifyBeforeCompletion (with source)); interceptor_error_fn!(read_after_execution => ReadAfterExecution (with source)); + interceptor_error_fn!(modify_before_attempt_completion_failed => ModifyBeforeAttemptCompletion (with source)); + interceptor_error_fn!(read_after_attempt_failed => ReadAfterAttempt (with source)); + interceptor_error_fn!(modify_before_completion_failed => ModifyBeforeCompletion (with source)); + interceptor_error_fn!(read_after_execution_failed => ReadAfterExecution (with source)); + interceptor_error_fn!(invalid_request_access => InvalidRequestAccess (invalid request access)); interceptor_error_fn!(invalid_response_access => InvalidResponseAccess (invalid response access)); interceptor_error_fn!(invalid_input_access => InvalidInputAccess (invalid input access)); diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 37c379a109..c2c9e9cd54 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +/// Errors that can occur while running the orchestrator. +mod error; + use crate::client::auth::{AuthOptionResolver, AuthOptionResolverParams, HttpAuthSchemes}; use crate::client::identity::IdentityResolvers; use crate::client::interceptors::context::{Error, Input, Output}; @@ -20,6 +23,8 @@ use std::pin::Pin; use std::sync::Arc; use std::time::SystemTime; +pub use error::OrchestratorError; + pub type HttpRequest = http::Request; pub type HttpResponse = http::Response; pub type BoxError = Box; @@ -31,12 +36,18 @@ pub trait RequestSerializer: Send + Sync + fmt::Debug { } pub trait ResponseDeserializer: Send + Sync + fmt::Debug { - fn deserialize_streaming(&self, response: &mut HttpResponse) -> Option> { + fn deserialize_streaming( + &self, + response: &mut HttpResponse, + ) -> Option>> { let _ = response; None } - fn deserialize_nonstreaming(&self, response: &HttpResponse) -> Result; + fn deserialize_nonstreaming( + &self, + response: &HttpResponse, + ) -> Result>; } pub trait Connection: Send + Sync + fmt::Debug { diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs new file mode 100644 index 0000000000..36b12d9d29 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs @@ -0,0 +1,117 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::BoxError; +use crate::client::interceptors::context::phase::Phase; +use crate::client::interceptors::InterceptorError; +use crate::client::orchestrator::HttpResponse; +use crate::type_erasure::TypeErasedError; +use aws_smithy_http::result::{ConnectorError, SdkError}; +use std::fmt::Debug; + +#[derive(Debug)] +#[non_exhaustive] +pub enum OrchestratorError { + /// An error occurred within an interceptor. + Interceptor { err: InterceptorError }, + /// An error returned by a service. + Operation { err: E }, + /// A general orchestrator error. + Other { err: BoxError }, +} + +impl OrchestratorError { + /// Create a new `OrchestratorError` from a [`BoxError`]. + pub fn other(err: BoxError) -> Self { + Self::Other { err } + } + + /// Create a new `OrchestratorError` from an error received from a service. + pub fn operation(err: E) -> Self { + Self::Operation { err } + } + + /// Create a new `OrchestratorError` from an [`InterceptorError`]. + pub fn interceptor(err: InterceptorError) -> Self { + Self::Interceptor { err } + } + + /// Convert the `OrchestratorError` into an [`SdkError`]. + pub fn into_sdk_error( + self, + phase: &Phase, + response: Option, + ) -> SdkError { + match self { + Self::Interceptor { err } => { + use Phase::*; + match phase { + BeforeSerialization | Serialization => SdkError::construction_failure(err), + BeforeTransmit | Transmit => match response { + Some(response) => SdkError::response_error(err, response), + None => SdkError::dispatch_failure(ConnectorError::other(err.into(), None)), + }, + BeforeDeserialization | Deserialization | AfterDeserialization => { + SdkError::response_error(err, response.expect("phase has a response")) + } + } + } + Self::Operation { err } => { + debug_assert!(phase.is_after_deserialization(), "operation errors are a result of successfully receiving and parsing a response from the server. Therefore, we must be in the 'After Deserialization' phase."); + SdkError::service_error(err, response.expect("phase has a response")) + } + Self::Other { err } => { + use Phase::*; + match phase { + BeforeSerialization | Serialization => SdkError::construction_failure(err), + BeforeTransmit | Transmit => convert_dispatch_error(err, response), + BeforeDeserialization | Deserialization | AfterDeserialization => { + SdkError::response_error(err, response.expect("phase has a response")) + } + } + } + } + } +} + +fn convert_dispatch_error( + err: BoxError, + response: Option, +) -> SdkError { + let err = match err.downcast::() { + Ok(connector_error) => { + return SdkError::dispatch_failure(*connector_error); + } + Err(e) => e, + }; + match response { + Some(response) => SdkError::response_error(err, response), + None => SdkError::dispatch_failure(ConnectorError::other(err, None)), + } +} + +impl From for OrchestratorError +where + E: Debug + std::error::Error + 'static, +{ + fn from(err: InterceptorError) -> Self { + Self::interceptor(err) + } +} + +impl From for OrchestratorError +where + E: Debug + std::error::Error + 'static, +{ + fn from(err: BoxError) -> Self { + Self::other(err) + } +} + +impl From for OrchestratorError { + fn from(err: TypeErasedError) -> Self { + Self::operation(err) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index 0d6678d359..fa42811ee1 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -3,10 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::client::interceptors::context::phase::AfterDeserialization; use crate::client::interceptors::context::Error; use crate::client::interceptors::InterceptorContext; -use crate::client::orchestrator::BoxError; +use crate::client::orchestrator::{BoxError, OrchestratorError}; use crate::config_bag::ConfigBag; use aws_smithy_types::retry::ErrorKind; use std::fmt::Debug; @@ -24,7 +23,7 @@ pub trait RetryStrategy: Send + Sync + Debug { fn should_attempt_retry( &self, - context: &InterceptorContext, + context: &InterceptorContext, cfg: &ConfigBag, ) -> Result; } @@ -40,7 +39,7 @@ pub enum RetryReason { pub trait ClassifyRetry: Send + Sync + Debug { /// Run this classifier against an error to determine if it should be retried. Returns /// `Some(RetryKind)` if the error should be retried; Otherwise returns `None`. - fn classify_retry(&self, error: &Error) -> Option; + fn classify_retry(&self, error: &OrchestratorError) -> Option; } #[derive(Debug)] @@ -68,7 +67,7 @@ impl RetryClassifiers { } impl ClassifyRetry for RetryClassifiers { - fn classify_retry(&self, error: &Error) -> Option { + fn classify_retry(&self, error: &OrchestratorError) -> Option { // return the first non-None result self.inner.iter().find_map(|cr| cr.classify_retry(error)) } diff --git a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs b/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs index f2269c1c44..e8043fc00e 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs @@ -226,7 +226,8 @@ impl TypeErasedError { #[cfg(test)] mod tests { - use super::*; + use super::{TypeErasedError, TypedBox}; + use std::fmt; #[derive(Debug)] struct Foo(&'static str); @@ -234,7 +235,7 @@ mod tests { struct Bar(isize); #[test] - fn test() { + fn test_typed_boxes() { let foo = TypedBox::new(Foo("1")); let bar = TypedBox::new(Bar(2)); @@ -268,4 +269,33 @@ mod tests { let foo = *foo_erased.downcast::().expect("it's a Foo"); assert_eq!("4", foo.0); } + + #[derive(Debug, Clone, PartialEq, Eq)] + struct TestErr { + inner: &'static str, + } + + impl TestErr { + fn new(inner: &'static str) -> Self { + Self { inner } + } + } + + impl fmt::Display for TestErr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error: {}", self.inner) + } + } + + impl std::error::Error for TestErr {} + + #[test] + fn test_typed_erased_errors_can_be_downcast() { + let test_err = TestErr::new("something failed!"); + let type_erased_test_err = TypeErasedError::new(test_err.clone()); + let actual = type_erased_test_err + .downcast::() + .expect("type erased error can be downcast into original type"); + assert_eq!(test_err, *actual); + } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs index 4e5de36b63..39ea3b35b3 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs @@ -4,8 +4,9 @@ */ use aws_smithy_http::body::SdkBody; -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; -use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeTransmitInterceptorContextMut, BoxError, Interceptor, +}; use aws_smithy_runtime_api::config_bag::ConfigBag; use std::fmt; use std::marker::PhantomData; @@ -37,7 +38,7 @@ where { fn modify_before_signing( &self, - context: &mut InterceptorContext, + context: &mut BeforeTransmitInterceptorContextMut<'_>, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let request = context.request_mut(); @@ -69,7 +70,7 @@ where { fn modify_before_signing( &self, - context: &mut InterceptorContext, + context: &mut BeforeTransmitInterceptorContextMut<'_>, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let request = context.request_mut(); diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index e3be9839c3..72cd3cbef5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -8,10 +8,7 @@ use crate::client::orchestrator::endpoints::orchestrate_endpoint; use crate::client::orchestrator::http::read_body; use crate::client::timeout::{MaybeTimeout, ProvideMaybeTimeoutConfig, TimeoutKind}; use aws_smithy_http::result::SdkError; -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeSerialization; -use aws_smithy_runtime_api::client::interceptors::context::{ - AttemptCheckpoint, Error, Input, Output, -}; +use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output}; use aws_smithy_runtime_api::client::interceptors::{InterceptorContext, Interceptors}; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpResponse}; use aws_smithy_runtime_api::client::retries::ShouldAttempt; @@ -25,41 +22,24 @@ pub mod endpoints; mod http; pub mod interceptors; -#[doc(hidden)] -#[macro_export] -macro_rules! handle_err { - ([$checkpoint:expr] => $expr:expr) => { +macro_rules! halt_on_err { + ([$ctx:ident] => $expr:expr) => { match $expr { Ok(ok) => ok, Err(err) => { - return Err($checkpoint.into_error(err.into())); - } - } - }; - ($ctx:expr => $expr:expr) => { - match $expr { - Ok(ok) => ok, - Err(err) => { - use aws_smithy_runtime_api::client::interceptors::context::phase::Phase; - let (_input, output_or_error, _request, response, phase) = $ctx.into_parts(); - return Err(phase.convert_error(err.into(), output_or_error, response)); + $ctx.fail(err); + return; } } }; } -#[doc(hidden)] -#[macro_export] -macro_rules! bail { - ([$checkpoint:expr], $reason:expr) => {{ - return Err($checkpoint.into_error($reason.into())); - }}; - ($ctx:expr, $reason:expr) => {{ - use aws_smithy_runtime_api::client::interceptors::context::phase::Phase; - let reason: BoxError = $reason.into(); - let (_input, output_or_error, _request, response, phase) = $ctx.into_parts(); - return Err(phase.convert_error(reason, output_or_error, response)); - }}; +macro_rules! continue_on_err { + ([$ctx:ident] => $expr:expr) => { + if let Err(err) = $expr { + $ctx.fail(err); + } + }; } #[tracing::instrument(skip_all)] @@ -71,134 +51,139 @@ pub async fn invoke( let cfg = &mut cfg; let mut interceptors = Interceptors::new(); - let context = InterceptorContext::<()>::new(input); - - // Client configuration - handle_err!(context => runtime_plugins.apply_client_configuration(cfg, interceptors.client_interceptors_mut())); - handle_err!(context => interceptors.client_read_before_execution(&context, cfg)); - // Operation configuration - handle_err!(context => runtime_plugins.apply_operation_configuration(cfg, interceptors.operation_interceptors_mut())); - handle_err!(context => interceptors.operation_read_before_execution(&context, cfg)); + let mut ctx = InterceptorContext::new(input); + if let Err(err) = apply_configuration(&mut ctx, cfg, &mut interceptors, runtime_plugins) { + return Err(SdkError::construction_failure(err)); + } let operation_timeout_config = cfg.maybe_timeout_config(TimeoutKind::Operation); - invoke_post_config(cfg, context, interceptors) - .maybe_timeout_with_config(operation_timeout_config) - .await + async { + // If running the pre-execution interceptors failed, then we skip running the op and run the + // final interceptors instead. + if !ctx.is_failed() { + try_op(&mut ctx, cfg, &interceptors).await; + } + finally_op(&mut ctx, cfg, &interceptors).await; + ctx.finalize() + } + .maybe_timeout_with_config(operation_timeout_config) + .await } -async fn invoke_post_config( +/// Apply configuration is responsible for apply runtime plugins to the config bag, as well as running +/// `read_before_execution` interceptors. If a failure occurs due to config construction, `invoke` +/// will raise it to the user. If an interceptor fails, then `invoke` +fn apply_configuration( + ctx: &mut InterceptorContext, cfg: &mut ConfigBag, - mut before_serialization: InterceptorContext, - interceptors: Interceptors, -) -> Result> { + interceptors: &mut Interceptors, + runtime_plugins: &RuntimePlugins, +) -> Result<(), BoxError> { + runtime_plugins.apply_client_configuration(cfg, interceptors.client_interceptors_mut())?; + continue_on_err!([ctx] =>interceptors.client_read_before_execution(ctx, cfg).map_err(Into::into)); + runtime_plugins + .apply_operation_configuration(cfg, interceptors.operation_interceptors_mut())?; + continue_on_err!([ctx] => interceptors.operation_read_before_execution(ctx, cfg).map_err(Into::into)); + + Ok(()) +} + +async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: &Interceptors) { // Before serialization - handle_err!(before_serialization => interceptors.read_before_serialization(&before_serialization, cfg)); - handle_err!(before_serialization => interceptors.modify_before_serialization(&mut before_serialization, cfg)); + halt_on_err!([ctx] => interceptors.read_before_serialization(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.modify_before_serialization(ctx, cfg).map_err(Into::into)); // Serialization - let mut serialization = before_serialization.into_serialization_phase(); + ctx.enter_serialization_phase(); { let request_serializer = cfg.request_serializer(); - let request = handle_err!(serialization => request_serializer - .serialize_input(serialization.take_input().expect("input set at this point"))); - serialization.set_request(request); + let input = ctx.take_input().expect("input set at this point"); + let request = + halt_on_err!([ctx] => request_serializer.serialize_input(input).map_err(Into::into)); + ctx.set_request(request); } // Before transmit - let mut before_transmit = serialization.into_before_transmit_phase(); - handle_err!(before_transmit => interceptors.read_after_serialization(&before_transmit, cfg)); - handle_err!(before_transmit => interceptors.modify_before_retry_loop(&mut before_transmit, cfg)); - - { - let retry_strategy = cfg.retry_strategy(); - match retry_strategy.should_attempt_initial_request(cfg) { - // Yes, let's make a request - Ok(ShouldAttempt::Yes) => {} - // No, this request shouldn't be sent - Ok(ShouldAttempt::No) => { - bail!(before_transmit, "The retry strategy indicates that an initial request shouldn't be made, but it didn't specify why."); - } - // No, we shouldn't make a request because... - Err(err) => bail!(before_transmit, err), - Ok(ShouldAttempt::YesAfterDelay(_)) => { - unreachable!("Delaying the initial request is currently unsupported. If this feature is important to you, please file an issue in GitHub.") - } + ctx.enter_before_transmit_phase(); + halt_on_err!([ctx] => interceptors.read_after_serialization(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.modify_before_retry_loop(ctx, cfg).map_err(Into::into)); + + let retry_strategy = cfg.retry_strategy(); + match retry_strategy.should_attempt_initial_request(cfg) { + // Yes, let's make a request + Ok(ShouldAttempt::Yes) => { /* Keep going */ } + // No, this request shouldn't be sent + Ok(ShouldAttempt::No) => { + let err: Box = "The retry strategy indicates that an initial request shouldn't be made, but it did specify why.".into(); + halt_on_err!([ctx] => Err(err.into())); + } + // No, we shouldn't make a request because... + Err(err) => halt_on_err!([ctx] => Err(err.into())), + Ok(ShouldAttempt::YesAfterDelay(_)) => { + unreachable!("Delaying the initial request is currently unsupported. If this feature is important to you, please file an issue in GitHub.") } } - let mut checkpoint = AttemptCheckpoint::new(before_transmit); - checkpoint = loop { - if !checkpoint.rewind(cfg) { - break checkpoint; - } + loop { let attempt_timeout_config = cfg.maybe_timeout_config(TimeoutKind::OperationAttempt); - - checkpoint = make_an_attempt(checkpoint, cfg, &interceptors) - .maybe_timeout_with_config(attempt_timeout_config) - .await?; - handle_err!([checkpoint] => interceptors.read_after_attempt(checkpoint.after_deser(), cfg)); - handle_err!([checkpoint] => interceptors.modify_before_attempt_completion(checkpoint.after_deser(), cfg)); - + async { + try_attempt(ctx, cfg, interceptors).await; + finally_attempt(ctx, cfg, interceptors).await; + Result::<_, SdkError>::Ok(()) + } + .maybe_timeout_with_config(attempt_timeout_config) + .await + .expect("These are infallible; The retry strategy will decide whether to stop or not."); let retry_strategy = cfg.retry_strategy(); - match retry_strategy.should_attempt_retry(checkpoint.after_deser(), cfg) { + let should_attempt = halt_on_err!([ctx] => retry_strategy.should_attempt_retry(ctx, cfg).map_err(Into::into)); + match should_attempt { // Yes, let's retry the request - Ok(ShouldAttempt::Yes) => continue, + ShouldAttempt::Yes => continue, // No, this request shouldn't be retried - Ok(ShouldAttempt::No) => {} - Ok(ShouldAttempt::YesAfterDelay(_delay)) => { + ShouldAttempt::No => { + break; + } + ShouldAttempt::YesAfterDelay(_delay) => { // TODO(enableNewSmithyRuntime): implement retries with explicit delay todo!("implement retries with an explicit delay.") } - // I couldn't determine if the request should be retried because an error occurred. - Err(err) => bail!([checkpoint], err), } - - break checkpoint; - }; - - handle_err!([checkpoint] => interceptors.modify_before_completion(checkpoint.after_deser(), cfg)); - handle_err!([checkpoint] => interceptors.read_after_execution(checkpoint.after_deser(), cfg)); - - checkpoint.finalize() + } } -// Making an HTTP request can fail for several reasons, but we still need to -// call lifecycle events when that happens. Therefore, we define this -// `make_an_attempt` function to make error handling simpler. -#[tracing::instrument(skip_all)] -async fn make_an_attempt( - mut checkpoint: AttemptCheckpoint, +async fn try_attempt( + ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: &Interceptors, -) -> Result> { - handle_err!([checkpoint] => interceptors.read_before_attempt(checkpoint.before_transmit(), cfg)); - handle_err!([checkpoint] => orchestrate_endpoint(checkpoint.before_transmit(), cfg)); - handle_err!([checkpoint] => interceptors.modify_before_signing(checkpoint.before_transmit(), cfg)); - handle_err!([checkpoint] => interceptors.read_before_signing(checkpoint.before_transmit(), cfg)); +) { + halt_on_err!([ctx] => interceptors.read_before_attempt(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => orchestrate_endpoint(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.modify_before_signing(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.read_before_signing(ctx, cfg).map_err(Into::into)); - checkpoint = orchestrate_auth(checkpoint, cfg).await?; + halt_on_err!([ctx] => orchestrate_auth(ctx, cfg).await.map_err(Into::into)); - handle_err!([checkpoint] => interceptors.read_after_signing(checkpoint.before_transmit(), cfg)); - handle_err!([checkpoint] => interceptors.modify_before_transmit(checkpoint.before_transmit(), cfg)); - handle_err!([checkpoint] => interceptors.read_before_transmit(checkpoint.before_transmit(), cfg)); + halt_on_err!([ctx] => interceptors.read_after_signing(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.modify_before_transmit(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.read_before_transmit(ctx, cfg).map_err(Into::into)); // The connection consumes the request but we need to keep a copy of it // within the interceptor context, so we clone it here. - checkpoint.transition_to_transmit(); - let call_result = handle_err!([checkpoint] => { - let request = checkpoint.transmit().take_request(); - cfg.connection().call(request).await + ctx.enter_transmit_phase(); + let call_result = halt_on_err!([ctx] => { + let request = ctx.take_request(); + cfg.connection().call(request).await.map_err(Into::into) }); - checkpoint.transmit().set_response(call_result); - checkpoint.transition_to_before_deserialization(); + ctx.set_response(call_result); + ctx.enter_before_deserialization_phase(); - handle_err!([checkpoint] => interceptors.read_after_transmit(checkpoint.before_deser(), cfg)); - handle_err!([checkpoint] => interceptors.modify_before_deserialization(checkpoint.before_deser(), cfg)); - handle_err!([checkpoint] => interceptors.read_before_deserialization(checkpoint.before_deser(), cfg)); + halt_on_err!([ctx] => interceptors.read_after_transmit(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.modify_before_deserialization(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.read_before_deserialization(ctx, cfg).map_err(Into::into)); - checkpoint.transition_to_deserialization(); - let output_or_error = handle_err!([checkpoint] => { - let response = checkpoint.deser().response_mut(); + ctx.enter_deserialization_phase(); + let output_or_error = async { + let response = ctx.response_mut(); let response_deserializer = cfg.response_deserializer(); match response_deserializer.deserialize_streaming(response) { Some(output_or_error) => Ok(output_or_error), @@ -207,14 +192,31 @@ async fn make_an_attempt( .await .map(|_| response_deserializer.deserialize_nonstreaming(response)), } - }); + } + .await + .expect("how should I insert this into the context?"); + ctx.set_output_or_error(output_or_error); - checkpoint.deser().set_output_or_error(output_or_error); + ctx.enter_after_deserialization_phase(); + halt_on_err!([ctx] => interceptors.read_after_deserialization(ctx, cfg).map_err(Into::into)); +} - checkpoint.transition_to_after_deserialization(); - handle_err!([checkpoint] => interceptors.read_after_deserialization(checkpoint.after_deser(), cfg)); +async fn finally_attempt( + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + interceptors: &Interceptors, +) { + continue_on_err!([ctx] => interceptors.modify_before_attempt_completion(ctx, cfg).map_err(Into::into)); + continue_on_err!([ctx] => interceptors.read_after_attempt(ctx, cfg).map_err(Into::into)); +} - Ok(checkpoint) +async fn finally_op( + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + interceptors: &Interceptors, +) { + continue_on_err!([ctx] => interceptors.modify_before_completion(ctx, cfg).map_err(Into::into)); + continue_on_err!([ctx] => interceptors.read_after_execution(ctx, cfg).map_err(Into::into)); } #[cfg(all(test, feature = "test-util", feature = "anonymous-auth"))] @@ -230,14 +232,20 @@ mod tests { serializer::CannedRequestSerializer, }; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::context::phase::{ - AfterDeserialization, BeforeDeserialization, BeforeSerialization, BeforeTransmit, + use aws_smithy_runtime_api::client::interceptors::context::wrappers::{ + FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, + }; + use aws_smithy_runtime_api::client::interceptors::context::Output; + use aws_smithy_runtime_api::client::interceptors::{ + AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, + BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, + BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, + BeforeTransmitInterceptorContextRef, }; - use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; use aws_smithy_runtime_api::client::interceptors::{ - Interceptor, InterceptorContext, InterceptorRegistrar, SharedInterceptor, + Interceptor, InterceptorRegistrar, SharedInterceptor, }; - use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; + use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, OrchestratorError}; use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin, RuntimePlugins}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_runtime_api::type_erasure::TypeErasedBox; @@ -257,7 +265,7 @@ mod tests { http::Response::builder() .status(StatusCode::OK) .body(SdkBody::empty()) - .map_err(|err| Error::new(Box::new(err))) + .map_err(|err| OrchestratorError::other(Box::new(err))) .map(|res| Output::new(Box::new(res))), ) } @@ -311,6 +319,21 @@ mod tests { } } + #[derive(Debug)] + struct FailingInterceptorsClientRuntimePlugin; + + impl RuntimePlugin for FailingInterceptorsClientRuntimePlugin { + fn configure( + &self, + _cfg: &mut ConfigBag, + interceptors: &mut InterceptorRegistrar, + ) -> Result<(), BoxError> { + interceptors.register(SharedInterceptor::new(FailingInterceptorA)); + + Ok(()) + } + } + #[derive(Debug)] struct FailingInterceptorsOperationRuntimePlugin; @@ -320,7 +343,6 @@ mod tests { _cfg: &mut ConfigBag, interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { - interceptors.register(SharedInterceptor::new(FailingInterceptorA)); interceptors.register(SharedInterceptor::new(FailingInterceptorB)); interceptors.register(SharedInterceptor::new(FailingInterceptorC)); @@ -330,6 +352,7 @@ mod tests { let input = TypeErasedBox::new(Box::new(())); let runtime_plugins = RuntimePlugins::new() + .with_client_plugin(FailingInterceptorsClientRuntimePlugin) .with_operation_plugin(TestOperationRuntimePlugin) .with_operation_plugin(AnonymousAuthRuntimePlugin) .with_operation_plugin(FailingInterceptorsOperationRuntimePlugin); @@ -351,7 +374,7 @@ mod tests { let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ReadBeforeExecution, source: Some(\"FailingInterceptorC\") } })""#.to_string(); interceptor_error_handling_test!( read_before_execution, - &InterceptorContext, + &BeforeSerializationInterceptorContextRef<'_>, expected ); } @@ -362,7 +385,7 @@ mod tests { let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeSerialization, source: Some(\"FailingInterceptorC\") } })""#.to_string(); interceptor_error_handling_test!( modify_before_serialization, - &mut InterceptorContext, + &mut BeforeSerializationInterceptorContextMut<'_>, expected ); } @@ -373,7 +396,7 @@ mod tests { let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ReadBeforeSerialization, source: Some(\"FailingInterceptorC\") } })""#.to_string(); interceptor_error_handling_test!( read_before_serialization, - &InterceptorContext, + &BeforeSerializationInterceptorContextRef<'_>, expected ); } @@ -384,7 +407,7 @@ mod tests { let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadAfterSerialization, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( read_after_serialization, - &InterceptorContext, + &BeforeTransmitInterceptorContextRef<'_>, expected ); } @@ -395,7 +418,7 @@ mod tests { let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeRetryLoop, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( modify_before_retry_loop, - &mut InterceptorContext, + &mut BeforeTransmitInterceptorContextMut<'_>, expected ); } @@ -406,7 +429,7 @@ mod tests { let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeAttempt, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( read_before_attempt, - &InterceptorContext, + &BeforeTransmitInterceptorContextRef<'_>, expected ); } @@ -417,7 +440,7 @@ mod tests { let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeSigning, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( modify_before_signing, - &mut InterceptorContext, + &mut BeforeTransmitInterceptorContextMut<'_>, expected ); } @@ -428,7 +451,7 @@ mod tests { let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeSigning, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( read_before_signing, - &InterceptorContext, + &BeforeTransmitInterceptorContextRef<'_>, expected ); } @@ -439,7 +462,7 @@ mod tests { let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadAfterSigning, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( read_after_signing, - &InterceptorContext, + &BeforeTransmitInterceptorContextRef<'_>, expected ); } @@ -450,7 +473,7 @@ mod tests { let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeTransmit, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( modify_before_transmit, - &mut InterceptorContext, + &mut BeforeTransmitInterceptorContextMut<'_>, expected ); } @@ -461,7 +484,7 @@ mod tests { let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeTransmit, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( read_before_transmit, - &InterceptorContext, + &BeforeTransmitInterceptorContextRef<'_>, expected ); } @@ -472,7 +495,7 @@ mod tests { let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterTransmit, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( read_after_transmit, - &InterceptorContext, + &BeforeDeserializationInterceptorContextRef<'_>, expected ); } @@ -483,7 +506,7 @@ mod tests { let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeDeserialization, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( modify_before_deserialization, - &mut InterceptorContext, + &mut BeforeDeserializationInterceptorContextMut<'_>, expected ); } @@ -494,7 +517,7 @@ mod tests { let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadBeforeDeserialization, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( read_before_deserialization, - &InterceptorContext, + &BeforeDeserializationInterceptorContextRef<'_>, expected ); } @@ -505,7 +528,7 @@ mod tests { let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterDeserialization, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( read_after_deserialization, - &InterceptorContext, + &AfterDeserializationInterceptorContextRef<'_>, expected ); } @@ -516,7 +539,7 @@ mod tests { let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( modify_before_attempt_completion, - &mut InterceptorContext, + &mut FinalizerInterceptorContextMut<'_>, expected ); } @@ -527,7 +550,7 @@ mod tests { let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterAttempt, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( read_after_attempt, - &InterceptorContext, + &FinalizerInterceptorContextRef<'_>, expected ); } @@ -538,7 +561,7 @@ mod tests { let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( modify_before_completion, - &mut InterceptorContext, + &mut FinalizerInterceptorContextMut<'_>, expected ); } @@ -549,7 +572,297 @@ mod tests { let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterExecution, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( read_after_execution, - &InterceptorContext, + &FinalizerInterceptorContextRef<'_>, + expected + ); + } + + macro_rules! interceptor_error_redirection_test { + ($origin_interceptor:ident, $origin_ctx:ty, $destination_interceptor:ident, $destination_ctx:ty, $expected:expr) => { + #[derive(Debug)] + struct OriginInterceptor; + impl Interceptor for OriginInterceptor { + fn $origin_interceptor( + &self, + _ctx: $origin_ctx, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + tracing::debug!("OriginInterceptor called!"); + Err("OriginInterceptor".into()) + } + } + + #[derive(Debug)] + struct DestinationInterceptor; + impl Interceptor for DestinationInterceptor { + fn $destination_interceptor( + &self, + _ctx: $destination_ctx, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + tracing::debug!("DestinationInterceptor called!"); + Err("DestinationInterceptor".into()) + } + } + + #[derive(Debug)] + struct InterceptorsTestOperationRuntimePlugin; + + impl RuntimePlugin for InterceptorsTestOperationRuntimePlugin { + fn configure( + &self, + _cfg: &mut ConfigBag, + interceptors: &mut InterceptorRegistrar, + ) -> Result<(), BoxError> { + interceptors.register(SharedInterceptor::new(OriginInterceptor)); + interceptors.register(SharedInterceptor::new(DestinationInterceptor)); + + Ok(()) + } + } + + let input = TypeErasedBox::new(Box::new(())); + let runtime_plugins = RuntimePlugins::new() + .with_operation_plugin(TestOperationRuntimePlugin) + .with_operation_plugin(AnonymousAuthRuntimePlugin) + .with_operation_plugin(InterceptorsTestOperationRuntimePlugin); + let actual = invoke(input, &runtime_plugins) + .await + .expect_err("should error"); + let actual = format!("{:?}", actual); + assert_eq!($expected, format!("{:?}", actual)); + + assert!(logs_contain("OriginInterceptor called!")); + assert!(logs_contain("DestinationInterceptor called!")); + }; + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_execution_error_causes_jump_to_modify_before_completion() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"DestinationInterceptor\") } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_execution, + &BeforeSerializationInterceptorContextRef<'_>, + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_serialization_error_causes_jump_to_modify_before_completion() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"DestinationInterceptor\") } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_serialization, + &mut BeforeSerializationInterceptorContextMut<'_>, + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_serialization_error_causes_jump_to_modify_before_completion() { + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"DestinationInterceptor\") } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_serialization, + &BeforeSerializationInterceptorContextRef<'_>, + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_serialization_error_causes_jump_to_modify_before_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + read_after_serialization, + &BeforeTransmitInterceptorContextRef<'_>, + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_retry_loop_error_causes_jump_to_modify_before_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_retry_loop, + &mut BeforeTransmitInterceptorContextMut<'_>, + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_attempt_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_attempt, + &BeforeTransmitInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_signing_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_signing, + &mut BeforeTransmitInterceptorContextMut<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_signing_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_signing, + &BeforeTransmitInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_signing_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + read_after_signing, + &BeforeTransmitInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_transmit_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_transmit, + &mut BeforeTransmitInterceptorContextMut<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_transmit_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_transmit, + &BeforeTransmitInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_transmit_error_causes_jump_to_modify_before_attempt_completion() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + read_after_transmit, + &BeforeDeserializationInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_deserialization_error_causes_jump_to_modify_before_attempt_completion( + ) { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_deserialization, + &mut BeforeDeserializationInterceptorContextMut<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_before_deserialization_error_causes_jump_to_modify_before_attempt_completion( + ) { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + read_before_deserialization, + &BeforeDeserializationInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_read_after_deserialization_error_causes_jump_to_modify_before_attempt_completion() + { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + read_after_deserialization, + &AfterDeserializationInterceptorContextRef<'_>, + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + expected + ); + } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_attempt_completion_error_causes_jump_to_read_after_attempt() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterAttempt, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_attempt_completion, + &mut FinalizerInterceptorContextMut<'_>, + read_after_attempt, + &FinalizerInterceptorContextRef<'_>, + expected + ); + } + + // #[tokio::test] + // #[traced_test] + // async fn test_read_after_attempt_error_causes_jump_to_modify_before_attempt_completion() { + // todo!("I'm confused by the behavior described in the spec") + // } + + #[tokio::test] + #[traced_test] + async fn test_modify_before_completion_error_causes_jump_to_read_after_execution() { + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterExecution, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + interceptor_error_redirection_test!( + modify_before_completion, + &mut FinalizerInterceptorContextMut<'_>, + read_after_execution, + &FinalizerInterceptorContextRef<'_>, expected ); } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index a3b053837a..883e628ada 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -3,25 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::{bail, handle_err}; -use aws_smithy_http::result::SdkError; -use aws_smithy_runtime_api::client::interceptors::context::{AttemptCheckpoint, Error}; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpResponse}; +use aws_smithy_runtime_api::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors}; use aws_smithy_runtime_api::config_bag::ConfigBag; +use std::fmt; pub(super) async fn orchestrate_auth( - mut checkpoint: AttemptCheckpoint, + ctx: &mut InterceptorContext, cfg: &ConfigBag, -) -> Result> { - fn construction_failure(err: impl Into) -> SdkError { - SdkError::construction_failure(err) - } - +) -> Result<(), BoxError> { let params = cfg.auth_option_resolver_params(); - let auth_options = cfg - .auth_option_resolver() - .resolve_auth_options(params) - .map_err(construction_failure)?; + let auth_options = cfg.auth_option_resolver().resolve_auth_options(params)?; let identity_resolvers = cfg.identity_resolvers(); tracing::trace!( @@ -35,23 +27,31 @@ pub(super) async fn orchestrate_auth( if let Some(identity_resolver) = auth_scheme.identity_resolver(identity_resolvers) { let request_signer = auth_scheme.request_signer(); - let identity = identity_resolver - .resolve_identity(cfg) - .await - .map_err(construction_failure)?; - let request = checkpoint.before_transmit().request_mut(); - handle_err!([checkpoint] => request_signer.sign_request(request, &identity, cfg)); - return Ok(checkpoint); + let identity = identity_resolver.resolve_identity(cfg).await?; + let request = ctx.request_mut(); + request_signer.sign_request(request, &identity, cfg)?; + return Ok(()); } } } - bail!( - [checkpoint], - "no auth scheme matched auth options. This is a bug. Please file an issue." - ); + Err(NoMatchingAuthScheme.into()) +} + +#[derive(Debug)] +struct NoMatchingAuthScheme; + +impl fmt::Display for NoMatchingAuthScheme { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "no auth scheme matched auth options. This is a bug. Please file an issue." + ) + } } +impl std::error::Error for NoMatchingAuthScheme {} + #[cfg(test)] mod tests { use super::*; @@ -115,11 +115,11 @@ mod tests { } } - let input = TypedBox::new("doesnt-matter").erase(); - let mut context = InterceptorContext::<()>::new(input).into_serialization_phase(); - context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); - let _ = context.take_input(); - let checkpoint = AttemptCheckpoint::new(context.into_before_transmit_phase()); + let mut ctx = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); + ctx.enter_serialization_phase(); + ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = ctx.take_input(); + ctx.enter_before_transmit_phase(); let mut cfg = ConfigBag::base(); cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); @@ -135,16 +135,11 @@ mod tests { .build(), ); - let mut checkpoint = orchestrate_auth(checkpoint, &cfg).await.expect("success"); + orchestrate_auth(&mut ctx, &cfg).await.expect("success"); assert_eq!( "success!", - checkpoint - .before_transmit() - .request() - .headers() - .get("Authorization") - .unwrap() + ctx.request().headers().get("Authorization").unwrap() ); } @@ -157,11 +152,11 @@ mod tests { }; use aws_smithy_runtime_api::client::identity::http::{Login, Token}; - let mut context = InterceptorContext::<()>::new(TypedBox::new("doesnt-matter").erase()) - .into_serialization_phase(); - context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); - let _ = context.take_input(); - let checkpoint = AttemptCheckpoint::new(context.into_before_transmit_phase()); + let mut ctx = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); + ctx.enter_serialization_phase(); + ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = ctx.take_input(); + ctx.enter_before_transmit_phase(); let mut cfg = ConfigBag::base(); cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); @@ -183,17 +178,11 @@ mod tests { .build(), ); - let mut checkpoint = orchestrate_auth(checkpoint, &cfg).await.expect("success"); - + orchestrate_auth(&mut ctx, &cfg).await.expect("success"); assert_eq!( // "YTpi" == "a:b" in base64 "Basic YTpi", - checkpoint - .before_transmit() - .request() - .headers() - .get("Authorization") - .unwrap() + ctx.request().headers().get("Authorization").unwrap() ); // Next, test the presence of a bearer token and absence of basic auth @@ -203,21 +192,15 @@ mod tests { .build(), ); - let mut context = InterceptorContext::<()>::new(TypedBox::new("doesnt-matter").erase()) - .into_serialization_phase(); - context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); - let _ = context.take_input(); - let checkpoint = AttemptCheckpoint::new(context.into_before_transmit_phase()); - - let mut checkpoint = orchestrate_auth(checkpoint, &cfg).await.expect("success"); + let mut ctx = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); + ctx.enter_serialization_phase(); + ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = ctx.take_input(); + ctx.enter_before_transmit_phase(); + orchestrate_auth(&mut ctx, &cfg).await.expect("success"); assert_eq!( "Bearer t", - checkpoint - .before_transmit() - .request() - .headers() - .get("Authorization") - .unwrap() + ctx.request().headers().get("Authorization").unwrap() ); } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 8761bc5d60..cb4e4e9c09 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -7,7 +7,6 @@ use aws_smithy_http::endpoint::error::ResolveEndpointError; use aws_smithy_http::endpoint::{ apply_endpoint, EndpointPrefix, ResolveEndpoint, SharedEndpointResolver, }; -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, EndpointResolver, EndpointResolverParams, HttpRequest, @@ -130,7 +129,7 @@ where } pub(super) fn orchestrate_endpoint( - ctx: &mut InterceptorContext, + ctx: &mut InterceptorContext, cfg: &ConfigBag, ) -> Result<(), BoxError> { let params = cfg.endpoint_resolver_params(); diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs index c69137b022..6d48abee6d 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; -use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeTransmitInterceptorContextMut, BoxError, Interceptor, +}; use aws_smithy_runtime_api::config_bag::ConfigBag; #[derive(Debug, Clone, Default)] @@ -48,7 +49,7 @@ impl RequestAttemptsInterceptor { impl Interceptor for RequestAttemptsInterceptor { fn modify_before_retry_loop( &self, - _ctx: &mut InterceptorContext, + _ctx: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { cfg.put(RequestAttempts::new()); @@ -57,7 +58,7 @@ impl Interceptor for RequestAttemptsInterceptor { fn modify_before_transmit( &self, - _ctx: &mut InterceptorContext, + _ctx: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { if let Some(request_attempts) = cfg.get::().cloned() { diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs index 579ecbb484..eb988c8572 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeDeserialization; -use aws_smithy_runtime_api::client::interceptors::{BoxError, Interceptor, InterceptorContext}; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeDeserializationInterceptorContextMut, BoxError, Interceptor, +}; use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_types::date_time::Format; use aws_smithy_types::DateTime; @@ -48,7 +49,7 @@ fn calculate_skew(time_sent: DateTime, time_received: DateTime) -> Duration { } fn extract_time_sent_from_response( - ctx: &mut InterceptorContext, + ctx: &mut BeforeDeserializationInterceptorContextMut<'_>, ) -> Result { let date_header = ctx .response() @@ -62,7 +63,7 @@ fn extract_time_sent_from_response( impl Interceptor for ServiceClockSkewInterceptor { fn modify_before_deserialization( &self, - ctx: &mut InterceptorContext, + ctx: &mut BeforeDeserializationInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let time_received = DateTime::from(SystemTime::now()); diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs index 1ee985c22e..d3f4abe569 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -4,7 +4,6 @@ */ use crate::client::orchestrator::interceptors::RequestAttempts; -use aws_smithy_runtime_api::client::interceptors::context::phase::AfterDeserialization; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::BoxError; use aws_smithy_runtime_api::client::retries::{ @@ -41,7 +40,7 @@ impl RetryStrategy for FixedDelayRetryStrategy { fn should_attempt_retry( &self, - ctx: &InterceptorContext, + ctx: &InterceptorContext, cfg: &ConfigBag, ) -> Result { // Look a the result. If it's OK then we're done; No retry required. Otherwise, we need to inspect it diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs index 3b1b56ffb7..f415af3691 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::interceptors::context::phase::AfterDeserialization; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::BoxError; use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; @@ -25,7 +24,7 @@ impl RetryStrategy for NeverRetryStrategy { fn should_attempt_retry( &self, - _context: &InterceptorContext, + _context: &InterceptorContext, _cfg: &ConfigBag, ) -> Result { Ok(ShouldAttempt::No) diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs index 3c7bc1610c..7df0e2fe78 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs @@ -6,7 +6,7 @@ use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, HttpResponse, ResponseDeserializer, + ConfigBagAccessors, HttpResponse, OrchestratorError, ResponseDeserializer, }; use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin}; use aws_smithy_runtime_api::config_bag::ConfigBag; @@ -14,17 +14,17 @@ use std::sync::Mutex; #[derive(Default, Debug)] pub struct CannedResponseDeserializer { - inner: Mutex>>, + inner: Mutex>>>, } impl CannedResponseDeserializer { - pub fn new(output: Result) -> Self { + pub fn new(output: Result>) -> Self { Self { inner: Mutex::new(Some(output)), } } - pub fn take(&self) -> Option> { + pub fn take(&self) -> Option>> { match self.inner.lock() { Ok(mut guard) => guard.take(), Err(_) => None, @@ -33,7 +33,10 @@ impl CannedResponseDeserializer { } impl ResponseDeserializer for CannedResponseDeserializer { - fn deserialize_nonstreaming(&self, _response: &HttpResponse) -> Result { + fn deserialize_nonstreaming( + &self, + _response: &HttpResponse, + ) -> Result> { self.take() .ok_or("CannedResponseDeserializer's inner value has already been taken.") .unwrap() From 4cc4fb1181006b8572c09a09803a25b47f4c53a8 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Thu, 18 May 2023 12:42:36 -0500 Subject: [PATCH 100/253] fix type-erasure bug for errors (#2713) ## Description We were unintentionally double-boxing type-erased errors. This fix correctly de-references the errors, solving the problem. ## Testing I wrote a test. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-smithy-runtime-api/src/type_erasure.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs b/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs index e8043fc00e..5f0f626050 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs @@ -69,8 +69,7 @@ where { /// Converts `TypedBox` to a `TypeErasedError` where `T` implements `Error`. pub fn erase_error(self) -> TypeErasedError { - let inner = self.inner.downcast::().expect("typechecked"); - TypeErasedError::new(inner) + TypeErasedError::new(self.unwrap()) } } @@ -298,4 +297,14 @@ mod tests { .expect("type erased error can be downcast into original type"); assert_eq!(test_err, *actual); } + + #[test] + fn test_typed_erased_errors_can_be_unwrapped() { + let test_err = TestErr::new("something failed!"); + let type_erased_test_err = TypedBox::new(test_err.clone()).erase_error(); + let actual = TypedBox::::assume_from(type_erased_test_err.into()) + .expect("type erased error can be downcast into original type") + .unwrap(); + assert_eq!(test_err, actual); + } } From 41774b8405de4bb8c7741075aee4e7397b2493a8 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Thu, 18 May 2023 16:12:00 -0500 Subject: [PATCH 101/253] Update AWS models (#2714) ## Motivation and Context Update AWS models based on the latest ones on 5/18/2023 ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- aws/sdk/aws-models/config.json | 344 +- aws/sdk/aws-models/ec2.json | 522 ++- aws/sdk/aws-models/ecs.json | 14 +- aws/sdk/aws-models/kms.json | 149 +- aws/sdk/aws-models/lambda.json | 6 + aws/sdk/aws-models/s3.json | 576 +-- aws/sdk/aws-models/sdk-endpoints.json | 3105 ++++++++++++++++- aws/sdk/aws-models/sts.json | 171 +- .../kms/tests/sensitive-it.rs | 4 +- 9 files changed, 4079 insertions(+), 812 deletions(-) diff --git a/aws/sdk/aws-models/config.json b/aws/sdk/aws-models/config.json index 92170f2532..6b4b82802d 100644 --- a/aws/sdk/aws-models/config.json +++ b/aws/sdk/aws-models/config.json @@ -13132,6 +13132,144 @@ "traits": { "smithy.api#enumValue": "AWS::Connect::PhoneNumber" } + }, + "AppConfigDeploymentStrategy": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AppConfig::DeploymentStrategy" + } + }, + "AppFlowFlow": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AppFlow::Flow" + } + }, + "AuditManagerAssessment": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::AuditManager::Assessment" + } + }, + "CloudWatchMetricStream": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::CloudWatch::MetricStream" + } + }, + "DeviceFarmInstanceProfile": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DeviceFarm::InstanceProfile" + } + }, + "DeviceFarmProject": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::DeviceFarm::Project" + } + }, + "EC2EC2Fleet": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::EC2Fleet" + } + }, + "EC2SubnetRouteTableAssociation": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::EC2::SubnetRouteTableAssociation" + } + }, + "ECRPullThroughCacheRule": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ECR::PullThroughCacheRule" + } + }, + "GroundStationConfig": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::GroundStation::Config" + } + }, + "ImageBuilderImagePipeline": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::ImageBuilder::ImagePipeline" + } + }, + "IoTFleetMetric": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoT::FleetMetric" + } + }, + "IoTWirelessServiceProfile": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::IoTWireless::ServiceProfile" + } + }, + "NetworkManagerDevice": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::NetworkManager::Device" + } + }, + "NetworkManagerGlobalNetwork": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::NetworkManager::GlobalNetwork" + } + }, + "NetworkManagerLink": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::NetworkManager::Link" + } + }, + "NetworkManagerSite": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::NetworkManager::Site" + } + }, + "PanoramaPackage": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Panorama::Package" + } + }, + "PinpointApp": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Pinpoint::App" + } + }, + "RedshiftScheduledAction": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Redshift::ScheduledAction" + } + }, + "Route53ResolverFirewallRuleGroupAssociation": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::Route53Resolver::FirewallRuleGroupAssociation" + } + }, + "SageMakerAppImageConfig": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SageMaker::AppImageConfig" + } + }, + "SageMakerImage": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "AWS::SageMaker::Image" + } } } }, @@ -14322,9 +14460,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "af-south-1", "UseFIPS": false, - "Region": "af-south-1" + "UseDualStack": false } }, { @@ -14335,9 +14473,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-east-1", "UseFIPS": false, - "Region": "ap-east-1" + "UseDualStack": false } }, { @@ -14348,9 +14486,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-1", "UseFIPS": false, - "Region": "ap-northeast-1" + "UseDualStack": false } }, { @@ -14361,9 +14499,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-2", "UseFIPS": false, - "Region": "ap-northeast-2" + "UseDualStack": false } }, { @@ -14374,9 +14512,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-northeast-3", "UseFIPS": false, - "Region": "ap-northeast-3" + "UseDualStack": false } }, { @@ -14387,9 +14525,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-south-1", "UseFIPS": false, - "Region": "ap-south-1" + "UseDualStack": false } }, { @@ -14400,9 +14538,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-1", "UseFIPS": false, - "Region": "ap-southeast-1" + "UseDualStack": false } }, { @@ -14413,9 +14551,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-2", "UseFIPS": false, - "Region": "ap-southeast-2" + "UseDualStack": false } }, { @@ -14426,9 +14564,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ap-southeast-3", "UseFIPS": false, - "Region": "ap-southeast-3" + "UseDualStack": false } }, { @@ -14439,9 +14577,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "ca-central-1", "UseFIPS": false, - "Region": "ca-central-1" + "UseDualStack": false } }, { @@ -14452,9 +14590,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-central-1", "UseFIPS": false, - "Region": "eu-central-1" + "UseDualStack": false } }, { @@ -14465,9 +14603,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-north-1", "UseFIPS": false, - "Region": "eu-north-1" + "UseDualStack": false } }, { @@ -14478,9 +14616,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-south-1", "UseFIPS": false, - "Region": "eu-south-1" + "UseDualStack": false } }, { @@ -14491,9 +14629,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-1", "UseFIPS": false, - "Region": "eu-west-1" + "UseDualStack": false } }, { @@ -14504,9 +14642,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-2", "UseFIPS": false, - "Region": "eu-west-2" + "UseDualStack": false } }, { @@ -14517,9 +14655,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "eu-west-3", "UseFIPS": false, - "Region": "eu-west-3" + "UseDualStack": false } }, { @@ -14530,9 +14668,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "me-south-1", "UseFIPS": false, - "Region": "me-south-1" + "UseDualStack": false } }, { @@ -14543,9 +14681,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "sa-east-1", "UseFIPS": false, - "Region": "sa-east-1" + "UseDualStack": false } }, { @@ -14556,9 +14694,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -14569,9 +14707,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -14582,9 +14720,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": false, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -14595,9 +14733,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-2", "UseFIPS": true, - "Region": "us-east-2" + "UseDualStack": false } }, { @@ -14608,9 +14746,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": false, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -14621,9 +14759,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-1", "UseFIPS": true, - "Region": "us-west-1" + "UseDualStack": false } }, { @@ -14634,9 +14772,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": false, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -14647,9 +14785,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": true, - "Region": "us-west-2" + "UseDualStack": false } }, { @@ -14660,9 +14798,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -14673,9 +14811,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -14686,9 +14824,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -14699,9 +14837,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-northwest-1", "UseFIPS": false, - "Region": "cn-northwest-1" + "UseDualStack": false } }, { @@ -14712,9 +14850,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -14725,9 +14863,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -14738,9 +14876,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -14751,9 +14889,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -14764,9 +14902,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -14777,9 +14915,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": false, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -14790,9 +14928,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-west-1", "UseFIPS": true, - "Region": "us-gov-west-1" + "UseDualStack": false } }, { @@ -14803,9 +14941,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -14816,9 +14954,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -14829,9 +14967,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": false, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -14842,9 +14980,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-west-1", "UseFIPS": false, - "Region": "us-iso-west-1" + "UseDualStack": false } }, { @@ -14853,9 +14991,9 @@ "error": "FIPS and DualStack are enabled, but this partition does not support one or both" }, "params": { - "UseDualStack": true, + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": true } }, { @@ -14866,9 +15004,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": false } }, { @@ -14877,9 +15015,9 @@ "error": "DualStack is enabled but this partition does not support DualStack" }, "params": { - "UseDualStack": true, + "Region": "us-iso-east-1", "UseFIPS": false, - "Region": "us-iso-east-1" + "UseDualStack": true } }, { @@ -14890,9 +15028,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": false, - "Region": "us-isob-east-1" + "UseDualStack": false } }, { @@ -14901,9 +15039,9 @@ "error": "FIPS and DualStack are enabled, but this partition does not support one or both" }, "params": { - "UseDualStack": true, + "Region": "us-isob-east-1", "UseFIPS": true, - "Region": "us-isob-east-1" + "UseDualStack": true } }, { @@ -14914,9 +15052,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": true, - "Region": "us-isob-east-1" + "UseDualStack": false } }, { @@ -14925,9 +15063,9 @@ "error": "DualStack is enabled but this partition does not support DualStack" }, "params": { - "UseDualStack": true, + "Region": "us-isob-east-1", "UseFIPS": false, - "Region": "us-isob-east-1" + "UseDualStack": true } }, { @@ -14938,9 +15076,9 @@ } }, "params": { - "UseDualStack": false, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -14952,8 +15090,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -14963,9 +15101,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseDualStack": false, - "UseFIPS": true, "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -14975,9 +15113,9 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseDualStack": true, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } }, diff --git a/aws/sdk/aws-models/ec2.json b/aws/sdk/aws-models/ec2.json index 47805fb881..ca86cbdf2b 100644 --- a/aws/sdk/aws-models/ec2.json +++ b/aws/sdk/aws-models/ec2.json @@ -4998,6 +4998,23 @@ } } }, + "com.amazonaws.ec2#AmdSevSnpSpecification": { + "type": "enum", + "members": { + "enabled": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "enabled" + } + }, + "disabled": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "disabled" + } + } + } + }, "com.amazonaws.ec2#AnalysisAclRule": { "type": "structure", "members": { @@ -7389,7 +7406,7 @@ "target": "com.amazonaws.ec2#AttachVerifiedAccessTrustProviderResult" }, "traits": { - "smithy.api#documentation": "

A trust provider is a third-party entity that creates, maintains, and manages identity\n information for users and devices. One or more trust providers can be attached to an Amazon Web Services Verified Access\n instance.

" + "smithy.api#documentation": "

Attaches the specified Amazon Web Services Verified Access trust provider to the specified Amazon Web Services Verified Access instance.

" } }, "com.amazonaws.ec2#AttachVerifiedAccessTrustProviderRequest": { @@ -7399,7 +7416,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, @@ -7407,7 +7424,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#required": {} } }, @@ -7438,7 +7455,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProvider", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProvider", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#xmlName": "verifiedAccessTrustProvider" } }, @@ -7446,7 +7463,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstance", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstance", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#xmlName": "verifiedAccessInstance" } } @@ -9711,7 +9728,7 @@ "target": "com.amazonaws.ec2#SpotInstanceRequestIdList", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

One or more Spot Instance request IDs.

", + "smithy.api#documentation": "

The IDs of the Spot Instance requests.

", "smithy.api#required": {}, "smithy.api#xmlName": "SpotInstanceRequestId" } @@ -9729,7 +9746,7 @@ "target": "com.amazonaws.ec2#CancelledSpotInstanceRequestList", "traits": { "aws.protocols#ec2QueryName": "SpotInstanceRequestSet", - "smithy.api#documentation": "

One or more Spot Instance requests.

", + "smithy.api#documentation": "

The Spot Instance requests.

", "smithy.api#xmlName": "spotInstanceRequestSet" } } @@ -11083,6 +11100,12 @@ "smithy.api#documentation": "

Current state of options for customizable text banner that will be displayed on\n\t\t\tAmazon Web Services provided clients when a VPN session is established.

" } }, + "com.amazonaws.ec2#ClientSecretType": { + "type": "string", + "traits": { + "smithy.api#sensitive": {} + } + }, "com.amazonaws.ec2#ClientVpnAssociationId": { "type": "string" }, @@ -12877,6 +12900,14 @@ "smithy.api#documentation": "

The number of threads per CPU core.

", "smithy.api#xmlName": "threadsPerCore" } + }, + "AmdSevSnp": { + "target": "com.amazonaws.ec2#AmdSevSnpSpecification", + "traits": { + "aws.protocols#ec2QueryName": "AmdSevSnp", + "smithy.api#documentation": "

Indicates whether the instance is enabled for AMD SEV-SNP.

", + "smithy.api#xmlName": "amdSevSnp" + } } }, "traits": { @@ -12901,6 +12932,12 @@ "smithy.api#default": 0, "smithy.api#documentation": "

The number of threads per CPU core. To disable multithreading for the instance,\n specify a value of 1. Otherwise, specify the default value of\n 2.

" } + }, + "AmdSevSnp": { + "target": "com.amazonaws.ec2#AmdSevSnpSpecification", + "traits": { + "smithy.api#documentation": "

Indicates whether to enable the instance for AMD SEV-SNP. AMD SEV-SNP is supported \n with M6a, R6a, and C6a instance types only.

" + } } }, "traits": { @@ -18821,7 +18858,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for a network interface-type endpoint.

" + "smithy.api#documentation": "

Describes the network interface options when creating an Amazon Web Services Verified Access endpoint using the\n network-interface type.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessEndpointLoadBalancerOptions": { @@ -18856,7 +18893,7 @@ } }, "traits": { - "smithy.api#documentation": "

Describes a load balancer when creating an Amazon Web Services Verified Access endpoint using the\n load-balancer type.

" + "smithy.api#documentation": "

Describes the load balancer options when creating an Amazon Web Services Verified Access endpoint using the\n load-balancer type.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessEndpointRequest": { @@ -18874,7 +18911,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointType", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The type of Amazon Web Services Verified Access endpoint to create.

", + "smithy.api#documentation": "

The type of Verified Access endpoint to create.

", "smithy.api#required": {} } }, @@ -18882,7 +18919,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointAttachmentType", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The Amazon Web Services network component Verified Access attaches to.

", + "smithy.api#documentation": "

The type of attachment.

", "smithy.api#required": {} } }, @@ -18906,45 +18943,45 @@ "target": "com.amazonaws.ec2#String", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

A custom identifier that gets prepended to a DNS name that is generated for the endpoint.

", + "smithy.api#documentation": "

A custom identifier that is prepended to the DNS name that is generated for the\n endpoint.

", "smithy.api#required": {} } }, "SecurityGroupIds": { "target": "com.amazonaws.ec2#SecurityGroupIdList", "traits": { - "smithy.api#documentation": "

The Amazon EC2 security groups to associate with the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The IDs of the security groups to associate with the Verified Access endpoint.

", "smithy.api#xmlName": "SecurityGroupId" } }, "LoadBalancerOptions": { "target": "com.amazonaws.ec2#CreateVerifiedAccessEndpointLoadBalancerOptions", "traits": { - "smithy.api#documentation": "

The load balancer details if creating the Amazon Web Services Verified Access endpoint as\n load-balancertype.

" + "smithy.api#documentation": "

The load balancer details. This parameter is required if the endpoint type is\n load-balancer.

" } }, "NetworkInterfaceOptions": { "target": "com.amazonaws.ec2#CreateVerifiedAccessEndpointEniOptions", "traits": { - "smithy.api#documentation": "

The network interface details if creating the Amazon Web Services Verified Access endpoint as\n network-interfacetype.

" + "smithy.api#documentation": "

The network interface details. This parameter is required if the endpoint type is\n network-interface.

" } }, "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access endpoint.

" + "smithy.api#documentation": "

A description for the Verified Access endpoint.

" } }, "PolicyDocument": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

" + "smithy.api#documentation": "

The Verified Access policy document.

" } }, "TagSpecifications": { "target": "com.amazonaws.ec2#TagSpecificationList", "traits": { - "smithy.api#documentation": "

The tags to assign to the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The tags to assign to the Verified Access endpoint.

", "smithy.api#xmlName": "TagSpecification" } }, @@ -18975,7 +19012,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpoint", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessEndpoint", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#xmlName": "verifiedAccessEndpoint" } } @@ -18999,7 +19036,7 @@ "target": "com.amazonaws.ec2#CreateVerifiedAccessGroupResult" }, "traits": { - "smithy.api#documentation": "

An Amazon Web Services Verified Access group is a collection of Amazon Web Services Verified Access endpoints who's associated applications have\n similar security requirements. Each instance within an Amazon Web Services Verified Access group shares an Amazon Web Services Verified Access policy. For\n example, you can group all Amazon Web Services Verified Access instances associated with “sales” applications together and\n use one common Amazon Web Services Verified Access policy.

" + "smithy.api#documentation": "

An Amazon Web Services Verified Access group is a collection of Amazon Web Services Verified Access endpoints who's associated applications have\n similar security requirements. Each instance within a Verified Access group shares an Verified Access policy. For\n example, you can group all Verified Access instances associated with \"sales\" applications together and\n use one common Verified Access policy.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessGroupRequest": { @@ -19009,26 +19046,26 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access group.

" + "smithy.api#documentation": "

A description for the Verified Access group.

" } }, "PolicyDocument": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

" + "smithy.api#documentation": "

The Verified Access policy document.

" } }, "TagSpecifications": { "target": "com.amazonaws.ec2#TagSpecificationList", "traits": { - "smithy.api#documentation": "

The tags to assign to the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The tags to assign to the Verified Access group.

", "smithy.api#xmlName": "TagSpecification" } }, @@ -19083,13 +19120,13 @@ "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

A description for the Verified Access instance.

" } }, "TagSpecifications": { "target": "com.amazonaws.ec2#TagSpecificationList", "traits": { - "smithy.api#documentation": "

The tags to assign to the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The tags to assign to the Verified Access instance.

", "smithy.api#xmlName": "TagSpecification" } }, @@ -19120,7 +19157,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstance", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstance", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#xmlName": "verifiedAccessInstance" } } @@ -19135,7 +19172,7 @@ "target": "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderResult" }, "traits": { - "smithy.api#documentation": "

A trust provider is a third-party entity that creates, maintains, and manages identity\n information for users and devices. When an application request is made, the identity\n information sent by the trust provider will be evaluated by Amazon Web Services Verified Access, before allowing or\n denying the application request.

" + "smithy.api#documentation": "

A trust provider is a third-party entity that creates, maintains, and manages identity\n information for users and devices. When an application request is made, the identity\n information sent by the trust provider is evaluated by Verified Access before allowing or\n denying the application request.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderDeviceOptions": { @@ -19149,7 +19186,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for a device-identity type trust provider.

" + "smithy.api#documentation": "

Describes the options when creating an Amazon Web Services Verified Access trust provider using the\n device type.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderOidcOptions": { @@ -19186,7 +19223,7 @@ } }, "ClientSecret": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#ClientSecretType", "traits": { "smithy.api#documentation": "

The client secret.

" } @@ -19199,7 +19236,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for an OIDC-based, user-identity type trust provider.

" + "smithy.api#documentation": "

Describes the options when creating an Amazon Web Services Verified Access trust provider using the user\n type.

" } }, "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderRequest": { @@ -19209,32 +19246,32 @@ "target": "com.amazonaws.ec2#TrustProviderType", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The type of trust provider can be either user or device-based.

", + "smithy.api#documentation": "

The type of trust provider.

", "smithy.api#required": {} } }, "UserTrustProviderType": { "target": "com.amazonaws.ec2#UserTrustProviderType", "traits": { - "smithy.api#documentation": "

The type of user-based trust provider.

" + "smithy.api#documentation": "

The type of user-based trust provider. This parameter is required when the provider type\n is user.

" } }, "DeviceTrustProviderType": { "target": "com.amazonaws.ec2#DeviceTrustProviderType", "traits": { - "smithy.api#documentation": "

The type of device-based trust provider.

" + "smithy.api#documentation": "

The type of device-based trust provider. This parameter is required when the provider\n type is device.

" } }, "OidcOptions": { "target": "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderOidcOptions", "traits": { - "smithy.api#documentation": "

The OpenID Connect details for an oidc-type, user-identity based trust provider.

" + "smithy.api#documentation": "

The options for a OpenID Connect-compatible user-identity trust provider. This parameter\n is required when the provider type is user.

" } }, "DeviceOptions": { "target": "com.amazonaws.ec2#CreateVerifiedAccessTrustProviderDeviceOptions", "traits": { - "smithy.api#documentation": "

The options for device identity based trust providers.

" + "smithy.api#documentation": "

The options for a device-based trust provider. This parameter is required when the\n provider type is device.

" } }, "PolicyReferenceName": { @@ -19248,13 +19285,13 @@ "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access trust provider.

" + "smithy.api#documentation": "

A description for the Verified Access trust provider.

" } }, "TagSpecifications": { "target": "com.amazonaws.ec2#TagSpecificationList", "traits": { - "smithy.api#documentation": "

The tags to assign to the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The tags to assign to the Verified Access trust provider.

", "smithy.api#xmlName": "TagSpecification" } }, @@ -19285,7 +19322,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProvider", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProvider", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#xmlName": "verifiedAccessTrustProvider" } } @@ -23835,7 +23872,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#required": {} } }, @@ -23866,7 +23903,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpoint", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessEndpoint", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#xmlName": "verifiedAccessEndpoint" } } @@ -23891,7 +23928,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The ID of the Verified Access group.

", "smithy.api#required": {} } }, @@ -23922,7 +23959,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroup", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessGroup", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The ID of the Verified Access group.

", "smithy.api#xmlName": "verifiedAccessGroup" } } @@ -23947,7 +23984,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, @@ -23978,7 +24015,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstance", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstance", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#xmlName": "verifiedAccessInstance" } } @@ -24003,7 +24040,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#required": {} } }, @@ -24034,7 +24071,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProvider", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProvider", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#xmlName": "verifiedAccessTrustProvider" } } @@ -29437,7 +29474,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters. Filter names and values are case-sensitive.

\n
    \n
  • \n

    \n auto-recovery-supported - Indicates whether auto recovery is supported (true | false).

    \n
  • \n
  • \n

    \n bare-metal - Indicates whether it is a bare metal instance type (true | false).

    \n
  • \n
  • \n

    \n burstable-performance-supported - Indicates whether it is a burstable\n performance instance type (true | false).

    \n
  • \n
  • \n

    \n current-generation - Indicates whether this instance type is the latest\n generation instance type of an instance family (true | false).

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-bandwidth-in-mbps - The baseline\n bandwidth performance for an EBS-optimized instance type, in Mbps.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-iops - The baseline input/output storage\n operations per second for an EBS-optimized instance type.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-throughput-in-mbps - The baseline\n throughput performance for an EBS-optimized instance type, in MB/s.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-bandwidth-in-mbps - The maximum bandwidth\n performance for an EBS-optimized instance type, in Mbps.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-iops - The maximum input/output storage\n operations per second for an EBS-optimized instance type.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-throughput-in-mbps - The maximum\n throughput performance for an EBS-optimized instance type, in MB/s.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-support - Indicates whether the instance type is\n EBS-optimized (supported | unsupported |\n default).

    \n
  • \n
  • \n

    \n ebs-info.encryption-support - Indicates whether EBS encryption is supported\n (supported | unsupported).

    \n
  • \n
  • \n

    \n ebs-info.nvme-support - Indicates whether non-volatile memory express (NVMe)\n is supported for EBS volumes (required | supported | unsupported).

    \n
  • \n
  • \n

    \n free-tier-eligible - Indicates whether the instance type is eligible to use\n in the free tier (true | false).

    \n
  • \n
  • \n

    \n hibernation-supported - Indicates whether On-Demand hibernation is supported (true | false).

    \n
  • \n
  • \n

    \n hypervisor - The hypervisor (nitro | xen).

    \n
  • \n
  • \n

    \n instance-storage-info.disk.count - The number of local disks.

    \n
  • \n
  • \n

    \n instance-storage-info.disk.size-in-gb - The storage size of each instance storage disk, in\n GB.

    \n
  • \n
  • \n

    \n instance-storage-info.disk.type - The storage technology for the local\n instance storage disks (hdd | ssd).

    \n
  • \n
  • \n

    \n instance-storage-info.encryption-support - Indicates whether data is encrypted at rest \n (required | supported | unsupported).

    \n
  • \n
  • \n

    \n instance-storage-info.nvme-support - Indicates whether non-volatile memory\n express (NVMe) is supported for instance store (required | supported |\n unsupported).

    \n
  • \n
  • \n

    \n instance-storage-info.total-size-in-gb - The total amount of storage available from all local\n instance storage, in GB.

    \n
  • \n
  • \n

    \n instance-storage-supported - Indicates whether the instance type has local\n instance storage (true | false).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example c5.2xlarge or\n c5*).

    \n
  • \n
  • \n

    \n memory-info.size-in-mib - The memory size.

    \n
  • \n
  • \n

    \n network-info.efa-info.maximum-efa-interfaces - The maximum number of Elastic \n Fabric Adapters (EFAs) per instance.

    \n
  • \n
  • \n

    \n network-info.efa-supported - Indicates whether the instance type supports\n Elastic Fabric Adapter (EFA) (true | false).

    \n
  • \n
  • \n

    \n network-info.ena-support - Indicates whether Elastic Network Adapter (ENA) is\n supported or required (required | supported |\n unsupported).

    \n
  • \n
  • \n

    \n network-info.encryption-in-transit-supported - Indicates whether the instance type \n automatically encrypts in-transit traffic between instances (true | false).

    \n
  • \n
  • \n

    \n network-info.ipv4-addresses-per-interface - The maximum number of private IPv4 addresses per\n network interface.

    \n
  • \n
  • \n

    \n network-info.ipv6-addresses-per-interface - The maximum number of private IPv6 addresses per\n network interface.

    \n
  • \n
  • \n

    \n network-info.ipv6-supported - Indicates whether the instance type supports IPv6 (true | false).

    \n
  • \n
  • \n

    \n network-info.maximum-network-cards - The maximum number of network cards per\n instance.

    \n
  • \n
  • \n

    \n network-info.maximum-network-interfaces - The maximum number of network interfaces per instance.

    \n
  • \n
  • \n

    \n network-info.network-performance - The network performance (for example, \"25\n Gigabit\").

    \n
  • \n
  • \n

    \n processor-info.supported-architecture - The CPU architecture\n (arm64 | i386 | x86_64).

    \n
  • \n
  • \n

    \n processor-info.sustained-clock-speed-in-ghz - The CPU clock speed, in GHz.

    \n
  • \n
  • \n

    \n supported-boot-mode - The boot mode (legacy-bios |\n uefi).

    \n
  • \n
  • \n

    \n supported-root-device-type - The root device type (ebs |\n instance-store).

    \n
  • \n
  • \n

    \n supported-usage-class - The usage class (on-demand |\n spot).

    \n
  • \n
  • \n

    \n supported-virtualization-type - The virtualization type (hvm |\n paravirtual).

    \n
  • \n
  • \n

    \n vcpu-info.default-cores - The default number of cores for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.default-threads-per-core - The default number of threads per core for the instance\n type.

    \n
  • \n
  • \n

    \n vcpu-info.default-vcpus - The default number of vCPUs for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.valid-cores - The number of cores that can be configured for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.valid-threads-per-core - The number of threads per core that can be configured for the instance type.\n For example, \"1\" or \"1,2\".

    \n
  • \n
", + "smithy.api#documentation": "

One or more filters. Filter names and values are case-sensitive.

\n
    \n
  • \n

    \n auto-recovery-supported - Indicates whether Amazon CloudWatch action based recovery is supported (true | false).

    \n
  • \n
  • \n

    \n bare-metal - Indicates whether it is a bare metal instance type (true | false).

    \n
  • \n
  • \n

    \n burstable-performance-supported - Indicates whether it is a burstable\n performance instance type (true | false).

    \n
  • \n
  • \n

    \n current-generation - Indicates whether this instance type is the latest\n generation instance type of an instance family (true | false).

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-bandwidth-in-mbps - The baseline\n bandwidth performance for an EBS-optimized instance type, in Mbps.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-iops - The baseline input/output storage\n operations per second for an EBS-optimized instance type.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.baseline-throughput-in-mbps - The baseline\n throughput performance for an EBS-optimized instance type, in MB/s.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-bandwidth-in-mbps - The maximum bandwidth\n performance for an EBS-optimized instance type, in Mbps.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-iops - The maximum input/output storage\n operations per second for an EBS-optimized instance type.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-info.maximum-throughput-in-mbps - The maximum\n throughput performance for an EBS-optimized instance type, in MB/s.

    \n
  • \n
  • \n

    \n ebs-info.ebs-optimized-support - Indicates whether the instance type is\n EBS-optimized (supported | unsupported |\n default).

    \n
  • \n
  • \n

    \n ebs-info.encryption-support - Indicates whether EBS encryption is supported\n (supported | unsupported).

    \n
  • \n
  • \n

    \n ebs-info.nvme-support - Indicates whether non-volatile memory express (NVMe)\n is supported for EBS volumes (required | supported | unsupported).

    \n
  • \n
  • \n

    \n free-tier-eligible - Indicates whether the instance type is eligible to use\n in the free tier (true | false).

    \n
  • \n
  • \n

    \n hibernation-supported - Indicates whether On-Demand hibernation is supported (true | false).

    \n
  • \n
  • \n

    \n hypervisor - The hypervisor (nitro | xen).

    \n
  • \n
  • \n

    \n instance-storage-info.disk.count - The number of local disks.

    \n
  • \n
  • \n

    \n instance-storage-info.disk.size-in-gb - The storage size of each instance storage disk, in\n GB.

    \n
  • \n
  • \n

    \n instance-storage-info.disk.type - The storage technology for the local\n instance storage disks (hdd | ssd).

    \n
  • \n
  • \n

    \n instance-storage-info.encryption-support - Indicates whether data is encrypted at rest \n (required | supported | unsupported).

    \n
  • \n
  • \n

    \n instance-storage-info.nvme-support - Indicates whether non-volatile memory\n express (NVMe) is supported for instance store (required | supported |\n unsupported).

    \n
  • \n
  • \n

    \n instance-storage-info.total-size-in-gb - The total amount of storage available from all local\n instance storage, in GB.

    \n
  • \n
  • \n

    \n instance-storage-supported - Indicates whether the instance type has local\n instance storage (true | false).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example c5.2xlarge or\n c5*).

    \n
  • \n
  • \n

    \n memory-info.size-in-mib - The memory size.

    \n
  • \n
  • \n

    \n network-info.efa-info.maximum-efa-interfaces - The maximum number of Elastic \n Fabric Adapters (EFAs) per instance.

    \n
  • \n
  • \n

    \n network-info.efa-supported - Indicates whether the instance type supports\n Elastic Fabric Adapter (EFA) (true | false).

    \n
  • \n
  • \n

    \n network-info.ena-support - Indicates whether Elastic Network Adapter (ENA) is\n supported or required (required | supported |\n unsupported).

    \n
  • \n
  • \n

    \n network-info.encryption-in-transit-supported - Indicates whether the instance type \n automatically encrypts in-transit traffic between instances (true | false).

    \n
  • \n
  • \n

    \n network-info.ipv4-addresses-per-interface - The maximum number of private IPv4 addresses per\n network interface.

    \n
  • \n
  • \n

    \n network-info.ipv6-addresses-per-interface - The maximum number of private IPv6 addresses per\n network interface.

    \n
  • \n
  • \n

    \n network-info.ipv6-supported - Indicates whether the instance type supports IPv6 (true | false).

    \n
  • \n
  • \n

    \n network-info.maximum-network-cards - The maximum number of network cards per\n instance.

    \n
  • \n
  • \n

    \n network-info.maximum-network-interfaces - The maximum number of network interfaces per instance.

    \n
  • \n
  • \n

    \n network-info.network-performance - The network performance (for example, \"25\n Gigabit\").

    \n
  • \n
  • \n

    \n processor-info.supported-architecture - The CPU architecture\n (arm64 | i386 | x86_64).

    \n
  • \n
  • \n

    \n processor-info.sustained-clock-speed-in-ghz - The CPU clock speed, in GHz.

    \n
  • \n
  • \n

    \n supported-boot-mode - The boot mode (legacy-bios |\n uefi).

    \n
  • \n
  • \n

    \n supported-root-device-type - The root device type (ebs |\n instance-store).

    \n
  • \n
  • \n

    \n supported-usage-class - The usage class (on-demand |\n spot).

    \n
  • \n
  • \n

    \n supported-virtualization-type - The virtualization type (hvm |\n paravirtual).

    \n
  • \n
  • \n

    \n vcpu-info.default-cores - The default number of cores for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.default-threads-per-core - The default number of threads per core for the instance\n type.

    \n
  • \n
  • \n

    \n vcpu-info.default-vcpus - The default number of vCPUs for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.valid-cores - The number of cores that can be configured for the instance type.

    \n
  • \n
  • \n

    \n vcpu-info.valid-threads-per-core - The number of threads per core that can be configured for the instance type.\n For example, \"1\" or \"1,2\".

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -29650,7 +29687,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n affinity - The affinity setting for an instance running on a\n Dedicated Host (default | host).

    \n
  • \n
  • \n

    \n architecture - The instance architecture (i386 |\n x86_64 | arm64).

    \n
  • \n
  • \n

    \n availability-zone - The Availability Zone of the instance.

    \n
  • \n
  • \n

    \n block-device-mapping.attach-time - The attach time for an EBS\n volume mapped to the instance, for example,\n 2010-09-15T17:15:20.000Z.

    \n
  • \n
  • \n

    \n block-device-mapping.delete-on-termination - A Boolean that\n indicates whether the EBS volume is deleted on instance termination.

    \n
  • \n
  • \n

    \n block-device-mapping.device-name - The device name specified in the\n block device mapping (for example, /dev/sdh or\n xvdh).

    \n
  • \n
  • \n

    \n block-device-mapping.status - The status for the EBS volume\n (attaching | attached | detaching |\n detached).

    \n
  • \n
  • \n

    \n block-device-mapping.volume-id - The volume ID of the EBS\n volume.

    \n
  • \n
  • \n

    \n capacity-reservation-id - The ID of the Capacity Reservation into which the\n instance was launched.

    \n
  • \n
  • \n

    \n client-token - The idempotency token you provided when you launched\n the instance.

    \n
  • \n
  • \n

    \n dns-name - The public DNS name of the instance.

    \n
  • \n
  • \n

    \n group-id - The ID of the security group for the instance.\n EC2-Classic only.

    \n
  • \n
  • \n

    \n group-name - The name of the security group for the instance.\n EC2-Classic only.

    \n
  • \n
  • \n

    \n hibernation-options.configured - A Boolean that indicates whether\n the instance is enabled for hibernation. A value of true means that\n the instance is enabled for hibernation.

    \n
  • \n
  • \n

    \n host-id - The ID of the Dedicated Host on which the instance is\n running, if applicable.

    \n
  • \n
  • \n

    \n hypervisor - The hypervisor type of the instance\n (ovm | xen). The value xen is used\n for both Xen and Nitro hypervisors.

    \n
  • \n
  • \n

    \n iam-instance-profile.arn - The instance profile associated with\n the instance. Specified as an ARN.

    \n
  • \n
  • \n

    \n image-id - The ID of the image used to launch the\n instance.

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance.

    \n
  • \n
  • \n

    \n instance-lifecycle - Indicates whether this is a Spot Instance or\n a Scheduled Instance (spot | scheduled).

    \n
  • \n
  • \n

    \n instance-state-code - The state of the instance, as a 16-bit\n unsigned integer. The high byte is used for internal purposes and should be\n ignored. The low byte is set based on the state represented. The valid values\n are: 0 (pending), 16 (running), 32 (shutting-down), 48 (terminated), 64\n (stopping), and 80 (stopped).

    \n
  • \n
  • \n

    \n instance-state-name - The state of the instance\n (pending | running | shutting-down |\n terminated | stopping |\n stopped).

    \n
  • \n
  • \n

    \n instance-type - The type of instance (for example,\n t2.micro).

    \n
  • \n
  • \n

    \n instance.group-id - The ID of the security group for the\n instance.

    \n
  • \n
  • \n

    \n instance.group-name - The name of the security group for the\n instance.

    \n
  • \n
  • \n

    \n ip-address - The public IPv4 address of the instance.

    \n
  • \n
  • \n

    \n kernel-id - The kernel ID.

    \n
  • \n
  • \n

    \n key-name - The name of the key pair used when the instance was\n launched.

    \n
  • \n
  • \n

    \n launch-index - When launching multiple instances, this is the\n index for the instance in the launch group (for example, 0, 1, 2, and so on).\n

    \n
  • \n
  • \n

    \n launch-time - The time when the instance was launched, in the ISO\n 8601 format in the UTC time zone (YYYY-MM-DDThh:mm:ss.sssZ), for example,\n 2021-09-29T11:04:43.305Z. You can use a wildcard\n (*), for example, 2021-09-29T*, which matches an\n entire day.

    \n
  • \n
  • \n

    \n metadata-options.http-tokens - The metadata request authorization\n state (optional | required)

    \n
  • \n
  • \n

    \n metadata-options.http-put-response-hop-limit - The HTTP metadata\n request put response hop limit (integer, possible values 1 to\n 64)

    \n
  • \n
  • \n

    \n metadata-options.http-endpoint - The status of access to the HTTP\n metadata endpoint on your instance (enabled |\n disabled)

    \n
  • \n
  • \n

    \n metadata-options.instance-metadata-tags - The status of access to\n instance tags from the instance metadata (enabled |\n disabled)

    \n
  • \n
  • \n

    \n monitoring-state - Indicates whether detailed monitoring is\n enabled (disabled | enabled).

    \n
  • \n
  • \n

    \n network-interface.addresses.private-ip-address - The private IPv4\n address associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.addresses.primary - Specifies whether the IPv4\n address of the network interface is the primary private IPv4 address.

    \n
  • \n
  • \n

    \n network-interface.addresses.association.public-ip - The ID of the\n association of an Elastic IP address (IPv4) with a network interface.

    \n
  • \n
  • \n

    \n network-interface.addresses.association.ip-owner-id - The owner\n ID of the private IPv4 address associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.public-ip - The address of the\n Elastic IP address (IPv4) bound to the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.ip-owner-id - The owner of the\n Elastic IP address (IPv4) associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.allocation-id - The allocation ID\n returned when you allocated the Elastic IP address (IPv4) for your network\n interface.

    \n
  • \n
  • \n

    \n network-interface.association.association-id - The association ID\n returned when the network interface was associated with an IPv4 address.

    \n
  • \n
  • \n

    \n network-interface.attachment.attachment-id - The ID of the\n interface attachment.

    \n
  • \n
  • \n

    \n network-interface.attachment.instance-id - The ID of the instance\n to which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.instance-owner-id - The owner ID of\n the instance to which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.device-index - The device index to\n which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.status - The status of the\n attachment (attaching | attached |\n detaching | detached).

    \n
  • \n
  • \n

    \n network-interface.attachment.attach-time - The time that the\n network interface was attached to an instance.

    \n
  • \n
  • \n

    \n network-interface.attachment.delete-on-termination - Specifies\n whether the attachment is deleted when an instance is terminated.

    \n
  • \n
  • \n

    \n network-interface.availability-zone - The Availability Zone for\n the network interface.

    \n
  • \n
  • \n

    \n network-interface.description - The description of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.group-id - The ID of a security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.group-name - The name of a security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.ipv6-addresses.ipv6-address - The IPv6 address\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.mac-address - The MAC address of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.network-interface-id - The ID of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.owner-id - The ID of the owner of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.private-dns-name - The private DNS name of the\n network interface.

    \n
  • \n
  • \n

    \n network-interface.requester-id - The requester ID for the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.requester-managed - Indicates whether the\n network interface is being managed by Amazon Web Services.

    \n
  • \n
  • \n

    \n network-interface.status - The status of the network interface\n (available) | in-use).

    \n
  • \n
  • \n

    \n network-interface.source-dest-check - Whether the network\n interface performs source/destination checking. A value of true\n means that checking is enabled, and false means that checking is\n disabled. The value must be false for the network interface to\n perform network address translation (NAT) in your VPC.

    \n
  • \n
  • \n

    \n network-interface.subnet-id - The ID of the subnet for the\n network interface.

    \n
  • \n
  • \n

    \n network-interface.vpc-id - The ID of the VPC for the network\n interface.

    \n
  • \n
  • \n

    \n outpost-arn - The Amazon Resource Name (ARN) of the\n Outpost.

    \n
  • \n
  • \n

    \n owner-id - The Amazon Web Services account ID of the instance\n owner.

    \n
  • \n
  • \n

    \n placement-group-name - The name of the placement group for the\n instance.

    \n
  • \n
  • \n

    \n placement-partition-number - The partition in which the instance is\n located.

    \n
  • \n
  • \n

    \n platform - The platform. To list only Windows instances, use\n windows.

    \n
  • \n
  • \n

    \n private-dns-name - The private IPv4 DNS name of the\n instance.

    \n
  • \n
  • \n

    \n private-ip-address - The private IPv4 address of the\n instance.

    \n
  • \n
  • \n

    \n product-code - The product code associated with the AMI used to\n launch the instance.

    \n
  • \n
  • \n

    \n product-code.type - The type of product code (devpay |\n marketplace).

    \n
  • \n
  • \n

    \n ramdisk-id - The RAM disk ID.

    \n
  • \n
  • \n

    \n reason - The reason for the current state of the instance (for\n example, shows \"User Initiated [date]\" when you stop or terminate the instance).\n Similar to the state-reason-code filter.

    \n
  • \n
  • \n

    \n requester-id - The ID of the entity that launched the instance on\n your behalf (for example, Amazon Web Services Management Console, Auto Scaling, and so\n on).

    \n
  • \n
  • \n

    \n reservation-id - The ID of the instance's reservation. A\n reservation ID is created any time you launch an instance. A reservation ID has\n a one-to-one relationship with an instance launch request, but can be associated\n with more than one instance if you launch multiple instances using the same\n launch request. For example, if you launch one instance, you get one reservation\n ID. If you launch ten instances using the same launch request, you also get one\n reservation ID.

    \n
  • \n
  • \n

    \n root-device-name - The device name of the root device volume (for\n example, /dev/sda1).

    \n
  • \n
  • \n

    \n root-device-type - The type of the root device volume\n (ebs | instance-store).

    \n
  • \n
  • \n

    \n source-dest-check - Indicates whether the instance performs\n source/destination checking. A value of true means that checking is\n enabled, and false means that checking is disabled. The value must\n be false for the instance to perform network address translation\n (NAT) in your VPC.

    \n
  • \n
  • \n

    \n spot-instance-request-id - The ID of the Spot Instance\n request.

    \n
  • \n
  • \n

    \n state-reason-code - The reason code for the state change.

    \n
  • \n
  • \n

    \n state-reason-message - A message that describes the state\n change.

    \n
  • \n
  • \n

    \n subnet-id - The ID of the subnet for the instance.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources that have a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n tenancy - The tenancy of an instance (dedicated |\n default | host).

    \n
  • \n
  • \n

    \n virtualization-type - The virtualization type of the instance\n (paravirtual | hvm).

    \n
  • \n
  • \n

    \n vpc-id - The ID of the VPC that the instance is running in.

    \n
  • \n
", + "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n affinity - The affinity setting for an instance running on a\n Dedicated Host (default | host).

    \n
  • \n
  • \n

    \n architecture - The instance architecture (i386 |\n x86_64 | arm64).

    \n
  • \n
  • \n

    \n availability-zone - The Availability Zone of the instance.

    \n
  • \n
  • \n

    \n block-device-mapping.attach-time - The attach time for an EBS\n volume mapped to the instance, for example,\n 2010-09-15T17:15:20.000Z.

    \n
  • \n
  • \n

    \n block-device-mapping.delete-on-termination - A Boolean that\n indicates whether the EBS volume is deleted on instance termination.

    \n
  • \n
  • \n

    \n block-device-mapping.device-name - The device name specified in the\n block device mapping (for example, /dev/sdh or\n xvdh).

    \n
  • \n
  • \n

    \n block-device-mapping.status - The status for the EBS volume\n (attaching | attached | detaching |\n detached).

    \n
  • \n
  • \n

    \n block-device-mapping.volume-id - The volume ID of the EBS\n volume.

    \n
  • \n
  • \n

    \n capacity-reservation-id - The ID of the Capacity Reservation into which the\n instance was launched.

    \n
  • \n
  • \n

    \n client-token - The idempotency token you provided when you launched\n the instance.

    \n
  • \n
  • \n

    \n dns-name - The public DNS name of the instance.

    \n
  • \n
  • \n

    \n hibernation-options.configured - A Boolean that indicates whether\n the instance is enabled for hibernation. A value of true means that\n the instance is enabled for hibernation.

    \n
  • \n
  • \n

    \n host-id - The ID of the Dedicated Host on which the instance is\n running, if applicable.

    \n
  • \n
  • \n

    \n hypervisor - The hypervisor type of the instance\n (ovm | xen). The value xen is used\n for both Xen and Nitro hypervisors.

    \n
  • \n
  • \n

    \n iam-instance-profile.arn - The instance profile associated with\n the instance. Specified as an ARN.

    \n
  • \n
  • \n

    \n image-id - The ID of the image used to launch the\n instance.

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance.

    \n
  • \n
  • \n

    \n instance-lifecycle - Indicates whether this is a Spot Instance or\n a Scheduled Instance (spot | scheduled).

    \n
  • \n
  • \n

    \n instance-state-code - The state of the instance, as a 16-bit\n unsigned integer. The high byte is used for internal purposes and should be\n ignored. The low byte is set based on the state represented. The valid values\n are: 0 (pending), 16 (running), 32 (shutting-down), 48 (terminated), 64\n (stopping), and 80 (stopped).

    \n
  • \n
  • \n

    \n instance-state-name - The state of the instance\n (pending | running | shutting-down |\n terminated | stopping |\n stopped).

    \n
  • \n
  • \n

    \n instance-type - The type of instance (for example,\n t2.micro).

    \n
  • \n
  • \n

    \n instance.group-id - The ID of the security group for the\n instance.

    \n
  • \n
  • \n

    \n instance.group-name - The name of the security group for the\n instance.

    \n
  • \n
  • \n

    \n ip-address - The public IPv4 address of the instance.

    \n
  • \n
  • \n

    \n kernel-id - The kernel ID.

    \n
  • \n
  • \n

    \n key-name - The name of the key pair used when the instance was\n launched.

    \n
  • \n
  • \n

    \n launch-index - When launching multiple instances, this is the\n index for the instance in the launch group (for example, 0, 1, 2, and so on).\n

    \n
  • \n
  • \n

    \n launch-time - The time when the instance was launched, in the ISO\n 8601 format in the UTC time zone (YYYY-MM-DDThh:mm:ss.sssZ), for example,\n 2021-09-29T11:04:43.305Z. You can use a wildcard\n (*), for example, 2021-09-29T*, which matches an\n entire day.

    \n
  • \n
  • \n

    \n metadata-options.http-tokens - The metadata request authorization\n state (optional | required)

    \n
  • \n
  • \n

    \n metadata-options.http-put-response-hop-limit - The HTTP metadata\n request put response hop limit (integer, possible values 1 to\n 64)

    \n
  • \n
  • \n

    \n metadata-options.http-endpoint - The status of access to the HTTP\n metadata endpoint on your instance (enabled |\n disabled)

    \n
  • \n
  • \n

    \n metadata-options.instance-metadata-tags - The status of access to\n instance tags from the instance metadata (enabled |\n disabled)

    \n
  • \n
  • \n

    \n monitoring-state - Indicates whether detailed monitoring is\n enabled (disabled | enabled).

    \n
  • \n
  • \n

    \n network-interface.addresses.private-ip-address - The private IPv4\n address associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.addresses.primary - Specifies whether the IPv4\n address of the network interface is the primary private IPv4 address.

    \n
  • \n
  • \n

    \n network-interface.addresses.association.public-ip - The ID of the\n association of an Elastic IP address (IPv4) with a network interface.

    \n
  • \n
  • \n

    \n network-interface.addresses.association.ip-owner-id - The owner\n ID of the private IPv4 address associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.public-ip - The address of the\n Elastic IP address (IPv4) bound to the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.ip-owner-id - The owner of the\n Elastic IP address (IPv4) associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.association.allocation-id - The allocation ID\n returned when you allocated the Elastic IP address (IPv4) for your network\n interface.

    \n
  • \n
  • \n

    \n network-interface.association.association-id - The association ID\n returned when the network interface was associated with an IPv4 address.

    \n
  • \n
  • \n

    \n network-interface.attachment.attachment-id - The ID of the\n interface attachment.

    \n
  • \n
  • \n

    \n network-interface.attachment.instance-id - The ID of the instance\n to which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.instance-owner-id - The owner ID of\n the instance to which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.device-index - The device index to\n which the network interface is attached.

    \n
  • \n
  • \n

    \n network-interface.attachment.status - The status of the\n attachment (attaching | attached |\n detaching | detached).

    \n
  • \n
  • \n

    \n network-interface.attachment.attach-time - The time that the\n network interface was attached to an instance.

    \n
  • \n
  • \n

    \n network-interface.attachment.delete-on-termination - Specifies\n whether the attachment is deleted when an instance is terminated.

    \n
  • \n
  • \n

    \n network-interface.availability-zone - The Availability Zone for\n the network interface.

    \n
  • \n
  • \n

    \n network-interface.description - The description of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.group-id - The ID of a security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.group-name - The name of a security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.ipv6-addresses.ipv6-address - The IPv6 address\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.mac-address - The MAC address of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.network-interface-id - The ID of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.owner-id - The ID of the owner of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.private-dns-name - The private DNS name of the\n network interface.

    \n
  • \n
  • \n

    \n network-interface.requester-id - The requester ID for the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.requester-managed - Indicates whether the\n network interface is being managed by Amazon Web Services.

    \n
  • \n
  • \n

    \n network-interface.status - The status of the network interface\n (available) | in-use).

    \n
  • \n
  • \n

    \n network-interface.source-dest-check - Whether the network\n interface performs source/destination checking. A value of true\n means that checking is enabled, and false means that checking is\n disabled. The value must be false for the network interface to\n perform network address translation (NAT) in your VPC.

    \n
  • \n
  • \n

    \n network-interface.subnet-id - The ID of the subnet for the\n network interface.

    \n
  • \n
  • \n

    \n network-interface.vpc-id - The ID of the VPC for the network\n interface.

    \n
  • \n
  • \n

    \n outpost-arn - The Amazon Resource Name (ARN) of the\n Outpost.

    \n
  • \n
  • \n

    \n owner-id - The Amazon Web Services account ID of the instance\n owner.

    \n
  • \n
  • \n

    \n placement-group-name - The name of the placement group for the\n instance.

    \n
  • \n
  • \n

    \n placement-partition-number - The partition in which the instance is\n located.

    \n
  • \n
  • \n

    \n platform - The platform. To list only Windows instances, use\n windows.

    \n
  • \n
  • \n

    \n private-dns-name - The private IPv4 DNS name of the\n instance.

    \n
  • \n
  • \n

    \n private-ip-address - The private IPv4 address of the\n instance.

    \n
  • \n
  • \n

    \n product-code - The product code associated with the AMI used to\n launch the instance.

    \n
  • \n
  • \n

    \n product-code.type - The type of product code (devpay |\n marketplace).

    \n
  • \n
  • \n

    \n ramdisk-id - The RAM disk ID.

    \n
  • \n
  • \n

    \n reason - The reason for the current state of the instance (for\n example, shows \"User Initiated [date]\" when you stop or terminate the instance).\n Similar to the state-reason-code filter.

    \n
  • \n
  • \n

    \n requester-id - The ID of the entity that launched the instance on\n your behalf (for example, Amazon Web Services Management Console, Auto Scaling, and so\n on).

    \n
  • \n
  • \n

    \n reservation-id - The ID of the instance's reservation. A\n reservation ID is created any time you launch an instance. A reservation ID has\n a one-to-one relationship with an instance launch request, but can be associated\n with more than one instance if you launch multiple instances using the same\n launch request. For example, if you launch one instance, you get one reservation\n ID. If you launch ten instances using the same launch request, you also get one\n reservation ID.

    \n
  • \n
  • \n

    \n root-device-name - The device name of the root device volume (for\n example, /dev/sda1).

    \n
  • \n
  • \n

    \n root-device-type - The type of the root device volume\n (ebs | instance-store).

    \n
  • \n
  • \n

    \n source-dest-check - Indicates whether the instance performs\n source/destination checking. A value of true means that checking is\n enabled, and false means that checking is disabled. The value must\n be false for the instance to perform network address translation\n (NAT) in your VPC.

    \n
  • \n
  • \n

    \n spot-instance-request-id - The ID of the Spot Instance\n request.

    \n
  • \n
  • \n

    \n state-reason-code - The reason code for the state change.

    \n
  • \n
  • \n

    \n state-reason-message - A message that describes the state\n change.

    \n
  • \n
  • \n

    \n subnet-id - The ID of the subnet for the instance.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources that have a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n tenancy - The tenancy of an instance (dedicated |\n default | host).

    \n
  • \n
  • \n

    \n virtualization-type - The virtualization type of the instance\n (paravirtual | hvm).

    \n
  • \n
  • \n

    \n vpc-id - The ID of the VPC that the instance is running in.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -32833,7 +32870,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n client-token - The idempotency token for the modification request.

    \n
  • \n
  • \n

    \n create-date - The time when the modification request was created.

    \n
  • \n
  • \n

    \n effective-date - The time when the modification becomes effective.

    \n
  • \n
  • \n

    \n modification-result.reserved-instances-id - The ID for the Reserved Instances created as part of the modification request. This ID is only available when the status of the modification is fulfilled.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.availability-zone - The Availability Zone for the new Reserved Instances.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.instance-count - The number of new Reserved Instances.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.instance-type - The instance type of the new Reserved Instances.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.platform - The network platform of the new Reserved Instances (EC2-Classic | EC2-VPC).

    \n
  • \n
  • \n

    \n reserved-instances-id - The ID of the Reserved Instances modified.

    \n
  • \n
  • \n

    \n reserved-instances-modification-id - The ID of the modification request.

    \n
  • \n
  • \n

    \n status - The status of the Reserved Instances modification request\n (processing | fulfilled | failed).

    \n
  • \n
  • \n

    \n status-message - The reason for the status.

    \n
  • \n
  • \n

    \n update-date - The time when the modification request was last updated.

    \n
  • \n
", + "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n client-token - The idempotency token for the modification request.

    \n
  • \n
  • \n

    \n create-date - The time when the modification request was created.

    \n
  • \n
  • \n

    \n effective-date - The time when the modification becomes effective.

    \n
  • \n
  • \n

    \n modification-result.reserved-instances-id - The ID for the Reserved Instances created as part of the modification request. This ID is only available when the status of the modification is fulfilled.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.availability-zone - The Availability Zone for the new Reserved Instances.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.instance-count - The number of new Reserved Instances.

    \n
  • \n
  • \n

    \n modification-result.target-configuration.instance-type - The instance type of the new Reserved Instances.

    \n
  • \n
  • \n

    \n reserved-instances-id - The ID of the Reserved Instances modified.

    \n
  • \n
  • \n

    \n reserved-instances-modification-id - The ID of the modification request.

    \n
  • \n
  • \n

    \n status - The status of the Reserved Instances modification request\n (processing | fulfilled | failed).

    \n
  • \n
  • \n

    \n status-message - The reason for the status.

    \n
  • \n
  • \n

    \n update-date - The time when the modification request was last updated.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -32912,7 +32949,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone where the Reserved Instance can be\n used.

    \n
  • \n
  • \n

    \n duration - The duration of the Reserved Instance (for example, one year or\n three years), in seconds (31536000 | 94608000).

    \n
  • \n
  • \n

    \n fixed-price - The purchase price of the Reserved Instance (for example,\n 9800.0).

    \n
  • \n
  • \n

    \n instance-type - The instance type that is covered by the\n reservation.

    \n
  • \n
  • \n

    \n marketplace - Set to true to show only Reserved Instance\n Marketplace offerings. When this filter is not used, which is the default behavior, all\n offerings from both Amazon Web Services and the Reserved Instance Marketplace are listed.

    \n
  • \n
  • \n

    \n product-description - The Reserved Instance product platform description.\n Instances that include (Amazon VPC) in the product platform description will\n only be displayed to EC2-Classic account holders and are for use with Amazon VPC.\n (Linux/UNIX | Linux/UNIX (Amazon VPC) | SUSE\n Linux | SUSE Linux (Amazon VPC) | Red Hat Enterprise\n Linux | Red Hat Enterprise Linux (Amazon VPC) | Red Hat\n Enterprise Linux with HA (Amazon VPC) | Windows | Windows\n (Amazon VPC) | Windows with SQL Server Standard | Windows with\n SQL Server Standard (Amazon VPC) | Windows with SQL Server Web |\n Windows with SQL Server Web (Amazon VPC) | Windows with SQL Server\n Enterprise | Windows with SQL Server Enterprise (Amazon VPC))

    \n
  • \n
  • \n

    \n reserved-instances-offering-id - The Reserved Instances offering\n ID.

    \n
  • \n
  • \n

    \n scope - The scope of the Reserved Instance (Availability Zone or\n Region).

    \n
  • \n
  • \n

    \n usage-price - The usage price of the Reserved Instance, per hour (for\n example, 0.84).

    \n
  • \n
", + "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone where the Reserved Instance can be\n used.

    \n
  • \n
  • \n

    \n duration - The duration of the Reserved Instance (for example, one year or\n three years), in seconds (31536000 | 94608000).

    \n
  • \n
  • \n

    \n fixed-price - The purchase price of the Reserved Instance (for example,\n 9800.0).

    \n
  • \n
  • \n

    \n instance-type - The instance type that is covered by the\n reservation.

    \n
  • \n
  • \n

    \n marketplace - Set to true to show only Reserved Instance\n Marketplace offerings. When this filter is not used, which is the default behavior, all\n offerings from both Amazon Web Services and the Reserved Instance Marketplace are listed.

    \n
  • \n
  • \n

    \n product-description - The Reserved Instance product platform description\n (Linux/UNIX | Linux with SQL Server Standard |\n Linux with SQL Server Web | Linux with SQL Server Enterprise |\n SUSE Linux | \n Red Hat Enterprise Linux | Red Hat Enterprise Linux with HA | \n Windows | Windows with SQL Server Standard |\n Windows with SQL Server Web | Windows with SQL Server Enterprise).

    \n
  • \n
  • \n

    \n reserved-instances-offering-id - The Reserved Instances offering\n ID.

    \n
  • \n
  • \n

    \n scope - The scope of the Reserved Instance (Availability Zone or\n Region).

    \n
  • \n
  • \n

    \n usage-price - The usage price of the Reserved Instance, per hour (for\n example, 0.84).

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -33053,7 +33090,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone where the Reserved Instance can be used.

    \n
  • \n
  • \n

    \n duration - The duration of the Reserved Instance (one year or three years), in seconds (31536000 | 94608000).

    \n
  • \n
  • \n

    \n end - The time when the Reserved Instance expires (for example, 2015-08-07T11:54:42.000Z).

    \n
  • \n
  • \n

    \n fixed-price - The purchase price of the Reserved Instance (for example, 9800.0).

    \n
  • \n
  • \n

    \n instance-type - The instance type that is covered by the reservation.

    \n
  • \n
  • \n

    \n scope - The scope of the Reserved Instance (Region or Availability Zone).

    \n
  • \n
  • \n

    \n product-description - The Reserved Instance product platform\n description. Instances that include (Amazon VPC) in the product platform\n description will only be displayed to EC2-Classic account holders and are for use with\n Amazon VPC (Linux/UNIX | Linux/UNIX (Amazon VPC) | SUSE\n Linux | SUSE Linux (Amazon VPC) | Red Hat Enterprise\n Linux | Red Hat Enterprise Linux (Amazon VPC) | Red Hat\n Enterprise Linux with HA (Amazon VPC) | Windows | Windows\n (Amazon VPC) | Windows with SQL Server Standard | Windows with\n SQL Server Standard (Amazon VPC) | Windows with SQL Server Web |\n Windows with SQL Server Web (Amazon VPC) | Windows with SQL Server\n Enterprise | Windows with SQL Server Enterprise (Amazon\n VPC)).

    \n
  • \n
  • \n

    \n reserved-instances-id - The ID of the Reserved Instance.

    \n
  • \n
  • \n

    \n start - The time at which the Reserved Instance purchase request was placed (for example, 2014-08-07T11:54:42.000Z).

    \n
  • \n
  • \n

    \n state - The state of the Reserved Instance (payment-pending | active | payment-failed | retired).

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n usage-price - The usage price of the Reserved Instance, per hour (for example, 0.84).

    \n
  • \n
", + "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone where the Reserved Instance can be used.

    \n
  • \n
  • \n

    \n duration - The duration of the Reserved Instance (one year or three years), in seconds (31536000 | 94608000).

    \n
  • \n
  • \n

    \n end - The time when the Reserved Instance expires (for example, 2015-08-07T11:54:42.000Z).

    \n
  • \n
  • \n

    \n fixed-price - The purchase price of the Reserved Instance (for example, 9800.0).

    \n
  • \n
  • \n

    \n instance-type - The instance type that is covered by the reservation.

    \n
  • \n
  • \n

    \n scope - The scope of the Reserved Instance (Region or Availability Zone).

    \n
  • \n
  • \n

    \n product-description - The Reserved Instance product platform description\n (Linux/UNIX | Linux with SQL Server Standard |\n Linux with SQL Server Web | Linux with SQL Server Enterprise |\n SUSE Linux | \n Red Hat Enterprise Linux | Red Hat Enterprise Linux with HA | \n Windows | Windows with SQL Server Standard |\n Windows with SQL Server Web | Windows with SQL Server Enterprise).

    \n
  • \n
  • \n

    \n reserved-instances-id - The ID of the Reserved Instance.

    \n
  • \n
  • \n

    \n start - The time at which the Reserved Instance purchase request was placed (for example, 2014-08-07T11:54:42.000Z).

    \n
  • \n
  • \n

    \n state - The state of the Reserved Instance (payment-pending | active | payment-failed | retired).

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n usage-price - The usage price of the Reserved Instance, per hour (for example, 0.84).

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -33250,7 +33287,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone (for example, us-west-2a).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example, c4.large).

    \n
  • \n
  • \n

    \n network-platform - The network platform (EC2-Classic or EC2-VPC).

    \n
  • \n
  • \n

    \n platform - The platform (Linux/UNIX or Windows).

    \n
  • \n
", + "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone (for example, us-west-2a).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example, c4.large).

    \n
  • \n
  • \n

    \n platform - The platform (Linux/UNIX or Windows).

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -33362,7 +33399,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone (for example, us-west-2a).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example, c4.large).

    \n
  • \n
  • \n

    \n network-platform - The network platform (EC2-Classic or EC2-VPC).

    \n
  • \n
  • \n

    \n platform - The platform (Linux/UNIX or Windows).

    \n
  • \n
", + "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone (for example, us-west-2a).

    \n
  • \n
  • \n

    \n instance-type - The instance type (for example, c4.large).

    \n
  • \n
  • \n

    \n platform - The platform (Linux/UNIX or Windows).

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -34444,7 +34481,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone-group - The Availability Zone group.

    \n
  • \n
  • \n

    \n create-time - The time stamp when the Spot Instance request was\n created.

    \n
  • \n
  • \n

    \n fault-code - The fault code related to the request.

    \n
  • \n
  • \n

    \n fault-message - The fault message related to the request.

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance that fulfilled the\n request.

    \n
  • \n
  • \n

    \n launch-group - The Spot Instance launch group.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.delete-on-termination - Indicates\n whether the EBS volume is deleted on instance termination.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.device-name - The device name for the\n volume in the block device mapping (for example, /dev/sdh or\n xvdh).

    \n
  • \n
  • \n

    \n launch.block-device-mapping.snapshot-id - The ID of the snapshot\n for the EBS volume.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.volume-size - The size of the EBS\n volume, in GiB.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.volume-type - The type of EBS volume:\n gp2 for General Purpose SSD, io1 or\n io2 for Provisioned IOPS SSD, st1 for Throughput\n Optimized HDD, sc1for Cold HDD, or standard for\n Magnetic.

    \n
  • \n
  • \n

    \n launch.group-id - The ID of the security group for the\n instance.

    \n
  • \n
  • \n

    \n launch.group-name - The name of the security group for the\n instance.

    \n
  • \n
  • \n

    \n launch.image-id - The ID of the AMI.

    \n
  • \n
  • \n

    \n launch.instance-type - The type of instance (for example,\n m3.medium).

    \n
  • \n
  • \n

    \n launch.kernel-id - The kernel ID.

    \n
  • \n
  • \n

    \n launch.key-name - The name of the key pair the instance launched\n with.

    \n
  • \n
  • \n

    \n launch.monitoring-enabled - Whether detailed monitoring is\n enabled for the Spot Instance.

    \n
  • \n
  • \n

    \n launch.ramdisk-id - The RAM disk ID.

    \n
  • \n
  • \n

    \n launched-availability-zone - The Availability Zone in which the\n request is launched.

    \n
  • \n
  • \n

    \n network-interface.addresses.primary - Indicates whether the IP\n address is the primary private IP address.

    \n
  • \n
  • \n

    \n network-interface.delete-on-termination - Indicates whether the\n network interface is deleted when the instance is terminated.

    \n
  • \n
  • \n

    \n network-interface.description - A description of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.device-index - The index of the device for the\n network interface attachment on the instance.

    \n
  • \n
  • \n

    \n network-interface.group-id - The ID of the security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.network-interface-id - The ID of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.private-ip-address - The primary private IP\n address of the network interface.

    \n
  • \n
  • \n

    \n network-interface.subnet-id - The ID of the subnet for the\n instance.

    \n
  • \n
  • \n

    \n product-description - The product description associated with the\n instance (Linux/UNIX | Windows).

    \n
  • \n
  • \n

    \n spot-instance-request-id - The Spot Instance request ID.

    \n
  • \n
  • \n

    \n spot-price - The maximum hourly price for any Spot Instance\n launched to fulfill the request.

    \n
  • \n
  • \n

    \n state - The state of the Spot Instance request (open\n | active | closed | cancelled |\n failed). Spot request status information can help you track\n your Amazon EC2 Spot Instance requests. For more information, see Spot\n request status in the Amazon EC2 User Guide for Linux Instances.

    \n
  • \n
  • \n

    \n status-code - The short code describing the most recent\n evaluation of your Spot Instance request.

    \n
  • \n
  • \n

    \n status-message - The message explaining the status of the Spot\n Instance request.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n type - The type of Spot Instance request (one-time |\n persistent).

    \n
  • \n
  • \n

    \n valid-from - The start date of the request.

    \n
  • \n
  • \n

    \n valid-until - The end date of the request.

    \n
  • \n
", + "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone-group - The Availability Zone group.

    \n
  • \n
  • \n

    \n create-time - The time stamp when the Spot Instance request was\n created.

    \n
  • \n
  • \n

    \n fault-code - The fault code related to the request.

    \n
  • \n
  • \n

    \n fault-message - The fault message related to the request.

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance that fulfilled the\n request.

    \n
  • \n
  • \n

    \n launch-group - The Spot Instance launch group.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.delete-on-termination - Indicates\n whether the EBS volume is deleted on instance termination.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.device-name - The device name for the\n volume in the block device mapping (for example, /dev/sdh or\n xvdh).

    \n
  • \n
  • \n

    \n launch.block-device-mapping.snapshot-id - The ID of the snapshot\n for the EBS volume.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.volume-size - The size of the EBS\n volume, in GiB.

    \n
  • \n
  • \n

    \n launch.block-device-mapping.volume-type - The type of EBS volume:\n gp2 for General Purpose SSD, io1 or\n io2 for Provisioned IOPS SSD, st1 for Throughput\n Optimized HDD, sc1for Cold HDD, or standard for\n Magnetic.

    \n
  • \n
  • \n

    \n launch.group-id - The ID of the security group for the\n instance.

    \n
  • \n
  • \n

    \n launch.group-name - The name of the security group for the\n instance.

    \n
  • \n
  • \n

    \n launch.image-id - The ID of the AMI.

    \n
  • \n
  • \n

    \n launch.instance-type - The type of instance (for example,\n m3.medium).

    \n
  • \n
  • \n

    \n launch.kernel-id - The kernel ID.

    \n
  • \n
  • \n

    \n launch.key-name - The name of the key pair the instance launched\n with.

    \n
  • \n
  • \n

    \n launch.monitoring-enabled - Whether detailed monitoring is\n enabled for the Spot Instance.

    \n
  • \n
  • \n

    \n launch.ramdisk-id - The RAM disk ID.

    \n
  • \n
  • \n

    \n launched-availability-zone - The Availability Zone in which the\n request is launched.

    \n
  • \n
  • \n

    \n network-interface.addresses.primary - Indicates whether the IP\n address is the primary private IP address.

    \n
  • \n
  • \n

    \n network-interface.delete-on-termination - Indicates whether the\n network interface is deleted when the instance is terminated.

    \n
  • \n
  • \n

    \n network-interface.description - A description of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.device-index - The index of the device for the\n network interface attachment on the instance.

    \n
  • \n
  • \n

    \n network-interface.group-id - The ID of the security group\n associated with the network interface.

    \n
  • \n
  • \n

    \n network-interface.network-interface-id - The ID of the network\n interface.

    \n
  • \n
  • \n

    \n network-interface.private-ip-address - The primary private IP\n address of the network interface.

    \n
  • \n
  • \n

    \n network-interface.subnet-id - The ID of the subnet for the\n instance.

    \n
  • \n
  • \n

    \n product-description - The product description associated with the\n instance (Linux/UNIX | Windows).

    \n
  • \n
  • \n

    \n spot-instance-request-id - The Spot Instance request ID.

    \n
  • \n
  • \n

    \n spot-price - The maximum hourly price for any Spot Instance\n launched to fulfill the request.

    \n
  • \n
  • \n

    \n state - The state of the Spot Instance request (open\n | active | closed | cancelled |\n failed). Spot request status information can help you track\n your Amazon EC2 Spot Instance requests. For more information, see Spot\n request status in the Amazon EC2 User Guide for Linux Instances.

    \n
  • \n
  • \n

    \n status-code - The short code describing the most recent\n evaluation of your Spot Instance request.

    \n
  • \n
  • \n

    \n status-message - The message explaining the status of the Spot\n Instance request.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n type - The type of Spot Instance request (one-time |\n persistent).

    \n
  • \n
  • \n

    \n valid-from - The start date of the request.

    \n
  • \n
  • \n

    \n valid-until - The end date of the request.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -34461,7 +34498,7 @@ "SpotInstanceRequestIds": { "target": "com.amazonaws.ec2#SpotInstanceRequestIdList", "traits": { - "smithy.api#documentation": "

One or more Spot Instance request IDs.

", + "smithy.api#documentation": "

The IDs of the Spot Instance requests.

", "smithy.api#xmlName": "SpotInstanceRequestId" } }, @@ -34492,7 +34529,7 @@ "target": "com.amazonaws.ec2#SpotInstanceRequestList", "traits": { "aws.protocols#ec2QueryName": "SpotInstanceRequestSet", - "smithy.api#documentation": "

One or more Spot Instance requests.

", + "smithy.api#documentation": "

The Spot Instance requests.

", "smithy.api#xmlName": "spotInstanceRequestSet" } }, @@ -34533,7 +34570,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone for which prices should\n be returned.

    \n
  • \n
  • \n

    \n instance-type - The type of instance (for example,\n m3.medium).

    \n
  • \n
  • \n

    \n product-description - The product description for the Spot price\n (Linux/UNIX | Red Hat Enterprise Linux |\n SUSE Linux | Windows | Linux/UNIX (Amazon\n VPC) | Red Hat Enterprise Linux (Amazon VPC) |\n SUSE Linux (Amazon VPC) | Windows (Amazon\n VPC)).

    \n
  • \n
  • \n

    \n spot-price - The Spot price. The value must match exactly (or use\n wildcards; greater than or less than comparison is not supported).

    \n
  • \n
  • \n

    \n timestamp - The time stamp of the Spot price history, in UTC format\n (for example,\n YYYY-MM-DDTHH:MM:SSZ).\n You can use wildcards (* and ?). Greater than or less than comparison is not\n supported.

    \n
  • \n
", + "smithy.api#documentation": "

The filters.

\n
    \n
  • \n

    \n availability-zone - The Availability Zone for which prices should\n be returned.

    \n
  • \n
  • \n

    \n instance-type - The type of instance (for example,\n m3.medium).

    \n
  • \n
  • \n

    \n product-description - The product description for the Spot price\n (Linux/UNIX | Red Hat Enterprise Linux |\n SUSE Linux | Windows | Linux/UNIX (Amazon\n VPC) | Red Hat Enterprise Linux (Amazon VPC) |\n SUSE Linux (Amazon VPC) | Windows (Amazon\n VPC)).

    \n
  • \n
  • \n

    \n spot-price - The Spot price. The value must match exactly (or use\n wildcards; greater than or less than comparison is not supported).

    \n
  • \n
  • \n

    \n timestamp - The time stamp of the Spot price history, in UTC format\n (for example,\n YYYY-MM-DDTHH:MM:SSZ).\n You can use wildcards (* and ?). Greater than or less than comparison is not\n supported.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -36187,7 +36224,7 @@ "target": "com.amazonaws.ec2#DescribeVerifiedAccessEndpointsResult" }, "traits": { - "smithy.api#documentation": "

Describe Amazon Web Services Verified Access endpoints.

", + "smithy.api#documentation": "

Describes the specified Amazon Web Services Verified Access endpoints.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -36212,20 +36249,20 @@ "VerifiedAccessEndpointIds": { "target": "com.amazonaws.ec2#VerifiedAccessEndpointIdList", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#xmlName": "VerifiedAccessEndpointId" } }, "VerifiedAccessInstanceId": { "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

The ID of the Verified Access instance.

" } }, "VerifiedAccessGroupId": { "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

" + "smithy.api#documentation": "

The ID of the Verified Access group.

" } }, "MaxResults": { @@ -36269,7 +36306,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointList", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessEndpointSet", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#xmlName": "verifiedAccessEndpointSet" } }, @@ -36302,7 +36339,7 @@ "target": "com.amazonaws.ec2#DescribeVerifiedAccessGroupsResult" }, "traits": { - "smithy.api#documentation": "

Describe details of existing Verified Access groups.

", + "smithy.api#documentation": "

Describes the specified Verified Access groups.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -36317,14 +36354,14 @@ "VerifiedAccessGroupIds": { "target": "com.amazonaws.ec2#VerifiedAccessGroupIdList", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access groups.

", + "smithy.api#documentation": "

The ID of the Verified Access groups.

", "smithy.api#xmlName": "VerifiedAccessGroupId" } }, "VerifiedAccessInstanceId": { "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

The ID of the Verified Access instance.

" } }, "MaxResults": { @@ -36391,7 +36428,7 @@ "target": "com.amazonaws.ec2#DescribeVerifiedAccessInstanceLoggingConfigurationsResult" }, "traits": { - "smithy.api#documentation": "

Describes the current logging configuration for the Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

Describes the specified Amazon Web Services Verified Access instances.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -36416,7 +36453,7 @@ "VerifiedAccessInstanceIds": { "target": "com.amazonaws.ec2#VerifiedAccessInstanceIdList", "traits": { - "smithy.api#documentation": "

The IDs of the Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

The IDs of the Verified Access instances.

", "smithy.api#xmlName": "VerifiedAccessInstanceId" } }, @@ -36461,7 +36498,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceLoggingConfigurationList", "traits": { "aws.protocols#ec2QueryName": "LoggingConfigurationSet", - "smithy.api#documentation": "

The current logging configuration for the Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

The current logging configuration for the Verified Access instances.

", "smithy.api#xmlName": "loggingConfigurationSet" } }, @@ -36484,7 +36521,7 @@ "target": "com.amazonaws.ec2#DescribeVerifiedAccessInstancesResult" }, "traits": { - "smithy.api#documentation": "

Describe Verified Access instances.

", + "smithy.api#documentation": "

Describes the specified Amazon Web Services Verified Access instances.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -36509,7 +36546,7 @@ "VerifiedAccessInstanceIds": { "target": "com.amazonaws.ec2#VerifiedAccessInstanceIdList", "traits": { - "smithy.api#documentation": "

The IDs of the Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

The IDs of the Verified Access instances.

", "smithy.api#xmlName": "VerifiedAccessInstanceId" } }, @@ -36554,7 +36591,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceList", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstanceSet", - "smithy.api#documentation": "

The IDs of the Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

The IDs of the Verified Access instances.

", "smithy.api#xmlName": "verifiedAccessInstanceSet" } }, @@ -36577,7 +36614,7 @@ "target": "com.amazonaws.ec2#DescribeVerifiedAccessTrustProvidersResult" }, "traits": { - "smithy.api#documentation": "

Describe details of existing Verified Access trust providers.

", + "smithy.api#documentation": "

Describes the specified Amazon Web Services Verified Access trust providers.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -36602,7 +36639,7 @@ "VerifiedAccessTrustProviderIds": { "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderIdList", "traits": { - "smithy.api#documentation": "

The IDs of the Amazon Web Services Verified Access trust providers.

", + "smithy.api#documentation": "

The IDs of the Verified Access trust providers.

", "smithy.api#xmlName": "VerifiedAccessTrustProviderId" } }, @@ -36647,7 +36684,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderList", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProviderSet", - "smithy.api#documentation": "

The IDs of the Amazon Web Services Verified Access trust providers.

", + "smithy.api#documentation": "

The IDs of the Verified Access trust providers.

", "smithy.api#xmlName": "verifiedAccessTrustProviderSet" } }, @@ -38505,7 +38542,7 @@ "target": "com.amazonaws.ec2#DetachVerifiedAccessTrustProviderResult" }, "traits": { - "smithy.api#documentation": "

Detach a trust provider from an Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

Detaches the specified Amazon Web Services Verified Access trust provider from the specified Amazon Web Services Verified Access instance.

" } }, "com.amazonaws.ec2#DetachVerifiedAccessTrustProviderRequest": { @@ -38515,7 +38552,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, @@ -38523,7 +38560,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#required": {} } }, @@ -38554,7 +38591,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProvider", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProvider", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#xmlName": "verifiedAccessTrustProvider" } }, @@ -38562,7 +38599,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstance", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstance", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#xmlName": "verifiedAccessInstance" } } @@ -38686,7 +38723,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for an Amazon Web Services Verified Access device-identity based trust provider.

" + "smithy.api#documentation": "

Describes the options for an Amazon Web Services Verified Access device-identity based trust provider.

" } }, "com.amazonaws.ec2#DeviceTrustProviderType": { @@ -40579,7 +40616,7 @@ } }, "ImportManifestUrl": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#ImportManifestUrl", "traits": { "aws.protocols#ec2QueryName": "ImportManifestUrl", "smithy.api#documentation": "

A presigned URL for the import manifest stored in Amazon S3. For information about creating a presigned URL for\n an Amazon S3 object, read the \"Query String Request Authentication Alternative\" section of the Authenticating REST Requests topic in\n the Amazon Simple Storage Service Developer Guide.

\n

For information about the import manifest referenced by this API action, see VM Import Manifest.

", @@ -48136,7 +48173,13 @@ "target": "com.amazonaws.ec2#GetNetworkInsightsAccessScopeAnalysisFindingsResult" }, "traits": { - "smithy.api#documentation": "

Gets the findings for the specified Network Access Scope analysis.

" + "smithy.api#documentation": "

Gets the findings for the specified Network Access Scope analysis.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "items": "AnalysisFindings", + "pageSize": "MaxResults" + } } }, "com.amazonaws.ec2#GetNetworkInsightsAccessScopeAnalysisFindingsRequest": { @@ -49318,7 +49361,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#required": {} } }, @@ -49352,7 +49395,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PolicyDocument", - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

", + "smithy.api#documentation": "

The Verified Access policy document.

", "smithy.api#xmlName": "policyDocument" } } @@ -49377,7 +49420,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The ID of the Verified Access group.

", "smithy.api#required": {} } }, @@ -49411,7 +49454,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PolicyDocument", - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

", + "smithy.api#documentation": "

The Verified Access policy document.

", "smithy.api#xmlName": "policyDocument" } } @@ -51273,7 +51316,7 @@ } }, "Url": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUrl", "traits": { "smithy.api#documentation": "

The URL to the Amazon S3-based disk image being imported. The URL can either be a https URL (https://..) or an\n Amazon S3 URL (s3://..)

" } @@ -52857,7 +52900,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PrivateDnsName", - "smithy.api#documentation": "

(IPv4 only) The private DNS hostname name assigned to the instance. This DNS hostname\n can only be used inside the Amazon EC2 network. This name is not available until the\n instance enters the running state.

\n

[EC2-VPC] The Amazon-provided DNS server resolves Amazon-provided private DNS\n hostnames if you've enabled DNS resolution and DNS hostnames in your VPC. If you are not\n using the Amazon-provided DNS server in your VPC, your custom domain name servers must\n resolve the hostname as appropriate.

", + "smithy.api#documentation": "

[IPv4 only] The private DNS hostname name assigned to the instance. This DNS hostname\n can only be used inside the Amazon EC2 network. This name is not available until the\n instance enters the running state.

\n

The Amazon-provided DNS server resolves Amazon-provided private DNS\n hostnames if you've enabled DNS resolution and DNS hostnames in your VPC. If you are not\n using the Amazon-provided DNS server in your VPC, your custom domain name servers must\n resolve the hostname as appropriate.

", "smithy.api#xmlName": "privateDnsName" } }, @@ -52881,7 +52924,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "DnsName", - "smithy.api#documentation": "

(IPv4 only) The public DNS name assigned to the instance. This name is not available\n until the instance enters the running state. For EC2-VPC, this name is only\n available if you've enabled DNS hostnames for your VPC.

", + "smithy.api#documentation": "

[IPv4 only] The public DNS name assigned to the instance. This name is not available\n until the instance enters the running state. This name is only\n available if you've enabled DNS hostnames for your VPC.

", "smithy.api#xmlName": "dnsName" } }, @@ -52921,7 +52964,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "SubnetId", - "smithy.api#documentation": "

[EC2-VPC] The ID of the subnet in which the instance is running.

", + "smithy.api#documentation": "

The ID of the subnet in which the instance is running.

", "smithy.api#xmlName": "subnetId" } }, @@ -52929,7 +52972,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "VpcId", - "smithy.api#documentation": "

[EC2-VPC] The ID of the VPC in which the instance is running.

", + "smithy.api#documentation": "

The ID of the VPC in which the instance is running.

", "smithy.api#xmlName": "vpcId" } }, @@ -53021,7 +53064,7 @@ "target": "com.amazonaws.ec2#InstanceNetworkInterfaceList", "traits": { "aws.protocols#ec2QueryName": "NetworkInterfaceSet", - "smithy.api#documentation": "

[EC2-VPC] The network interfaces for the instance.

", + "smithy.api#documentation": "

The network interfaces for the instance.

", "smithy.api#xmlName": "networkInterfaceSet" } }, @@ -59816,6 +59859,72 @@ "traits": { "smithy.api#enumValue": "r6idn.metal" } + }, + "inf2_xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "inf2.xlarge" + } + }, + "inf2_8xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "inf2.8xlarge" + } + }, + "inf2_24xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "inf2.24xlarge" + } + }, + "inf2_48xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "inf2.48xlarge" + } + }, + "trn1n_32xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "trn1n.32xlarge" + } + }, + "i4g_large": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.large" + } + }, + "i4g_xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.xlarge" + } + }, + "i4g_2xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.2xlarge" + } + }, + "i4g_4xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.4xlarge" + } + }, + "i4g_8xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.8xlarge" + } + }, + "i4g_16xlarge": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "i4g.16xlarge" + } } } }, @@ -60019,7 +60128,7 @@ "target": "com.amazonaws.ec2#AutoRecoveryFlag", "traits": { "aws.protocols#ec2QueryName": "AutoRecoverySupported", - "smithy.api#documentation": "

Indicates whether auto recovery is supported.

", + "smithy.api#documentation": "

Indicates whether Amazon CloudWatch action based recovery is supported.

", "smithy.api#xmlName": "autoRecoverySupported" } }, @@ -63211,7 +63320,7 @@ "target": "com.amazonaws.ec2#GroupIdentifierList", "traits": { "aws.protocols#ec2QueryName": "GroupSet", - "smithy.api#documentation": "

One or more security groups. When requesting instances in a VPC, you must specify the IDs of the security groups. When requesting instances in EC2-Classic, you can specify the names or the IDs of the security groups.

", + "smithy.api#documentation": "

The IDs of the security groups.

", "smithy.api#xmlName": "groupSet" } }, @@ -63227,7 +63336,7 @@ "target": "com.amazonaws.ec2#BlockDeviceMappingList", "traits": { "aws.protocols#ec2QueryName": "BlockDeviceMapping", - "smithy.api#documentation": "

One or more block device mapping entries.

", + "smithy.api#documentation": "

The block device mapping entries.

", "smithy.api#xmlName": "blockDeviceMapping" } }, @@ -63285,7 +63394,7 @@ "target": "com.amazonaws.ec2#InstanceNetworkInterfaceSpecificationList", "traits": { "aws.protocols#ec2QueryName": "NetworkInterfaceSet", - "smithy.api#documentation": "

One or more network interfaces. If you specify a network interface, you must specify \n subnet IDs and security group IDs using the network interface.

", + "smithy.api#documentation": "

The network interfaces. If you specify a network interface, you must specify \n subnet IDs and security group IDs using the network interface.

", "smithy.api#xmlName": "networkInterfaceSet" } }, @@ -63632,6 +63741,14 @@ "smithy.api#documentation": "

The number of threads per CPU core.

", "smithy.api#xmlName": "threadsPerCore" } + }, + "AmdSevSnp": { + "target": "com.amazonaws.ec2#AmdSevSnpSpecification", + "traits": { + "aws.protocols#ec2QueryName": "AmdSevSnp", + "smithy.api#documentation": "

Indicates whether the instance is enabled for \n AMD SEV-SNP.

", + "smithy.api#xmlName": "amdSevSnp" + } } }, "traits": { @@ -63656,6 +63773,12 @@ "smithy.api#default": 0, "smithy.api#documentation": "

The number of threads per CPU core. To disable multithreading for the instance,\n specify a value of 1. Otherwise, specify the default value of\n 2.

" } + }, + "AmdSevSnp": { + "target": "com.amazonaws.ec2#AmdSevSnpSpecification", + "traits": { + "smithy.api#documentation": "

Indicates whether to enable the instance for AMD SEV-SNP. AMD SEV-SNP is supported \n with M6a, R6a, and C6a instance types only.

" + } } }, "traits": { @@ -64831,7 +64954,7 @@ "target": "com.amazonaws.ec2#Tenancy", "traits": { "aws.protocols#ec2QueryName": "Tenancy", - "smithy.api#documentation": "

The tenancy of the instance (if the instance is running in a VPC). An instance with a\n tenancy of dedicated runs on single-tenant hardware.

", + "smithy.api#documentation": "

The tenancy of the instance. An instance with a\n tenancy of dedicated runs on single-tenant hardware.

", "smithy.api#xmlName": "tenancy" } }, @@ -64904,7 +65027,7 @@ "Tenancy": { "target": "com.amazonaws.ec2#Tenancy", "traits": { - "smithy.api#documentation": "

The tenancy of the instance (if the instance is running in a VPC). An instance with a\n tenancy of dedicated runs on single-tenant hardware.

" + "smithy.api#documentation": "

The tenancy of the instance. An instance with a\n tenancy of dedicated runs on single-tenant hardware.

" } }, "SpreadDomain": { @@ -67889,7 +68012,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Modifies the specified attribute of the specified instance. You can specify only one\n attribute at a time.

\n

\n Note: Using this action to change the security groups\n associated with an elastic network interface (ENI) attached to an instance in a VPC can\n result in an error if the instance has more than one ENI. To change the security groups\n associated with an ENI attached to an instance that has multiple ENIs, we recommend that\n you use the ModifyNetworkInterfaceAttribute action.

\n

To modify some attributes, the instance must be stopped. For more information, see\n Modify a stopped instance in the\n Amazon EC2 User Guide.

" + "smithy.api#documentation": "

Modifies the specified attribute of the specified instance. You can specify only one\n attribute at a time.

\n

\n Note: Using this action to change the security groups\n associated with an elastic network interface (ENI) attached to an instance can\n result in an error if the instance has more than one ENI. To change the security groups\n associated with an ENI attached to an instance that has multiple ENIs, we recommend that\n you use the ModifyNetworkInterfaceAttribute action.

\n

To modify some attributes, the instance must be stopped. For more information, see\n Modify a stopped instance in the\n Amazon EC2 User Guide.

" } }, "com.amazonaws.ec2#ModifyInstanceAttributeRequest": { @@ -67954,7 +68077,7 @@ "Groups": { "target": "com.amazonaws.ec2#GroupIdStringList", "traits": { - "smithy.api#documentation": "

[EC2-VPC] Replaces the security groups of the instance with the specified security\n groups. You must specify at least one security group, even if it's just the default\n security group for the VPC. You must specify the security group ID, not the security\n group name.

", + "smithy.api#documentation": "

Replaces the security groups of the instance with the specified security groups.\n You must specify the ID of at least one security group, even if it's just the default\n security group for the VPC.

", "smithy.api#xmlName": "GroupId" } }, @@ -69312,7 +69435,7 @@ "target": "com.amazonaws.ec2#ModifyReservedInstancesResult" }, "traits": { - "smithy.api#documentation": "

Modifies the configuration of your Reserved Instances, such as the Availability Zone, \n instance count, or instance type. The Reserved Instances to be modified must be identical, \n except for Availability Zone, network platform, and instance type.

\n

For more information, see Modifying Reserved\n\t\t\t\tInstances in the Amazon EC2 User Guide.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "

Modifies the configuration of your Reserved Instances, such as the Availability Zone, \n instance count, or instance type. The Reserved Instances to be modified must be identical, \n except for Availability Zone, network platform, and instance type.

\n

For more information, see Modifying Reserved\n\t\t\t\tInstances in the Amazon EC2 User Guide.

" } }, "com.amazonaws.ec2#ModifyReservedInstancesRequest": { @@ -70319,7 +70442,7 @@ "target": "com.amazonaws.ec2#ModifyVerifiedAccessEndpointResult" }, "traits": { - "smithy.api#documentation": "

Modifies the configuration of an Amazon Web Services Verified Access endpoint.

" + "smithy.api#documentation": "

Modifies the configuration of the specified Amazon Web Services Verified Access endpoint.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpointEniOptions": { @@ -70341,7 +70464,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for a network-interface type Verified Access endpoint.

" + "smithy.api#documentation": "

Describes the options when modifying a Verified Access endpoint with the\n network-interface type.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpointLoadBalancerOptions": { @@ -70382,7 +70505,7 @@ "target": "com.amazonaws.ec2#ModifyVerifiedAccessEndpointPolicyResult" }, "traits": { - "smithy.api#documentation": "

Modifies the specified Verified Access endpoint policy.

" + "smithy.api#documentation": "

Modifies the specified Amazon Web Services Verified Access endpoint policy.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpointPolicyRequest": { @@ -70392,7 +70515,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#required": {} } }, @@ -70408,7 +70531,7 @@ "PolicyDocument": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

" + "smithy.api#documentation": "

The Verified Access policy document.

" } }, "ClientToken": { @@ -70448,7 +70571,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PolicyDocument", - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

", + "smithy.api#documentation": "

The Verified Access policy document.

", "smithy.api#xmlName": "policyDocument" } } @@ -70461,20 +70584,20 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpointId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access endpoint.

", + "smithy.api#documentation": "

The ID of the Verified Access endpoint.

", "smithy.api#required": {} } }, "VerifiedAccessGroupId": { "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

" + "smithy.api#documentation": "

The ID of the Verified Access group.

" } }, "LoadBalancerOptions": { "target": "com.amazonaws.ec2#ModifyVerifiedAccessEndpointLoadBalancerOptions", "traits": { - "smithy.api#documentation": "

The load balancer details if creating the Amazon Web Services Verified Access endpoint as\n load-balancertype.

" + "smithy.api#documentation": "

The load balancer details if creating the Verified Access endpoint as\n load-balancertype.

" } }, "NetworkInterfaceOptions": { @@ -70486,7 +70609,7 @@ "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access endpoint.

" + "smithy.api#documentation": "

A description for the Verified Access endpoint.

" } }, "ClientToken": { @@ -70516,7 +70639,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessEndpoint", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessEndpoint", - "smithy.api#documentation": "

The Amazon Web Services Verified Access endpoint details.

", + "smithy.api#documentation": "

The Verified Access endpoint details.

", "smithy.api#xmlName": "verifiedAccessEndpoint" } } @@ -70540,7 +70663,7 @@ "target": "com.amazonaws.ec2#ModifyVerifiedAccessGroupResult" }, "traits": { - "smithy.api#documentation": "

Modifies the specified Verified Access group configuration.

" + "smithy.api#documentation": "

Modifies the specified Amazon Web Services Verified Access group configuration.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessGroupPolicy": { @@ -70552,7 +70675,7 @@ "target": "com.amazonaws.ec2#ModifyVerifiedAccessGroupPolicyResult" }, "traits": { - "smithy.api#documentation": "

Modifies the specified Verified Access group policy.

" + "smithy.api#documentation": "

Modifies the specified Amazon Web Services Verified Access group policy.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessGroupPolicyRequest": { @@ -70562,7 +70685,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The ID of the Verified Access group.

", "smithy.api#required": {} } }, @@ -70578,7 +70701,7 @@ "PolicyDocument": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

" + "smithy.api#documentation": "

The Verified Access policy document.

" } }, "ClientToken": { @@ -70618,7 +70741,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PolicyDocument", - "smithy.api#documentation": "

The Amazon Web Services Verified Access policy document.

", + "smithy.api#documentation": "

The Verified Access policy document.

", "smithy.api#xmlName": "policyDocument" } } @@ -70631,20 +70754,20 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroupId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

The ID of the Verified Access group.

", "smithy.api#required": {} } }, "VerifiedAccessInstanceId": { "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

The ID of the Verified Access instance.

" } }, "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access group.

" + "smithy.api#documentation": "

A description for the Verified Access group.

" } }, "ClientToken": { @@ -70674,7 +70797,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessGroup", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessGroup", - "smithy.api#documentation": "

Details of Amazon Web Services Verified Access group.

", + "smithy.api#documentation": "

Details of Verified Access group.

", "smithy.api#xmlName": "verifiedAccessGroup" } } @@ -70689,7 +70812,7 @@ "target": "com.amazonaws.ec2#ModifyVerifiedAccessInstanceResult" }, "traits": { - "smithy.api#documentation": "

Modifies the configuration of the specified Verified Access instance.

" + "smithy.api#documentation": "

Modifies the configuration of the specified Amazon Web Services Verified Access instance.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessInstanceLoggingConfiguration": { @@ -70711,7 +70834,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, @@ -70719,7 +70842,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessLogOptions", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The configuration options for Amazon Web Services Verified Access instances.

", + "smithy.api#documentation": "

The configuration options for Verified Access instances.

", "smithy.api#required": {} } }, @@ -70750,7 +70873,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceLoggingConfiguration", "traits": { "aws.protocols#ec2QueryName": "LoggingConfiguration", - "smithy.api#documentation": "

The logging configuration for Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The logging configuration for the Verified Access instance.

", "smithy.api#xmlName": "loggingConfiguration" } } @@ -70763,14 +70886,14 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstanceId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#required": {} } }, "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access instance.

" + "smithy.api#documentation": "

A description for the Verified Access instance.

" } }, "DryRun": { @@ -70800,7 +70923,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessInstance", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessInstance", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access instance.

", + "smithy.api#documentation": "

The ID of the Verified Access instance.

", "smithy.api#xmlName": "verifiedAccessInstance" } } @@ -70821,6 +70944,42 @@ "com.amazonaws.ec2#ModifyVerifiedAccessTrustProviderOidcOptions": { "type": "structure", "members": { + "Issuer": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

The OIDC issuer.

" + } + }, + "AuthorizationEndpoint": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

The OIDC authorization endpoint.

" + } + }, + "TokenEndpoint": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

The OIDC token endpoint.

" + } + }, + "UserInfoEndpoint": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

The OIDC user info endpoint.

" + } + }, + "ClientId": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

The client identifier.

" + } + }, + "ClientSecret": { + "target": "com.amazonaws.ec2#ClientSecretType", + "traits": { + "smithy.api#documentation": "

The client secret.

" + } + }, "Scope": { "target": "com.amazonaws.ec2#String", "traits": { @@ -70829,7 +70988,7 @@ } }, "traits": { - "smithy.api#documentation": "

OpenID Connect options for an oidc-type, user-identity based trust\n provider.

" + "smithy.api#documentation": "

Options for an OpenID Connect-compatible user-identity trust provider.

" } }, "com.amazonaws.ec2#ModifyVerifiedAccessTrustProviderRequest": { @@ -70839,20 +70998,20 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProviderId", "traits": { "smithy.api#clientOptional": {}, - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#required": {} } }, "OidcOptions": { "target": "com.amazonaws.ec2#ModifyVerifiedAccessTrustProviderOidcOptions", "traits": { - "smithy.api#documentation": "

The OpenID Connect details for an oidc-type, user-identity based trust provider.

" + "smithy.api#documentation": "

The options for an OpenID Connect-compatible user-identity trust provider.

" } }, "Description": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

A description for the Amazon Web Services Verified Access trust provider.

" + "smithy.api#documentation": "

A description for the Verified Access trust provider.

" } }, "DryRun": { @@ -70882,7 +71041,7 @@ "target": "com.amazonaws.ec2#VerifiedAccessTrustProvider", "traits": { "aws.protocols#ec2QueryName": "VerifiedAccessTrustProvider", - "smithy.api#documentation": "

The ID of the Amazon Web Services Verified Access trust provider.

", + "smithy.api#documentation": "

The ID of the Verified Access trust provider.

", "smithy.api#xmlName": "verifiedAccessTrustProvider" } } @@ -74619,7 +74778,7 @@ } }, "ClientSecret": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#ClientSecretType", "traits": { "aws.protocols#ec2QueryName": "ClientSecret", "smithy.api#documentation": "

The client secret.

", @@ -74636,7 +74795,7 @@ } }, "traits": { - "smithy.api#documentation": "

Options for OIDC-based, user-identity type trust provider.

" + "smithy.api#documentation": "

Describes the options for an OpenID Connect-compatible user-identity trust\n provider.

" } }, "com.amazonaws.ec2#OnDemandAllocationStrategy": { @@ -75860,7 +76019,7 @@ "target": "com.amazonaws.ec2#Tenancy", "traits": { "aws.protocols#ec2QueryName": "Tenancy", - "smithy.api#documentation": "

The tenancy of the instance (if the instance is running in a VPC). An instance with a\n tenancy of dedicated runs on single-tenant hardware.

\n

This parameter is not supported for CreateFleet. The\n host tenancy is not supported for ImportInstance or\n for T3 instances that are configured for the unlimited CPU credit\n option.

", + "smithy.api#documentation": "

The tenancy of the instance. An instance with a\n tenancy of dedicated runs on single-tenant hardware.

\n

This parameter is not supported for CreateFleet. The\n host tenancy is not supported for ImportInstance or\n for T3 instances that are configured for the unlimited CPU credit\n option.

", "smithy.api#xmlName": "tenancy" } }, @@ -76863,7 +77022,7 @@ "smithy.api#default": 0, "smithy.api#range": { "min": 1, - "max": 7 + "max": 31 } } }, @@ -76929,6 +77088,14 @@ "smithy.api#documentation": "

The speed of the processor, in GHz.

", "smithy.api#xmlName": "sustainedClockSpeedInGhz" } + }, + "SupportedFeatures": { + "target": "com.amazonaws.ec2#SupportedAdditionalProcessorFeatureList", + "traits": { + "aws.protocols#ec2QueryName": "SupportedFeatures", + "smithy.api#documentation": "

Indicates whether the instance type supports AMD SEV-SNP. If the request returns \n amd-sev-snp, AMD SEV-SNP is supported. Otherwise, it is not supported.

", + "smithy.api#xmlName": "supportedFeatures" + } } }, "traits": { @@ -77777,7 +77944,7 @@ "target": "com.amazonaws.ec2#PurchaseReservedInstancesOfferingResult" }, "traits": { - "smithy.api#documentation": "

Purchases a Reserved Instance for use with your account. With Reserved Instances, you pay a lower \n hourly rate compared to On-Demand instance pricing.

\n

Use DescribeReservedInstancesOfferings to get a list of Reserved Instance offerings \n\t\t\tthat match your specifications. After you've purchased a Reserved Instance, you can check for your\n\t\t\tnew Reserved Instance with DescribeReservedInstances.

\n

To queue a purchase for a future date and time, specify a purchase time. If you do not specify a\n purchase time, the default is the current time.

\n

For more information, see Reserved Instances and \n \t Reserved Instance Marketplace \n \t in the Amazon EC2 User Guide.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "

Purchases a Reserved Instance for use with your account. With Reserved Instances, you pay a lower \n hourly rate compared to On-Demand instance pricing.

\n

Use DescribeReservedInstancesOfferings to get a list of Reserved Instance offerings \n\t\t\tthat match your specifications. After you've purchased a Reserved Instance, you can check for your\n\t\t\tnew Reserved Instance with DescribeReservedInstances.

\n

To queue a purchase for a future date and time, specify a purchase time. If you do not specify a\n purchase time, the default is the current time.

\n

For more information, see Reserved Instances and \n \t Reserved Instance Marketplace \n \t in the Amazon EC2 User Guide.

" } }, "com.amazonaws.ec2#PurchaseReservedInstancesOfferingRequest": { @@ -80040,7 +80207,7 @@ "ImageId": { "target": "com.amazonaws.ec2#ImageId", "traits": { - "smithy.api#documentation": "

The ID of the AMI. Alternatively, you can specify a Systems Manager parameter, which\n will resolve to an AMI ID on launch.

\n

Valid formats:

\n
    \n
  • \n

    \n ami-17characters00000\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name:version-number\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name:label\n

    \n
  • \n
\n

For more information, see Use a Systems Manager parameter to find an AMI in the Amazon Elastic Compute Cloud User Guide.

" + "smithy.api#documentation": "

The ID of the AMI. Alternatively, you can specify a Systems Manager parameter, which\n will resolve to an AMI ID on launch.

\n

Valid formats:

\n
    \n
  • \n

    \n ami-17characters00000\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name:version-number\n

    \n
  • \n
  • \n

    \n resolve:ssm:parameter-name:label\n

    \n
  • \n
  • \n

    \n resolve:ssm:public-parameter\n

    \n
  • \n
\n \n

Currently, EC2 Fleet and Spot Fleet do not support specifying a Systems Manager parameter. \n If the launch template will be used by an EC2 Fleet or Spot Fleet, you must specify the AMI ID.

\n
\n

For more information, see Use a Systems Manager parameter instead of an AMI ID in the Amazon Elastic Compute Cloud User Guide.

" } }, "InstanceType": { @@ -80088,7 +80255,7 @@ } }, "UserData": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUserData", "traits": { "smithy.api#documentation": "

The user data to make available to the instance. You must provide base64-encoded text.\n User data is limited to 16 KB. For more information, see Run commands on your Linux instance at\n launch (Linux) or Work with instance\n user data (Windows) in the Amazon Elastic Compute Cloud User Guide.

\n

If you are creating the launch template for use with Batch, the user\n data must be provided in the MIME multi-part archive format. For more information, see Amazon EC2 user data in launch templates in the Batch User Guide.

" } @@ -80205,8 +80372,7 @@ } }, "traits": { - "smithy.api#documentation": "

The information to include in the launch template.

\n \n

You must specify at least one parameter for the launch template data.

\n
", - "smithy.api#sensitive": {} + "smithy.api#documentation": "

The information to include in the launch template.

\n \n

You must specify at least one parameter for the launch template data.

\n
" } }, "com.amazonaws.ec2#RequestSpotFleet": { @@ -80276,7 +80442,7 @@ "target": "com.amazonaws.ec2#RequestSpotInstancesResult" }, "traits": { - "smithy.api#documentation": "

Creates a Spot Instance request.

\n

For more information, see Spot Instance requests in\n the Amazon EC2 User Guide for Linux Instances.

\n \n

We strongly discourage using the RequestSpotInstances API because it is a legacy\n API with no planned investment. For options for requesting Spot Instances, see\n Which\n is the best Spot request method to use? in the\n Amazon EC2 User Guide for Linux Instances.

\n
\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon EC2 User Guide for Linux Instances.

\n
" + "smithy.api#documentation": "

Creates a Spot Instance request.

\n

For more information, see Spot Instance requests in\n the Amazon EC2 User Guide for Linux Instances.

\n \n

We strongly discourage using the RequestSpotInstances API because it is a legacy\n API with no planned investment. For options for requesting Spot Instances, see\n Which\n is the best Spot request method to use? in the\n Amazon EC2 User Guide for Linux Instances.

\n
" } }, "com.amazonaws.ec2#RequestSpotInstancesRequest": { @@ -80400,7 +80566,7 @@ "target": "com.amazonaws.ec2#SpotInstanceRequestList", "traits": { "aws.protocols#ec2QueryName": "SpotInstanceRequestSet", - "smithy.api#documentation": "

One or more Spot Instance requests.

", + "smithy.api#documentation": "

The Spot Instance requests.

", "smithy.api#xmlName": "spotInstanceRequestSet" } } @@ -80415,14 +80581,14 @@ "SecurityGroupIds": { "target": "com.amazonaws.ec2#RequestSpotLaunchSpecificationSecurityGroupIdList", "traits": { - "smithy.api#documentation": "

One or more security group IDs.

", + "smithy.api#documentation": "

The IDs of the security groups.

", "smithy.api#xmlName": "SecurityGroupId" } }, "SecurityGroups": { "target": "com.amazonaws.ec2#RequestSpotLaunchSpecificationSecurityGroupList", "traits": { - "smithy.api#documentation": "

One or more security groups. When requesting instances in a VPC, you must specify the IDs of the security groups. When requesting instances in EC2-Classic, you can specify the names or the IDs of the security groups.

", + "smithy.api#documentation": "

Not supported.

", "smithy.api#xmlName": "SecurityGroup" } }, @@ -80438,7 +80604,7 @@ "target": "com.amazonaws.ec2#BlockDeviceMappingList", "traits": { "aws.protocols#ec2QueryName": "BlockDeviceMapping", - "smithy.api#documentation": "

One or more block device mapping entries. You can't specify both a snapshot ID and an encryption value. \n This is because only blank volumes can be encrypted on creation. If a snapshot is the basis for a volume, \n it is not blank and its encryption status is used for the volume encryption status.

", + "smithy.api#documentation": "

The block device mapping entries. You can't specify both a snapshot ID and an encryption value. \n This is because only blank volumes can be encrypted on creation. If a snapshot is the basis for a volume, \n it is not blank and its encryption status is used for the volume encryption status.

", "smithy.api#xmlName": "blockDeviceMapping" } }, @@ -80503,7 +80669,7 @@ "NetworkInterfaces": { "target": "com.amazonaws.ec2#InstanceNetworkInterfaceSpecificationList", "traits": { - "smithy.api#documentation": "

One or more network interfaces. If you specify a network interface, you must specify \n subnet IDs and security group IDs using the network interface.

", + "smithy.api#documentation": "

The network interfaces. If you specify a network interface, you must specify \n subnet IDs and security group IDs using the network interface.

", "smithy.api#xmlName": "NetworkInterface" } }, @@ -80569,7 +80735,7 @@ "target": "com.amazonaws.ec2#GroupIdentifierList", "traits": { "aws.protocols#ec2QueryName": "GroupSet", - "smithy.api#documentation": "

[EC2-Classic only] The security groups.

", + "smithy.api#documentation": "

Not supported.

", "smithy.api#xmlName": "groupSet" } }, @@ -81043,7 +81209,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "Platform", - "smithy.api#documentation": "

The network platform of the modified Reserved Instances, which is either EC2-Classic or EC2-VPC.

", + "smithy.api#documentation": "

The network platform of the modified Reserved Instances.

", "smithy.api#xmlName": "platform" } }, @@ -84047,7 +84213,7 @@ "target": "com.amazonaws.ec2#Reservation" }, "traits": { - "smithy.api#documentation": "

Launches the specified number of instances using an AMI for which you have\n permissions.

\n

You can specify a number of options, or leave the default options. The following rules\n apply:

\n
    \n
  • \n

    [EC2-VPC] If you don't specify a subnet ID, we choose a default subnet from\n your default VPC for you. If you don't have a default VPC, you must specify a\n subnet ID in the request.

    \n
  • \n
  • \n

    [EC2-Classic] If don't specify an Availability Zone, we choose one for\n you.

    \n
  • \n
  • \n

    Some instance types must be launched into a VPC. If you do not have a default\n VPC, or if you do not specify a subnet ID, the request fails. For more\n information, see Instance types available only in a VPC.

    \n
  • \n
  • \n

    [EC2-VPC] All instances have a network interface with a primary private IPv4\n address. If you don't specify this address, we choose one from the IPv4 range of\n your subnet.

    \n
  • \n
  • \n

    Not all instance types support IPv6 addresses. For more information, see\n Instance\n types.

    \n
  • \n
  • \n

    If you don't specify a security group ID, we use the default security group.\n For more information, see Security\n groups.

    \n
  • \n
  • \n

    If any of the AMIs have a product code attached for which the user has not\n subscribed, the request fails.

    \n
  • \n
\n

You can create a launch template,\n which is a resource that contains the parameters to launch an instance. When you launch\n an instance using RunInstances, you can specify the launch template\n instead of specifying the launch parameters.

\n

To ensure faster instance launches, break up large requests into smaller batches. For\n example, create five separate launch requests for 100 instances each instead of one\n launch request for 500 instances.

\n

An instance is ready for you to use when it's in the running state. You\n can check the state of your instance using DescribeInstances. You can\n tag instances and EBS volumes during launch, after launch, or both. For more\n information, see CreateTags and Tagging your Amazon EC2\n resources.

\n

Linux instances have access to the public key of the key pair at boot. You can use\n this key to provide secure access to the instance. Amazon EC2 public images use this\n feature to provide secure access without passwords. For more information, see Key\n pairs.

\n

For troubleshooting, see What to do if\n an instance immediately terminates, and Troubleshooting connecting to your instance.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a\n VPC. For more information, see Migrate from EC2-Classic to a\n VPC in the Amazon EC2 User Guide.

\n
" + "smithy.api#documentation": "

Launches the specified number of instances using an AMI for which you have\n permissions.

\n

You can specify a number of options, or leave the default options. The following rules\n apply:

\n
    \n
  • \n

    If you don't specify a subnet ID, we choose a default subnet from\n your default VPC for you. If you don't have a default VPC, you must specify a\n subnet ID in the request.

    \n
  • \n
  • \n

    All instances have a network interface with a primary private IPv4\n address. If you don't specify this address, we choose one from the IPv4 range of\n your subnet.

    \n
  • \n
  • \n

    Not all instance types support IPv6 addresses. For more information, see\n Instance\n types.

    \n
  • \n
  • \n

    If you don't specify a security group ID, we use the default security group.\n For more information, see Security\n groups.

    \n
  • \n
  • \n

    If any of the AMIs have a product code attached for which the user has not\n subscribed, the request fails.

    \n
  • \n
\n

You can create a launch template,\n which is a resource that contains the parameters to launch an instance. When you launch\n an instance using RunInstances, you can specify the launch template\n instead of specifying the launch parameters.

\n

To ensure faster instance launches, break up large requests into smaller batches. For\n example, create five separate launch requests for 100 instances each instead of one\n launch request for 500 instances.

\n

An instance is ready for you to use when it's in the running state. You\n can check the state of your instance using DescribeInstances. You can\n tag instances and EBS volumes during launch, after launch, or both. For more\n information, see CreateTags and Tagging your Amazon EC2\n resources.

\n

Linux instances have access to the public key of the key pair at boot. You can use\n this key to provide secure access to the instance. Amazon EC2 public images use this\n feature to provide secure access without passwords. For more information, see Key\n pairs.

\n

For troubleshooting, see What to do if\n an instance immediately terminates, and Troubleshooting connecting to your instance.

" } }, "com.amazonaws.ec2#RunInstancesMonitoringEnabled": { @@ -84096,13 +84262,13 @@ "traits": { "smithy.api#clientOptional": {}, "smithy.api#default": 0, - "smithy.api#documentation": "

[EC2-VPC] The number of IPv6 addresses to associate with the primary network\n interface. Amazon EC2 chooses the IPv6 addresses from the range of your subnet. You\n cannot specify this option and the option to assign specific IPv6 addresses in the same\n request. You can specify this option if you've specified a minimum number of instances\n to launch.

\n

You cannot specify this option and the network interfaces option in the same\n request.

" + "smithy.api#documentation": "

The number of IPv6 addresses to associate with the primary network\n interface. Amazon EC2 chooses the IPv6 addresses from the range of your subnet. You\n cannot specify this option and the option to assign specific IPv6 addresses in the same\n request. You can specify this option if you've specified a minimum number of instances\n to launch.

\n

You cannot specify this option and the network interfaces option in the same\n request.

" } }, "Ipv6Addresses": { "target": "com.amazonaws.ec2#InstanceIpv6AddressList", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The IPv6 addresses from the range of the subnet to associate with the\n primary network interface. You cannot specify this option and the option to assign a\n number of IPv6 addresses in the same request. You cannot specify this option if you've\n specified a minimum number of instances to launch.

\n

You cannot specify this option and the network interfaces option in the same\n request.

", + "smithy.api#documentation": "

The IPv6 addresses from the range of the subnet to associate with the\n primary network interface. You cannot specify this option and the option to assign a\n number of IPv6 addresses in the same request. You cannot specify this option if you've\n specified a minimum number of instances to launch.

\n

You cannot specify this option and the network interfaces option in the same\n request.

", "smithy.api#xmlName": "Ipv6Address" } }, @@ -84164,14 +84330,14 @@ "SecurityGroups": { "target": "com.amazonaws.ec2#SecurityGroupStringList", "traits": { - "smithy.api#documentation": "

[EC2-Classic, default VPC] The names of the security groups.

\n

If you specify a network interface, you must specify any security groups as part of\n the network interface.

\n

Default: Amazon EC2 uses the default security group.

", + "smithy.api#documentation": "

[Default VPC] The names of the security groups.

\n

If you specify a network interface, you must specify any security groups as part of\n the network interface.

\n

Default: Amazon EC2 uses the default security group.

", "smithy.api#xmlName": "SecurityGroup" } }, "SubnetId": { "target": "com.amazonaws.ec2#SubnetId", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The ID of the subnet to launch the instance into.

\n

If you specify a network interface, you must specify any subnets as part of the\n network interface.

" + "smithy.api#documentation": "

The ID of the subnet to launch the instance into.

\n

If you specify a network interface, you must specify any subnets as part of the\n network interface.

" } }, "UserData": { @@ -84255,7 +84421,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PrivateIpAddress", - "smithy.api#documentation": "

[EC2-VPC] The primary IPv4 address. You must specify a value from the IPv4 address\n range of the subnet.

\n

Only one private IP address can be designated as primary. You can't specify this\n option if you've specified the option to designate a private IP address as the primary\n IP address in a network interface specification. You cannot specify this option if\n you're launching more than one instance in the request.

\n

You cannot specify this option and the network interfaces option in the same\n request.

", + "smithy.api#documentation": "

The primary IPv4 address. You must specify a value from the IPv4 address\n range of the subnet.

\n

Only one private IP address can be designated as primary. You can't specify this\n option if you've specified the option to designate a private IP address as the primary\n IP address in a network interface specification. You cannot specify this option if\n you're launching more than one instance in the request.

\n

You cannot specify this option and the network interfaces option in the same\n request.

", "smithy.api#xmlName": "privateIpAddress" } }, @@ -84565,7 +84731,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NetworkPlatform", - "smithy.api#documentation": "

The network platform (EC2-Classic or EC2-VPC).

", + "smithy.api#documentation": "

The network platform.

", "smithy.api#xmlName": "networkPlatform" } }, @@ -84719,7 +84885,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "NetworkPlatform", - "smithy.api#documentation": "

The network platform (EC2-Classic or EC2-VPC).

", + "smithy.api#documentation": "

The network platform.

", "smithy.api#xmlName": "networkPlatform" } }, @@ -86035,6 +86201,12 @@ "smithy.api#input": {} } }, + "com.amazonaws.ec2#SensitiveUrl": { + "type": "string", + "traits": { + "smithy.api#sensitive": {} + } + }, "com.amazonaws.ec2#SensitiveUserData": { "type": "string", "traits": { @@ -86721,7 +86893,7 @@ } }, "Url": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUrl", "traits": { "aws.protocols#ec2QueryName": "Url", "smithy.api#documentation": "

The URL used to access the disk image.

", @@ -86766,7 +86938,7 @@ } }, "Url": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUrl", "traits": { "smithy.api#documentation": "

The URL to the Amazon S3-based disk image being imported. It can either be a https URL (https://..) or an Amazon\n S3 URL (s3://..).

" } @@ -87084,7 +87256,7 @@ } }, "Url": { - "target": "com.amazonaws.ec2#String", + "target": "com.amazonaws.ec2#SensitiveUrl", "traits": { "aws.protocols#ec2QueryName": "Url", "smithy.api#documentation": "

The URL of the disk image from which the snapshot is created.

", @@ -87326,7 +87498,7 @@ "target": "com.amazonaws.ec2#GroupIdentifierList", "traits": { "aws.protocols#ec2QueryName": "GroupSet", - "smithy.api#documentation": "

One or more security groups. When requesting instances in a VPC, you must specify the IDs of the security groups. When requesting instances in EC2-Classic, you can specify the names or the IDs of the security groups.

", + "smithy.api#documentation": "

The security groups.

", "smithy.api#xmlName": "groupSet" } }, @@ -89908,6 +90080,26 @@ } } }, + "com.amazonaws.ec2#SupportedAdditionalProcessorFeature": { + "type": "enum", + "members": { + "AMD_SEV_SNP": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "amd-sev-snp" + } + } + } + }, + "com.amazonaws.ec2#SupportedAdditionalProcessorFeatureList": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#SupportedAdditionalProcessorFeature", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#SupportedIpAddressTypes": { "type": "list", "member": { @@ -96466,7 +96658,7 @@ "target": "com.amazonaws.ec2#OidcOptions", "traits": { "aws.protocols#ec2QueryName": "OidcOptions", - "smithy.api#documentation": "

The OpenID Connect details for an oidc-type, user-identity based trust provider.

", + "smithy.api#documentation": "

The options for an OpenID Connect-compatible user-identity trust provider.

", "smithy.api#xmlName": "oidcOptions" } }, @@ -96474,7 +96666,7 @@ "target": "com.amazonaws.ec2#DeviceOptions", "traits": { "aws.protocols#ec2QueryName": "DeviceOptions", - "smithy.api#documentation": "

The options for device-identity type trust provider.

", + "smithy.api#documentation": "

The options for device-identity trust provider.

", "smithy.api#xmlName": "deviceOptions" } }, diff --git a/aws/sdk/aws-models/ecs.json b/aws/sdk/aws-models/ecs.json index 76f7118492..51c17eda2f 100644 --- a/aws/sdk/aws-models/ecs.json +++ b/aws/sdk/aws-models/ecs.json @@ -2137,7 +2137,7 @@ "imageDigest": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The container image manifest digest.

\n \n

The imageDigest is only returned if the container is using an image\n\t\t\t\thosted in Amazon ECR, otherwise it is omitted.

\n
" + "smithy.api#documentation": "

The container image manifest digest.

" } }, "runtimeId": { @@ -2937,6 +2937,9 @@ { "target": "com.amazonaws.ecs#InvalidParameterException" }, + { + "target": "com.amazonaws.ecs#NamespaceNotFoundException" + }, { "target": "com.amazonaws.ecs#ServerException" } @@ -3087,7 +3090,7 @@ "desiredCount": { "target": "com.amazonaws.ecs#BoxedInteger", "traits": { - "smithy.api#documentation": "

The number of instantiations of the specified task definition to place and keep\n\t\t\trunning on your cluster.

\n

This is required if schedulingStrategy is REPLICA or isn't\n\t\t\tspecified. If schedulingStrategy is DAEMON then this isn't\n\t\t\trequired.

" + "smithy.api#documentation": "

The number of instantiations of the specified task definition to place and keep running in your service.

\n

This is required if schedulingStrategy is REPLICA or isn't\n\t\t\tspecified. If schedulingStrategy is DAEMON then this isn't\n\t\t\trequired.

" } }, "clientToken": { @@ -7847,7 +7850,7 @@ "value": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The account setting value for the specified principal ARN. Accepted values are\n\t\t\t\tenabled and disabled.

", + "smithy.api#documentation": "

The account setting value for the specified principal ARN. Accepted values are\n\t\t\t\tenabled, disabled, on, and\n\t\t\toff.

", "smithy.api#required": {} } } @@ -7883,7 +7886,7 @@ "value": { "target": "com.amazonaws.ecs#String", "traits": { - "smithy.api#documentation": "

The account setting value for the specified principal ARN. Accepted values are\n\t\t\t\tenabled and disabled.

", + "smithy.api#documentation": "

The account setting value for the specified principal ARN. Accepted values are\n\t\t\t\tenabled, disabled, on, and\n\t\t\toff.

", "smithy.api#required": {} } }, @@ -11112,6 +11115,9 @@ { "target": "com.amazonaws.ecs#InvalidParameterException" }, + { + "target": "com.amazonaws.ecs#NamespaceNotFoundException" + }, { "target": "com.amazonaws.ecs#ServerException" } diff --git a/aws/sdk/aws-models/kms.json b/aws/sdk/aws-models/kms.json index 7a9449eb24..c380116092 100644 --- a/aws/sdk/aws-models/kms.json +++ b/aws/sdk/aws-models/kms.json @@ -135,6 +135,15 @@ } } }, + "com.amazonaws.kms#AttestationDocumentType": { + "type": "blob", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 262144 + } + } + }, "com.amazonaws.kms#BooleanType": { "type": "boolean", "traits": { @@ -214,7 +223,8 @@ "smithy.api#length": { "min": 19, "max": 24 - } + }, + "smithy.api#pattern": "^cluster-[2-7a-zA-Z]{11,16}$" } }, "com.amazonaws.kms#CloudHsmClusterInUseException": { @@ -542,7 +552,7 @@ "AliasName": { "target": "com.amazonaws.kms#AliasNameType", "traits": { - "smithy.api#documentation": "

Specifies the alias name. This value must begin with alias/ followed by a\n name, such as alias/ExampleAlias.

\n

The AliasName value must be string of 1-256 characters. It can contain only\n alphanumeric characters, forward slashes (/), underscores (_), and dashes (-). The alias name\n cannot begin with alias/aws/. The alias/aws/ prefix is reserved for\n Amazon Web Services managed\n keys.

", + "smithy.api#documentation": "

Specifies the alias name. This value must begin with alias/ followed by a\n name, such as alias/ExampleAlias.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

The AliasName value must be string of 1-256 characters. It can contain only\n alphanumeric characters, forward slashes (/), underscores (_), and dashes (-). The alias name\n cannot begin with alias/aws/. The alias/aws/ prefix is reserved for\n Amazon Web Services managed\n keys.

", "smithy.api#required": {} } }, @@ -629,7 +639,7 @@ "CustomKeyStoreName": { "target": "com.amazonaws.kms#CustomKeyStoreNameType", "traits": { - "smithy.api#documentation": "

Specifies a friendly name for the custom key store. The name must be unique in your\n Amazon Web Services account and Region. This parameter is required for all custom key stores.

", + "smithy.api#documentation": "

Specifies a friendly name for the custom key store. The name must be unique in your\n Amazon Web Services account and Region. This parameter is required for all custom key stores.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
", "smithy.api#required": {} } }, @@ -777,7 +787,7 @@ "Constraints": { "target": "com.amazonaws.kms#GrantConstraints", "traits": { - "smithy.api#documentation": "

Specifies a grant constraint.

\n

KMS supports the EncryptionContextEquals and\n EncryptionContextSubset grant constraints. Each constraint value can include up\n to 8 encryption context pairs. The encryption context value in each constraint cannot exceed\n 384 characters. For information about grant constraints, see Using grant\n constraints in the Key Management Service Developer Guide. For more information about encryption context,\n see Encryption\n context in the \n Key Management Service Developer Guide\n .

\n

The encryption context grant constraints allow the permissions in the grant only when the\n encryption context in the request matches (EncryptionContextEquals) or includes\n (EncryptionContextSubset) the encryption context specified in this structure.

\n

The encryption context grant constraints are supported only on grant operations that include\n an EncryptionContext parameter, such as cryptographic operations on symmetric\n encryption KMS keys. Grants with grant constraints can include the DescribeKey and RetireGrant operations, but the constraint doesn't apply to these\n operations. If a grant with a grant constraint includes the CreateGrant\n operation, the constraint requires that any grants created with the CreateGrant\n permission have an equally strict or stricter encryption context constraint.

\n

You cannot use an encryption context grant constraint for cryptographic operations with\n asymmetric KMS keys or HMAC KMS keys. These keys don't support an encryption context.

\n

" + "smithy.api#documentation": "

Specifies a grant constraint.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

KMS supports the EncryptionContextEquals and\n EncryptionContextSubset grant constraints, which allow the permissions in the\n grant only when the encryption context in the request matches\n (EncryptionContextEquals) or includes (EncryptionContextSubset)\n the encryption context specified in the constraint.

\n

The encryption context grant constraints are supported only on grant operations that include\n an EncryptionContext parameter, such as cryptographic operations on symmetric\n encryption KMS keys. Grants with grant constraints can include the DescribeKey and RetireGrant operations, but the constraint doesn't apply to these\n operations. If a grant with a grant constraint includes the CreateGrant\n operation, the constraint requires that any grants created with the CreateGrant\n permission have an equally strict or stricter encryption context constraint.

\n

You cannot use an encryption context grant constraint for cryptographic operations with\n asymmetric KMS keys or HMAC KMS keys. Operations with these keys don't support an encryption\n context.

\n

Each constraint value can include up to 8 encryption context pairs. The encryption context\n value in each constraint cannot exceed 384 characters. For information about grant\n constraints, see Using grant\n constraints in the Key Management Service Developer Guide. For more information about encryption context,\n see Encryption\n context in the \n Key Management Service Developer Guide\n .

" } }, "GrantTokens": { @@ -789,7 +799,7 @@ "Name": { "target": "com.amazonaws.kms#GrantNameType", "traits": { - "smithy.api#documentation": "

A friendly name for the grant. Use this value to prevent the unintended creation of\n duplicate grants when retrying this request.

\n

When this value is absent, all CreateGrant requests result in a new grant\n with a unique GrantId even if all the supplied parameters are identical. This can\n result in unintended duplicates when you retry the CreateGrant request.

\n

When this value is present, you can retry a CreateGrant request with\n identical parameters; if the grant already exists, the original GrantId is\n returned without creating a new grant. Note that the returned grant token is unique with every\n CreateGrant request, even when a duplicate GrantId is returned.\n All grant tokens for the same grant ID can be used interchangeably.

" + "smithy.api#documentation": "

A friendly name for the grant. Use this value to prevent the unintended creation of\n duplicate grants when retrying this request.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

When this value is absent, all CreateGrant requests result in a new grant\n with a unique GrantId even if all the supplied parameters are identical. This can\n result in unintended duplicates when you retry the CreateGrant request.

\n

When this value is present, you can retry a CreateGrant request with\n identical parameters; if the grant already exists, the original GrantId is\n returned without creating a new grant. Note that the returned grant token is unique with every\n CreateGrant request, even when a duplicate GrantId is returned.\n All grant tokens for the same grant ID can be used interchangeably.

" } } }, @@ -882,7 +892,7 @@ "Description": { "target": "com.amazonaws.kms#DescriptionType", "traits": { - "smithy.api#documentation": "

A description of the KMS key.

\n

Use a description that helps you decide whether the KMS key is appropriate for a task. The\n default value is an empty string (no description).

\n

To set or change the description after the key is created, use UpdateKeyDescription.

" + "smithy.api#documentation": "

A description of the KMS key. Use a description that helps you decide whether the KMS key is appropriate for a task. The\n default value is an empty string (no description).

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

To set or change the description after the key is created, use UpdateKeyDescription.

" } }, "KeyUsage": { @@ -928,7 +938,7 @@ "Tags": { "target": "com.amazonaws.kms#TagList", "traits": { - "smithy.api#documentation": "

Assigns one or more tags to the KMS key. Use this parameter to tag the KMS key when it is\n created. To tag an existing KMS key, use the TagResource operation.

\n \n

Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see ABAC for KMS in the Key Management Service Developer Guide.

\n
\n

To use this parameter, you must have kms:TagResource permission in an IAM policy.

\n

Each tag consists of a tag key and a tag value. Both the tag key and the tag value are\n required, but the tag value can be an empty (null) string. You cannot have more than one tag\n on a KMS key with the same tag key. If you specify an existing tag key with a different tag\n value, KMS replaces the current tag value with the specified one.

\n

When you add tags to an Amazon Web Services resource, Amazon Web Services generates a cost allocation\n report with usage and costs aggregated by tags. Tags can also be used to control access to a KMS key. For details,\n see Tagging Keys.

" + "smithy.api#documentation": "

Assigns one or more tags to the KMS key. Use this parameter to tag the KMS key when it is\n created. To tag an existing KMS key, use the TagResource operation.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n \n

Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see ABAC for KMS in the Key Management Service Developer Guide.

\n
\n

To use this parameter, you must have kms:TagResource permission in an IAM policy.

\n

Each tag consists of a tag key and a tag value. Both the tag key and the tag value are\n required, but the tag value can be an empty (null) string. You cannot have more than one tag\n on a KMS key with the same tag key. If you specify an existing tag key with a different tag\n value, KMS replaces the current tag value with the specified one.

\n

When you add tags to an Amazon Web Services resource, Amazon Web Services generates a cost allocation\n report with usage and costs aggregated by tags. Tags can also be used to control access to a KMS key. For details,\n see Tagging Keys.

" } }, "MultiRegion": { @@ -1335,7 +1345,7 @@ } ], "traits": { - "smithy.api#documentation": "

Decrypts ciphertext that was encrypted by a KMS key using any of the following\n operations:

\n \n

You can use this operation to decrypt ciphertext that was encrypted under a symmetric\n encryption KMS key or an asymmetric encryption KMS key. When the KMS key is asymmetric, you\n must specify the KMS key and the encryption algorithm that was used to encrypt the ciphertext.\n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

The Decrypt operation also decrypts ciphertext that was encrypted outside of\n KMS by the public key in an KMS asymmetric KMS key. However, it cannot decrypt symmetric\n ciphertext produced by other libraries, such as the Amazon Web Services Encryption SDK or Amazon S3 client-side encryption.\n These libraries return a ciphertext format that is incompatible with KMS.

\n

If the ciphertext was encrypted under a symmetric encryption KMS key, the\n KeyId parameter is optional. KMS can get this information from metadata that\n it adds to the symmetric ciphertext blob. This feature adds durability to your implementation\n by ensuring that authorized users can decrypt ciphertext decades after it was encrypted, even\n if they've lost track of the key ID. However, specifying the KMS key is always recommended as\n a best practice. When you use the KeyId parameter to specify a KMS key, KMS\n only uses the KMS key you specify. If the ciphertext was encrypted under a different KMS key,\n the Decrypt operation fails. This practice ensures that you use the KMS key that\n you intend.

\n

Whenever possible, use key policies to give users permission to call the\n Decrypt operation on a particular KMS key, instead of using &IAM; policies.\n Otherwise, you might create an &IAM; policy that gives the user Decrypt\n permission on all KMS keys. This user could decrypt ciphertext that was encrypted by KMS keys\n in other accounts if the key policy for the cross-account KMS key permits it. If you must use\n an IAM policy for Decrypt permissions, limit the user to particular KMS keys or\n particular trusted accounts. For details, see Best practices for IAM\n policies in the Key Management Service Developer Guide.

\n

Applications in Amazon Web Services Nitro Enclaves can call this operation by using the Amazon Web Services Nitro Enclaves Development Kit. For information about the supporting parameters, see How Amazon Web Services Nitro Enclaves use KMS in the Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. If you use the KeyId\n parameter to identify a KMS key in a different Amazon Web Services account, specify the key ARN or the alias\n ARN of the KMS key.

\n

\n Required permissions: kms:Decrypt (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Decrypts ciphertext that was encrypted by a KMS key using any of the following\n operations:

\n \n

You can use this operation to decrypt ciphertext that was encrypted under a symmetric\n encryption KMS key or an asymmetric encryption KMS key. When the KMS key is asymmetric, you\n must specify the KMS key and the encryption algorithm that was used to encrypt the ciphertext.\n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

The Decrypt operation also decrypts ciphertext that was encrypted outside of\n KMS by the public key in an KMS asymmetric KMS key. However, it cannot decrypt symmetric\n ciphertext produced by other libraries, such as the Amazon Web Services Encryption SDK or Amazon S3 client-side encryption.\n These libraries return a ciphertext format that is incompatible with KMS.

\n

If the ciphertext was encrypted under a symmetric encryption KMS key, the\n KeyId parameter is optional. KMS can get this information from metadata that\n it adds to the symmetric ciphertext blob. This feature adds durability to your implementation\n by ensuring that authorized users can decrypt ciphertext decades after it was encrypted, even\n if they've lost track of the key ID. However, specifying the KMS key is always recommended as\n a best practice. When you use the KeyId parameter to specify a KMS key, KMS\n only uses the KMS key you specify. If the ciphertext was encrypted under a different KMS key,\n the Decrypt operation fails. This practice ensures that you use the KMS key that\n you intend.

\n

Whenever possible, use key policies to give users permission to call the\n Decrypt operation on a particular KMS key, instead of using &IAM; policies.\n Otherwise, you might create an &IAM; policy that gives the user Decrypt\n permission on all KMS keys. This user could decrypt ciphertext that was encrypted by KMS keys\n in other accounts if the key policy for the cross-account KMS key permits it. If you must use\n an IAM policy for Decrypt permissions, limit the user to particular KMS keys or\n particular trusted accounts. For details, see Best practices for IAM\n policies in the Key Management Service Developer Guide.

\n

\n Decrypt also supports Amazon Web Services Nitro Enclaves, which provide an\n isolated compute environment in Amazon EC2. To call Decrypt for a Nitro enclave, use\n the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK. Use the Recipient parameter to provide the\n attestation document for the enclave. Instead of the plaintext data, the response includes the\n plaintext data encrypted with the public key from the attestation document\n (CiphertextForRecipient).For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide..

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. If you use the KeyId\n parameter to identify a KMS key in a different Amazon Web Services account, specify the key ARN or the alias\n ARN of the KMS key.

\n

\n Required permissions: kms:Decrypt (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#DecryptRequest": { @@ -1371,6 +1381,12 @@ "traits": { "smithy.api#documentation": "

Specifies the encryption algorithm that will be used to decrypt the ciphertext. Specify\n the same algorithm that was used to encrypt the data. If you specify a different algorithm,\n the Decrypt operation fails.

\n

This parameter is required only when the ciphertext was encrypted under an asymmetric KMS\n key. The default value, SYMMETRIC_DEFAULT, represents the only supported\n algorithm that is valid for symmetric encryption KMS keys.

" } + }, + "Recipient": { + "target": "com.amazonaws.kms#RecipientInfo", + "traits": { + "smithy.api#documentation": "

A signed attestation document from\n an Amazon Web Services Nitro enclave and the encryption algorithm to use with the enclave's public key.\n The only valid encryption algorithm is RSAES_OAEP_SHA_256.

\n

This parameter only supports attestation documents for Amazon Web Services Nitro Enclaves. To include this\n parameter, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK.

\n

When you use this parameter, instead of returning the plaintext data, KMS encrypts the\n plaintext data with the public key in the attestation document, and returns the resulting\n ciphertext in the CiphertextForRecipient field in the response. This ciphertext\n can be decrypted only with the private key in the enclave. The Plaintext field in\n the response is null or empty.

\n

For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -1389,7 +1405,7 @@ "Plaintext": { "target": "com.amazonaws.kms#PlaintextType", "traits": { - "smithy.api#documentation": "

Decrypted plaintext data. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

" + "smithy.api#documentation": "

Decrypted plaintext data. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

\n

If the response includes the CiphertextForRecipient field, the\n Plaintext field is null or empty.

" } }, "EncryptionAlgorithm": { @@ -1397,6 +1413,12 @@ "traits": { "smithy.api#documentation": "

The encryption algorithm that was used to decrypt the ciphertext.

" } + }, + "CiphertextForRecipient": { + "target": "com.amazonaws.kms#CiphertextType", + "traits": { + "smithy.api#documentation": "

The plaintext data encrypted with the public key in the attestation document.

\n

This field is included in the response only when the Recipient parameter in\n the request includes a valid attestation document from an Amazon Web Services Nitro enclave.\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -2025,7 +2047,7 @@ "EncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies the encryption context that will be used to encrypt the data.\n An encryption context is valid only for cryptographic operations with a symmetric encryption KMS key. The standard asymmetric encryption algorithms and HMAC algorithms that KMS uses do not support an encryption context.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the encryption context that will be used to encrypt the data.\n An encryption context is valid only for cryptographic operations with a symmetric encryption KMS key. The standard asymmetric encryption algorithms and HMAC algorithms that KMS uses do not support an encryption context.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "GrantTokens": { @@ -2193,7 +2215,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a unique symmetric data key for use outside of KMS. This operation returns a\n plaintext copy of the data key and a copy that is encrypted under a symmetric encryption KMS\n key that you specify. The bytes in the plaintext key are random; they are not related \n to the caller or the KMS key. You can use the plaintext key to encrypt your data outside of KMS \n and store the encrypted data key with the encrypted data.

\n

To generate a data key, specify the symmetric encryption KMS key that will be used to\n encrypt the data key. You cannot use an asymmetric KMS key to encrypt data keys. To get the\n type of your KMS key, use the DescribeKey operation.

\n

You must also specify the length of the data key. Use either the KeySpec or \n NumberOfBytes parameters (but not both). For 128-bit and 256-bit data keys, use \n the KeySpec parameter.

\n

To generate a 128-bit SM4 data key (China Regions only), specify a KeySpec value of\n AES_128 or a NumberOfBytes value of 16. The symmetric \n encryption key used in China Regions to encrypt your data key is an SM4 encryption key.

\n

To get only an encrypted copy of the data key, use GenerateDataKeyWithoutPlaintext. To generate an asymmetric data key pair, use\n the GenerateDataKeyPair or GenerateDataKeyPairWithoutPlaintext operation. To get a cryptographically secure\n random byte string, use GenerateRandom.

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

Applications in Amazon Web Services Nitro Enclaves can call this operation by using the Amazon Web Services Nitro Enclaves Development Kit. For information about the supporting parameters, see How Amazon Web Services Nitro Enclaves use KMS in the Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n How to use your data key\n

\n

We recommend that you use the following pattern to encrypt data locally in your\n application. You can write your own code or use a client-side encryption library, such as the\n Amazon Web Services Encryption SDK, the\n Amazon DynamoDB Encryption Client,\n or Amazon S3\n client-side encryption to do these tasks for you.

\n

To encrypt data outside of KMS:

\n
    \n
  1. \n

    Use the GenerateDataKey operation to get a data key.

    \n
  2. \n
  3. \n

    Use the plaintext data key (in the Plaintext field of the response) to\n encrypt your data outside of KMS. Then erase the plaintext data key from memory.

    \n
  4. \n
  5. \n

    Store the encrypted data key (in the CiphertextBlob field of the\n response) with the encrypted data.

    \n
  6. \n
\n

To decrypt data outside of KMS:

\n
    \n
  1. \n

    Use the Decrypt operation to decrypt the encrypted data key. The\n operation returns a plaintext copy of the data key.

    \n
  2. \n
  3. \n

    Use the plaintext data key to decrypt data outside of KMS, then erase the plaintext\n data key from memory.

    \n
  4. \n
\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKey (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Returns a unique symmetric data key for use outside of KMS. This operation returns a\n plaintext copy of the data key and a copy that is encrypted under a symmetric encryption KMS\n key that you specify. The bytes in the plaintext key are random; they are not related \n to the caller or the KMS key. You can use the plaintext key to encrypt your data outside of KMS \n and store the encrypted data key with the encrypted data.

\n

To generate a data key, specify the symmetric encryption KMS key that will be used to\n encrypt the data key. You cannot use an asymmetric KMS key to encrypt data keys. To get the\n type of your KMS key, use the DescribeKey operation.

\n

You must also specify the length of the data key. Use either the KeySpec or \n NumberOfBytes parameters (but not both). For 128-bit and 256-bit data keys, use \n the KeySpec parameter.

\n

To generate a 128-bit SM4 data key (China Regions only), specify a KeySpec value of\n AES_128 or a NumberOfBytes value of 16. The symmetric \n encryption key used in China Regions to encrypt your data key is an SM4 encryption key.

\n

To get only an encrypted copy of the data key, use GenerateDataKeyWithoutPlaintext. To generate an asymmetric data key pair, use\n the GenerateDataKeyPair or GenerateDataKeyPairWithoutPlaintext operation. To get a cryptographically secure\n random byte string, use GenerateRandom.

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

\n GenerateDataKey also supports Amazon Web Services Nitro Enclaves, which provide an\n isolated compute environment in Amazon EC2. To call GenerateDataKey for an Amazon Web Services Nitro\n enclave, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK. Use the Recipient parameter\n to provide the attestation document for the enclave. GenerateDataKey returns a\n copy of the data key encrypted under the specified KMS key, as usual. But instead of a\n plaintext copy of the data key, the response includes a copy of the data key encrypted under\n the public key from the attestation document (CiphertextForRecipient).\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide..

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n How to use your data key\n

\n

We recommend that you use the following pattern to encrypt data locally in your\n application. You can write your own code or use a client-side encryption library, such as the\n Amazon Web Services Encryption SDK, the\n Amazon DynamoDB Encryption Client,\n or Amazon S3\n client-side encryption to do these tasks for you.

\n

To encrypt data outside of KMS:

\n
    \n
  1. \n

    Use the GenerateDataKey operation to get a data key.

    \n
  2. \n
  3. \n

    Use the plaintext data key (in the Plaintext field of the response) to\n encrypt your data outside of KMS. Then erase the plaintext data key from memory.

    \n
  4. \n
  5. \n

    Store the encrypted data key (in the CiphertextBlob field of the\n response) with the encrypted data.

    \n
  6. \n
\n

To decrypt data outside of KMS:

\n
    \n
  1. \n

    Use the Decrypt operation to decrypt the encrypted data key. The\n operation returns a plaintext copy of the data key.

    \n
  2. \n
  3. \n

    Use the plaintext data key to decrypt data outside of KMS, then erase the plaintext\n data key from memory.

    \n
  4. \n
\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKey (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#GenerateDataKeyPair": { @@ -2234,7 +2256,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a unique asymmetric data key pair for use outside of KMS. This operation returns\n a plaintext public key, a plaintext private key, and a copy of the private key that is\n encrypted under the symmetric encryption KMS key you specify. You can use the data key pair to\n perform asymmetric cryptography and implement digital signatures outside of KMS. The bytes\n in the keys are random; they not related to the caller or to the KMS key that is used to\n encrypt the private key.

\n

You can use the public key that GenerateDataKeyPair returns to encrypt data\n or verify a signature outside of KMS. Then, store the encrypted private key with the data.\n When you are ready to decrypt data or sign a message, you can use the Decrypt operation to decrypt the encrypted private key.

\n

To generate a data key pair, you must specify a symmetric encryption KMS key to encrypt\n the private key in a data key pair. You cannot use an asymmetric KMS key or a KMS key in a\n custom key store. To get the type and origin of your KMS key, use the DescribeKey operation.

\n

Use the KeyPairSpec parameter to choose an RSA or Elliptic Curve (ECC) data\n key pair. In China Regions, you can also choose an SM2 data key pair. KMS recommends that you use\n ECC key pairs for signing, and use RSA and SM2 key pairs for either encryption or signing, but not both.\n However, KMS cannot enforce any restrictions on the use of data key pairs outside of KMS.

\n

If you are using the data key pair to encrypt data, or for any operation where you don't\n immediately need a private key, consider using the GenerateDataKeyPairWithoutPlaintext operation.\n GenerateDataKeyPairWithoutPlaintext returns a plaintext public key and an\n encrypted private key, but omits the plaintext private key that you need only to decrypt\n ciphertext or sign a message. Later, when you need to decrypt the data or sign a message, use\n the Decrypt operation to decrypt the encrypted private key in the data key\n pair.

\n

\n GenerateDataKeyPair returns a unique data key pair for each request. The\n bytes in the keys are random; they are not related to the caller or the KMS key that is used\n to encrypt the private key. The public key is a DER-encoded X.509 SubjectPublicKeyInfo, as\n specified in RFC 5280. The private\n key is a DER-encoded PKCS8 PrivateKeyInfo, as specified in RFC 5958.

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKeyPair (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Returns a unique asymmetric data key pair for use outside of KMS. This operation returns\n a plaintext public key, a plaintext private key, and a copy of the private key that is\n encrypted under the symmetric encryption KMS key you specify. You can use the data key pair to\n perform asymmetric cryptography and implement digital signatures outside of KMS. The bytes\n in the keys are random; they not related to the caller or to the KMS key that is used to\n encrypt the private key.

\n

You can use the public key that GenerateDataKeyPair returns to encrypt data\n or verify a signature outside of KMS. Then, store the encrypted private key with the data.\n When you are ready to decrypt data or sign a message, you can use the Decrypt operation to decrypt the encrypted private key.

\n

To generate a data key pair, you must specify a symmetric encryption KMS key to encrypt\n the private key in a data key pair. You cannot use an asymmetric KMS key or a KMS key in a\n custom key store. To get the type and origin of your KMS key, use the DescribeKey operation.

\n

Use the KeyPairSpec parameter to choose an RSA or Elliptic Curve (ECC) data\n key pair. In China Regions, you can also choose an SM2 data key pair. KMS recommends that you use\n ECC key pairs for signing, and use RSA and SM2 key pairs for either encryption or signing, but not both.\n However, KMS cannot enforce any restrictions on the use of data key pairs outside of KMS.

\n

If you are using the data key pair to encrypt data, or for any operation where you don't\n immediately need a private key, consider using the GenerateDataKeyPairWithoutPlaintext operation.\n GenerateDataKeyPairWithoutPlaintext returns a plaintext public key and an\n encrypted private key, but omits the plaintext private key that you need only to decrypt\n ciphertext or sign a message. Later, when you need to decrypt the data or sign a message, use\n the Decrypt operation to decrypt the encrypted private key in the data key\n pair.

\n

\n GenerateDataKeyPair returns a unique data key pair for each request. The\n bytes in the keys are random; they are not related to the caller or the KMS key that is used\n to encrypt the private key. The public key is a DER-encoded X.509 SubjectPublicKeyInfo, as\n specified in RFC 5280. The private\n key is a DER-encoded PKCS8 PrivateKeyInfo, as specified in RFC 5958.

\n

\n GenerateDataKeyPair also supports Amazon Web Services Nitro Enclaves, which provide an\n isolated compute environment in Amazon EC2. To call GenerateDataKeyPair for an Amazon Web Services Nitro\n enclave, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK. Use the Recipient parameter\n to provide the attestation document for the enclave. GenerateDataKeyPair returns the public data key and a\n copy of the private data key encrypted under the specified KMS key, as usual. But instead of a\n plaintext copy of the private data key (PrivateKeyPlaintext), the response includes a copy of the private data key encrypted under\n the public key from the attestation document (CiphertextForRecipient).\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide..

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKeyPair (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#GenerateDataKeyPairRequest": { @@ -2243,7 +2265,7 @@ "EncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the private key in the\n data key pair.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the private key in the\n data key pair.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "KeyId": { @@ -2265,6 +2287,12 @@ "traits": { "smithy.api#documentation": "

A list of grant tokens.

\n

Use a grant token when your permission to call this operation comes from a new grant that has not yet achieved eventual consistency. For more information, see Grant token and Using a grant token in the\n Key Management Service Developer Guide.

" } + }, + "Recipient": { + "target": "com.amazonaws.kms#RecipientInfo", + "traits": { + "smithy.api#documentation": "

A signed attestation document from\n an Amazon Web Services Nitro enclave and the encryption algorithm to use with the enclave's public key.\n The only valid encryption algorithm is RSAES_OAEP_SHA_256.

\n

This parameter only supports attestation documents for Amazon Web Services Nitro Enclaves. To include this\n parameter, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK.

\n

When you use this parameter, instead of returning a plaintext copy of the private data key, KMS encrypts\n the plaintext private data key under the public key in the attestation document, and returns the\n resulting ciphertext in the CiphertextForRecipient field in the response. This\n ciphertext can be decrypted only with the private key in the enclave. The\n CiphertextBlob field in the response contains a copy of the private data key encrypted\n under the KMS key specified by the KeyId parameter. The PrivateKeyPlaintext\n field in the response is null or empty.

\n

For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -2283,7 +2311,7 @@ "PrivateKeyPlaintext": { "target": "com.amazonaws.kms#PlaintextType", "traits": { - "smithy.api#documentation": "

The plaintext copy of the private key. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

" + "smithy.api#documentation": "

The plaintext copy of the private key. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

\n

If the response includes the CiphertextForRecipient field, the\n PrivateKeyPlaintext field is null or empty.

" } }, "PublicKey": { @@ -2303,6 +2331,12 @@ "traits": { "smithy.api#documentation": "

The type of data key pair that was generated.

" } + }, + "CiphertextForRecipient": { + "target": "com.amazonaws.kms#CiphertextType", + "traits": { + "smithy.api#documentation": "

The plaintext private data key encrypted with the public key from the Nitro enclave. This ciphertext can\n be decrypted only by using a private key in the Nitro enclave.

\n

This field is included in the response only when the Recipient parameter in\n the request includes a valid attestation document from an Amazon Web Services Nitro enclave.\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -2356,7 +2390,7 @@ "EncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the private key in the\n data key pair.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the private key in the\n data key pair.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "KeyId": { @@ -2429,7 +2463,7 @@ "EncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the data key.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the data key.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "NumberOfBytes": { @@ -2449,6 +2483,12 @@ "traits": { "smithy.api#documentation": "

A list of grant tokens.

\n

Use a grant token when your permission to call this operation comes from a new grant that has not yet achieved eventual consistency. For more information, see Grant token and Using a grant token in the\n Key Management Service Developer Guide.

" } + }, + "Recipient": { + "target": "com.amazonaws.kms#RecipientInfo", + "traits": { + "smithy.api#documentation": "

A signed attestation document from\n an Amazon Web Services Nitro enclave and the encryption algorithm to use with the enclave's public key.\n The only valid encryption algorithm is RSAES_OAEP_SHA_256.

\n

This parameter only supports attestation documents for Amazon Web Services Nitro Enclaves. To include this\n parameter, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK.

\n

When you use this parameter, instead of returning the plaintext data key, KMS encrypts\n the plaintext data key under the public key in the attestation document, and returns the\n resulting ciphertext in the CiphertextForRecipient field in the response. This\n ciphertext can be decrypted only with the private key in the enclave. The\n CiphertextBlob field in the response contains a copy of the data key encrypted\n under the KMS key specified by the KeyId parameter. The Plaintext\n field in the response is null or empty.

\n

For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -2467,7 +2507,7 @@ "Plaintext": { "target": "com.amazonaws.kms#PlaintextType", "traits": { - "smithy.api#documentation": "

The plaintext data key. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded. Use this data key to encrypt your data outside of\n KMS. Then, remove it from memory as soon as possible.

" + "smithy.api#documentation": "

The plaintext data key. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded. Use this data key to encrypt your data outside of\n KMS. Then, remove it from memory as soon as possible.

\n

If the response includes the CiphertextForRecipient field, the\n Plaintext field is null or empty.

" } }, "KeyId": { @@ -2475,6 +2515,12 @@ "traits": { "smithy.api#documentation": "

The Amazon Resource Name (key ARN) of the KMS key that encrypted the data key.

" } + }, + "CiphertextForRecipient": { + "target": "com.amazonaws.kms#CiphertextType", + "traits": { + "smithy.api#documentation": "

The plaintext data key encrypted with the public key from the Nitro enclave. This ciphertext can\n be decrypted only by using a private key in the Nitro enclave.

\n

This field is included in the response only when the Recipient parameter in\n the request includes a valid attestation document from an Amazon Web Services Nitro enclave.\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } } }, "traits": { @@ -2516,7 +2562,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a unique symmetric data key for use outside of KMS. This operation returns a\n data key that is encrypted under a symmetric encryption KMS key that you specify. The bytes in\n the key are random; they are not related to the caller or to the KMS key.

\n

\n GenerateDataKeyWithoutPlaintext is identical to the GenerateDataKey operation except that it does not return a plaintext copy of the\n data key.

\n

This operation is useful for systems that need to encrypt data at some point, but not\n immediately. When you need to encrypt the data, you call the Decrypt\n operation on the encrypted copy of the key.

\n

It's also useful in distributed systems with different levels of trust. For example, you\n might store encrypted data in containers. One component of your system creates new containers\n and stores an encrypted data key with each container. Then, a different component puts the\n data into the containers. That component first decrypts the data key, uses the plaintext data\n key to encrypt data, puts the encrypted data into the container, and then destroys the\n plaintext data key. In this system, the component that creates the containers never sees the\n plaintext data key.

\n

To request an asymmetric data key pair, use the GenerateDataKeyPair or\n GenerateDataKeyPairWithoutPlaintext operations.

\n

To generate a data key, you must specify the symmetric encryption KMS key that is used to\n encrypt the data key. You cannot use an asymmetric KMS key or a key in a custom key store to generate a data key. To get the\n type of your KMS key, use the DescribeKey operation.

\n

You must also specify the length of the data key. Use either the KeySpec or \n NumberOfBytes parameters (but not both). For 128-bit and 256-bit data keys, use \n the KeySpec parameter.

\n

To generate an SM4 data key (China Regions only), specify a KeySpec value of\n AES_128 or NumberOfBytes value of 128. The symmetric \n encryption key used in China Regions to encrypt your data key is an SM4 encryption key.

\n

If the operation succeeds, you will find the encrypted copy of the data key in the\n CiphertextBlob field.

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKeyWithoutPlaintext (key\n policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Returns a unique symmetric data key for use outside of KMS. This operation returns a\n data key that is encrypted under a symmetric encryption KMS key that you specify. The bytes in\n the key are random; they are not related to the caller or to the KMS key.

\n

\n GenerateDataKeyWithoutPlaintext is identical to the GenerateDataKey operation except that it does not return a plaintext copy of the\n data key.

\n

This operation is useful for systems that need to encrypt data at some point, but not\n immediately. When you need to encrypt the data, you call the Decrypt\n operation on the encrypted copy of the key.

\n

It's also useful in distributed systems with different levels of trust. For example, you\n might store encrypted data in containers. One component of your system creates new containers\n and stores an encrypted data key with each container. Then, a different component puts the\n data into the containers. That component first decrypts the data key, uses the plaintext data\n key to encrypt data, puts the encrypted data into the container, and then destroys the\n plaintext data key. In this system, the component that creates the containers never sees the\n plaintext data key.

\n

To request an asymmetric data key pair, use the GenerateDataKeyPair or\n GenerateDataKeyPairWithoutPlaintext operations.

\n

To generate a data key, you must specify the symmetric encryption KMS key that is used to\n encrypt the data key. You cannot use an asymmetric KMS key or a key in a custom key store to generate a data key. To get the\n type of your KMS key, use the DescribeKey operation.

\n

You must also specify the length of the data key. Use either the KeySpec or \n NumberOfBytes parameters (but not both). For 128-bit and 256-bit data keys, use \n the KeySpec parameter.

\n

To generate an SM4 data key (China Regions only), specify a KeySpec value of\n AES_128 or NumberOfBytes value of 16. The symmetric\n encryption key used in China Regions to encrypt your data key is an SM4 encryption key.

\n

If the operation succeeds, you will find the encrypted copy of the data key in the\n CiphertextBlob field.

\n

You can use an optional encryption context to add additional security to the encryption\n operation. If you specify an EncryptionContext, you must specify the same\n encryption context (a case-sensitive exact match) when decrypting the encrypted data key.\n Otherwise, the request to decrypt fails with an InvalidCiphertextException. For more information, see Encryption Context in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: Yes. To perform this operation with a KMS key in a different Amazon Web Services account, specify\n the key ARN or alias ARN in the value of the KeyId parameter.

\n

\n Required permissions: kms:GenerateDataKeyWithoutPlaintext (key\n policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#GenerateDataKeyWithoutPlaintextRequest": { @@ -2532,7 +2578,7 @@ "EncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the data key.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the encryption context that will be used when encrypting the data key.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "KeySpec": { @@ -2700,7 +2746,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a random byte string that is cryptographically secure.

\n

You must use the NumberOfBytes parameter to specify the length of the random\n byte string. There is no default value for string length.

\n

By default, the random byte string is generated in KMS. To generate the byte string in\n the CloudHSM cluster associated with an CloudHSM key store, use the CustomKeyStoreId\n parameter.

\n

Applications in Amazon Web Services Nitro Enclaves can call this operation by using the Amazon Web Services Nitro Enclaves Development Kit. For information about the supporting parameters, see How Amazon Web Services Nitro Enclaves use KMS in the Key Management Service Developer Guide.

\n

For more information about entropy and random number generation, see\n Key Management Service Cryptographic Details.

\n

\n Cross-account use: Not applicable.\n GenerateRandom does not use any account-specific resources, such as KMS\n keys.

\n

\n Required permissions: kms:GenerateRandom (IAM policy)

" + "smithy.api#documentation": "

Returns a random byte string that is cryptographically secure.

\n

You must use the NumberOfBytes parameter to specify the length of the random\n byte string. There is no default value for string length.

\n

By default, the random byte string is generated in KMS. To generate the byte string in\n the CloudHSM cluster associated with an CloudHSM key store, use the CustomKeyStoreId\n parameter.

\n

\n GenerateRandom also supports Amazon Web Services Nitro Enclaves, which provide an\n isolated compute environment in Amazon EC2. To call GenerateRandom for a Nitro\n enclave, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK. Use the Recipient parameter\n to provide the attestation document for the enclave. Instead of plaintext bytes, the response\n includes the plaintext bytes encrypted under the public key from the attestation document\n (CiphertextForRecipient).For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

\n

For more information about entropy and random number generation, see\n Key Management Service Cryptographic Details.

\n

\n Cross-account use: Not applicable.\n GenerateRandom does not use any account-specific resources, such as KMS\n keys.

\n

\n Required permissions: kms:GenerateRandom (IAM policy)

" } }, "com.amazonaws.kms#GenerateRandomRequest": { @@ -2715,7 +2761,13 @@ "CustomKeyStoreId": { "target": "com.amazonaws.kms#CustomKeyStoreIdType", "traits": { - "smithy.api#documentation": "

Generates the random byte string in the CloudHSM cluster that is associated with the\n specified CloudHSM key store. To find the ID of a custom key store, use the DescribeCustomKeyStores operation.

\n

External key store IDs are not valid for this parameter. If you specify the ID of an\n external key store, GenerateRandom throws an\n UnsupportedOperationException.

" + "smithy.api#documentation": "

Generates the random byte string in the CloudHSM cluster that is associated with the\n specified CloudHSM key store. To find the ID of a custom key store, use the DescribeCustomKeyStores operation.

\n

External key store IDs are not valid for this parameter. If you specify the ID of an\n external key store, GenerateRandom throws an\n UnsupportedOperationException.

" + } + }, + "Recipient": { + "target": "com.amazonaws.kms#RecipientInfo", + "traits": { + "smithy.api#documentation": "

A signed attestation document from\n an Amazon Web Services Nitro enclave and the encryption algorithm to use with the enclave's public key.\n The only valid encryption algorithm is RSAES_OAEP_SHA_256.

\n

This parameter only supports attestation documents for Amazon Web Services Nitro Enclaves. To include this\n parameter, use the Amazon Web Services Nitro Enclaves SDK or any Amazon Web Services SDK.

\n

When you use this parameter, instead of returning plaintext bytes, KMS encrypts the\n plaintext bytes under the public key in the attestation document, and returns the resulting\n ciphertext in the CiphertextForRecipient field in the response. This ciphertext\n can be decrypted only with the private key in the enclave. The Plaintext field in\n the response is null or empty.

\n

For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" } } }, @@ -2729,7 +2781,13 @@ "Plaintext": { "target": "com.amazonaws.kms#PlaintextType", "traits": { - "smithy.api#documentation": "

The random byte string. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

" + "smithy.api#documentation": "

The random byte string. When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

\n

If the response includes the CiphertextForRecipient field, the\n Plaintext field is null or empty.

" + } + }, + "CiphertextForRecipient": { + "target": "com.amazonaws.kms#CiphertextType", + "traits": { + "smithy.api#documentation": "

The plaintext random bytes encrypted with the public key from the Nitro enclave. This ciphertext can\n be decrypted only by using a private key in the Nitro enclave.

\n

This field is included in the response only when the Recipient parameter in\n the request includes a valid attestation document from an Amazon Web Services Nitro enclave.\n For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" } } }, @@ -3657,6 +3715,17 @@ "smithy.api#httpError": 409 } }, + "com.amazonaws.kms#KeyEncryptionMechanism": { + "type": "enum", + "members": { + "RSAES_OAEP_SHA_256": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RSAES_OAEP_SHA_256" + } + } + } + }, "com.amazonaws.kms#KeyIdType": { "type": "string", "traits": { @@ -5003,7 +5072,7 @@ "DestinationEncryptionContext": { "target": "com.amazonaws.kms#EncryptionContextType", "traits": { - "smithy.api#documentation": "

Specifies that encryption context to use when the reencrypting the data.

\n

A destination encryption context is valid only when the destination KMS key is a symmetric\n encryption KMS key. The standard ciphertext format for asymmetric KMS keys does not include\n fields for metadata.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies that encryption context to use when the reencrypting the data.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

A destination encryption context is valid only when the destination KMS key is a symmetric\n encryption KMS key. The standard ciphertext format for asymmetric KMS keys does not include\n fields for metadata.

\n

An encryption context is a collection of non-secret key-value pairs that represent additional authenticated data. \nWhen you use an encryption context to encrypt data, you must specify the same (an exact case-sensitive match) encryption context to decrypt the data. An encryption context is supported\nonly on operations with symmetric encryption KMS keys. On operations with symmetric encryption KMS keys, an encryption context is optional, but it is strongly recommended.

\n

For more information, see\nEncryption context in the Key Management Service Developer Guide.

" } }, "SourceEncryptionAlgorithm": { @@ -5067,6 +5136,26 @@ "smithy.api#output": {} } }, + "com.amazonaws.kms#RecipientInfo": { + "type": "structure", + "members": { + "KeyEncryptionAlgorithm": { + "target": "com.amazonaws.kms#KeyEncryptionMechanism", + "traits": { + "smithy.api#documentation": "

The encryption algorithm that KMS should use with the public key for an Amazon Web Services Nitro Enclave to encrypt plaintext \n values for the response. The only valid value is RSAES_OAEP_SHA_256.

" + } + }, + "AttestationDocument": { + "target": "com.amazonaws.kms#AttestationDocumentType", + "traits": { + "smithy.api#documentation": "

The attestation document for an Amazon Web Services Nitro Enclave. This document includes the enclave's public\n key.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains information about the party that receives the response from the API\n operation.

\n

This data type is designed to support Amazon Web Services Nitro Enclaves, which lets you create an isolated\n compute environment in Amazon EC2. For information about the interaction between KMS and Amazon Web Services Nitro Enclaves, see How Amazon Web Services Nitro Enclaves uses KMS in the Key Management Service Developer Guide.

" + } + }, "com.amazonaws.kms#RegionType": { "type": "string", "traits": { @@ -5154,13 +5243,13 @@ "Description": { "target": "com.amazonaws.kms#DescriptionType", "traits": { - "smithy.api#documentation": "

A description of the KMS key. The default value is an empty string (no\n description).

\n

The description is not a shared property of multi-Region keys. You can specify the same\n description or a different description for each key in a set of related multi-Region keys.\n KMS does not synchronize this property.

" + "smithy.api#documentation": "

A description of the KMS key. The default value is an empty string (no\n description).

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

The description is not a shared property of multi-Region keys. You can specify the same\n description or a different description for each key in a set of related multi-Region keys.\n KMS does not synchronize this property.

" } }, "Tags": { "target": "com.amazonaws.kms#TagList", "traits": { - "smithy.api#documentation": "

Assigns one or more tags to the replica key. Use this parameter to tag the KMS key when it\n is created. To tag an existing KMS key, use the TagResource\n operation.

\n \n

Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see ABAC for KMS in the Key Management Service Developer Guide.

\n
\n

To use this parameter, you must have kms:TagResource permission in an IAM policy.

\n

Tags are not a shared property of multi-Region keys. You can specify the same tags or\n different tags for each key in a set of related multi-Region keys. KMS does not synchronize\n this property.

\n

Each tag consists of a tag key and a tag value. Both the tag key and the tag value are\n required, but the tag value can be an empty (null) string. You cannot have more than one tag\n on a KMS key with the same tag key. If you specify an existing tag key with a different tag\n value, KMS replaces the current tag value with the specified one.

\n

When you add tags to an Amazon Web Services resource, Amazon Web Services generates a cost allocation\n report with usage and costs aggregated by tags. Tags can also be used to control access to a KMS key. For details,\n see Tagging Keys.

" + "smithy.api#documentation": "

Assigns one or more tags to the replica key. Use this parameter to tag the KMS key when it\n is created. To tag an existing KMS key, use the TagResource\n operation.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n \n

Tagging or untagging a KMS key can allow or deny permission to the KMS key. For details, see ABAC for KMS in the Key Management Service Developer Guide.

\n
\n

To use this parameter, you must have kms:TagResource permission in an IAM policy.

\n

Tags are not a shared property of multi-Region keys. You can specify the same tags or\n different tags for each key in a set of related multi-Region keys. KMS does not synchronize\n this property.

\n

Each tag consists of a tag key and a tag value. Both the tag key and the tag value are\n required, but the tag value can be an empty (null) string. You cannot have more than one tag\n on a KMS key with the same tag key. If you specify an existing tag key with a different tag\n value, KMS replaces the current tag value with the specified one.

\n

When you add tags to an Amazon Web Services resource, Amazon Web Services generates a cost allocation\n report with usage and costs aggregated by tags. Tags can also be used to control access to a KMS key. For details,\n see Tagging Keys.

" } } }, @@ -5586,7 +5675,7 @@ } }, "traits": { - "smithy.api#documentation": "

A key-value pair. A tag consists of a tag key and a tag value. Tag keys and tag values are\n both required, but tag values can be empty (null) strings.

\n

For information about the rules that apply to tag keys and tag values, see User-Defined Tag Restrictions in the Amazon Web Services Billing and Cost Management\n User Guide.

" + "smithy.api#documentation": "

A key-value pair. A tag consists of a tag key and a tag value. Tag keys and tag values are\n both required, but tag values can be empty (null) strings.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

For information about the rules that apply to tag keys and tag values, see User-Defined Tag Restrictions in the Amazon Web Services Billing and Cost Management\n User Guide.

" } }, "com.amazonaws.kms#TagException": { @@ -5672,7 +5761,7 @@ "Tags": { "target": "com.amazonaws.kms#TagList", "traits": { - "smithy.api#documentation": "

One or more tags.

\n

Each tag consists of a tag key and a tag value. The tag value can be an empty (null)\n string.

\n

You cannot have more than one tag on a KMS key with the same tag key. If you specify an\n existing tag key with a different tag value, KMS replaces the current tag value with the\n specified one.

", + "smithy.api#documentation": "

One or more tags. Each tag consists of a tag key and a tag value. The tag value can be an empty (null)\n string.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

You cannot have more than one tag on a KMS key with the same tag key. If you specify an\n existing tag key with a different tag value, KMS replaces the current tag value with the\n specified one.

", "smithy.api#required": {} } } @@ -7251,7 +7340,7 @@ "AliasName": { "target": "com.amazonaws.kms#AliasNameType", "traits": { - "smithy.api#documentation": "

Identifies the alias that is changing its KMS key. This value must begin with\n alias/ followed by the alias name, such as alias/ExampleAlias. You\n cannot use UpdateAlias to change the alias name.

", + "smithy.api#documentation": "

Identifies the alias that is changing its KMS key. This value must begin with\n alias/ followed by the alias name, such as alias/ExampleAlias. You\n cannot use UpdateAlias to change the alias name.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
", "smithy.api#required": {} } }, @@ -7345,7 +7434,7 @@ "NewCustomKeyStoreName": { "target": "com.amazonaws.kms#CustomKeyStoreNameType", "traits": { - "smithy.api#documentation": "

Changes the friendly name of the custom key store to the value that you specify. The\n custom key store name must be unique in the Amazon Web Services account.

\n

To change this value, an CloudHSM key store must be disconnected. An external key store can\n be connected or disconnected.

" + "smithy.api#documentation": "

Changes the friendly name of the custom key store to the value that you specify. The\n custom key store name must be unique in the Amazon Web Services account.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
\n

To change this value, an CloudHSM key store must be disconnected. An external key store can\n be connected or disconnected.

" } }, "KeyStorePassword": { @@ -7444,7 +7533,7 @@ "Description": { "target": "com.amazonaws.kms#DescriptionType", "traits": { - "smithy.api#documentation": "

New description for the KMS key.

", + "smithy.api#documentation": "

New description for the KMS key.

\n \n

Do not include confidential or sensitive information in this field. This field may be displayed in plaintext in CloudTrail logs and other output.

\n
", "smithy.api#required": {} } } diff --git a/aws/sdk/aws-models/lambda.json b/aws/sdk/aws-models/lambda.json index 8ed02f91b7..b7c3fb34ca 100644 --- a/aws/sdk/aws-models/lambda.json +++ b/aws/sdk/aws-models/lambda.json @@ -9936,6 +9936,12 @@ "traits": { "smithy.api#enumValue": "python3.10" } + }, + "java17": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "java17" + } } } }, diff --git a/aws/sdk/aws-models/s3.json b/aws/sdk/aws-models/s3.json index f6a09bc273..a08417723f 100644 --- a/aws/sdk/aws-models/s3.json +++ b/aws/sdk/aws-models/s3.json @@ -44,7 +44,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies the days since the initiation of an incomplete multipart upload that Amazon S3 will\n wait before permanently removing all parts of the upload. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Policy in the\n Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies the days since the initiation of an incomplete multipart upload that Amazon S3 will\n wait before permanently removing all parts of the upload. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Configuration in the\n Amazon S3 User Guide.

" } }, "com.amazonaws.s3#AbortMultipartUpload": { @@ -61,7 +61,7 @@ } ], "traits": { - "smithy.api#documentation": "

This action aborts a multipart upload. After a multipart upload is aborted, no\n additional parts can be uploaded using that upload ID. The storage consumed by any\n previously uploaded parts will be freed. However, if any part uploads are currently in\n progress, those part uploads might or might not succeed. As a result, it might be necessary\n to abort a given multipart upload multiple times in order to completely free all storage\n consumed by all parts.

\n

To verify that all parts have been removed, so you don't get charged for the part\n storage, you should call the ListParts action and ensure that\n the parts list is empty.

\n

For information about permissions required to use the multipart upload, see Multipart Upload and\n Permissions.

\n

The following operations are related to AbortMultipartUpload:

\n ", + "smithy.api#documentation": "

This action aborts a multipart upload. After a multipart upload is aborted, no\n additional parts can be uploaded using that upload ID. The storage consumed by any\n previously uploaded parts will be freed. However, if any part uploads are currently in\n progress, those part uploads might or might not succeed. As a result, it might be necessary\n to abort a given multipart upload multiple times in order to completely free all storage\n consumed by all parts.

\n

To verify that all parts have been removed, so you don't get charged for the part\n storage, you should call the ListParts action and ensure that\n the parts list is empty.

\n

For information about permissions required to use the multipart upload, see Multipart Upload\n and Permissions.

\n

The following operations are related to AbortMultipartUpload:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}/{Key+}?x-id=AbortMultipartUpload", @@ -89,7 +89,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name to which the upload was taking place.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name to which the upload was taking place.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -19196,7 +19196,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies the configuration and any analyses for the analytics filter of an Amazon S3 bucket.

" + "smithy.api#documentation": "

Specifies the configuration and any analyses for the analytics filter of an Amazon S3\n bucket.

" } }, "com.amazonaws.s3#AnalyticsConfigurationList": { @@ -19326,7 +19326,7 @@ "CreationDate": { "target": "com.amazonaws.s3#CreationDate", "traits": { - "smithy.api#documentation": "

Date the bucket was created. This date can change when making changes to your bucket, such as editing its bucket policy.

" + "smithy.api#documentation": "

Date the bucket was created. This date can change when making changes to your bucket,\n such as editing its bucket policy.

" } } }, @@ -19681,7 +19681,7 @@ } }, "traits": { - "smithy.api#documentation": "

Describes the cross-origin access configuration for objects in an Amazon S3 bucket. For more\n information, see Enabling\n Cross-Origin Resource Sharing in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Describes the cross-origin access configuration for objects in an Amazon S3 bucket. For more\n information, see Enabling\n Cross-Origin Resource Sharing in the\n Amazon S3 User Guide.

" } }, "com.amazonaws.s3#CORSRule": { @@ -19757,7 +19757,7 @@ "Comments": { "target": "com.amazonaws.s3#Comments", "traits": { - "smithy.api#documentation": "

A single character used to indicate that a row should be ignored when the character is\n present at the start of that row. You can specify any character to indicate a comment\n line.

" + "smithy.api#documentation": "

A single character used to indicate that a row should be ignored when the character is\n present at the start of that row. You can specify any character to indicate a comment line.\n The default character is #.

\n

Default: #\n

" } }, "QuoteEscapeCharacter": { @@ -19962,7 +19962,7 @@ "target": "com.amazonaws.s3#CompleteMultipartUploadOutput" }, "traits": { - "smithy.api#documentation": "

Completes a multipart upload by assembling previously uploaded parts.

\n

You first initiate the multipart upload and then upload all parts using the UploadPart\n operation. After successfully uploading all relevant parts of an upload, you call this\n action to complete the upload. Upon receiving this request, Amazon S3 concatenates all\n the parts in ascending order by part number to create a new object. In the Complete\n Multipart Upload request, you must provide the parts list. You must ensure that the parts\n list is complete. This action concatenates the parts that you provide in the list. For\n each part in the list, you must provide the part number and the ETag value,\n returned after that part was uploaded.

\n

Processing of a Complete Multipart Upload request could take several minutes to\n complete. After Amazon S3 begins processing the request, it sends an HTTP response header that\n specifies a 200 OK response. While processing is in progress, Amazon S3 periodically sends white\n space characters to keep the connection from timing out. Because a request could fail after\n the initial 200 OK response has been sent, it is important that you check the response body\n to determine whether the request succeeded.

\n

Note that if CompleteMultipartUpload fails, applications should be prepared\n to retry the failed requests. For more information, see Amazon S3 Error Best Practices.

\n \n

You cannot use Content-Type: application/x-www-form-urlencoded with Complete\n Multipart Upload requests. Also, if you do not provide a Content-Type header, CompleteMultipartUpload returns a 200 OK response.

\n
\n

For more information about multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information about permissions required to use the multipart upload API, see Multipart Upload and\n Permissions.

\n

\n CompleteMultipartUpload has the following special errors:

\n
    \n
  • \n

    Error code: EntityTooSmall\n

    \n
      \n
    • \n

      Description: Your proposed upload is smaller than the minimum allowed object\n size. Each part must be at least 5 MB in size, except the last part.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InvalidPart\n

    \n
      \n
    • \n

      Description: One or more of the specified parts could not be found. The part\n might not have been uploaded, or the specified entity tag might not have\n matched the part's entity tag.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InvalidPartOrder\n

    \n
      \n
    • \n

      Description: The list of parts was not in ascending order. The parts list\n must be specified in order by part number.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: NoSuchUpload\n

    \n
      \n
    • \n

      Description: The specified multipart upload does not exist. The upload ID\n might be invalid, or the multipart upload might have been aborted or\n completed.

      \n
    • \n
    • \n

      404 Not Found

      \n
    • \n
    \n
  • \n
\n

The following operations are related to CompleteMultipartUpload:

\n ", + "smithy.api#documentation": "

Completes a multipart upload by assembling previously uploaded parts.

\n

You first initiate the multipart upload and then upload all parts using the UploadPart\n operation. After successfully uploading all relevant parts of an upload, you call this\n action to complete the upload. Upon receiving this request, Amazon S3 concatenates all the\n parts in ascending order by part number to create a new object. In the Complete Multipart\n Upload request, you must provide the parts list. You must ensure that the parts list is\n complete. This action concatenates the parts that you provide in the list. For each part in\n the list, you must provide the part number and the ETag value, returned after\n that part was uploaded.

\n

Processing of a Complete Multipart Upload request could take several minutes to\n complete. After Amazon S3 begins processing the request, it sends an HTTP response header that\n specifies a 200 OK response. While processing is in progress, Amazon S3 periodically sends white\n space characters to keep the connection from timing out. A request could fail after the\n initial 200 OK response has been sent. This means that a 200 OK response can\n contain either a success or an error. If you call the S3 API directly, make sure to design\n your application to parse the contents of the response and handle it appropriately. If you\n use Amazon Web Services SDKs, SDKs handle this condition. The SDKs detect the embedded error and apply\n error handling per your configuration settings (including automatically retrying the\n request as appropriate). If the condition persists, the SDKs throws an exception (or, for\n the SDKs that don't use exceptions, they return the error).

\n

Note that if CompleteMultipartUpload fails, applications should be prepared\n to retry the failed requests. For more information, see Amazon S3 Error Best\n Practices.

\n \n

You cannot use Content-Type: application/x-www-form-urlencoded with\n Complete Multipart Upload requests. Also, if you do not provide a\n Content-Type header, CompleteMultipartUpload returns a 200\n OK response.

\n
\n

For more information about multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information about permissions required to use the multipart upload API, see Multipart Upload\n and Permissions.

\n

\n CompleteMultipartUpload has the following special errors:

\n
    \n
  • \n

    Error code: EntityTooSmall\n

    \n
      \n
    • \n

      Description: Your proposed upload is smaller than the minimum allowed object\n size. Each part must be at least 5 MB in size, except the last part.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InvalidPart\n

    \n
      \n
    • \n

      Description: One or more of the specified parts could not be found. The part\n might not have been uploaded, or the specified entity tag might not have\n matched the part's entity tag.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InvalidPartOrder\n

    \n
      \n
    • \n

      Description: The list of parts was not in ascending order. The parts list\n must be specified in order by part number.

      \n
    • \n
    • \n

      400 Bad Request

      \n
    • \n
    \n
  • \n
  • \n

    Error code: NoSuchUpload\n

    \n
      \n
    • \n

      Description: The specified multipart upload does not exist. The upload ID\n might be invalid, or the multipart upload might have been aborted or\n completed.

      \n
    • \n
    • \n

      404 Not Found

      \n
    • \n
    \n
  • \n
\n

The following operations are related to CompleteMultipartUpload:

\n ", "smithy.api#http": { "method": "POST", "uri": "/{Bucket}/{Key+}?x-id=CompleteMultipartUpload", @@ -19982,7 +19982,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket that contains the newly created object. Does not return the access point ARN or access point alias if used.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

The name of the bucket that contains the newly created object. Does not return the access point\n ARN or access point alias if used.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

" } }, "Key": { @@ -20001,7 +20001,7 @@ "ETag": { "target": "com.amazonaws.s3#ETag", "traits": { - "smithy.api#documentation": "

Entity tag that identifies the newly created object's data. Objects with different\n object data will have different entity tags. The entity tag is an opaque string. The entity\n tag may or may not be an MD5 digest of the object data. If the entity tag is not an MD5\n digest of the object data, it will contain one or more nonhexadecimal characters and/or\n will consist of less than 32 or more than 32 hexadecimal digits. For more information about\n how the entity tag is calculated, see\n Checking\n object integrity in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Entity tag that identifies the newly created object's data. Objects with different\n object data will have different entity tags. The entity tag is an opaque string. The entity\n tag may or may not be an MD5 digest of the object data. If the entity tag is not an MD5\n digest of the object data, it will contain one or more nonhexadecimal characters and/or\n will consist of less than 32 or more than 32 hexadecimal digits. For more information about\n how the entity tag is calculated, see Checking object\n integrity in the Amazon S3 User Guide.

" } }, "ChecksumCRC32": { @@ -20031,7 +20031,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

If you specified server-side encryption either with an Amazon S3-managed encryption key or an\n Amazon Web Services KMS key in your initiate multipart upload request, the response\n includes this header. It confirms the encryption algorithm that Amazon S3 used to encrypt the\n object.

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -20045,7 +20045,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -20053,7 +20053,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -20075,7 +20075,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

Name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -20180,7 +20180,7 @@ "Parts": { "target": "com.amazonaws.s3#CompletedPartList", "traits": { - "smithy.api#documentation": "

Array of CompletedPart data types.

\n

If you do not supply a valid Part with your request, the service sends back an HTTP\n 400 response.

", + "smithy.api#documentation": "

Array of CompletedPart data types.

\n

If you do not supply a valid Part with your request, the service sends back\n an HTTP 400 response.

", "smithy.api#xmlFlattened": {}, "smithy.api#xmlName": "Part" } @@ -20335,7 +20335,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a copy of an object that is already stored in Amazon S3.

\n \n

You can store individual objects of up to 5 TB in Amazon S3. You create a copy of your\n object up to 5 GB in size in a single atomic action using this API. However, to copy an\n object greater than 5 GB, you must use the multipart upload Upload Part - Copy\n (UploadPartCopy) API. For more information, see Copy Object Using the\n REST Multipart Upload API.

\n
\n

All copy requests must be authenticated. Additionally, you must have\n read access to the source object and write\n access to the destination bucket. For more information, see REST Authentication. Both the Region\n that you want to copy the object from and the Region that you want to copy the object to\n must be enabled for your account.

\n

A copy request might return an error when Amazon S3 receives the copy request or while Amazon S3\n is copying the files. If the error occurs before the copy action starts, you receive a\n standard Amazon S3 error. If the error occurs during the copy operation, the error response is\n embedded in the 200 OK response. This means that a 200 OK\n response can contain either a success or an error. Design your application to parse the\n contents of the response and handle it appropriately.

\n

If the copy is successful, you receive a response with information about the copied\n object.

\n \n

If the request is an HTTP 1.1 request, the response is chunk encoded. If it were not,\n it would not contain the content-length, and you would need to read the entire\n body.

\n
\n

The copy request charge is based on the storage class and Region that you specify for\n the destination object. For pricing information, see Amazon S3 pricing.

\n \n

Amazon S3 transfer acceleration does not support cross-Region copies. If you request a\n cross-Region copy using a transfer acceleration endpoint, you get a 400 Bad\n Request error. For more information, see Transfer Acceleration.

\n
\n

\n Metadata\n

\n

When copying an object, you can preserve all metadata (default) or specify new metadata.\n However, the ACL is not preserved and is set to private for the user making the request. To\n override the default ACL setting, specify a new ACL when generating a copy request. For\n more information, see Using ACLs.

\n

To specify whether you want the object metadata copied from the source object or\n replaced with metadata provided in the request, you can optionally add the\n x-amz-metadata-directive header. When you grant permissions, you can use\n the s3:x-amz-metadata-directive condition key to enforce certain metadata\n behavior when objects are uploaded. For more information, see Specifying Conditions in a\n Policy in the Amazon S3 User Guide. For a complete list of\n Amazon S3-specific condition keys, see Actions, Resources, and Condition Keys for\n Amazon S3.

\n

\n x-amz-copy-source-if Headers\n

\n

To only copy an object under certain conditions, such as whether the Etag\n matches or whether the object was modified before or after a specified date, use the\n following request parameters:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-none-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since\n

    \n
  • \n
\n

If both the x-amz-copy-source-if-match and\n x-amz-copy-source-if-unmodified-since headers are present in the request\n and evaluate as follows, Amazon S3 returns 200 OK and copies the data:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match condition evaluates to true

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since condition evaluates to\n false

    \n
  • \n
\n

If both the x-amz-copy-source-if-none-match and\n x-amz-copy-source-if-modified-since headers are present in the request and\n evaluate as follows, Amazon S3 returns the 412 Precondition Failed response\n code:

\n
    \n
  • \n

    \n x-amz-copy-source-if-none-match condition evaluates to false

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since condition evaluates to\n true

    \n
  • \n
\n \n

All headers with the x-amz- prefix, including\n x-amz-copy-source, must be signed.

\n
\n

\n Server-side encryption\n

\n

When you perform a CopyObject operation, you can optionally use the appropriate encryption-related \n headers to encrypt the object using server-side encryption with Amazon Web Services managed encryption keys \n (SSE-S3 or SSE-KMS) or a customer-provided encryption key. With server-side encryption, Amazon S3 \n encrypts your data as it writes it to disks in its data centers and decrypts the data when \n you access it. For more information about server-side encryption, see Using\n Server-Side Encryption.

\n

If a target object uses SSE-KMS, you can enable an S3 Bucket Key for the object. For more\n information, see Amazon S3 Bucket Keys in the Amazon S3 User Guide.

\n

\n Access Control List (ACL)-Specific Request\n Headers\n

\n

When copying an object, you can optionally use headers to grant ACL-based permissions.\n By default, all objects are private. Only the owner has full access control. When adding a\n new object, you can grant permissions to individual Amazon Web Services accounts or to predefined groups\n defined by Amazon S3. These permissions are then added to the ACL on the object. For more\n information, see Access Control List (ACL) Overview and Managing ACLs Using the REST\n API.

\n

If the bucket that you're copying objects to uses the bucket owner enforced setting for\n S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that\n use this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control canned\n ACL or an equivalent form of this ACL expressed in the XML format.

\n

For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, \n all objects written to the bucket by any account will be owned by the bucket owner.

\n
\n

\n Checksums\n

\n

When copying an object, if it has a checksum, that checksum will be copied to the new object\n by default. When you copy the object over, you may optionally specify a different checksum\n algorithm to use with the x-amz-checksum-algorithm header.

\n

\n Storage Class Options\n

\n

You can use the CopyObject action to change the storage class of an\n object that is already stored in Amazon S3 using the StorageClass parameter. For\n more information, see Storage\n Classes in the Amazon S3 User Guide.

\n

\n Versioning\n

\n

By default, x-amz-copy-source identifies the current version of an object\n to copy. If the current version is a delete marker, Amazon S3 behaves as if the object was\n deleted. To copy a different version, use the versionId subresource.

\n

If you enable versioning on the target bucket, Amazon S3 generates a unique version ID for\n the object being copied. This version ID is different from the version ID of the source\n object. Amazon S3 returns the version ID of the copied object in the\n x-amz-version-id response header in the response.

\n

If you do not enable versioning or suspend it on the target bucket, the version ID that\n Amazon S3 generates is always null.

\n

If the source object's storage class is GLACIER, you must restore a copy of this object\n before you can use it as a source object for the copy operation. For more information, see\n RestoreObject.

\n

The following operations are related to CopyObject:

\n \n

For more information, see Copying\n Objects.

", + "smithy.api#documentation": "

Creates a copy of an object that is already stored in Amazon S3.

\n \n

You can store individual objects of up to 5 TB in Amazon S3. You create a copy of your\n object up to 5 GB in size in a single atomic action using this API. However, to copy an\n object greater than 5 GB, you must use the multipart upload Upload Part - Copy\n (UploadPartCopy) API. For more information, see Copy Object Using the\n REST Multipart Upload API.

\n
\n

All copy requests must be authenticated. Additionally, you must have\n read access to the source object and write\n access to the destination bucket. For more information, see REST Authentication. Both the\n Region that you want to copy the object from and the Region that you want to copy the\n object to must be enabled for your account.

\n

A copy request might return an error when Amazon S3 receives the copy request or while Amazon S3\n is copying the files. If the error occurs before the copy action starts, you receive a\n standard Amazon S3 error. If the error occurs during the copy operation, the error response is\n embedded in the 200 OK response. This means that a 200 OK\n response can contain either a success or an error. If you call the S3 API directly, make\n sure to design your application to parse the contents of the response and handle it\n appropriately. If you use Amazon Web Services SDKs, SDKs handle this condition. The SDKs detect the\n embedded error and apply error handling per your configuration settings (including\n automatically retrying the request as appropriate). If the condition persists, the SDKs\n throws an exception (or, for the SDKs that don't use exceptions, they return the\n error).

\n

If the copy is successful, you receive a response with information about the copied\n object.

\n \n

If the request is an HTTP 1.1 request, the response is chunk encoded. If it were not,\n it would not contain the content-length, and you would need to read the entire\n body.

\n
\n

The copy request charge is based on the storage class and Region that you specify for\n the destination object. For pricing information, see Amazon S3 pricing.

\n \n

Amazon S3 transfer acceleration does not support cross-Region copies. If you request a\n cross-Region copy using a transfer acceleration endpoint, you get a 400 Bad\n Request error. For more information, see Transfer\n Acceleration.

\n
\n
\n
Metadata
\n
\n

When copying an object, you can preserve all metadata (default) or specify new metadata.\n However, the ACL is not preserved and is set to private for the user making the request. To\n override the default ACL setting, specify a new ACL when generating a copy request. For\n more information, see Using ACLs.

\n

To specify whether you want the object metadata copied from the source object or\n replaced with metadata provided in the request, you can optionally add the\n x-amz-metadata-directive header. When you grant permissions, you can use\n the s3:x-amz-metadata-directive condition key to enforce certain metadata\n behavior when objects are uploaded. For more information, see Specifying Conditions in a\n Policy in the Amazon S3 User Guide. For a complete list of\n Amazon S3-specific condition keys, see Actions, Resources, and Condition Keys for\n Amazon S3.

\n \n

\n x-amz-website-redirect-location is unique to each object and must be\n specified in the request headers to copy the value.

\n
\n
\n
x-amz-copy-source-if Headers
\n
\n

To only copy an object under certain conditions, such as whether the Etag\n matches or whether the object was modified before or after a specified date, use the\n following request parameters:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-none-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since\n

    \n
  • \n
\n

If both the x-amz-copy-source-if-match and\n x-amz-copy-source-if-unmodified-since headers are present in the request\n and evaluate as follows, Amazon S3 returns 200 OK and copies the data:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match condition evaluates to true

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since condition evaluates to\n false

    \n
  • \n
\n

If both the x-amz-copy-source-if-none-match and\n x-amz-copy-source-if-modified-since headers are present in the request and\n evaluate as follows, Amazon S3 returns the 412 Precondition Failed response\n code:

\n
    \n
  • \n

    \n x-amz-copy-source-if-none-match condition evaluates to false

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since condition evaluates to\n true

    \n
  • \n
\n \n

All headers with the x-amz- prefix, including\n x-amz-copy-source, must be signed.

\n
\n
\n
Server-side encryption
\n
\n

Amazon S3 automatically encrypts all new objects that are copied to an S3 bucket. When\n copying an object, if you don't specify encryption information in your copy request, the\n encryption setting of the target object is set to the default encryption configuration of\n the destination bucket. By default, all buckets have a base level of encryption\n configuration that uses server-side encryption with Amazon S3 managed keys (SSE-S3). If the\n destination bucket has a default encryption configuration that uses server-side encryption\n with an Key Management Service (KMS) key (SSE-KMS), or a customer-provided encryption key (SSE-C),\n Amazon S3 uses the corresponding KMS key, or a customer-provided key to encrypt the target\n object copy.

\n

When you perform a CopyObject operation, if you want to use a different type\n of encryption setting for the target object, you can use other appropriate\n encryption-related headers to encrypt the target object with a KMS key, an Amazon S3 managed\n key, or a customer-provided key. With server-side encryption, Amazon S3 encrypts your data as it\n writes it to disks in its data centers and decrypts the data when you access it. If the\n encryption setting in your request is different from the default encryption configuration\n of the destination bucket, the encryption setting in your request takes precedence. If the\n source object for the copy is stored in Amazon S3 using SSE-C, you must provide the necessary\n encryption information in your request so that Amazon S3 can decrypt the object for copying. For\n more information about server-side encryption, see Using Server-Side\n Encryption.

\n

If a target object uses SSE-KMS, you can enable an S3 Bucket Key for the object. For\n more information, see Amazon S3 Bucket Keys in the Amazon S3 User Guide.

\n
\n
Access Control List (ACL)-Specific Request\n Headers
\n
\n

When copying an object, you can optionally use headers to grant ACL-based permissions.\n By default, all objects are private. Only the owner has full access control. When adding a\n new object, you can grant permissions to individual Amazon Web Services accounts or to predefined groups\n defined by Amazon S3. These permissions are then added to the ACL on the object. For more\n information, see Access Control List (ACL) Overview and Managing ACLs Using the REST\n API.

\n

If the bucket that you're copying objects to uses the bucket owner enforced setting for\n S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that use\n this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control\n canned ACL or an equivalent form of this ACL expressed in the XML format.

\n

For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, all\n objects written to the bucket by any account will be owned by the bucket owner.

\n
\n
\n
Checksums
\n
\n

When copying an object, if it has a checksum, that checksum will be copied to the new\n object by default. When you copy the object over, you may optionally specify a different\n checksum algorithm to use with the x-amz-checksum-algorithm header.

\n
\n
Storage Class Options
\n
\n

You can use the CopyObject action to change the storage class of an object\n that is already stored in Amazon S3 using the StorageClass parameter. For more\n information, see Storage Classes in the\n Amazon S3 User Guide.

\n

If the source object's storage class is GLACIER, you must restore a copy of\n this object before you can use it as a source object for the copy operation. For\n more information, see RestoreObject. For\n more information, see Copying\n Objects.

\n
\n
Versioning
\n
\n

By default, x-amz-copy-source identifies the current version of an object\n to copy. If the current version is a delete marker, Amazon S3 behaves as if the object was\n deleted. To copy a different version, use the versionId subresource.

\n

If you enable versioning on the target bucket, Amazon S3 generates a unique version ID for\n the object being copied. This version ID is different from the version ID of the source\n object. Amazon S3 returns the version ID of the copied object in the\n x-amz-version-id response header in the response.

\n

If you do not enable versioning or suspend it on the target bucket, the version ID that\n Amazon S3 generates is always null.

\n
\n
\n

The following operations are related to CopyObject:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?x-id=CopyObject", @@ -20377,7 +20377,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -20398,7 +20398,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -20413,7 +20413,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the copied object uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the copied object uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -20441,7 +20441,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the destination bucket.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the destination bucket.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -20537,14 +20537,14 @@ "GrantFullControl": { "target": "com.amazonaws.s3#GrantFullControl", "traits": { - "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-full-control" } }, "GrantRead": { "target": "com.amazonaws.s3#GrantRead", "traits": { - "smithy.api#documentation": "

Allows grantee to read the object data and its\n metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to read the object data and its metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-read" } }, @@ -20558,7 +20558,7 @@ "GrantWriteACP": { "target": "com.amazonaws.s3#GrantWriteACP", "traits": { - "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-write-acp" } }, @@ -20594,21 +20594,21 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", + "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-storage-class" } }, "WebsiteRedirectLocation": { "target": "com.amazonaws.s3#WebsiteRedirectLocation", "traits": { - "smithy.api#documentation": "

If the bucket is configured as a website, redirects requests for this object to another\n object in the same bucket or to an external URL. Amazon S3 stores the value of this header in\n the object metadata.

", + "smithy.api#documentation": "

If the bucket is configured as a website, redirects requests for this object to another\n object in the same bucket or to an external URL. Amazon S3 stores the value of this header in\n the object metadata. This value is unique to each object and is not copied when using the\n x-amz-metadata-directive header. Instead, you may opt to provide this\n header in combination with the directive.

", "smithy.api#httpHeader": "x-amz-website-redirect-location" } }, @@ -20636,14 +20636,14 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

Specifies the Amazon Web Services KMS key ID to use for object encryption. All GET and PUT requests for\n an object protected by Amazon Web Services KMS will fail if not made via SSL or using SigV4. For\n information about configuring using any of the officially supported Amazon Web Services SDKs and Amazon Web Services CLI,\n see Specifying the\n Signature Version in Request Authentication in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Specifies the Amazon Web Services KMS key ID to use for object encryption. All GET and PUT requests\n for an object protected by Amazon Web Services KMS will fail if not made via SSL or using SigV4. For\n information about configuring using any of the officially supported Amazon Web Services SDKs and Amazon Web Services\n CLI, see Specifying the\n Signature Version in Request Authentication in the\n Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, "SSEKMSEncryptionContext": { "target": "com.amazonaws.s3#SSEKMSEncryptionContext", "traits": { - "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of this\n header is a base64-encoded UTF-8 string holding JSON with the encryption context key-value\n pairs.

", + "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of\n this header is a base64-encoded UTF-8 string holding JSON with the encryption context\n key-value pairs.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-context" } }, @@ -20651,7 +20651,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with server-side encryption using AWS KMS (SSE-KMS). Setting this header to true causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with a COPY action doesn’t affect bucket-level settings for S3 Bucket Key.

", + "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using AWS KMS (SSE-KMS). Setting this header to true\n causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with a COPY action doesn’t affect bucket-level settings for S3\n Bucket Key.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -20735,7 +20735,7 @@ "ETag": { "target": "com.amazonaws.s3#ETag", "traits": { - "smithy.api#documentation": "

Returns the ETag of the new object. The ETag reflects only changes to the contents of an object, not its metadata.

" + "smithy.api#documentation": "

Returns the ETag of the new object. The ETag reflects only changes to the contents of an\n object, not its metadata.

" } }, "LastModified": { @@ -20870,7 +20870,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new S3 bucket. To create a bucket, you must register with Amazon S3 and have a\n valid Amazon Web Services Access Key ID to authenticate requests. Anonymous requests are never allowed to\n create buckets. By creating the bucket, you become the bucket owner.

\n

Not every string is an acceptable bucket name. For information about bucket naming\n restrictions, see Bucket naming rules.

\n

If you want to create an Amazon S3 on Outposts bucket, see Create Bucket.

\n

By default, the bucket is created in the US East (N. Virginia) Region. You can\n optionally specify a Region in the request body. You might choose a Region to optimize\n latency, minimize costs, or address regulatory requirements. For example, if you reside in\n Europe, you will probably find it advantageous to create buckets in the Europe (Ireland)\n Region. For more information, see Accessing a\n bucket.

\n \n

If you send your create bucket request to the s3.amazonaws.com endpoint,\n the request goes to the us-east-1 Region. Accordingly, the signature calculations in\n Signature Version 4 must use us-east-1 as the Region, even if the location constraint in\n the request specifies another Region where the bucket is to be created. If you create a\n bucket in a Region other than US East (N. Virginia), your application must be able to\n handle 307 redirect. For more information, see Virtual hosting of buckets.

\n
\n

\n Access control lists (ACLs)\n

\n

When creating a bucket using this operation, you can optionally configure the bucket ACL to specify the accounts or\n groups that should be granted specific permissions on the bucket.

\n \n

If your CreateBucket request sets bucket owner enforced for S3 Object Ownership and\n specifies a bucket ACL that provides access to an external Amazon Web Services account, your request\n fails with a 400 error and returns the\n InvalidBucketAclWithObjectOwnership error code. For more information,\n see Controlling object\n ownership in the Amazon S3 User Guide.

\n
\n

There are two ways to grant the appropriate permissions using the request headers.

\n
    \n
  • \n

    Specify a canned ACL using the x-amz-acl request header. Amazon S3\n supports a set of predefined ACLs, known as canned ACLs. Each\n canned ACL has a predefined set of grantees and permissions. For more information,\n see Canned ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly using the x-amz-grant-read,\n x-amz-grant-write, x-amz-grant-read-acp,\n x-amz-grant-write-acp, and x-amz-grant-full-control\n headers. These headers map to the set of permissions Amazon S3 supports in an ACL. For\n more information, see Access control list\n (ACL) overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants the Amazon Web Services accounts identified by account IDs permissions to read object data and its metadata:

    \n

    \n x-amz-grant-read: id=\"11112222333\", id=\"444455556666\" \n

    \n
  • \n
\n \n

You can use either a canned ACL or specify access permissions explicitly. You cannot\n do both.

\n
\n

\n Permissions\n

\n

In addition to s3:CreateBucket, the following permissions are required when your CreateBucket includes specific headers:

\n
    \n
  • \n

    \n ACLs - If your CreateBucket request specifies ACL permissions and the ACL is public-read, public-read-write, \n authenticated-read, or if you specify access permissions explicitly through any other ACL, both \n s3:CreateBucket and s3:PutBucketAcl permissions are needed. If the ACL the \n CreateBucket request is private or doesn't specify any ACLs, only s3:CreateBucket permission is needed.

    \n
  • \n
  • \n

    \n Object Lock - If\n ObjectLockEnabledForBucket is set to true in your\n CreateBucket request,\n s3:PutBucketObjectLockConfiguration and\n s3:PutBucketVersioning permissions are required.

    \n
  • \n
  • \n

    \n S3 Object Ownership - If your CreateBucket\n request includes the the x-amz-object-ownership header,\n s3:PutBucketOwnershipControls permission is required.

    \n
  • \n
\n

The following operations are related to CreateBucket:

\n ", + "smithy.api#documentation": "

Creates a new S3 bucket. To create a bucket, you must register with Amazon S3 and have a\n valid Amazon Web Services Access Key ID to authenticate requests. Anonymous requests are never allowed to\n create buckets. By creating the bucket, you become the bucket owner.

\n

Not every string is an acceptable bucket name. For information about bucket naming\n restrictions, see Bucket naming\n rules.

\n

If you want to create an Amazon S3 on Outposts bucket, see Create Bucket.

\n

By default, the bucket is created in the US East (N. Virginia) Region. You can\n optionally specify a Region in the request body. You might choose a Region to optimize\n latency, minimize costs, or address regulatory requirements. For example, if you reside in\n Europe, you will probably find it advantageous to create buckets in the Europe (Ireland)\n Region. For more information, see Accessing a\n bucket.

\n \n

If you send your create bucket request to the s3.amazonaws.com endpoint,\n the request goes to the us-east-1 Region. Accordingly, the signature calculations in\n Signature Version 4 must use us-east-1 as the Region, even if the location constraint in\n the request specifies another Region where the bucket is to be created. If you create a\n bucket in a Region other than US East (N. Virginia), your application must be able to\n handle 307 redirect. For more information, see Virtual hosting of\n buckets.

\n
\n
\n
Access control lists (ACLs)
\n
\n

When creating a bucket using this operation, you can optionally configure the bucket ACL\n to specify the accounts or groups that should be granted specific permissions on the\n bucket.

\n \n

If your CreateBucket request sets bucket owner enforced for S3 Object Ownership and\n specifies a bucket ACL that provides access to an external Amazon Web Services account, your request\n fails with a 400 error and returns the\n InvalidBucketAclWithObjectOwnership error code. For more information,\n see Controlling object\n ownership in the Amazon S3 User Guide.

\n
\n

There are two ways to grant the appropriate permissions using the request\n headers.

\n
    \n
  • \n

    Specify a canned ACL using the x-amz-acl request header. Amazon S3\n supports a set of predefined ACLs, known as canned ACLs. Each\n canned ACL has a predefined set of grantees and permissions. For more information,\n see Canned ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly using the x-amz-grant-read,\n x-amz-grant-write, x-amz-grant-read-acp,\n x-amz-grant-write-acp, and x-amz-grant-full-control\n headers. These headers map to the set of permissions Amazon S3 supports in an ACL. For\n more information, see Access control list (ACL)\n overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an\n Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants the Amazon Web Services accounts identified by account IDs permissions to read object data and its metadata:

    \n

    \n x-amz-grant-read: id=\"11112222333\", id=\"444455556666\" \n

    \n
  • \n
\n \n

You can use either a canned ACL or specify access permissions explicitly. You cannot\n do both.

\n
\n
\n
Permissions
\n
\n

In addition to s3:CreateBucket, the following permissions are required when\n your CreateBucket includes specific headers:

\n
    \n
  • \n

    \n ACLs - If your CreateBucket request\n specifies ACL permissions and the ACL is public-read, public-read-write,\n authenticated-read, or if you specify access permissions explicitly through any other\n ACL, both s3:CreateBucket and s3:PutBucketAcl permissions\n are needed. If the ACL the CreateBucket request is private or doesn't\n specify any ACLs, only s3:CreateBucket permission is needed.

    \n
  • \n
  • \n

    \n Object Lock - If\n ObjectLockEnabledForBucket is set to true in your\n CreateBucket request,\n s3:PutBucketObjectLockConfiguration and\n s3:PutBucketVersioning permissions are required.

    \n
  • \n
  • \n

    \n S3 Object Ownership - If your CreateBucket\n request includes the x-amz-object-ownership header,\n s3:PutBucketOwnershipControls permission is required.

    \n
  • \n
\n
\n
\n

The following operations are related to CreateBucket:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}", @@ -20965,7 +20965,7 @@ "GrantWrite": { "target": "com.amazonaws.s3#GrantWrite", "traits": { - "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and overwrites of those objects.

", + "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and\n overwrites of those objects.

", "smithy.api#httpHeader": "x-amz-grant-write" } }, @@ -21004,7 +21004,7 @@ "target": "com.amazonaws.s3#CreateMultipartUploadOutput" }, "traits": { - "smithy.api#documentation": "

This action initiates a multipart upload and returns an upload ID. This upload ID is\n used to associate all of the parts in the specific multipart upload. You specify this\n upload ID in each of your subsequent upload part requests (see UploadPart). You also include this\n upload ID in the final request to either complete or abort the multipart upload\n request.

\n

For more information about multipart uploads, see Multipart Upload Overview.

\n

If you have configured a lifecycle rule to abort incomplete multipart uploads, the\n upload must complete within the number of days specified in the bucket lifecycle\n configuration. Otherwise, the incomplete multipart upload becomes eligible for an abort\n action and Amazon S3 aborts the multipart upload. For more information, see Aborting\n Incomplete Multipart Uploads Using a Bucket Lifecycle Policy.

\n

For information about the permissions required to use the multipart upload API, see\n Multipart Upload and\n Permissions.

\n

For request signing, multipart upload is just a series of regular requests. You initiate\n a multipart upload, send one or more requests to upload parts, and then complete the\n multipart upload process. You sign each request individually. There is nothing special\n about signing multipart upload requests. For more information about signing, see Authenticating\n Requests (Amazon Web Services Signature Version 4).

\n \n

After you initiate a multipart upload and upload one or more parts, to stop being\n charged for storing the uploaded parts, you must either complete or abort the multipart\n upload. Amazon S3 frees up the space used to store the parts and stop charging you for\n storing them only after you either complete or abort a multipart upload.

\n
\n

You can optionally request server-side encryption. For server-side encryption, Amazon S3\n encrypts your data as it writes it to disks in its data centers and decrypts it when you\n access it. You can provide your own encryption key, or use Amazon Web Services KMS keys or Amazon S3-managed encryption keys. If you choose to provide\n your own encryption key, the request headers you provide in UploadPart and UploadPartCopy requests must match the headers you used in the request to\n initiate the upload by using CreateMultipartUpload.

\n

To perform a multipart upload with encryption using an Amazon Web Services KMS key, the requester must\n have permission to the kms:Decrypt and kms:GenerateDataKey*\n actions on the key. These permissions are required because Amazon S3 must decrypt and read data\n from the encrypted file parts before it completes the multipart upload. For more\n information, see Multipart upload API\n and permissions in the Amazon S3 User Guide.

\n

If your Identity and Access Management (IAM) user or role is in the same Amazon Web Services account\n as the KMS key, then you must have these permissions on the key policy. If your IAM\n user or role belongs to a different account than the key, then you must have the\n permissions on both the key policy and your IAM user or role.

\n

For more information, see Protecting\n Data Using Server-Side Encryption.

\n
\n
Access Permissions
\n
\n

When copying an object, you can optionally specify the accounts or groups that\n should be granted specific permissions on the new object. There are two ways to\n grant the permissions using the request headers:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. For\n more information, see Canned ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the\n x-amz-grant-read, x-amz-grant-read-acp,\n x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. These parameters map to\n the set of permissions that Amazon S3 supports in an ACL. For more information,\n see Access Control List (ACL)\n Overview.

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You\n cannot do both.

\n
\n
Server-Side- Encryption-Specific Request Headers
\n
\n

You can optionally tell Amazon S3 to encrypt data at rest using server-side\n encryption. Server-side encryption is for data encryption at rest. Amazon S3 encrypts\n your data as it writes it to disks in its data centers and decrypts it when you\n access it. The option you use depends on whether you want to use Amazon Web Services managed\n encryption keys or provide your own encryption key.

\n
    \n
  • \n

    Use encryption keys managed by Amazon S3 or customer managed key stored\n in Amazon Web Services Key Management Service (Amazon Web Services KMS) – If you want Amazon Web Services to manage the keys\n used to encrypt data, specify the following headers in the request.

    \n
      \n
    • \n

      \n x-amz-server-side-encryption\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-aws-kms-key-id\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-context\n

      \n
    • \n
    \n \n

    If you specify x-amz-server-side-encryption:aws:kms, but\n don't provide x-amz-server-side-encryption-aws-kms-key-id,\n Amazon S3 uses the Amazon Web Services managed key in Amazon Web Services KMS to protect the data.

    \n
    \n \n

    All GET and PUT requests for an object protected by Amazon Web Services KMS fail if\n you don't make them with SSL or by using SigV4.

    \n
    \n

    For more information about server-side encryption with KMS key (SSE-KMS),\n see Protecting Data Using Server-Side Encryption with KMS keys.

    \n
  • \n
  • \n

    Use customer-provided encryption keys – If you want to manage your own\n encryption keys, provide all the following headers in the request.

    \n
      \n
    • \n

      \n x-amz-server-side-encryption-customer-algorithm\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-customer-key\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-customer-key-MD5\n

      \n
    • \n
    \n

    For more information about server-side encryption with KMS keys (SSE-KMS),\n see Protecting Data Using Server-Side Encryption with KMS keys.

    \n
  • \n
\n
\n
Access-Control-List (ACL)-Specific Request Headers
\n
\n

You also can use the following access control–related headers with this\n operation. By default, all objects are private. Only the owner has full access\n control. When adding a new object, you can grant permissions to individual Amazon Web Services accounts or to predefined groups defined by Amazon S3. These permissions are then added\n to the access control list (ACL) on the object. For more information, see Using ACLs. With this\n operation, you can grant access permissions using one of the following two\n methods:

\n
    \n
  • \n

    Specify a canned ACL (x-amz-acl) — Amazon S3 supports a set of\n predefined ACLs, known as canned ACLs. Each canned ACL\n has a predefined set of grantees and permissions. For more information, see\n Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly — To explicitly grant access\n permissions to specific Amazon Web Services accounts or groups, use the following headers.\n Each header maps to specific permissions that Amazon S3 supports in an ACL. For\n more information, see Access\n Control List (ACL) Overview. In the header, you specify a list of\n grantees who get the specific permission. To grant permissions explicitly,\n use:

    \n
      \n
    • \n

      \n x-amz-grant-read\n

      \n
    • \n
    • \n

      \n x-amz-grant-write\n

      \n
    • \n
    • \n

      \n x-amz-grant-read-acp\n

      \n
    • \n
    • \n

      \n x-amz-grant-write-acp\n

      \n
    • \n
    • \n

      \n x-amz-grant-full-control\n

      \n
    • \n
    \n

    You specify each grantee as a type=value pair, where the type is one of\n the following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID\n of an Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email\n address of an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants the Amazon Web Services accounts identified by account IDs permissions to read object data and its metadata:

    \n

    \n x-amz-grant-read: id=\"11112222333\", id=\"444455556666\" \n

    \n
  • \n
\n
\n
\n

The following operations are related to CreateMultipartUpload:

\n ", + "smithy.api#documentation": "

This action initiates a multipart upload and returns an upload ID. This upload ID is\n used to associate all of the parts in the specific multipart upload. You specify this\n upload ID in each of your subsequent upload part requests (see UploadPart). You also include this\n upload ID in the final request to either complete or abort the multipart upload\n request.

\n

For more information about multipart uploads, see Multipart Upload Overview.

\n

If you have configured a lifecycle rule to abort incomplete multipart uploads, the\n upload must complete within the number of days specified in the bucket lifecycle\n configuration. Otherwise, the incomplete multipart upload becomes eligible for an abort\n action and Amazon S3 aborts the multipart upload. For more information, see Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Configuration.

\n

For information about the permissions required to use the multipart upload API, see\n Multipart\n Upload and Permissions.

\n

For request signing, multipart upload is just a series of regular requests. You initiate\n a multipart upload, send one or more requests to upload parts, and then complete the\n multipart upload process. You sign each request individually. There is nothing special\n about signing multipart upload requests. For more information about signing, see Authenticating Requests (Amazon Web Services Signature Version 4).

\n \n

After you initiate a multipart upload and upload one or more parts, to stop being\n charged for storing the uploaded parts, you must either complete or abort the multipart\n upload. Amazon S3 frees up the space used to store the parts and stop charging you for\n storing them only after you either complete or abort a multipart upload.

\n
\n

Server-side encryption is for data encryption at rest. Amazon S3 encrypts your data as it\n writes it to disks in its data centers and decrypts it when you access it. Amazon S3\n automatically encrypts all new objects that are uploaded to an S3 bucket. When doing a\n multipart upload, if you don't specify encryption information in your request, the\n encryption setting of the uploaded parts is set to the default encryption configuration of\n the destination bucket. By default, all buckets have a base level of encryption\n configuration that uses server-side encryption with Amazon S3 managed keys (SSE-S3). If the\n destination bucket has a default encryption configuration that uses server-side encryption\n with an Key Management Service (KMS) key (SSE-KMS), or a customer-provided encryption key (SSE-C),\n Amazon S3 uses the corresponding KMS key, or a customer-provided key to encrypt the uploaded\n parts. When you perform a CreateMultipartUpload operation, if you want to use a different\n type of encryption setting for the uploaded parts, you can request that Amazon S3 encrypts the\n object with a KMS key, an Amazon S3 managed key, or a customer-provided key. If the encryption\n setting in your request is different from the default encryption configuration of the\n destination bucket, the encryption setting in your request takes precedence. If you choose\n to provide your own encryption key, the request headers you provide in UploadPart\n and UploadPartCopy requests must match the headers you used in the request to\n initiate the upload by using CreateMultipartUpload. You can request that Amazon S3\n save the uploaded parts encrypted with server-side encryption with an Amazon S3 managed key\n (SSE-S3), an Key Management Service (KMS) key (SSE-KMS), or a customer-provided encryption key\n (SSE-C).

\n

To perform a multipart upload with encryption by using an Amazon Web Services KMS key, the requester\n must have permission to the kms:Decrypt and kms:GenerateDataKey*\n actions on the key. These permissions are required because Amazon S3 must decrypt and read data\n from the encrypted file parts before it completes the multipart upload. For more\n information, see Multipart upload API\n and permissions and Protecting data using\n server-side encryption with Amazon Web Services KMS in the\n Amazon S3 User Guide.

\n

If your Identity and Access Management (IAM) user or role is in the same Amazon Web Services account as the KMS key,\n then you must have these permissions on the key policy. If your IAM user or role belongs\n to a different account than the key, then you must have the permissions on both the key\n policy and your IAM user or role.

\n

For more information, see Protecting Data Using Server-Side\n Encryption.

\n
\n
Access Permissions
\n
\n

When copying an object, you can optionally specify the accounts or groups that\n should be granted specific permissions on the new object. There are two ways to\n grant the permissions using the request headers:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. For\n more information, see Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the\n x-amz-grant-read, x-amz-grant-read-acp,\n x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. These parameters map to\n the set of permissions that Amazon S3 supports in an ACL. For more information,\n see Access Control List (ACL) Overview.

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You\n cannot do both.

\n
\n
Server-Side- Encryption-Specific Request Headers
\n
\n

Amazon S3 encrypts data\n by using server-side encryption with an Amazon S3 managed key (SSE-S3) by default. Server-side encryption is for data encryption at rest. Amazon S3 encrypts\n your data as it writes it to disks in its data centers and decrypts it when you\n access it. You can request that Amazon S3 encrypts\n data at rest by using server-side encryption with other key options. The option you use depends on\n whether you want to use KMS keys (SSE-KMS) or provide your own encryption keys\n (SSE-C).

\n
    \n
  • \n

    Use KMS keys (SSE-KMS) that include the Amazon Web Services managed key\n (aws/s3) and KMS customer managed keys stored in Key Management Service (KMS) – If you\n want Amazon Web Services to manage the keys used to encrypt data, specify the following\n headers in the request.

    \n
      \n
    • \n

      \n x-amz-server-side-encryption\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-aws-kms-key-id\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-context\n

      \n
    • \n
    \n \n

    If you specify x-amz-server-side-encryption:aws:kms, but\n don't provide x-amz-server-side-encryption-aws-kms-key-id,\n Amazon S3 uses the Amazon Web Services managed key (aws/s3 key) in KMS to\n protect the data.

    \n
    \n \n

    All GET and PUT requests for an object protected\n by KMS fail if you don't make them by using Secure Sockets Layer (SSL),\n Transport Layer Security (TLS), or Signature Version 4.

    \n
    \n

    For more information about server-side encryption with KMS keys\n (SSE-KMS), see Protecting Data\n Using Server-Side Encryption with KMS keys.

    \n
  • \n
  • \n

    Use customer-provided encryption keys (SSE-C) – If you want to manage\n your own encryption keys, provide all the following headers in the\n request.

    \n
      \n
    • \n

      \n x-amz-server-side-encryption-customer-algorithm\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-customer-key\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-customer-key-MD5\n

      \n
    • \n
    \n

    For more information about server-side encryption with customer-provided\n encryption keys (SSE-C), see \n Protecting data using server-side encryption with customer-provided\n encryption keys (SSE-C).

    \n
  • \n
\n
\n
Access-Control-List (ACL)-Specific Request Headers
\n
\n

You also can use the following access control–related headers with this\n operation. By default, all objects are private. Only the owner has full access\n control. When adding a new object, you can grant permissions to individual\n Amazon Web Services accounts or to predefined groups defined by Amazon S3. These permissions are then\n added to the access control list (ACL) on the object. For more information, see\n Using ACLs. With this operation, you can grant access permissions\n using one of the following two methods:

\n
    \n
  • \n

    Specify a canned ACL (x-amz-acl) — Amazon S3 supports a set of\n predefined ACLs, known as canned ACLs. Each canned ACL\n has a predefined set of grantees and permissions. For more information, see\n Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly — To explicitly grant access\n permissions to specific Amazon Web Services accounts or groups, use the following headers.\n Each header maps to specific permissions that Amazon S3 supports in an ACL. For\n more information, see Access Control List (ACL)\n Overview. In the header, you specify a list of grantees who get\n the specific permission. To grant permissions explicitly, use:

    \n
      \n
    • \n

      \n x-amz-grant-read\n

      \n
    • \n
    • \n

      \n x-amz-grant-write\n

      \n
    • \n
    • \n

      \n x-amz-grant-read-acp\n

      \n
    • \n
    • \n

      \n x-amz-grant-write-acp\n

      \n
    • \n
    • \n

      \n x-amz-grant-full-control\n

      \n
    • \n
    \n

    You specify each grantee as a type=value pair, where the type is one of\n the following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID\n of an Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email\n address of an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants the Amazon Web Services accounts identified by account IDs permissions to read object data and its metadata:

    \n

    \n x-amz-grant-read: id=\"11112222333\", id=\"444455556666\" \n

    \n
  • \n
\n
\n
\n

The following operations are related to CreateMultipartUpload:

\n ", "smithy.api#http": { "method": "POST", "uri": "/{Bucket}/{Key+}?uploads&x-id=CreateMultipartUpload", @@ -21018,7 +21018,7 @@ "AbortDate": { "target": "com.amazonaws.s3#AbortDate", "traits": { - "smithy.api#documentation": "

If the bucket has a lifecycle rule configured with an action to abort incomplete\n multipart uploads and the prefix in the lifecycle rule matches the object name in the\n request, the response includes this header. The header indicates when the initiated\n multipart upload becomes eligible for an abort operation. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Policy.

\n

The response also includes the x-amz-abort-rule-id header that provides the\n ID of the lifecycle configuration rule that defines this action.

", + "smithy.api#documentation": "

If the bucket has a lifecycle rule configured with an action to abort incomplete\n multipart uploads and the prefix in the lifecycle rule matches the object name in the\n request, the response includes this header. The header indicates when the initiated\n multipart upload becomes eligible for an abort operation. For more information, see \n Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Configuration.

\n

The response also includes the x-amz-abort-rule-id header that provides the\n ID of the lifecycle configuration rule that defines this action.

", "smithy.api#httpHeader": "x-amz-abort-date" } }, @@ -21032,7 +21032,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the access point ARN or access point alias if used.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the\n access point ARN or access point alias if used.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#xmlName": "Bucket" } }, @@ -21051,7 +21051,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -21072,7 +21072,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -21087,7 +21087,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -21123,7 +21123,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which to initiate the upload

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket to which to initiate the upload

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -21176,14 +21176,14 @@ "GrantFullControl": { "target": "com.amazonaws.s3#GrantFullControl", "traits": { - "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-full-control" } }, "GrantRead": { "target": "com.amazonaws.s3#GrantRead", "traits": { - "smithy.api#documentation": "

Allows grantee to read the object data and its\n metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to read the object data and its metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-read" } }, @@ -21197,7 +21197,7 @@ "GrantWriteACP": { "target": "com.amazonaws.s3#GrantWriteACP", "traits": { - "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-write-acp" } }, @@ -21219,14 +21219,14 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", + "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-storage-class" } }, @@ -21261,14 +21261,14 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

Specifies the ID of the symmetric customer managed key to use for object\n encryption. All GET and PUT requests for an object protected by Amazon Web Services KMS will fail if not\n made via SSL or using SigV4. For information about configuring using any of the officially\n supported Amazon Web Services SDKs and Amazon Web Services CLI, see Specifying the Signature Version in Request Authentication\n in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Specifies the ID of the symmetric encryption customer managed key to use for object encryption.\n All GET and PUT requests for an object protected by Amazon Web Services KMS will fail if not made via SSL\n or using SigV4. For information about configuring using any of the officially supported\n Amazon Web Services SDKs and Amazon Web Services CLI, see Specifying the Signature Version in Request Authentication\n in the Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, "SSEKMSEncryptionContext": { "target": "com.amazonaws.s3#SSEKMSEncryptionContext", "traits": { - "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of this\n header is a base64-encoded UTF-8 string holding JSON with the encryption context key-value\n pairs.

", + "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of\n this header is a base64-encoded UTF-8 string holding JSON with the encryption context\n key-value pairs.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-context" } }, @@ -21276,7 +21276,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with server-side encryption using AWS KMS (SSE-KMS). Setting this header to true causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with an object action doesn’t affect bucket-level settings for S3 Bucket Key.

", + "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using AWS KMS (SSE-KMS). Setting this header to true\n causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with an object action doesn’t affect bucket-level settings for S3\n Bucket Key.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -21379,7 +21379,7 @@ } }, "traits": { - "smithy.api#documentation": "

The container element for specifying the default Object Lock retention settings for new\n objects placed in the specified bucket.

\n \n
    \n
  • \n

    The DefaultRetention settings require both a mode and a\n period.

    \n
  • \n
  • \n

    The DefaultRetention period can be either Days\n or Years but you must select one. You cannot specify Days\n and Years at the same time.

    \n
  • \n
\n
" + "smithy.api#documentation": "

The container element for specifying the default Object Lock retention settings for new\n objects placed in the specified bucket.

\n \n
    \n
  • \n

    The DefaultRetention settings require both a mode and a\n period.

    \n
  • \n
  • \n

    The DefaultRetention period can be either Days or\n Years but you must select one. You cannot specify\n Days and Years at the same time.

    \n
  • \n
\n
" } }, "com.amazonaws.s3#Delete": { @@ -21415,7 +21415,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes the S3 bucket. All objects (including all object versions and delete markers) in\n the bucket must be deleted before the bucket itself can be deleted.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Deletes the S3 bucket. All objects (including all object versions and delete markers) in\n the bucket must be deleted before the bucket itself can be deleted.

\n

The following operations are related to DeleteBucket:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}", @@ -21432,7 +21432,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes an analytics configuration for the bucket (specified by the analytics\n configuration ID).

\n

To use this operation, you must have permissions to perform the\n s3:PutAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about the Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis.

\n

The following operations are related to\n DeleteBucketAnalyticsConfiguration:

\n ", + "smithy.api#documentation": "

Deletes an analytics configuration for the bucket (specified by the analytics\n configuration ID).

\n

To use this operation, you must have permissions to perform the\n s3:PutAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about the Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis.

\n

The following operations are related to\n DeleteBucketAnalyticsConfiguration:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?analytics", @@ -21483,7 +21483,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes the cors configuration information set for the bucket.

\n

To use this operation, you must have permission to perform the\n s3:PutBucketCORS action. The bucket owner has this permission by default\n and can grant this permission to others.

\n

For information about cors, see Enabling\n Cross-Origin Resource Sharing in the Amazon S3 User Guide.

\n

\n Related Resources:\n

\n ", + "smithy.api#documentation": "

Deletes the cors configuration information set for the bucket.

\n

To use this operation, you must have permission to perform the\n s3:PutBucketCORS action. The bucket owner has this permission by default\n and can grant this permission to others.

\n

For information about cors, see Enabling Cross-Origin Resource Sharing in\n the Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketCors:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?cors", @@ -21526,7 +21526,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

This implementation of the DELETE action removes default encryption from the bucket.\n For information about the Amazon S3 default encryption feature, see Amazon S3 Default Bucket Encryption in the\n Amazon S3 User Guide.

\n

To use this operation, you must have permissions to perform the\n s3:PutEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This implementation of the DELETE action resets the default encryption for the\n bucket as server-side encryption with Amazon S3 managed keys (SSE-S3). For information about the\n bucket default encryption feature, see Amazon S3 Bucket Default Encryption\n in the Amazon S3 User Guide.

\n

To use this operation, you must have permissions to perform the\n s3:PutEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketEncryption:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?encryption", @@ -21569,7 +21569,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to\n DeleteBucketIntelligentTieringConfiguration include:

\n ", + "smithy.api#documentation": "

Deletes the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to DeleteBucketIntelligentTieringConfiguration include:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?intelligent-tiering", @@ -21613,7 +21613,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes an inventory configuration (identified by the inventory ID) from the\n bucket.

\n

To use this operation, you must have permissions to perform the\n s3:PutInventoryConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory.

\n

Operations related to DeleteBucketInventoryConfiguration include:

\n ", + "smithy.api#documentation": "

Deletes an inventory configuration (identified by the inventory ID) from the\n bucket.

\n

To use this operation, you must have permissions to perform the\n s3:PutInventoryConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory.

\n

Operations related to DeleteBucketInventoryConfiguration include:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?inventory", @@ -21664,7 +21664,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes the lifecycle configuration from the specified bucket. Amazon S3 removes all the\n lifecycle configuration rules in the lifecycle subresource associated with the bucket. Your\n objects never expire, and Amazon S3 no longer automatically deletes any objects on the basis of\n rules contained in the deleted lifecycle configuration.

\n

To use this operation, you must have permission to perform the\n s3:PutLifecycleConfiguration action. By default, the bucket owner has this\n permission and the bucket owner can grant this permission to others.

\n

There is usually some time lag before lifecycle configuration deletion is fully\n propagated to all the Amazon S3 systems.

\n

For more information about the object expiration, see Elements to\n Describe Lifecycle Actions.

\n

Related actions include:

\n ", + "smithy.api#documentation": "

Deletes the lifecycle configuration from the specified bucket. Amazon S3 removes all the\n lifecycle configuration rules in the lifecycle subresource associated with the bucket. Your\n objects never expire, and Amazon S3 no longer automatically deletes any objects on the basis of\n rules contained in the deleted lifecycle configuration.

\n

To use this operation, you must have permission to perform the\n s3:PutLifecycleConfiguration action. By default, the bucket owner has this\n permission and the bucket owner can grant this permission to others.

\n

There is usually some time lag before lifecycle configuration deletion is fully\n propagated to all the Amazon S3 systems.

\n

For more information about the object expiration, see Elements to Describe Lifecycle Actions.

\n

Related actions include:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?lifecycle", @@ -21707,7 +21707,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes a metrics configuration for the Amazon CloudWatch request metrics (specified by the\n metrics configuration ID) from the bucket. Note that this doesn't include the daily storage\n metrics.

\n

To use this operation, you must have permissions to perform the\n s3:PutMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring Metrics with Amazon CloudWatch.

\n

The following operations are related to\n DeleteBucketMetricsConfiguration:

\n ", + "smithy.api#documentation": "

Deletes a metrics configuration for the Amazon CloudWatch request metrics (specified by the\n metrics configuration ID) from the bucket. Note that this doesn't include the daily storage\n metrics.

\n

To use this operation, you must have permissions to perform the\n s3:PutMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring Metrics with\n Amazon CloudWatch.

\n

The following operations are related to\n DeleteBucketMetricsConfiguration:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?metrics", @@ -21732,7 +21732,7 @@ "Id": { "target": "com.amazonaws.s3#MetricsId", "traits": { - "smithy.api#documentation": "

The ID used to identify the metrics configuration.

", + "smithy.api#documentation": "

The ID used to identify the metrics configuration. The ID has a 64 character limit and\n can only contain letters, numbers, periods, dashes, and underscores.

", "smithy.api#httpQuery": "id", "smithy.api#required": {} } @@ -21758,7 +21758,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Removes OwnershipControls for an Amazon S3 bucket. To use this operation, you\n must have the s3:PutBucketOwnershipControls permission. For more information\n about Amazon S3 permissions, see Specifying\n Permissions in a Policy.

\n

For information about Amazon S3 Object Ownership, see Using Object Ownership.

\n

The following operations are related to\n DeleteBucketOwnershipControls:

\n ", + "smithy.api#documentation": "

Removes OwnershipControls for an Amazon S3 bucket. To use this operation, you\n must have the s3:PutBucketOwnershipControls permission. For more information\n about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n

For information about Amazon S3 Object Ownership, see Using Object Ownership.

\n

The following operations are related to\n DeleteBucketOwnershipControls:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?ownershipControls", @@ -21801,7 +21801,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

This implementation of the DELETE action uses the policy subresource to delete the\n policy of a specified bucket. If you are using an identity other than the root user of the\n Amazon Web Services account that owns the bucket, the calling identity must have the\n DeleteBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account to use this operation.

\n

If you don't have DeleteBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

As a security precaution, the root user of the Amazon Web Services account that owns a bucket can\n always use this operation, even if the policy explicitly denies the root user the\n ability to perform this action.

\n
\n

For more information about bucket policies, see Using Bucket Policies and\n UserPolicies.

\n

The following operations are related to DeleteBucketPolicy\n

\n ", + "smithy.api#documentation": "

This implementation of the DELETE action uses the policy subresource to delete the\n policy of a specified bucket. If you are using an identity other than the root user of the\n Amazon Web Services account that owns the bucket, the calling identity must have the\n DeleteBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account to use this operation.

\n

If you don't have DeleteBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

To ensure that bucket owners don't inadvertently lock themselves out of their own\n buckets, the root principal in a bucket owner's Amazon Web Services account can perform the\n GetBucketPolicy, PutBucketPolicy, and\n DeleteBucketPolicy API actions, even if their bucket policy explicitly\n denies the root principal's access. Bucket owner root principals can only be blocked from performing \n these API actions by VPC endpoint policies and Amazon Web Services Organizations policies.

\n
\n

For more information about bucket policies, see Using Bucket Policies and\n UserPolicies.

\n

The following operations are related to DeleteBucketPolicy\n

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?policy", @@ -21844,7 +21844,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes the replication configuration from the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:PutReplicationConfiguration action. The bucket owner has these\n permissions by default and can grant it to others. For more information about permissions,\n see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n \n

It can take a while for the deletion of a replication configuration to fully\n propagate.

\n
\n

For information about replication configuration, see Replication in the Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketReplication:

\n ", + "smithy.api#documentation": "

Deletes the replication configuration from the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:PutReplicationConfiguration action. The bucket owner has these\n permissions by default and can grant it to others. For more information about permissions,\n see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n \n

It can take a while for the deletion of a replication configuration to fully\n propagate.

\n
\n

For information about replication configuration, see Replication in the\n Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketReplication:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?replication", @@ -22084,7 +22084,7 @@ "target": "com.amazonaws.s3#DeleteObjectOutput" }, "traits": { - "smithy.api#documentation": "

Removes the null version (if there is one) of an object and inserts a delete marker,\n which becomes the latest version of the object. If there isn't a null version, Amazon S3 does\n not remove any objects but will still respond that the command was successful.

\n

To remove a specific version, you must be the bucket owner and you must use the version\n Id subresource. Using this subresource permanently deletes the version. If the object\n deleted is a delete marker, Amazon S3 sets the response header,\n x-amz-delete-marker, to true.

\n

If the object you want to delete is in a bucket where the bucket versioning\n configuration is MFA Delete enabled, you must include the x-amz-mfa request\n header in the DELETE versionId request. Requests that include\n x-amz-mfa must use HTTPS.

\n

For more information about MFA Delete, see Using MFA Delete. To see sample requests that use versioning, see Sample Request.

\n

You can delete objects by explicitly calling DELETE Object or configure its\n lifecycle (PutBucketLifecycle) to\n enable Amazon S3 to remove them for you. If you want to block users or accounts from removing or\n deleting objects from your bucket, you must deny them the s3:DeleteObject,\n s3:DeleteObjectVersion, and s3:PutLifeCycleConfiguration\n actions.

\n

The following action is related to DeleteObject:

\n ", + "smithy.api#documentation": "

Removes the null version (if there is one) of an object and inserts a delete marker,\n which becomes the latest version of the object. If there isn't a null version, Amazon S3 does\n not remove any objects but will still respond that the command was successful.

\n

To remove a specific version, you must use the version Id subresource. Using this\n subresource permanently deletes the version. If the object deleted is a delete marker, Amazon S3\n sets the response header, x-amz-delete-marker, to true.

\n

If the object you want to delete is in a bucket where the bucket versioning\n configuration is MFA Delete enabled, you must include the x-amz-mfa request\n header in the DELETE versionId request. Requests that include\n x-amz-mfa must use HTTPS.

\n

For more information about MFA Delete, see Using MFA Delete. To see sample\n requests that use versioning, see Sample\n Request.

\n

You can delete objects by explicitly calling DELETE Object or configure its lifecycle\n (PutBucketLifecycle) to enable Amazon S3 to remove them for you. If you want to block\n users or accounts from removing or deleting objects from your bucket, you must deny them\n the s3:DeleteObject, s3:DeleteObjectVersion, and\n s3:PutLifeCycleConfiguration actions.

\n

The following action is related to DeleteObject:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}/{Key+}?x-id=DeleteObject", @@ -22127,7 +22127,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name of the bucket containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name of the bucket containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -22167,7 +22167,7 @@ "target": "com.amazonaws.s3#BypassGovernanceRetention", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether S3 Object Lock should bypass Governance-mode restrictions to process\n this operation. To use this header, you must have the s3:BypassGovernanceRetention\n permission.

", + "smithy.api#documentation": "

Indicates whether S3 Object Lock should bypass Governance-mode restrictions to process\n this operation. To use this header, you must have the\n s3:BypassGovernanceRetention permission.

", "smithy.api#httpHeader": "x-amz-bypass-governance-retention" } }, @@ -22192,7 +22192,7 @@ "target": "com.amazonaws.s3#DeleteObjectTaggingOutput" }, "traits": { - "smithy.api#documentation": "

Removes the entire tag set from the specified object. For more information about\n managing object tags, see Object\n Tagging.

\n

To use this operation, you must have permission to perform the\n s3:DeleteObjectTagging action.

\n

To delete tags of a specific object version, add the versionId query\n parameter in the request. You will need permission for the\n s3:DeleteObjectVersionTagging action.

\n

The following operations are related to\n DeleteBucketMetricsConfiguration:

\n ", + "smithy.api#documentation": "

Removes the entire tag set from the specified object. For more information about\n managing object tags, see Object Tagging.

\n

To use this operation, you must have permission to perform the\n s3:DeleteObjectTagging action.

\n

To delete tags of a specific object version, add the versionId query\n parameter in the request. You will need permission for the\n s3:DeleteObjectVersionTagging action.

\n

The following operations are related to DeleteObjectTagging:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}/{Key+}?tagging", @@ -22221,7 +22221,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the objects from which to remove the tags.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the objects from which to remove the tags.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -22269,7 +22269,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

This action enables you to delete multiple objects from a bucket using a single HTTP\n request. If you know the object keys that you want to delete, then this action provides\n a suitable alternative to sending individual delete requests, reducing per-request\n overhead.

\n

The request contains a list of up to 1000 keys that you want to delete. In the XML, you\n provide the object key names, and optionally, version IDs if you want to delete a specific\n version of the object from a versioning-enabled bucket. For each key, Amazon S3 performs a\n delete action and returns the result of that delete, success, or failure, in the\n response. Note that if the object specified in the request is not found, Amazon S3 returns the\n result as deleted.

\n

The action supports two modes for the response: verbose and quiet. By default, the\n action uses verbose mode in which the response includes the result of deletion of each\n key in your request. In quiet mode the response includes only keys where the delete\n action encountered an error. For a successful deletion, the action does not return\n any information about the delete in the response body.

\n

When performing this action on an MFA Delete enabled bucket, that attempts to delete\n any versioned objects, you must include an MFA token. If you do not provide one, the entire\n request will fail, even if there are non-versioned objects you are trying to delete. If you\n provide an invalid token, whether there are versioned keys in the request or not, the\n entire Multi-Object Delete request will fail. For information about MFA Delete, see MFA\n Delete.

\n

Finally, the Content-MD5 header is required for all Multi-Object Delete requests. Amazon\n S3 uses the header value to ensure that your request body has not been altered in\n transit.

\n

The following operations are related to DeleteObjects:

\n ", + "smithy.api#documentation": "

This action enables you to delete multiple objects from a bucket using a single HTTP\n request. If you know the object keys that you want to delete, then this action provides a\n suitable alternative to sending individual delete requests, reducing per-request\n overhead.

\n

The request contains a list of up to 1000 keys that you want to delete. In the XML, you\n provide the object key names, and optionally, version IDs if you want to delete a specific\n version of the object from a versioning-enabled bucket. For each key, Amazon S3 performs a\n delete action and returns the result of that delete, success, or failure, in the response.\n Note that if the object specified in the request is not found, Amazon S3 returns the result as\n deleted.

\n

The action supports two modes for the response: verbose and quiet. By default, the\n action uses verbose mode in which the response includes the result of deletion of each key\n in your request. In quiet mode the response includes only keys where the delete action\n encountered an error. For a successful deletion, the action does not return any information\n about the delete in the response body.

\n

When performing this action on an MFA Delete enabled bucket, that attempts to delete any\n versioned objects, you must include an MFA token. If you do not provide one, the entire\n request will fail, even if there are non-versioned objects you are trying to delete. If you\n provide an invalid token, whether there are versioned keys in the request or not, the\n entire Multi-Object Delete request will fail. For information about MFA Delete, see MFA\n Delete.

\n

Finally, the Content-MD5 header is required for all Multi-Object Delete requests. Amazon S3 uses the header value to ensure that your request body has not been altered in\n transit.

\n

The following operations are related to DeleteObjects:

\n ", "smithy.api#http": { "method": "POST", "uri": "/{Bucket}?delete&x-id=DeleteObjects", @@ -22313,7 +22313,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the objects to delete.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the objects to delete.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -22347,7 +22347,7 @@ "target": "com.amazonaws.s3#BypassGovernanceRetention", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether you want to delete this object even if it has a Governance-type Object\n Lock in place. To use this header, you must have the s3:BypassGovernanceRetention\n permission.

", + "smithy.api#documentation": "

Specifies whether you want to delete this object even if it has a Governance-type Object\n Lock in place. To use this header, you must have the\n s3:BypassGovernanceRetention permission.

", "smithy.api#httpHeader": "x-amz-bypass-governance-retention" } }, @@ -22361,7 +22361,7 @@ "ChecksumAlgorithm": { "target": "com.amazonaws.s3#ChecksumAlgorithm", "traits": { - "smithy.api#documentation": "

Indicates the algorithm used to create the checksum for the object when using the SDK. This header will not provide any\n additional functionality if not using the SDK. When sending this header, there must be a corresponding x-amz-checksum or\n x-amz-trailer header sent. Otherwise, Amazon S3 fails the request with the HTTP status code 400 Bad Request. For more\n information, see Checking object integrity in\n the Amazon S3 User Guide.

\n

If you provide an individual checksum, Amazon S3 ignores any provided\n ChecksumAlgorithm parameter.

\n

This checksum algorithm must be the same for all parts and it match the checksum\n value supplied in the CreateMultipartUpload request.

", + "smithy.api#documentation": "

Indicates the algorithm used to create the checksum for the object when using the SDK. This header will not provide any\n additional functionality if not using the SDK. When sending this header, there must be a corresponding x-amz-checksum or\n x-amz-trailer header sent. Otherwise, Amazon S3 fails the request with the HTTP status code 400 Bad Request. For more\n information, see Checking object integrity in\n the Amazon S3 User Guide.

\n

If you provide an individual checksum, Amazon S3 ignores any provided\n ChecksumAlgorithm parameter.

\n

This checksum algorithm must be the same for all parts and it match the checksum value\n supplied in the CreateMultipartUpload request.

", "smithy.api#httpHeader": "x-amz-sdk-checksum-algorithm" } } @@ -22379,7 +22379,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Removes the PublicAccessBlock configuration for an Amazon S3 bucket. To use this\n operation, you must have the s3:PutBucketPublicAccessBlock permission. For\n more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

The following operations are related to DeletePublicAccessBlock:

\n ", + "smithy.api#documentation": "

Removes the PublicAccessBlock configuration for an Amazon S3 bucket. To use this\n operation, you must have the s3:PutBucketPublicAccessBlock permission. For\n more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

The following operations are related to DeletePublicAccessBlock:

\n ", "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?publicAccessBlock", @@ -22464,14 +22464,14 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the bucket where you want Amazon S3 to store the results.

", + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the bucket where you want Amazon S3 to store the\n results.

", "smithy.api#required": {} } }, "Account": { "target": "com.amazonaws.s3#AccountId", "traits": { - "smithy.api#documentation": "

Destination bucket owner account ID. In a cross-account scenario, if you direct Amazon S3 to\n change replica ownership to the Amazon Web Services account that owns the destination bucket by specifying\n the AccessControlTranslation property, this is the account ID of the\n destination bucket owner. For more information, see Replication Additional\n Configuration: Changing the Replica Owner in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Destination bucket owner account ID. In a cross-account scenario, if you direct Amazon S3 to\n change replica ownership to the Amazon Web Services account that owns the destination bucket by\n specifying the AccessControlTranslation property, this is the account ID of\n the destination bucket owner. For more information, see Replication Additional\n Configuration: Changing the Replica Owner in the\n Amazon S3 User Guide.

" } }, "StorageClass": { @@ -22483,7 +22483,7 @@ "AccessControlTranslation": { "target": "com.amazonaws.s3#AccessControlTranslation", "traits": { - "smithy.api#documentation": "

Specify this only in a cross-account scenario (where source and destination bucket\n owners are not the same), and you want to change replica ownership to the Amazon Web Services account that\n owns the destination bucket. If this is not specified in the replication configuration, the\n replicas are owned by same Amazon Web Services account that owns the source object.

" + "smithy.api#documentation": "

Specify this only in a cross-account scenario (where source and destination bucket\n owners are not the same), and you want to change replica ownership to the Amazon Web Services account\n that owns the destination bucket. If this is not specified in the replication\n configuration, the replicas are owned by same Amazon Web Services account that owns the source\n object.

" } }, "EncryptionConfiguration": { @@ -22544,14 +22544,14 @@ "EncryptionType": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing job results in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing job results in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#required": {} } }, "KMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If the encryption type is aws:kms, this optional value specifies the ID of\n the symmetric customer managed key to use for encryption of job results. Amazon S3 only\n supports symmetric keys. For more information, see Using symmetric and\n asymmetric keys in the Amazon Web Services Key Management Service Developer\n Guide.

" + "smithy.api#documentation": "

If the encryption type is aws:kms, this optional value specifies the ID of\n the symmetric encryption customer managed key to use for encryption of job results. Amazon S3 only\n supports symmetric encryption KMS keys. For more information, see Asymmetric keys in Amazon Web Services KMS in the Amazon Web Services Key Management Service\n Developer Guide.

" } }, "KMSContext": { @@ -22571,7 +22571,7 @@ "ReplicaKmsKeyID": { "target": "com.amazonaws.s3#ReplicaKmsKeyID", "traits": { - "smithy.api#documentation": "

Specifies the ID (Key ARN or Alias ARN) of the customer managed Amazon Web Services KMS key\n stored in Amazon Web Services Key Management Service (KMS) for the destination bucket. Amazon S3 uses\n this key to encrypt replica objects. Amazon S3 only supports symmetric, customer managed KMS keys.\n For more information, see Using symmetric and\n asymmetric keys in the Amazon Web Services Key Management Service Developer Guide.

" + "smithy.api#documentation": "

Specifies the ID (Key ARN or Alias ARN) of the customer managed Amazon Web Services KMS key stored in\n Amazon Web Services Key Management Service (KMS) for the destination bucket. Amazon S3 uses this key to\n encrypt replica objects. Amazon S3 only supports symmetric encryption KMS keys. For more\n information, see Asymmetric keys in Amazon Web Services\n KMS in the Amazon Web Services Key Management Service Developer\n Guide.

" } } }, @@ -22610,7 +22610,7 @@ "Code": { "target": "com.amazonaws.s3#Code", "traits": { - "smithy.api#documentation": "

The error code is a string that uniquely identifies an error condition. It is meant to\n be read and understood by programs that detect and handle errors by type.

\n

\n Amazon S3 error codes\n

\n
    \n
  • \n
      \n
    • \n

      \n Code: AccessDenied

      \n
    • \n
    • \n

      \n Description: Access Denied

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AccountProblem

      \n
    • \n
    • \n

      \n Description: There is a problem with your Amazon Web Services account\n that prevents the action from completing successfully. Contact Amazon Web Services Support\n for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AllAccessDisabled

      \n
    • \n
    • \n

      \n Description: All access to this Amazon S3 resource has been\n disabled. Contact Amazon Web Services Support for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AmbiguousGrantByEmailAddress

      \n
    • \n
    • \n

      \n Description: The email address you provided is\n associated with more than one account.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AuthorizationHeaderMalformed

      \n
    • \n
    • \n

      \n Description: The authorization header you provided is\n invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BadDigest

      \n
    • \n
    • \n

      \n Description: The Content-MD5 you specified did not\n match what we received.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketAlreadyExists

      \n
    • \n
    • \n

      \n Description: The requested bucket name is not\n available. The bucket namespace is shared by all users of the system. Please\n select a different name and try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketAlreadyOwnedByYou

      \n
    • \n
    • \n

      \n Description: The bucket you tried to create already\n exists, and you own it. Amazon S3 returns this error in all Amazon Web Services Regions except in\n the North Virginia Region. For legacy compatibility, if you re-create an\n existing bucket that you already own in the North Virginia Region, Amazon S3 returns\n 200 OK and resets the bucket access control lists (ACLs).

      \n
    • \n
    • \n

      \n Code: 409 Conflict (in all Regions except the North\n Virginia Region)

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketNotEmpty

      \n
    • \n
    • \n

      \n Description: The bucket you tried to delete is not\n empty.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: CredentialsNotSupported

      \n
    • \n
    • \n

      \n Description: This request does not support\n credentials.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: CrossLocationLoggingProhibited

      \n
    • \n
    • \n

      \n Description: Cross-location logging not allowed.\n Buckets in one geographic location cannot log information to a bucket in\n another location.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: EntityTooSmall

      \n
    • \n
    • \n

      \n Description: Your proposed upload is smaller than the\n minimum allowed object size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: EntityTooLarge

      \n
    • \n
    • \n

      \n Description: Your proposed upload exceeds the maximum\n allowed object size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: ExpiredToken

      \n
    • \n
    • \n

      \n Description: The provided token has expired.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IllegalVersioningConfigurationException

      \n
    • \n
    • \n

      \n Description: Indicates that the versioning\n configuration specified in the request is invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IncompleteBody

      \n
    • \n
    • \n

      \n Description: You did not provide the number of bytes\n specified by the Content-Length HTTP header

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IncorrectNumberOfFilesInPostRequest

      \n
    • \n
    • \n

      \n Description: POST requires exactly one file upload per\n request.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InlineDataTooLarge

      \n
    • \n
    • \n

      \n Description: Inline data exceeds the maximum allowed\n size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InternalError

      \n
    • \n
    • \n

      \n Description: We encountered an internal error. Please\n try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 500 Internal Server Error

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidAccessKeyId

      \n
    • \n
    • \n

      \n Description: The Amazon Web Services access key ID you provided does\n not exist in our records.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidAddressingHeader

      \n
    • \n
    • \n

      \n Description: You must specify the Anonymous\n role.

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidArgument

      \n
    • \n
    • \n

      \n Description: Invalid Argument

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidBucketName

      \n
    • \n
    • \n

      \n Description: The specified bucket is not valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidBucketState

      \n
    • \n
    • \n

      \n Description: The request is not valid with the current\n state of the bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidDigest

      \n
    • \n
    • \n

      \n Description: The Content-MD5 you specified is not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidEncryptionAlgorithmError

      \n
    • \n
    • \n

      \n Description: The encryption request you specified is\n not valid. The valid value is AES256.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidLocationConstraint

      \n
    • \n
    • \n

      \n Description: The specified location constraint is not\n valid. For more information about Regions, see How to Select a\n Region for Your Buckets.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidObjectState

      \n
    • \n
    • \n

      \n Description: The action is not valid for the current\n state of the object.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPart

      \n
    • \n
    • \n

      \n Description: One or more of the specified parts could\n not be found. The part might not have been uploaded, or the specified entity\n tag might not have matched the part's entity tag.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPartOrder

      \n
    • \n
    • \n

      \n Description: The list of parts was not in ascending\n order. Parts list must be specified in order by part number.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPayer

      \n
    • \n
    • \n

      \n Description: All access to this object has been\n disabled. Please contact Amazon Web Services Support for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPolicyDocument

      \n
    • \n
    • \n

      \n Description: The content of the form does not meet the\n conditions specified in the policy document.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRange

      \n
    • \n
    • \n

      \n Description: The requested range cannot be\n satisfied.

      \n
    • \n
    • \n

      \n HTTP Status Code: 416 Requested Range Not\n Satisfiable

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Please use AWS4-HMAC-SHA256.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: SOAP requests must be made over an HTTPS\n connection.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported for buckets with non-DNS compliant names.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported for buckets with periods (.) in their names.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate endpoint only\n supports virtual style requests.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate is not configured\n on this bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate is disabled on\n this bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported on this bucket. Contact Amazon Web Services Support for more information.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration cannot be\n enabled on this bucket. Contact Amazon Web Services Support for more information.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidSecurity

      \n
    • \n
    • \n

      \n Description: The provided security credentials are not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidSOAPRequest

      \n
    • \n
    • \n

      \n Description: The SOAP request body is invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidStorageClass

      \n
    • \n
    • \n

      \n Description: The storage class you specified is not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidTargetBucketForLogging

      \n
    • \n
    • \n

      \n Description: The target bucket for logging does not\n exist, is not owned by you, or does not have the appropriate grants for the\n log-delivery group.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidToken

      \n
    • \n
    • \n

      \n Description: The provided token is malformed or\n otherwise invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidURI

      \n
    • \n
    • \n

      \n Description: Couldn't parse the specified URI.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: KeyTooLongError

      \n
    • \n
    • \n

      \n Description: Your key is too long.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedACLError

      \n
    • \n
    • \n

      \n Description: The XML you provided was not well-formed\n or did not validate against our published schema.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedPOSTRequest

      \n
    • \n
    • \n

      \n Description: The body of your POST request is not\n well-formed multipart/form-data.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedXML

      \n
    • \n
    • \n

      \n Description: This happens when the user sends malformed\n XML (XML that doesn't conform to the published XSD) for the configuration. The\n error message is, \"The XML you provided was not well-formed or did not validate\n against our published schema.\"

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MaxMessageLengthExceeded

      \n
    • \n
    • \n

      \n Description: Your request was too big.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MaxPostPreDataLengthExceededError

      \n
    • \n
    • \n

      \n Description: Your POST request fields preceding the\n upload file were too large.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MetadataTooLarge

      \n
    • \n
    • \n

      \n Description: Your metadata headers exceed the maximum\n allowed metadata size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MethodNotAllowed

      \n
    • \n
    • \n

      \n Description: The specified method is not allowed\n against this resource.

      \n
    • \n
    • \n

      \n HTTP Status Code: 405 Method Not Allowed

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingAttachment

      \n
    • \n
    • \n

      \n Description: A SOAP attachment was expected, but none\n were found.

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingContentLength

      \n
    • \n
    • \n

      \n Description: You must provide the Content-Length HTTP\n header.

      \n
    • \n
    • \n

      \n HTTP Status Code: 411 Length Required

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingRequestBodyError

      \n
    • \n
    • \n

      \n Description: This happens when the user sends an empty\n XML document as a request. The error message is, \"Request body is empty.\"\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingSecurityElement

      \n
    • \n
    • \n

      \n Description: The SOAP 1.1 request is missing a security\n element.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingSecurityHeader

      \n
    • \n
    • \n

      \n Description: Your request is missing a required\n header.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoLoggingStatusForKey

      \n
    • \n
    • \n

      \n Description: There is no such thing as a logging status\n subresource for a key.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchBucket

      \n
    • \n
    • \n

      \n Description: The specified bucket does not\n exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchBucketPolicy

      \n
    • \n
    • \n

      \n Description: The specified bucket does not have a\n bucket policy.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchKey

      \n
    • \n
    • \n

      \n Description: The specified key does not exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchLifecycleConfiguration

      \n
    • \n
    • \n

      \n Description: The lifecycle configuration does not\n exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload

      \n
    • \n
    • \n

      \n Description: The specified multipart upload does not\n exist. The upload ID might be invalid, or the multipart upload might have been\n aborted or completed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchVersion

      \n
    • \n
    • \n

      \n Description: Indicates that the version ID specified in\n the request does not match an existing version.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NotImplemented

      \n
    • \n
    • \n

      \n Description: A header you provided implies\n functionality that is not implemented.

      \n
    • \n
    • \n

      \n HTTP Status Code: 501 Not Implemented

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NotSignedUp

      \n
    • \n
    • \n

      \n Description: Your account is not signed up for the Amazon S3\n service. You must sign up before you can use Amazon S3. You can sign up at the\n following URL: Amazon S3\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: OperationAborted

      \n
    • \n
    • \n

      \n Description: A conflicting conditional action is\n currently in progress against this resource. Try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: PermanentRedirect

      \n
    • \n
    • \n

      \n Description: The bucket you are attempting to access\n must be addressed using the specified endpoint. Send all future requests to\n this endpoint.

      \n
    • \n
    • \n

      \n HTTP Status Code: 301 Moved Permanently

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: PreconditionFailed

      \n
    • \n
    • \n

      \n Description: At least one of the preconditions you\n specified did not hold.

      \n
    • \n
    • \n

      \n HTTP Status Code: 412 Precondition Failed

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: Redirect

      \n
    • \n
    • \n

      \n Description: Temporary redirect.

      \n
    • \n
    • \n

      \n HTTP Status Code: 307 Moved Temporarily

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RestoreAlreadyInProgress

      \n
    • \n
    • \n

      \n Description: Object restore is already in\n progress.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestIsNotMultiPartContent

      \n
    • \n
    • \n

      \n Description: Bucket POST must be of the enclosure-type\n multipart/form-data.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTimeout

      \n
    • \n
    • \n

      \n Description: Your socket connection to the server was\n not read from or written to within the timeout period.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTimeTooSkewed

      \n
    • \n
    • \n

      \n Description: The difference between the request time\n and the server's time is too large.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTorrentOfBucketError

      \n
    • \n
    • \n

      \n Description: Requesting the torrent file of a bucket is\n not permitted.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: SignatureDoesNotMatch

      \n
    • \n
    • \n

      \n Description: The request signature we calculated does\n not match the signature you provided. Check your Amazon Web Services secret access key and\n signing method. For more information, see REST Authentication and\n SOAP Authentication\n for details.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: ServiceUnavailable

      \n
    • \n
    • \n

      \n Description: Reduce your request rate.

      \n
    • \n
    • \n

      \n HTTP Status Code: 503 Service Unavailable

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: SlowDown

      \n
    • \n
    • \n

      \n Description: Reduce your request rate.

      \n
    • \n
    • \n

      \n HTTP Status Code: 503 Slow Down

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TemporaryRedirect

      \n
    • \n
    • \n

      \n Description: You are being redirected to the bucket\n while DNS updates.

      \n
    • \n
    • \n

      \n HTTP Status Code: 307 Moved Temporarily

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TokenRefreshRequired

      \n
    • \n
    • \n

      \n Description: The provided token must be\n refreshed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TooManyBuckets

      \n
    • \n
    • \n

      \n Description: You have attempted to create more buckets\n than allowed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UnexpectedContent

      \n
    • \n
    • \n

      \n Description: This request does not support\n content.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UnresolvableGrantByEmailAddress

      \n
    • \n
    • \n

      \n Description: The email address you provided does not\n match any account on record.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UserKeyMustBeSpecified

      \n
    • \n
    • \n

      \n Description: The bucket POST must contain the specified\n field name. If it is specified, check the order of the fields.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
\n

" + "smithy.api#documentation": "

The error code is a string that uniquely identifies an error condition. It is meant to\n be read and understood by programs that detect and handle errors by type. The following is\n a list of Amazon S3 error codes. For more information, see Error responses.

\n
    \n
  • \n
      \n
    • \n

      \n Code: AccessDenied

      \n
    • \n
    • \n

      \n Description: Access Denied

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AccountProblem

      \n
    • \n
    • \n

      \n Description: There is a problem with your Amazon Web Services account\n that prevents the action from completing successfully. Contact Amazon Web Services Support\n for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AllAccessDisabled

      \n
    • \n
    • \n

      \n Description: All access to this Amazon S3 resource has been\n disabled. Contact Amazon Web Services Support for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AmbiguousGrantByEmailAddress

      \n
    • \n
    • \n

      \n Description: The email address you provided is\n associated with more than one account.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: AuthorizationHeaderMalformed

      \n
    • \n
    • \n

      \n Description: The authorization header you provided is\n invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BadDigest

      \n
    • \n
    • \n

      \n Description: The Content-MD5 you specified did not\n match what we received.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketAlreadyExists

      \n
    • \n
    • \n

      \n Description: The requested bucket name is not\n available. The bucket namespace is shared by all users of the system. Please\n select a different name and try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketAlreadyOwnedByYou

      \n
    • \n
    • \n

      \n Description: The bucket you tried to create already\n exists, and you own it. Amazon S3 returns this error in all Amazon Web Services Regions except in\n the North Virginia Region. For legacy compatibility, if you re-create an\n existing bucket that you already own in the North Virginia Region, Amazon S3 returns\n 200 OK and resets the bucket access control lists (ACLs).

      \n
    • \n
    • \n

      \n Code: 409 Conflict (in all Regions except the North\n Virginia Region)

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: BucketNotEmpty

      \n
    • \n
    • \n

      \n Description: The bucket you tried to delete is not\n empty.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: CredentialsNotSupported

      \n
    • \n
    • \n

      \n Description: This request does not support\n credentials.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: CrossLocationLoggingProhibited

      \n
    • \n
    • \n

      \n Description: Cross-location logging not allowed.\n Buckets in one geographic location cannot log information to a bucket in\n another location.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: EntityTooSmall

      \n
    • \n
    • \n

      \n Description: Your proposed upload is smaller than the\n minimum allowed object size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: EntityTooLarge

      \n
    • \n
    • \n

      \n Description: Your proposed upload exceeds the maximum\n allowed object size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: ExpiredToken

      \n
    • \n
    • \n

      \n Description: The provided token has expired.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IllegalVersioningConfigurationException

      \n
    • \n
    • \n

      \n Description: Indicates that the versioning\n configuration specified in the request is invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IncompleteBody

      \n
    • \n
    • \n

      \n Description: You did not provide the number of bytes\n specified by the Content-Length HTTP header

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: IncorrectNumberOfFilesInPostRequest

      \n
    • \n
    • \n

      \n Description: POST requires exactly one file upload per\n request.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InlineDataTooLarge

      \n
    • \n
    • \n

      \n Description: Inline data exceeds the maximum allowed\n size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InternalError

      \n
    • \n
    • \n

      \n Description: We encountered an internal error. Please\n try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 500 Internal Server Error

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidAccessKeyId

      \n
    • \n
    • \n

      \n Description: The Amazon Web Services access key ID you provided does\n not exist in our records.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidAddressingHeader

      \n
    • \n
    • \n

      \n Description: You must specify the Anonymous\n role.

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidArgument

      \n
    • \n
    • \n

      \n Description: Invalid Argument

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidBucketName

      \n
    • \n
    • \n

      \n Description: The specified bucket is not valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidBucketState

      \n
    • \n
    • \n

      \n Description: The request is not valid with the current\n state of the bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidDigest

      \n
    • \n
    • \n

      \n Description: The Content-MD5 you specified is not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidEncryptionAlgorithmError

      \n
    • \n
    • \n

      \n Description: The encryption request you specified is\n not valid. The valid value is AES256.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidLocationConstraint

      \n
    • \n
    • \n

      \n Description: The specified location constraint is not\n valid. For more information about Regions, see How to Select\n a Region for Your Buckets.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidObjectState

      \n
    • \n
    • \n

      \n Description: The action is not valid for the current\n state of the object.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPart

      \n
    • \n
    • \n

      \n Description: One or more of the specified parts could\n not be found. The part might not have been uploaded, or the specified entity\n tag might not have matched the part's entity tag.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPartOrder

      \n
    • \n
    • \n

      \n Description: The list of parts was not in ascending\n order. Parts list must be specified in order by part number.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPayer

      \n
    • \n
    • \n

      \n Description: All access to this object has been\n disabled. Please contact Amazon Web Services Support for further assistance.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidPolicyDocument

      \n
    • \n
    • \n

      \n Description: The content of the form does not meet the\n conditions specified in the policy document.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRange

      \n
    • \n
    • \n

      \n Description: The requested range cannot be\n satisfied.

      \n
    • \n
    • \n

      \n HTTP Status Code: 416 Requested Range Not\n Satisfiable

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Please use\n AWS4-HMAC-SHA256.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: SOAP requests must be made over an HTTPS\n connection.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported for buckets with non-DNS compliant names.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported for buckets with periods (.) in their names.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate endpoint only\n supports virtual style requests.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate is not configured\n on this bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Accelerate is disabled on\n this bucket.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration is not\n supported on this bucket. Contact Amazon Web Services Support for more information.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest

      \n
    • \n
    • \n

      \n Description: Amazon S3 Transfer Acceleration cannot be\n enabled on this bucket. Contact Amazon Web Services Support for more information.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n Code: N/A

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidSecurity

      \n
    • \n
    • \n

      \n Description: The provided security credentials are not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidSOAPRequest

      \n
    • \n
    • \n

      \n Description: The SOAP request body is invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidStorageClass

      \n
    • \n
    • \n

      \n Description: The storage class you specified is not\n valid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidTargetBucketForLogging

      \n
    • \n
    • \n

      \n Description: The target bucket for logging does not\n exist, is not owned by you, or does not have the appropriate grants for the\n log-delivery group.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidToken

      \n
    • \n
    • \n

      \n Description: The provided token is malformed or\n otherwise invalid.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidURI

      \n
    • \n
    • \n

      \n Description: Couldn't parse the specified URI.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: KeyTooLongError

      \n
    • \n
    • \n

      \n Description: Your key is too long.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedACLError

      \n
    • \n
    • \n

      \n Description: The XML you provided was not well-formed\n or did not validate against our published schema.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedPOSTRequest

      \n
    • \n
    • \n

      \n Description: The body of your POST request is not\n well-formed multipart/form-data.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedXML

      \n
    • \n
    • \n

      \n Description: This happens when the user sends malformed\n XML (XML that doesn't conform to the published XSD) for the configuration. The\n error message is, \"The XML you provided was not well-formed or did not validate\n against our published schema.\"

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MaxMessageLengthExceeded

      \n
    • \n
    • \n

      \n Description: Your request was too big.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MaxPostPreDataLengthExceededError

      \n
    • \n
    • \n

      \n Description: Your POST request fields preceding the\n upload file were too large.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MetadataTooLarge

      \n
    • \n
    • \n

      \n Description: Your metadata headers exceed the maximum\n allowed metadata size.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MethodNotAllowed

      \n
    • \n
    • \n

      \n Description: The specified method is not allowed\n against this resource.

      \n
    • \n
    • \n

      \n HTTP Status Code: 405 Method Not Allowed

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingAttachment

      \n
    • \n
    • \n

      \n Description: A SOAP attachment was expected, but none\n were found.

      \n
    • \n
    • \n

      \n HTTP Status Code: N/A

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingContentLength

      \n
    • \n
    • \n

      \n Description: You must provide the Content-Length HTTP\n header.

      \n
    • \n
    • \n

      \n HTTP Status Code: 411 Length Required

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingRequestBodyError

      \n
    • \n
    • \n

      \n Description: This happens when the user sends an empty\n XML document as a request. The error message is, \"Request body is empty.\"\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingSecurityElement

      \n
    • \n
    • \n

      \n Description: The SOAP 1.1 request is missing a security\n element.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MissingSecurityHeader

      \n
    • \n
    • \n

      \n Description: Your request is missing a required\n header.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoLoggingStatusForKey

      \n
    • \n
    • \n

      \n Description: There is no such thing as a logging status\n subresource for a key.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchBucket

      \n
    • \n
    • \n

      \n Description: The specified bucket does not\n exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchBucketPolicy

      \n
    • \n
    • \n

      \n Description: The specified bucket does not have a\n bucket policy.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchKey

      \n
    • \n
    • \n

      \n Description: The specified key does not exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchLifecycleConfiguration

      \n
    • \n
    • \n

      \n Description: The lifecycle configuration does not\n exist.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload

      \n
    • \n
    • \n

      \n Description: The specified multipart upload does not\n exist. The upload ID might be invalid, or the multipart upload might have been\n aborted or completed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NoSuchVersion

      \n
    • \n
    • \n

      \n Description: Indicates that the version ID specified in\n the request does not match an existing version.

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NotImplemented

      \n
    • \n
    • \n

      \n Description: A header you provided implies\n functionality that is not implemented.

      \n
    • \n
    • \n

      \n HTTP Status Code: 501 Not Implemented

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: NotSignedUp

      \n
    • \n
    • \n

      \n Description: Your account is not signed up for the Amazon S3\n service. You must sign up before you can use Amazon S3. You can sign up at the\n following URL: Amazon S3\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: OperationAborted

      \n
    • \n
    • \n

      \n Description: A conflicting conditional action is\n currently in progress against this resource. Try again.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: PermanentRedirect

      \n
    • \n
    • \n

      \n Description: The bucket you are attempting to access\n must be addressed using the specified endpoint. Send all future requests to\n this endpoint.

      \n
    • \n
    • \n

      \n HTTP Status Code: 301 Moved Permanently

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: PreconditionFailed

      \n
    • \n
    • \n

      \n Description: At least one of the preconditions you\n specified did not hold.

      \n
    • \n
    • \n

      \n HTTP Status Code: 412 Precondition Failed

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: Redirect

      \n
    • \n
    • \n

      \n Description: Temporary redirect.

      \n
    • \n
    • \n

      \n HTTP Status Code: 307 Moved Temporarily

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RestoreAlreadyInProgress

      \n
    • \n
    • \n

      \n Description: Object restore is already in\n progress.

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestIsNotMultiPartContent

      \n
    • \n
    • \n

      \n Description: Bucket POST must be of the enclosure-type\n multipart/form-data.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTimeout

      \n
    • \n
    • \n

      \n Description: Your socket connection to the server was\n not read from or written to within the timeout period.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTimeTooSkewed

      \n
    • \n
    • \n

      \n Description: The difference between the request time\n and the server's time is too large.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: RequestTorrentOfBucketError

      \n
    • \n
    • \n

      \n Description: Requesting the torrent file of a bucket is\n not permitted.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: SignatureDoesNotMatch

      \n
    • \n
    • \n

      \n Description: The request signature we calculated does\n not match the signature you provided. Check your Amazon Web Services secret access key and\n signing method. For more information, see REST\n Authentication and SOAP\n Authentication for details.

      \n
    • \n
    • \n

      \n HTTP Status Code: 403 Forbidden

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: ServiceUnavailable

      \n
    • \n
    • \n

      \n Description: Service is unable to handle\n request.

      \n
    • \n
    • \n

      \n HTTP Status Code: 503 Service Unavailable

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: SlowDown

      \n
    • \n
    • \n

      \n Description: Reduce your request rate.

      \n
    • \n
    • \n

      \n HTTP Status Code: 503 Slow Down

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Server

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TemporaryRedirect

      \n
    • \n
    • \n

      \n Description: You are being redirected to the bucket\n while DNS updates.

      \n
    • \n
    • \n

      \n HTTP Status Code: 307 Moved Temporarily

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TokenRefreshRequired

      \n
    • \n
    • \n

      \n Description: The provided token must be\n refreshed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: TooManyBuckets

      \n
    • \n
    • \n

      \n Description: You have attempted to create more buckets\n than allowed.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UnexpectedContent

      \n
    • \n
    • \n

      \n Description: This request does not support\n content.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UnresolvableGrantByEmailAddress

      \n
    • \n
    • \n

      \n Description: The email address you provided does not\n match any account on record.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: UserKeyMustBeSpecified

      \n
    • \n
    • \n

      \n Description: The bucket POST must contain the specified\n field name. If it is specified, check the order of the fields.

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
\n

" } }, "Message": { @@ -22840,7 +22840,7 @@ "Status": { "target": "com.amazonaws.s3#ExistingObjectReplicationStatus", "traits": { - "smithy.api#documentation": "

", + "smithy.api#documentation": "

Specifies whether Amazon S3 replicates existing source bucket objects.

", "smithy.api#required": {} } } @@ -23008,7 +23008,7 @@ "target": "com.amazonaws.s3#GetBucketAccelerateConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

This implementation of the GET action uses the accelerate subresource to\n return the Transfer Acceleration state of a bucket, which is either Enabled or\n Suspended. Amazon S3 Transfer Acceleration is a bucket-level feature that\n enables you to perform faster data transfers to and from Amazon S3.

\n

To use this operation, you must have permission to perform the\n s3:GetAccelerateConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

You set the Transfer Acceleration state of an existing bucket to Enabled or\n Suspended by using the PutBucketAccelerateConfiguration operation.

\n

A GET accelerate request does not return a state value for a bucket that\n has no transfer acceleration state. A bucket has no Transfer Acceleration state if a state\n has never been set on the bucket.

\n

For more information about transfer acceleration, see Transfer Acceleration in the\n Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This implementation of the GET action uses the accelerate subresource to\n return the Transfer Acceleration state of a bucket, which is either Enabled or\n Suspended. Amazon S3 Transfer Acceleration is a bucket-level feature that\n enables you to perform faster data transfers to and from Amazon S3.

\n

To use this operation, you must have permission to perform the\n s3:GetAccelerateConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n

You set the Transfer Acceleration state of an existing bucket to Enabled or\n Suspended by using the PutBucketAccelerateConfiguration operation.

\n

A GET accelerate request does not return a state value for a bucket that\n has no transfer acceleration state. A bucket has no Transfer Acceleration state if a state\n has never been set on the bucket.

\n

For more information about transfer acceleration, see Transfer Acceleration in\n the Amazon S3 User Guide.

\n

The following operations are related to GetBucketAccelerateConfiguration:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?accelerate", @@ -23066,7 +23066,7 @@ "target": "com.amazonaws.s3#GetBucketAclOutput" }, "traits": { - "smithy.api#documentation": "

This implementation of the GET action uses the acl\n subresource to return the access control list (ACL) of a bucket. To use GET to\n return the ACL of the bucket, you must have READ_ACP access to the bucket. If\n READ_ACP permission is granted to the anonymous user, you can return the\n ACL of the bucket without using an authorization header.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, \n requests to read ACLs are still supported and return the bucket-owner-full-control \n ACL with the owner being the account that created the bucket. For more information, see \n \n Controlling object ownership and disabling ACLs in the Amazon S3 User Guide.

\n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This implementation of the GET action uses the acl subresource\n to return the access control list (ACL) of a bucket. To use GET to return the\n ACL of the bucket, you must have READ_ACP access to the bucket. If\n READ_ACP permission is granted to the anonymous user, you can return the\n ACL of the bucket without using an authorization header.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership,\n requests to read ACLs are still supported and return the\n bucket-owner-full-control ACL with the owner being the account that\n created the bucket. For more information, see Controlling object\n ownership and disabling ACLs in the\n Amazon S3 User Guide.

\n
\n

The following operations are related to GetBucketAcl:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?acl", @@ -23102,7 +23102,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

Specifies the S3 bucket whose ACL is being requested.

", + "smithy.api#documentation": "

Specifies the S3 bucket whose ACL is being requested.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -23131,7 +23131,7 @@ "target": "com.amazonaws.s3#GetBucketAnalyticsConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

This implementation of the GET action returns an analytics configuration (identified\n by the analytics configuration ID) from the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:GetAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

For information about Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis in the Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This implementation of the GET action returns an analytics configuration (identified by\n the analytics configuration ID) from the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:GetAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n

For information about Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis in the Amazon S3 User Guide.

\n

The following operations are related to GetBucketAnalyticsConfiguration:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?analytics&x-id=GetBucketAnalyticsConfiguration", @@ -23197,7 +23197,7 @@ "target": "com.amazonaws.s3#GetBucketCorsOutput" }, "traits": { - "smithy.api#documentation": "

Returns the Cross-Origin Resource Sharing (CORS) configuration information set for the\n bucket.

\n

To use this operation, you must have permission to perform the\n s3:GetBucketCORS action. By default, the bucket owner has this permission\n and can grant it to others.

\n

For more information about CORS, see Enabling Cross-Origin Resource\n Sharing.

\n

The following operations are related to GetBucketCors:

\n ", + "smithy.api#documentation": "

Returns the Cross-Origin Resource Sharing (CORS) configuration information set for the\n bucket.

\n

To use this operation, you must have permission to perform the\n s3:GetBucketCORS action. By default, the bucket owner has this permission\n and can grant it to others.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n

For more information about CORS, see Enabling Cross-Origin Resource\n Sharing.

\n

The following operations are related to GetBucketCors:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?cors", @@ -23228,7 +23228,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name for which to get the cors configuration.

", + "smithy.api#documentation": "

The bucket name for which to get the cors configuration.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -23257,7 +23257,7 @@ "target": "com.amazonaws.s3#GetBucketEncryptionOutput" }, "traits": { - "smithy.api#documentation": "

Returns the default encryption configuration for an Amazon S3 bucket. If the bucket does not\n have a default encryption configuration, GetBucketEncryption returns\n ServerSideEncryptionConfigurationNotFoundError.

\n

For information about the Amazon S3 default encryption feature, see Amazon S3 Default Bucket Encryption.

\n

To use this operation, you must have permission to perform the\n s3:GetEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

The following operations are related to GetBucketEncryption:

\n ", + "smithy.api#documentation": "

Returns the default encryption configuration for an Amazon S3 bucket. By default, all buckets have a default encryption configuration that\n uses server-side encryption with Amazon S3 managed keys (SSE-S3). For information\n about the bucket default encryption feature, see Amazon S3 Bucket\n Default Encryption in the Amazon S3 User Guide.

\n

To use this operation, you must have permission to perform the\n s3:GetEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

The following operations are related to GetBucketEncryption:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?encryption", @@ -23314,7 +23314,7 @@ "target": "com.amazonaws.s3#GetBucketIntelligentTieringConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

Gets the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to\n GetBucketIntelligentTieringConfiguration include:

\n ", + "smithy.api#documentation": "

Gets the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to GetBucketIntelligentTieringConfiguration include:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?intelligent-tiering&x-id=GetBucketIntelligentTieringConfiguration", @@ -23373,7 +23373,7 @@ "target": "com.amazonaws.s3#GetBucketInventoryConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

Returns an inventory configuration (identified by the inventory configuration ID) from\n the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:GetInventoryConfiguration action. The bucket owner has this permission\n by default and can grant this permission to others. For more information about permissions,\n see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory.

\n

The following operations are related to\n GetBucketInventoryConfiguration:

\n ", + "smithy.api#documentation": "

Returns an inventory configuration (identified by the inventory configuration ID) from\n the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:GetInventoryConfiguration action. The bucket owner has this permission\n by default and can grant this permission to others. For more information about permissions,\n see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory.

\n

The following operations are related to\n GetBucketInventoryConfiguration:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?inventory&x-id=GetBucketInventoryConfiguration", @@ -23439,7 +23439,7 @@ "target": "com.amazonaws.s3#GetBucketLifecycleConfigurationOutput" }, "traits": { - "smithy.api#documentation": "\n

Bucket lifecycle configuration now supports specifying a lifecycle rule using an\n object key name prefix, one or more object tags, or a combination of both. Accordingly,\n this section describes the latest API. The response describes the new filter element\n that you can use to specify a filter to select a subset of objects to which the rule\n applies. If you are using a previous version of the lifecycle configuration, it still\n works. For the earlier action, see GetBucketLifecycle.

\n
\n

Returns the lifecycle configuration information set on the bucket. For information about\n lifecycle configuration, see Object\n Lifecycle Management.

\n

To use this operation, you must have permission to perform the\n s3:GetLifecycleConfiguration action. The bucket owner has this permission,\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

\n GetBucketLifecycleConfiguration has the following special error:

\n
    \n
  • \n

    Error code: NoSuchLifecycleConfiguration\n

    \n
      \n
    • \n

      Description: The lifecycle configuration does not exist.

      \n
    • \n
    • \n

      HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
\n

The following operations are related to\n GetBucketLifecycleConfiguration:

\n ", + "smithy.api#documentation": "\n

Bucket lifecycle configuration now supports specifying a lifecycle rule using an\n object key name prefix, one or more object tags, or a combination of both. Accordingly,\n this section describes the latest API. The response describes the new filter element\n that you can use to specify a filter to select a subset of objects to which the rule\n applies. If you are using a previous version of the lifecycle configuration, it still\n works. For the earlier action, see GetBucketLifecycle.

\n
\n

Returns the lifecycle configuration information set on the bucket. For information about\n lifecycle configuration, see Object Lifecycle\n Management.

\n

To use this operation, you must have permission to perform the\n s3:GetLifecycleConfiguration action. The bucket owner has this permission,\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

\n GetBucketLifecycleConfiguration has the following special error:

\n
    \n
  • \n

    Error code: NoSuchLifecycleConfiguration\n

    \n
      \n
    • \n

      Description: The lifecycle configuration does not exist.

      \n
    • \n
    • \n

      HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
\n

The following operations are related to\n GetBucketLifecycleConfiguration:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?lifecycle", @@ -23500,7 +23500,7 @@ }, "traits": { "aws.customizations#s3UnwrappedXmlOutput": {}, - "smithy.api#documentation": "

Returns the Region the bucket resides in. You set the bucket's Region using the\n LocationConstraint request parameter in a CreateBucket\n request. For more information, see CreateBucket.

\n

To use this implementation of the operation, you must be the bucket owner.

\n

To use this API against an access point, provide the alias of the access point in place of the bucket name.

\n

The following operations are related to GetBucketLocation:

\n ", + "smithy.api#documentation": "

Returns the Region the bucket resides in. You set the bucket's Region using the\n LocationConstraint request parameter in a CreateBucket\n request. For more information, see CreateBucket.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n \n

We recommend that you use HeadBucket to return the Region\n that a bucket resides in. For backward compatibility, Amazon S3 continues to support\n GetBucketLocation.

\n
\n

The following operations are related to GetBucketLocation:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?location", @@ -23514,7 +23514,7 @@ "LocationConstraint": { "target": "com.amazonaws.s3#BucketLocationConstraint", "traits": { - "smithy.api#documentation": "

Specifies the Region where the bucket resides. For a list of all the Amazon S3 supported\n location constraints by Region, see Regions and Endpoints.\n Buckets in Region us-east-1 have a LocationConstraint of\n null.

" + "smithy.api#documentation": "

Specifies the Region where the bucket resides. For a list of all the Amazon S3 supported\n location constraints by Region, see Regions and Endpoints. Buckets in\n Region us-east-1 have a LocationConstraint of null.

" } } }, @@ -23529,7 +23529,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket for which to get the location.

", + "smithy.api#documentation": "

The name of the bucket for which to get the location.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -23558,7 +23558,7 @@ "target": "com.amazonaws.s3#GetBucketLoggingOutput" }, "traits": { - "smithy.api#documentation": "

Returns the logging status of a bucket and the permissions users have to view and modify\n that status. To use GET, you must be the bucket owner.

\n

The following operations are related to GetBucketLogging:

\n ", + "smithy.api#documentation": "

Returns the logging status of a bucket and the permissions users have to view and modify\n that status.

\n

The following operations are related to GetBucketLogging:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?logging", @@ -23613,7 +23613,7 @@ "target": "com.amazonaws.s3#GetBucketMetricsConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

Gets a metrics configuration (specified by the metrics configuration ID) from the\n bucket. Note that this doesn't include the daily storage metrics.

\n

To use this operation, you must have permissions to perform the\n s3:GetMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring Metrics with Amazon\n CloudWatch.

\n

The following operations are related to\n GetBucketMetricsConfiguration:

\n ", + "smithy.api#documentation": "

Gets a metrics configuration (specified by the metrics configuration ID) from the\n bucket. Note that this doesn't include the daily storage metrics.

\n

To use this operation, you must have permissions to perform the\n s3:GetMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring\n Metrics with Amazon CloudWatch.

\n

The following operations are related to\n GetBucketMetricsConfiguration:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?metrics&x-id=GetBucketMetricsConfiguration", @@ -23653,7 +23653,7 @@ "Id": { "target": "com.amazonaws.s3#MetricsId", "traits": { - "smithy.api#documentation": "

The ID used to identify the metrics configuration.

", + "smithy.api#documentation": "

The ID used to identify the metrics configuration. The ID has a 64 character limit and\n can only contain letters, numbers, periods, dashes, and underscores.

", "smithy.api#httpQuery": "id", "smithy.api#required": {} } @@ -23679,7 +23679,7 @@ "target": "com.amazonaws.s3#NotificationConfiguration" }, "traits": { - "smithy.api#documentation": "

Returns the notification configuration of a bucket.

\n

If notifications are not enabled on the bucket, the action returns an empty\n NotificationConfiguration element.

\n

By default, you must be the bucket owner to read the notification configuration of a\n bucket. However, the bucket owner can use a bucket policy to grant permission to other\n users to read this configuration with the s3:GetBucketNotification\n permission.

\n

For more information about setting and reading the notification configuration on a\n bucket, see Setting Up Notification of\n Bucket Events. For more information about bucket policies, see Using Bucket Policies.

\n

The following action is related to GetBucketNotification:

\n ", + "smithy.api#documentation": "

Returns the notification configuration of a bucket.

\n

If notifications are not enabled on the bucket, the action returns an empty\n NotificationConfiguration element.

\n

By default, you must be the bucket owner to read the notification configuration of a\n bucket. However, the bucket owner can use a bucket policy to grant permission to other\n users to read this configuration with the s3:GetBucketNotification\n permission.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n

For more information about setting and reading the notification configuration on a\n bucket, see Setting Up Notification of Bucket Events. For more information about bucket\n policies, see Using Bucket Policies.

\n

The following action is related to GetBucketNotification:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?notification", @@ -23693,7 +23693,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket for which to get the notification configuration.

", + "smithy.api#documentation": "

The name of the bucket for which to get the notification configuration.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -23722,7 +23722,7 @@ "target": "com.amazonaws.s3#GetBucketOwnershipControlsOutput" }, "traits": { - "smithy.api#documentation": "

Retrieves OwnershipControls for an Amazon S3 bucket. To use this operation, you\n must have the s3:GetBucketOwnershipControls permission. For more information\n about Amazon S3 permissions, see Specifying\n permissions in a policy.

\n

For information about Amazon S3 Object Ownership, see Using Object Ownership.

\n

The following operations are related to GetBucketOwnershipControls:

\n ", + "smithy.api#documentation": "

Retrieves OwnershipControls for an Amazon S3 bucket. To use this operation, you\n must have the s3:GetBucketOwnershipControls permission. For more information\n about Amazon S3 permissions, see Specifying permissions in a\n policy.

\n

For information about Amazon S3 Object Ownership, see Using Object\n Ownership.

\n

The following operations are related to GetBucketOwnershipControls:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?ownershipControls", @@ -23736,7 +23736,7 @@ "OwnershipControls": { "target": "com.amazonaws.s3#OwnershipControls", "traits": { - "smithy.api#documentation": "

The OwnershipControls (BucketOwnerEnforced, BucketOwnerPreferred, or ObjectWriter) currently in\n effect for this Amazon S3 bucket.

", + "smithy.api#documentation": "

The OwnershipControls (BucketOwnerEnforced, BucketOwnerPreferred, or\n ObjectWriter) currently in effect for this Amazon S3 bucket.

", "smithy.api#httpPayload": {} } } @@ -23780,7 +23780,7 @@ "target": "com.amazonaws.s3#GetBucketPolicyOutput" }, "traits": { - "smithy.api#documentation": "

Returns the policy of a specified bucket. If you are using an identity other than the\n root user of the Amazon Web Services account that owns the bucket, the calling identity must have the\n GetBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account in order to use this operation.

\n

If you don't have GetBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

As a security precaution, the root user of the Amazon Web Services account that owns a bucket can\n always use this operation, even if the policy explicitly denies the root user the\n ability to perform this action.

\n
\n

For more information about bucket policies, see Using Bucket Policies and User\n Policies.

\n

The following action is related to GetBucketPolicy:

\n ", + "smithy.api#documentation": "

Returns the policy of a specified bucket. If you are using an identity other than the\n root user of the Amazon Web Services account that owns the bucket, the calling identity must have the\n GetBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account in order to use this operation.

\n

If you don't have GetBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

To ensure that bucket owners don't inadvertently lock themselves out of their own\n buckets, the root principal in a bucket owner's Amazon Web Services account can perform the\n GetBucketPolicy, PutBucketPolicy, and\n DeleteBucketPolicy API actions, even if their bucket policy explicitly\n denies the root principal's access. Bucket owner root principals can only be blocked from performing \n these API actions by VPC endpoint policies and Amazon Web Services Organizations policies.

\n
\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n

For more information about bucket policies, see Using Bucket Policies and User\n Policies.

\n

The following action is related to GetBucketPolicy:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?policy", @@ -23809,7 +23809,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name for which to get the bucket policy.

", + "smithy.api#documentation": "

The bucket name for which to get the bucket policy.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -23838,7 +23838,7 @@ "target": "com.amazonaws.s3#GetBucketPolicyStatusOutput" }, "traits": { - "smithy.api#documentation": "

Retrieves the policy status for an Amazon S3 bucket, indicating whether the bucket is public.\n In order to use this operation, you must have the s3:GetBucketPolicyStatus\n permission. For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n

For more information about when Amazon S3 considers a bucket public, see The Meaning of \"Public\".

\n

The following operations are related to GetBucketPolicyStatus:

\n ", + "smithy.api#documentation": "

Retrieves the policy status for an Amazon S3 bucket, indicating whether the bucket is public.\n In order to use this operation, you must have the s3:GetBucketPolicyStatus\n permission. For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n

For more information about when Amazon S3 considers a bucket public, see The Meaning of \"Public\".

\n

The following operations are related to GetBucketPolicyStatus:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?policyStatus", @@ -23953,7 +23953,7 @@ "target": "com.amazonaws.s3#GetBucketRequestPaymentOutput" }, "traits": { - "smithy.api#documentation": "

Returns the request payment configuration of a bucket. To use this version of the\n operation, you must be the bucket owner. For more information, see Requester Pays Buckets.

\n

The following operations are related to GetBucketRequestPayment:

\n ", + "smithy.api#documentation": "

Returns the request payment configuration of a bucket. To use this version of the\n operation, you must be the bucket owner. For more information, see Requester Pays\n Buckets.

\n

The following operations are related to GetBucketRequestPayment:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?requestPayment", @@ -24135,7 +24135,7 @@ "target": "com.amazonaws.s3#GetBucketWebsiteOutput" }, "traits": { - "smithy.api#documentation": "

Returns the website configuration for a bucket. To host website on Amazon S3, you can\n configure a bucket as website by adding a website configuration. For more information about\n hosting websites, see Hosting Websites on\n Amazon S3.

\n

This GET action requires the S3:GetBucketWebsite permission. By default,\n only the bucket owner can read the bucket website configuration. However, bucket owners can\n allow other users to read the website configuration by writing a bucket policy granting\n them the S3:GetBucketWebsite permission.

\n

The following operations are related to DeleteBucketWebsite:

\n ", + "smithy.api#documentation": "

Returns the website configuration for a bucket. To host website on Amazon S3, you can\n configure a bucket as website by adding a website configuration. For more information about\n hosting websites, see Hosting Websites on Amazon S3.

\n

This GET action requires the S3:GetBucketWebsite permission. By default,\n only the bucket owner can read the bucket website configuration. However, bucket owners can\n allow other users to read the website configuration by writing a bucket policy granting\n them the S3:GetBucketWebsite permission.

\n

The following operations are related to GetBucketWebsite:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?website", @@ -24228,7 +24228,7 @@ "SHA1" ] }, - "smithy.api#documentation": "

Retrieves objects from Amazon S3. To use GET, you must have READ\n access to the object. If you grant READ access to the anonymous user, you can\n return the object without using an authorization header.

\n

An Amazon S3 bucket has no directory hierarchy such as you would find in a typical computer\n file system. You can, however, create a logical hierarchy by using object key names that\n imply a folder structure. For example, instead of naming an object sample.jpg,\n you can name it photos/2006/February/sample.jpg.

\n

To get an object from such a logical hierarchy, specify the full key name for the object\n in the GET operation. For a virtual hosted-style request example, if you have\n the object photos/2006/February/sample.jpg, specify the resource as\n /photos/2006/February/sample.jpg. For a path-style request example, if you\n have the object photos/2006/February/sample.jpg in the bucket named\n examplebucket, specify the resource as\n /examplebucket/photos/2006/February/sample.jpg. For more information about\n request types, see HTTP Host Header Bucket Specification.

\n

For more information about returning the ACL of an object, see GetObjectAcl.

\n

If the object you are retrieving is stored in the S3 Glacier or\n S3 Glacier Deep Archive storage class, or S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers, before you can retrieve the object you must first restore a\n copy using RestoreObject. Otherwise, this action returns an\n InvalidObjectStateError error. For information about restoring archived\n objects, see Restoring Archived\n Objects.

\n

Encryption request headers, like x-amz-server-side-encryption, should not\n be sent for GET requests if your object uses server-side encryption with KMS keys (SSE-KMS) \n or server-side encryption with Amazon S3–managed encryption keys (SSE-S3). If your\n object does use these types of keys, you’ll get an HTTP 400 BadRequest error.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you GET the object,\n you must use the following headers:

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption (Using\n Customer-Provided Encryption Keys).

\n

Assuming you have the relevant permission to read object tags, the response also returns the\n x-amz-tagging-count header that provides the count of number of tags\n associated with the object. You can use GetObjectTagging to retrieve\n the tag set associated with an object.

\n

\n Permissions\n

\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Specifying Permissions\n in a Policy. If the object you request does not exist, the error Amazon S3 returns\n depends on whether you also have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 will\n return an HTTP status code 404 (\"no such key\") error.

    \n
  • \n
  • \n

    If you don’t have the s3:ListBucket permission, Amazon S3 will return an\n HTTP status code 403 (\"access denied\") error.

    \n
  • \n
\n

\n Versioning\n

\n

By default, the GET action returns the current version of an object. To return a\n different version, use the versionId subresource.

\n \n
    \n
  • \n

    \n If you supply a versionId, you need the s3:GetObjectVersion permission to\n access a specific version of an object. If you request a specific version, you do not need to have\n the s3:GetObject permission.\n

    \n
  • \n
  • \n

    If the current version of the object is a delete marker, Amazon S3 behaves as if the\n object was deleted and includes x-amz-delete-marker: true in the\n response.

    \n
  • \n
\n
\n

For more information about versioning, see PutBucketVersioning.

\n

\n Overriding Response Header Values\n

\n

There are times when you want to override certain response header values in a GET\n response. For example, you might override the Content-Disposition response\n header value in your GET request.

\n

You can override values for a set of response headers using the following query\n parameters. These response header values are sent only on a successful request, that is,\n when status code 200 OK is returned. The set of headers you can override using these\n parameters is a subset of the headers that Amazon S3 accepts when you create an object. The\n response headers that you can override for the GET response are Content-Type,\n Content-Language, Expires, Cache-Control,\n Content-Disposition, and Content-Encoding. To override these\n header values in the GET response, you use the following request parameters.

\n \n

You must sign the request, either using an Authorization header or a presigned URL,\n when using these parameters. They cannot be used with an unsigned (anonymous)\n request.

\n
\n
    \n
  • \n

    \n response-content-type\n

    \n
  • \n
  • \n

    \n response-content-language\n

    \n
  • \n
  • \n

    \n response-expires\n

    \n
  • \n
  • \n

    \n response-cache-control\n

    \n
  • \n
  • \n

    \n response-content-disposition\n

    \n
  • \n
  • \n

    \n response-content-encoding\n

    \n
  • \n
\n

\n Additional Considerations about Request Headers\n

\n

If both of the If-Match and If-Unmodified-Since headers are\n present in the request as follows: If-Match condition evaluates to\n true, and; If-Unmodified-Since condition evaluates to\n false; then, S3 returns 200 OK and the data requested.

\n

If both of the If-None-Match and If-Modified-Since headers are\n present in the request as follows: If-None-Match condition evaluates to\n false, and; If-Modified-Since condition evaluates to\n true; then, S3 returns 304 Not Modified response code.

\n

For more information about conditional requests, see RFC 7232.

\n

The following operations are related to GetObject:

\n ", + "smithy.api#documentation": "

Retrieves objects from Amazon S3. To use GET, you must have READ\n access to the object. If you grant READ access to the anonymous user, you can\n return the object without using an authorization header.

\n

An Amazon S3 bucket has no directory hierarchy such as you would find in a typical computer\n file system. You can, however, create a logical hierarchy by using object key names that\n imply a folder structure. For example, instead of naming an object sample.jpg,\n you can name it photos/2006/February/sample.jpg.

\n

To get an object from such a logical hierarchy, specify the full key name for the object\n in the GET operation. For a virtual hosted-style request example, if you have\n the object photos/2006/February/sample.jpg, specify the resource as\n /photos/2006/February/sample.jpg. For a path-style request example, if you\n have the object photos/2006/February/sample.jpg in the bucket named\n examplebucket, specify the resource as\n /examplebucket/photos/2006/February/sample.jpg. For more information about\n request types, see HTTP Host\n Header Bucket Specification.

\n

For more information about returning the ACL of an object, see GetObjectAcl.

\n

If the object you are retrieving is stored in the S3 Glacier or\n S3 Glacier Deep Archive storage class, or S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers, before you can retrieve the object you must first restore a\n copy using RestoreObject. Otherwise, this action returns an\n InvalidObjectState error. For information about restoring archived objects,\n see Restoring\n Archived Objects.

\n

Encryption request headers, like x-amz-server-side-encryption, should not\n be sent for GET requests if your object uses server-side encryption with KMS keys\n (SSE-KMS) or server-side encryption with Amazon S3–managed encryption keys (SSE-S3). If your\n object does use these types of keys, you’ll get an HTTP 400 BadRequest error.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you GET the object,\n you must use the following headers:

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys).

\n

Assuming you have the relevant permission to read object tags, the response also returns\n the x-amz-tagging-count header that provides the count of number of tags\n associated with the object. You can use GetObjectTagging to retrieve\n the tag set associated with an object.

\n
\n
Permissions
\n
\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Specifying Permissions in a\n Policy. If the object you request does not exist, the error Amazon S3 returns depends\n on whether you also have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 will\n return an HTTP status code 404 (\"no such key\") error.

    \n
  • \n
  • \n

    If you don’t have the s3:ListBucket permission, Amazon S3 will return an\n HTTP status code 403 (\"access denied\") error.

    \n
  • \n
\n
\n
Versioning
\n
\n

By default, the GET action returns the current version of an object. To return a\n different version, use the versionId subresource.

\n \n
    \n
  • \n

    If you supply a versionId, you need the\n s3:GetObjectVersion permission to access a specific version of an\n object. If you request a specific version, you do not need to have the\n s3:GetObject permission. If you request the current version\n without a specific version ID, only s3:GetObject permission is\n required. s3:GetObjectVersion permission won't be required.

    \n
  • \n
  • \n

    If the current version of the object is a delete marker, Amazon S3 behaves as if the\n object was deleted and includes x-amz-delete-marker: true in the\n response.

    \n
  • \n
\n
\n

For more information about versioning, see PutBucketVersioning.

\n
\n
Overriding Response Header Values
\n
\n

There are times when you want to override certain response header values in a GET\n response. For example, you might override the Content-Disposition response\n header value in your GET request.

\n

You can override values for a set of response headers using the following query\n parameters. These response header values are sent only on a successful request, that is,\n when status code 200 OK is returned. The set of headers you can override using these\n parameters is a subset of the headers that Amazon S3 accepts when you create an object. The\n response headers that you can override for the GET response are Content-Type,\n Content-Language, Expires, Cache-Control,\n Content-Disposition, and Content-Encoding. To override these\n header values in the GET response, you use the following request parameters.

\n \n

You must sign the request, either using an Authorization header or a presigned URL,\n when using these parameters. They cannot be used with an unsigned (anonymous)\n request.

\n
\n
    \n
  • \n

    \n response-content-type\n

    \n
  • \n
  • \n

    \n response-content-language\n

    \n
  • \n
  • \n

    \n response-expires\n

    \n
  • \n
  • \n

    \n response-cache-control\n

    \n
  • \n
  • \n

    \n response-content-disposition\n

    \n
  • \n
  • \n

    \n response-content-encoding\n

    \n
  • \n
\n
\n
Overriding Response Header Values
\n
\n

If both of the If-Match and If-Unmodified-Since headers are\n present in the request as follows: If-Match condition evaluates to\n true, and; If-Unmodified-Since condition evaluates to\n false; then, S3 returns 200 OK and the data requested.

\n

If both of the If-None-Match and If-Modified-Since headers are\n present in the request as follows: If-None-Match condition evaluates to\n false, and; If-Modified-Since condition evaluates to\n true; then, S3 returns 304 Not Modified response code.

\n

For more information about conditional requests, see RFC 7232.

\n
\n
\n

The following operations are related to GetObject:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?x-id=GetObject", @@ -24250,7 +24250,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the access control list (ACL) of an object. To use this operation, you must have\n s3:GetObjectAcl permissions or READ_ACP access to the object.\n For more information, see Mapping of ACL permissions and access policy permissions in the Amazon S3\n User Guide\n

\n

This action is not supported by Amazon S3 on Outposts.

\n

\n Versioning\n

\n

By default, GET returns ACL information about the current version of an object. To\n return ACL information about a different version, use the versionId subresource.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, \n requests to read ACLs are still supported and return the bucket-owner-full-control \n ACL with the owner being the account that created the bucket. For more information, see \n \n Controlling object ownership and disabling ACLs in the Amazon S3 User Guide.

\n
\n

The following operations are related to GetObjectAcl:

\n ", + "smithy.api#documentation": "

Returns the access control list (ACL) of an object. To use this operation, you must have\n s3:GetObjectAcl permissions or READ_ACP access to the object.\n For more information, see Mapping of ACL permissions and access policy permissions in the Amazon S3\n User Guide\n

\n

This action is not supported by Amazon S3 on Outposts.

\n

By default, GET returns ACL information about the current version of an object. To\n return ACL information about a different version, use the versionId subresource.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership,\n requests to read ACLs are still supported and return the\n bucket-owner-full-control ACL with the owner being the account that\n created the bucket. For more information, see Controlling object\n ownership and disabling ACLs in the\n Amazon S3 User Guide.

\n
\n

The following operations are related to GetObjectAcl:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?acl", @@ -24347,7 +24347,7 @@ } ], "traits": { - "smithy.api#documentation": "

Retrieves all the metadata from an object without returning the object itself. This\n action is useful if you're interested only in an object's metadata. To use\n GetObjectAttributes, you must have READ access to the object.

\n

\n GetObjectAttributes combines the functionality of\n GetObjectAcl, GetObjectLegalHold,\n GetObjectLockConfiguration, GetObjectRetention,\n GetObjectTagging, HeadObject, and ListParts. All\n of the data returned with each of those individual calls can be returned with a single call\n to GetObjectAttributes.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you retrieve the\n metadata from the object, you must use the following headers:

\n
    \n
  • \n

    \n x-amz-server-side-encryption-customer-algorithm\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key-MD5\n

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys) in the\n Amazon S3 User Guide.

\n \n
    \n
  • \n

    Encryption request headers, such as\n x-amz-server-side-encryption, should not be sent for GET requests\n if your object uses server-side encryption with Amazon Web Services KMS keys stored in Amazon Web Services Key\n Management Service (SSE-KMS) or server-side encryption with Amazon S3 managed\n encryption keys (SSE-S3). If your object does use these types of keys, you'll get\n an HTTP 400 Bad Request error.

    \n
  • \n
  • \n

    \n The last modified property in this case is the creation date of the object.

    \n
  • \n
\n
\n

Consider the following when using request headers:

\n
    \n
  • \n

    If both of the If-Match and If-Unmodified-Since\n headers are present in the request as follows, then Amazon S3 returns the HTTP\n status code 200 OK and the data requested:

    \n
      \n
    • \n

      \n If-Match condition evaluates to true.

      \n
    • \n
    • \n

      \n If-Unmodified-Since condition evaluates to\n false.

      \n
    • \n
    \n
  • \n
  • \n

    If both of the If-None-Match and If-Modified-Since\n headers are present in the request as follows, then Amazon S3 returns the HTTP status code\n 304 Not Modified:

    \n
      \n
    • \n

      \n If-None-Match condition evaluates to\n false.

      \n
    • \n
    • \n

      \n If-Modified-Since condition evaluates to\n true.

      \n
    • \n
    \n
  • \n
\n

For more information about conditional requests, see RFC 7232.

\n

\n Permissions\n

\n

The permissions that you need to use this operation depend on whether the bucket is\n versioned. If the bucket is versioned, you need both the s3:GetObjectVersion\n and s3:GetObjectVersionAttributes permissions for this operation. If the\n bucket is not versioned, you need the s3:GetObject and\n s3:GetObjectAttributes permissions. For more information, see Specifying\n Permissions in a Policy in the Amazon S3 User Guide. If the\n object that you request does not exist, the error Amazon S3 returns depends on whether you also\n have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3\n returns an HTTP status code 404 Not Found (\"no such key\") error.

    \n
  • \n
  • \n

    If you don't have the s3:ListBucket permission, Amazon S3 returns an\n HTTP status code 403 Forbidden (\"access denied\") error.

    \n
  • \n
\n

The following actions are related to GetObjectAttributes:

\n ", + "smithy.api#documentation": "

Retrieves all the metadata from an object without returning the object itself. This\n action is useful if you're interested only in an object's metadata. To use\n GetObjectAttributes, you must have READ access to the object.

\n

\n GetObjectAttributes combines the functionality of HeadObject\n and ListParts. All of the data returned with each of those individual calls\n can be returned with a single call to GetObjectAttributes.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you retrieve the\n metadata from the object, you must use the following headers:

\n
    \n
  • \n

    \n x-amz-server-side-encryption-customer-algorithm\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key-MD5\n

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys) in the\n Amazon S3 User Guide.

\n \n
    \n
  • \n

    Encryption request headers, such as x-amz-server-side-encryption,\n should not be sent for GET requests if your object uses server-side encryption\n with Amazon Web Services KMS keys stored in Amazon Web Services Key Management Service (SSE-KMS) or\n server-side encryption with Amazon S3 managed keys (SSE-S3). If your object does use\n these types of keys, you'll get an HTTP 400 Bad Request error.

    \n
  • \n
  • \n

    The last modified property in this case is the creation date of the\n object.

    \n
  • \n
\n
\n

Consider the following when using request headers:

\n
    \n
  • \n

    If both of the If-Match and If-Unmodified-Since headers\n are present in the request as follows, then Amazon S3 returns the HTTP status code\n 200 OK and the data requested:

    \n
      \n
    • \n

      \n If-Match condition evaluates to true.

      \n
    • \n
    • \n

      \n If-Unmodified-Since condition evaluates to\n false.

      \n
    • \n
    \n
  • \n
  • \n

    If both of the If-None-Match and If-Modified-Since\n headers are present in the request as follows, then Amazon S3 returns the HTTP status code\n 304 Not Modified:

    \n
      \n
    • \n

      \n If-None-Match condition evaluates to false.

      \n
    • \n
    • \n

      \n If-Modified-Since condition evaluates to\n true.

      \n
    • \n
    \n
  • \n
\n

For more information about conditional requests, see RFC 7232.

\n
\n
Permissions
\n
\n

The permissions that you need to use this operation depend on whether the bucket is\n versioned. If the bucket is versioned, you need both the s3:GetObjectVersion\n and s3:GetObjectVersionAttributes permissions for this operation. If the\n bucket is not versioned, you need the s3:GetObject and\n s3:GetObjectAttributes permissions. For more information, see Specifying\n Permissions in a Policy in the Amazon S3 User Guide. If the\n object that you request does not exist, the error Amazon S3 returns depends on whether you also\n have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 returns\n an HTTP status code 404 Not Found (\"no such key\") error.

    \n
  • \n
  • \n

    If you don't have the s3:ListBucket permission, Amazon S3 returns an HTTP\n status code 403 Forbidden (\"access denied\") error.

    \n
  • \n
\n
\n
\n

The following actions are related to GetObjectAttributes:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?attributes", @@ -24389,7 +24389,7 @@ "ETag": { "target": "com.amazonaws.s3#ETag", "traits": { - "smithy.api#documentation": "

An ETag is an opaque identifier assigned by a web server to a specific version of a\n resource found at a URL.

" + "smithy.api#documentation": "

An ETag is an opaque identifier assigned by a web server to a specific version of a\n resource found at a URL.

" } }, "Checksum": { @@ -24407,7 +24407,7 @@ "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

Provides the storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage\n Classes.

" + "smithy.api#documentation": "

Provides the storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage Classes.

" } }, "ObjectSize": { @@ -24456,7 +24456,7 @@ "target": "com.amazonaws.s3#IsTruncated", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the returned list of parts is truncated. A value of\n true indicates that the list was truncated. A list can be truncated if the\n number of parts exceeds the limit returned in the MaxParts element.

" + "smithy.api#documentation": "

Indicates whether the returned list of parts is truncated. A value of true\n indicates that the list was truncated. A list can be truncated if the number of parts\n exceeds the limit returned in the MaxParts element.

" } }, "Parts": { @@ -24478,7 +24478,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket that contains the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket that contains the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -24512,28 +24512,28 @@ "PartNumberMarker": { "target": "com.amazonaws.s3#PartNumberMarker", "traits": { - "smithy.api#documentation": "

Specifies the part after which listing should begin. Only parts with higher part numbers\n will be listed.

", + "smithy.api#documentation": "

Specifies the part after which listing should begin. Only parts with higher part numbers\n will be listed.

", "smithy.api#httpHeader": "x-amz-part-number-marker" } }, "SSECustomerAlgorithm": { "target": "com.amazonaws.s3#SSECustomerAlgorithm", "traits": { - "smithy.api#documentation": "

Specifies the algorithm to use when encrypting the object (for example,\n AES256).

", + "smithy.api#documentation": "

Specifies the algorithm to use when encrypting the object (for example, AES256).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-customer-algorithm" } }, "SSECustomerKey": { "target": "com.amazonaws.s3#SSECustomerKey", "traits": { - "smithy.api#documentation": "

Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This\n value is used to store the object and then it is discarded; Amazon S3 does not store the\n encryption key. The key must be appropriate for use with the algorithm specified in the\n x-amz-server-side-encryption-customer-algorithm header.

", + "smithy.api#documentation": "

Specifies the customer-provided encryption key for Amazon S3 to use in encrypting data. This\n value is used to store the object and then it is discarded; Amazon S3 does not store the\n encryption key. The key must be appropriate for use with the algorithm specified in the\n x-amz-server-side-encryption-customer-algorithm header.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-customer-key" } }, "SSECustomerKeyMD5": { "target": "com.amazonaws.s3#SSECustomerKeyMD5", "traits": { - "smithy.api#documentation": "

Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses\n this header for a message integrity check to ensure that the encryption key was transmitted\n without error.

", + "smithy.api#documentation": "

Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321. Amazon S3 uses\n this header for a message integrity check to ensure that the encryption key was transmitted\n without error.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-customer-key-MD5" } }, @@ -24553,7 +24553,7 @@ "ObjectAttributes": { "target": "com.amazonaws.s3#ObjectAttributesList", "traits": { - "smithy.api#documentation": "

An XML header that specifies the fields at the root level that you want returned in\n the response. Fields that you do not specify are not returned.

", + "smithy.api#documentation": "

An XML header that specifies the fields at the root level that you want returned in the\n response. Fields that you do not specify are not returned.

", "smithy.api#httpHeader": "x-amz-object-attributes", "smithy.api#required": {} } @@ -24651,7 +24651,7 @@ "target": "com.amazonaws.s3#GetObjectLockConfigurationOutput" }, "traits": { - "smithy.api#documentation": "

Gets the Object Lock configuration for a bucket. The rule specified in the Object Lock\n configuration will be applied by default to every new object placed in the specified\n bucket. For more information, see Locking\n Objects.

\n

The following action is related to GetObjectLockConfiguration:

\n ", + "smithy.api#documentation": "

Gets the Object Lock configuration for a bucket. The rule specified in the Object Lock\n configuration will be applied by default to every new object placed in the specified\n bucket. For more information, see Locking Objects.

\n

The following action is related to GetObjectLockConfiguration:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?object-lock", @@ -24736,7 +24736,7 @@ "Restore": { "target": "com.amazonaws.s3#Restore", "traits": { - "smithy.api#documentation": "

Provides information about object restoration action and expiration time of the\n restored object copy.

", + "smithy.api#documentation": "

Provides information about object restoration action and expiration time of the restored\n object copy.

", "smithy.api#httpHeader": "x-amz-restore" } }, @@ -24864,7 +24864,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -24892,7 +24892,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -24900,7 +24900,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with Amazon Web Services\n KMS (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -24928,7 +24928,7 @@ "target": "com.amazonaws.s3#PartsCount", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The count of parts this object has. This value is only returned if you specify partNumber\n in your request and the object was uploaded as a multipart upload.

", + "smithy.api#documentation": "

The count of parts this object has. This value is only returned if you specify\n partNumber in your request and the object was uploaded as a multipart\n upload.

", "smithy.api#httpHeader": "x-amz-mp-parts-count" } }, @@ -24972,7 +24972,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using an Object Lambda access point the hostname takes the form AccessPointName-AccountId.s3-object-lambda.Region.amazonaws.com.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using an Object Lambda access point the hostname takes the form AccessPointName-AccountId.s3-object-lambda.Region.amazonaws.com.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -25019,7 +25019,7 @@ "Range": { "target": "com.amazonaws.s3#Range", "traits": { - "smithy.api#documentation": "

Downloads the specified range bytes of an object. For more information about the HTTP\n Range header, see https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.

\n \n

Amazon S3 doesn't support retrieving multiple ranges of data per GET\n request.

\n
", + "smithy.api#documentation": "

Downloads the specified range bytes of an object. For more information about the HTTP\n Range header, see https://www.rfc-editor.org/rfc/rfc9110.html#name-range.

\n \n

Amazon S3 doesn't support retrieving multiple ranges of data per GET\n request.

\n
", "smithy.api#httpHeader": "Range" } }, @@ -25082,7 +25082,7 @@ "SSECustomerKey": { "target": "com.amazonaws.s3#SSECustomerKey", "traits": { - "smithy.api#documentation": "

Specifies the customer-provided encryption key for Amazon S3 used to encrypt the data. This\n value is used to decrypt the object when recovering it and must match the one used when \n storing the data. The key must be appropriate for use with the algorithm specified in the\n x-amz-server-side-encryption-customer-algorithm header.

", + "smithy.api#documentation": "

Specifies the customer-provided encryption key for Amazon S3 used to encrypt the data. This\n value is used to decrypt the object when recovering it and must match the one used when\n storing the data. The key must be appropriate for use with the algorithm specified in the\n x-amz-server-side-encryption-customer-algorithm header.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-customer-key" } }, @@ -25141,7 +25141,7 @@ "target": "com.amazonaws.s3#GetObjectRetentionOutput" }, "traits": { - "smithy.api#documentation": "

Retrieves an object's retention settings. For more information, see Locking Objects.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following action is related to GetObjectRetention:

\n ", + "smithy.api#documentation": "

Retrieves an object's retention settings. For more information, see Locking\n Objects.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following action is related to GetObjectRetention:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?retention", @@ -25220,7 +25220,7 @@ "target": "com.amazonaws.s3#GetObjectTaggingOutput" }, "traits": { - "smithy.api#documentation": "

Returns the tag-set of an object. You send the GET request against the tagging\n subresource associated with the object.

\n

To use this operation, you must have permission to perform the\n s3:GetObjectTagging action. By default, the GET action returns\n information about current version of an object. For a versioned bucket, you can have\n multiple versions of an object in your bucket. To retrieve tags of any other version, use\n the versionId query parameter. You also need permission for the\n s3:GetObjectVersionTagging action.

\n

By default, the bucket owner has this permission and can grant this permission to\n others.

\n

For information about the Amazon S3 object tagging feature, see Object Tagging.

\n

The following actions are related to GetObjectTagging:

\n ", + "smithy.api#documentation": "

Returns the tag-set of an object. You send the GET request against the tagging\n subresource associated with the object.

\n

To use this operation, you must have permission to perform the\n s3:GetObjectTagging action. By default, the GET action returns information\n about current version of an object. For a versioned bucket, you can have multiple versions\n of an object in your bucket. To retrieve tags of any other version, use the versionId query\n parameter. You also need permission for the s3:GetObjectVersionTagging\n action.

\n

By default, the bucket owner has this permission and can grant this permission to\n others.

\n

For information about the Amazon S3 object tagging feature, see Object Tagging.

\n

The following actions are related to GetObjectTagging:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?tagging", @@ -25257,7 +25257,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the object for which to get the tagging information.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the object for which to get the tagging information.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -25307,7 +25307,7 @@ "target": "com.amazonaws.s3#GetObjectTorrentOutput" }, "traits": { - "smithy.api#documentation": "

Returns torrent files from a bucket. BitTorrent can save you bandwidth when you're\n distributing large files. For more information about BitTorrent, see Using BitTorrent with Amazon S3.

\n \n

You can get torrent only for objects that are less than 5 GB in size, and that are\n not encrypted using server-side encryption with a customer-provided encryption\n key.

\n
\n

To use GET, you must have READ access to the object.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following action is related to GetObjectTorrent:

\n ", + "smithy.api#documentation": "

Returns torrent files from a bucket. BitTorrent can save you bandwidth when you're\n distributing large files.

\n \n

You can get torrent only for objects that are less than 5 GB in size, and that are\n not encrypted using server-side encryption with a customer-provided encryption\n key.

\n
\n

To use GET, you must have READ access to the object.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following action is related to GetObjectTorrent:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?torrent", @@ -25386,7 +25386,7 @@ "target": "com.amazonaws.s3#GetPublicAccessBlockOutput" }, "traits": { - "smithy.api#documentation": "

Retrieves the PublicAccessBlock configuration for an Amazon S3 bucket. To use\n this operation, you must have the s3:GetBucketPublicAccessBlock permission.\n For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n \n

When Amazon S3 evaluates the PublicAccessBlock configuration for a bucket or\n an object, it checks the PublicAccessBlock configuration for both the\n bucket (or the bucket that contains the object) and the bucket owner's account. If the\n PublicAccessBlock settings are different between the bucket and the\n account, Amazon S3 uses the most restrictive combination of the bucket-level and\n account-level settings.

\n
\n

For more information about when Amazon S3 considers a bucket or an object public, see The Meaning of \"Public\".

\n

The following operations are related to GetPublicAccessBlock:

\n ", + "smithy.api#documentation": "

Retrieves the PublicAccessBlock configuration for an Amazon S3 bucket. To use\n this operation, you must have the s3:GetBucketPublicAccessBlock permission.\n For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n \n

When Amazon S3 evaluates the PublicAccessBlock configuration for a bucket or\n an object, it checks the PublicAccessBlock configuration for both the\n bucket (or the bucket that contains the object) and the bucket owner's account. If the\n PublicAccessBlock settings are different between the bucket and the\n account, Amazon S3 uses the most restrictive combination of the bucket-level and\n account-level settings.

\n
\n

For more information about when Amazon S3 considers a bucket or an object public, see The Meaning of \"Public\".

\n

The following operations are related to GetPublicAccessBlock:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?publicAccessBlock", @@ -25553,7 +25553,7 @@ } ], "traits": { - "smithy.api#documentation": "

This action is useful to determine if a bucket exists and you have permission to\n access it. The action returns a 200 OK if the bucket exists and you have\n permission to access it.

\n

If the bucket does not exist or you do not have permission to access it, the HEAD request\n returns a generic 404 Not Found or 403 Forbidden code. A message body is not \n included, so you cannot determine the exception beyond these error codes.

\n

To use this operation, you must have permissions to perform the\n s3:ListBucket action. The bucket owner has this permission by default and\n can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

To use this API against an access point, you must provide the alias of the access point in place of the bucket name or specify the access point ARN. When using the access point ARN, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using the Amazon Web Services SDKs, you provide the ARN in place of the bucket name. For more information see, Using access points.

", + "smithy.api#documentation": "

This action is useful to determine if a bucket exists and you have permission to access\n it. The action returns a 200 OK if the bucket exists and you have permission\n to access it.

\n

If the bucket does not exist or you do not have permission to access it, the\n HEAD request returns a generic 400 Bad Request, 403\n Forbidden or 404 Not Found code. A message body is not included, so\n you cannot determine the exception beyond these error codes.

\n

To use this operation, you must have permissions to perform the\n s3:ListBucket action. The bucket owner has this permission by default and\n can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

To use this API operation against an access point, you must provide the alias of the access point in place of the\n bucket name or specify the access point ARN. When using the access point ARN, you must direct requests to\n the access point hostname. The access point hostname takes the form\n AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com.\n When using the Amazon Web Services SDKs, you provide the ARN in place of the bucket name. For more\n information, see Using access points.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", "smithy.api#http": { "method": "HEAD", "uri": "/{Bucket}", @@ -25597,7 +25597,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \n If the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \n For more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -25631,7 +25631,7 @@ } ], "traits": { - "smithy.api#documentation": "

The HEAD action retrieves metadata from an object without returning the object\n itself. This action is useful if you're only interested in an object's metadata. To use\n HEAD, you must have READ access to the object.

\n

A HEAD request has the same options as a GET action on an\n object. The response is identical to the GET response except that there is no\n response body. Because of this, if the HEAD request generates an error, it\n returns a generic 404 Not Found or 403 Forbidden code. It is not \n possible to retrieve the exact exception beyond these error codes.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you retrieve the\n metadata from the object, you must use the following headers:

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption (Using\n Customer-Provided Encryption Keys).

\n \n
    \n
  • \n

    Encryption request headers, like x-amz-server-side-encryption, should\n not be sent for GET requests if your object uses server-side encryption with KMS keys (SSE-KMS)\n or server-side encryption with Amazon S3–managed encryption keys\n (SSE-S3). If your object does use these types of keys, you’ll get an HTTP 400 BadRequest\n error.

    \n
  • \n
  • \n

    \n The last modified property in this case is the creation date of the object.

    \n
  • \n
\n
\n

Request headers are limited to 8 KB in size. For more information, see Common Request\n Headers.

\n

Consider the following when using request headers:

\n
    \n
  • \n

    Consideration 1 – If both of the If-Match and\n If-Unmodified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-Match condition evaluates to true, and;

      \n
    • \n
    • \n

      \n If-Unmodified-Since condition evaluates to\n false;

      \n
    • \n
    \n

    Then Amazon S3 returns 200 OK and the data requested.

    \n
  • \n
  • \n

    Consideration 2 – If both of the If-None-Match and\n If-Modified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-None-Match condition evaluates to false,\n and;

      \n
    • \n
    • \n

      \n If-Modified-Since condition evaluates to\n true;

      \n
    • \n
    \n

    Then Amazon S3 returns the 304 Not Modified response code.

    \n
  • \n
\n

For more information about conditional requests, see RFC 7232.

\n

\n Permissions\n

\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Specifying Permissions\n in a Policy. If the object you request does not exist, the error Amazon S3 returns\n depends on whether you also have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 returns\n an HTTP status code 404 (\"no such key\") error.

    \n
  • \n
  • \n

    If you don’t have the s3:ListBucket permission, Amazon S3 returns an HTTP\n status code 403 (\"access denied\") error.

    \n
  • \n
\n

The following actions are related to HeadObject:

\n ", + "smithy.api#documentation": "

The HEAD action retrieves metadata from an object without returning the object itself.\n This action is useful if you're only interested in an object's metadata. To use HEAD, you\n must have READ access to the object.

\n

A HEAD request has the same options as a GET action on an\n object. The response is identical to the GET response except that there is no\n response body. Because of this, if the HEAD request generates an error, it\n returns a generic 400 Bad Request, 403 Forbidden or 404 Not\n Found code. It is not possible to retrieve the exact exception beyond these error\n codes.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you retrieve the\n metadata from the object, you must use the following headers:

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys).

\n \n
    \n
  • \n

    Encryption request headers, like x-amz-server-side-encryption,\n should not be sent for GET requests if your object uses server-side encryption\n with KMS keys (SSE-KMS) or server-side encryption with Amazon S3–managed encryption\n keys (SSE-S3). If your object does use these types of keys, you’ll get an HTTP 400\n BadRequest error.

    \n
  • \n
  • \n

    The last modified property in this case is the creation date of the\n object.

    \n
  • \n
\n
\n

Request headers are limited to 8 KB in size. For more information, see Common\n Request Headers.

\n

Consider the following when using request headers:

\n
    \n
  • \n

    Consideration 1 – If both of the If-Match and\n If-Unmodified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-Match condition evaluates to true, and;

      \n
    • \n
    • \n

      \n If-Unmodified-Since condition evaluates to\n false;

      \n
    • \n
    \n

    Then Amazon S3 returns 200 OK and the data requested.

    \n
  • \n
  • \n

    Consideration 2 – If both of the If-None-Match and\n If-Modified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-None-Match condition evaluates to false,\n and;

      \n
    • \n
    • \n

      \n If-Modified-Since condition evaluates to\n true;

      \n
    • \n
    \n

    Then Amazon S3 returns the 304 Not Modified response code.

    \n
  • \n
\n

For more information about conditional requests, see RFC 7232.

\n
\n
Permissions
\n
\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Actions, resources, and condition keys for Amazon S3. \n If the object you request does not exist, the error Amazon S3 returns depends\n on whether you also have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 returns\n an HTTP status code 404 (\"no such key\") error.

    \n
  • \n
  • \n

    If you don’t have the s3:ListBucket permission, Amazon S3 returns an HTTP\n status code 403 (\"access denied\") error.

    \n
  • \n
\n
\n
\n

The following actions are related to HeadObject:

\n ", "smithy.api#http": { "method": "HEAD", "uri": "/{Bucket}/{Key+}", @@ -25825,7 +25825,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

If the object is stored using server-side encryption either with an Amazon Web Services KMS key or \n an Amazon S3-managed encryption key, the response includes this header with\n the value of the server-side encryption algorithm used when storing this object in Amazon\n S3 (for example, AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -25853,7 +25853,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -25861,14 +25861,14 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with Amazon Web Services\n KMS (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

Provides storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage\n Classes.

", + "smithy.api#documentation": "

Provides storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage Classes.

", "smithy.api#httpHeader": "x-amz-storage-class" } }, @@ -25881,7 +25881,7 @@ "ReplicationStatus": { "target": "com.amazonaws.s3#ReplicationStatus", "traits": { - "smithy.api#documentation": "

Amazon S3 can return this header if your request involves a bucket that is either a source or\n a destination in a replication rule.

\n

In replication, you have a source bucket on which you configure replication and\n destination bucket or buckets where Amazon S3 stores object replicas. When you request an object\n (GetObject) or object metadata (HeadObject) from these\n buckets, Amazon S3 will return the x-amz-replication-status header in the response\n as follows:

\n
    \n
  • \n

    \n If requesting an object from the source bucket, Amazon S3 will return the\n x-amz-replication-status header if the object in your request is\n eligible for replication.

    \n

    For example, suppose that in your replication configuration, you specify object\n prefix TaxDocs requesting Amazon S3 to replicate objects with key prefix\n TaxDocs. Any objects you upload with this key name prefix, for\n example TaxDocs/document1.pdf, are eligible for replication. For any\n object request with this key name prefix, Amazon S3 will return the\n x-amz-replication-status header with value PENDING, COMPLETED or\n FAILED indicating object replication status.

    \n
  • \n
  • \n

    \n If requesting an object from a destination bucket, Amazon S3 will return the\n x-amz-replication-status header with value REPLICA if the object in\n your request is a replica that Amazon S3 created and there is no replica modification\n replication in progress.

    \n
  • \n
  • \n

    \n When replicating objects to multiple destination buckets, the\n x-amz-replication-status header acts differently. The header of the\n source object will only return a value of COMPLETED when replication is successful to\n all destinations. The header will remain at value PENDING until replication has\n completed for all destinations. If one or more destinations fails replication the\n header will return FAILED.

    \n
  • \n
\n

For more information, see Replication.

", + "smithy.api#documentation": "

Amazon S3 can return this header if your request involves a bucket that is either a source or\n a destination in a replication rule.

\n

In replication, you have a source bucket on which you configure replication and\n destination bucket or buckets where Amazon S3 stores object replicas. When you request an object\n (GetObject) or object metadata (HeadObject) from these\n buckets, Amazon S3 will return the x-amz-replication-status header in the response\n as follows:

\n
    \n
  • \n

    \n If requesting an object from the source bucket,\n Amazon S3 will return the x-amz-replication-status header if the object in\n your request is eligible for replication.

    \n

    For example, suppose that in your replication configuration, you specify object\n prefix TaxDocs requesting Amazon S3 to replicate objects with key prefix\n TaxDocs. Any objects you upload with this key name prefix, for\n example TaxDocs/document1.pdf, are eligible for replication. For any\n object request with this key name prefix, Amazon S3 will return the\n x-amz-replication-status header with value PENDING, COMPLETED or\n FAILED indicating object replication status.

    \n
  • \n
  • \n

    \n If requesting an object from a destination\n bucket, Amazon S3 will return the x-amz-replication-status header\n with value REPLICA if the object in your request is a replica that Amazon S3 created and\n there is no replica modification replication in progress.

    \n
  • \n
  • \n

    \n When replicating objects to multiple destination\n buckets, the x-amz-replication-status header acts\n differently. The header of the source object will only return a value of COMPLETED\n when replication is successful to all destinations. The header will remain at value\n PENDING until replication has completed for all destinations. If one or more\n destinations fails replication the header will return FAILED.

    \n
  • \n
\n

For more information, see Replication.

", "smithy.api#httpHeader": "x-amz-replication-status" } }, @@ -25889,14 +25889,14 @@ "target": "com.amazonaws.s3#PartsCount", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The count of parts this object has. This value is only returned if you specify partNumber\n in your request and the object was uploaded as a multipart upload.

", + "smithy.api#documentation": "

The count of parts this object has. This value is only returned if you specify\n partNumber in your request and the object was uploaded as a multipart\n upload.

", "smithy.api#httpHeader": "x-amz-mp-parts-count" } }, "ObjectLockMode": { "target": "com.amazonaws.s3#ObjectLockMode", "traits": { - "smithy.api#documentation": "

The Object Lock mode, if any, that's in effect for this object. This header is only\n returned if the requester has the s3:GetObjectRetention permission. For more\n information about S3 Object Lock, see Object\n Lock.

", + "smithy.api#documentation": "

The Object Lock mode, if any, that's in effect for this object. This header is only\n returned if the requester has the s3:GetObjectRetention permission. For more\n information about S3 Object Lock, see Object Lock.

", "smithy.api#httpHeader": "x-amz-object-lock-mode" } }, @@ -25925,7 +25925,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -25972,7 +25972,7 @@ "Range": { "target": "com.amazonaws.s3#Range", "traits": { - "smithy.api#documentation": "

Because HeadObject returns only the metadata for an object, this parameter\n has no effect.

", + "smithy.api#documentation": "

HeadObject returns only the metadata for an object. If the Range is satisfiable, only\n the ContentLength is affected in the response. If the Range is not\n satisfiable, S3 returns a 416 - Requested Range Not Satisfiable error.

", "smithy.api#httpHeader": "Range" } }, @@ -26028,7 +26028,7 @@ "ChecksumMode": { "target": "com.amazonaws.s3#ChecksumMode", "traits": { - "smithy.api#documentation": "

To retrieve the checksum, this parameter must be enabled.

\n

In addition, if you enable ChecksumMode and the object is encrypted with\n Amazon Web Services Key Management Service (Amazon Web Services KMS), you must have permission to use the\n kms:Decrypt action for the request to succeed.

", + "smithy.api#documentation": "

To retrieve the checksum, this parameter must be enabled.

\n

In addition, if you enable ChecksumMode and the object is encrypted with\n Amazon Web Services Key Management Service (Amazon Web Services KMS), you must have permission to use the\n kms:Decrypt action for the request to succeed.

", "smithy.api#httpHeader": "x-amz-checksum-mode" } } @@ -26085,7 +26085,7 @@ "ID": { "target": "com.amazonaws.s3#ID", "traits": { - "smithy.api#documentation": "

If the principal is an Amazon Web Services account, it provides the Canonical User ID. If the principal\n is an IAM User, it provides a user ARN value.

" + "smithy.api#documentation": "

If the principal is an Amazon Web Services account, it provides the Canonical User ID. If the\n principal is an IAM User, it provides a user ARN value.

" } }, "DisplayName": { @@ -26204,7 +26204,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies the S3 Intelligent-Tiering configuration for an Amazon S3 bucket.

\n

For information about the S3 Intelligent-Tiering storage class, see Storage class for\n automatically optimizing frequently and infrequently accessed objects.

" + "smithy.api#documentation": "

Specifies the S3 Intelligent-Tiering configuration for an Amazon S3 bucket.

\n

For information about the S3 Intelligent-Tiering storage class, see Storage class\n for automatically optimizing frequently and infrequently accessed\n objects.

" } }, "com.amazonaws.s3#IntelligentTieringConfigurationList": { @@ -26330,7 +26330,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies the inventory configuration for an Amazon S3 bucket. For more information, see\n GET Bucket inventory in the Amazon S3 API Reference.\n

" + "smithy.api#documentation": "

Specifies the inventory configuration for an Amazon S3 bucket. For more information, see\n GET Bucket inventory in the Amazon S3 API Reference.

" } }, "com.amazonaws.s3#InventoryConfigurationList": { @@ -26731,7 +26731,7 @@ "Date": { "target": "com.amazonaws.s3#Date", "traits": { - "smithy.api#documentation": "

Indicates at what date the object is to be moved or deleted. Should be in GMT ISO 8601\n Format.

" + "smithy.api#documentation": "

Indicates at what date the object is to be moved or deleted. The date value must conform to the ISO 8601 format. \n The time is always midnight UTC.

" } }, "Days": { @@ -26750,7 +26750,7 @@ } }, "traits": { - "smithy.api#documentation": "

Container for the expiration for the lifecycle of the object.

" + "smithy.api#documentation": "

Container for the expiration for the lifecycle of the object.

\n

For more information see, Managing your storage\n lifecycle in the Amazon S3 User Guide.

" } }, "com.amazonaws.s3#LifecycleRule": { @@ -26812,7 +26812,7 @@ } }, "traits": { - "smithy.api#documentation": "

A lifecycle rule for individual objects in an Amazon S3 bucket.

" + "smithy.api#documentation": "

A lifecycle rule for individual objects in an Amazon S3 bucket.

\n

For more information see, Managing your storage\n lifecycle in the Amazon S3 User Guide.

" } }, "com.amazonaws.s3#LifecycleRuleAndOperator": { @@ -26901,7 +26901,7 @@ "target": "com.amazonaws.s3#ListBucketAnalyticsConfigurationsOutput" }, "traits": { - "smithy.api#documentation": "

Lists the analytics configurations for the bucket. You can have up to 1,000 analytics\n configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations\n at a time. You should always check the IsTruncated element in the response. If\n there are no more configurations to list, IsTruncated is set to false. If\n there are more configurations to list, IsTruncated is set to true, and there\n will be a value in NextContinuationToken. You use the\n NextContinuationToken value to continue the pagination of the list by\n passing the value in continuation-token in the request to GET the next\n page.

\n

To use this operation, you must have permissions to perform the\n s3:GetAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis.

\n

The following operations are related to\n ListBucketAnalyticsConfigurations:

\n ", + "smithy.api#documentation": "

Lists the analytics configurations for the bucket. You can have up to 1,000 analytics\n configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations at\n a time. You should always check the IsTruncated element in the response. If\n there are no more configurations to list, IsTruncated is set to false. If\n there are more configurations to list, IsTruncated is set to true, and there\n will be a value in NextContinuationToken. You use the\n NextContinuationToken value to continue the pagination of the list by\n passing the value in continuation-token in the request to GET the next\n page.

\n

To use this operation, you must have permissions to perform the\n s3:GetAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about Amazon S3 analytics feature, see Amazon S3 Analytics – Storage Class\n Analysis.

\n

The following operations are related to\n ListBucketAnalyticsConfigurations:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?analytics&x-id=ListBucketAnalyticsConfigurations", @@ -26987,7 +26987,7 @@ "target": "com.amazonaws.s3#ListBucketIntelligentTieringConfigurationsOutput" }, "traits": { - "smithy.api#documentation": "

Lists the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to\n ListBucketIntelligentTieringConfigurations include:

\n ", + "smithy.api#documentation": "

Lists the S3 Intelligent-Tiering configuration from the specified bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to ListBucketIntelligentTieringConfigurations include:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?intelligent-tiering&x-id=ListBucketIntelligentTieringConfigurations", @@ -27065,7 +27065,7 @@ "target": "com.amazonaws.s3#ListBucketInventoryConfigurationsOutput" }, "traits": { - "smithy.api#documentation": "

Returns a list of inventory configurations for the bucket. You can have up to 1,000\n analytics configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations\n at a time. Always check the IsTruncated element in the response. If there are\n no more configurations to list, IsTruncated is set to false. If there are more\n configurations to list, IsTruncated is set to true, and there is a value in\n NextContinuationToken. You use the NextContinuationToken value\n to continue the pagination of the list by passing the value in continuation-token in the\n request to GET the next page.

\n

To use this operation, you must have permissions to perform the\n s3:GetInventoryConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory\n

\n

The following operations are related to\n ListBucketInventoryConfigurations:

\n ", + "smithy.api#documentation": "

Returns a list of inventory configurations for the bucket. You can have up to 1,000\n analytics configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations at\n a time. Always check the IsTruncated element in the response. If there are no\n more configurations to list, IsTruncated is set to false. If there are more\n configurations to list, IsTruncated is set to true, and there is a value in\n NextContinuationToken. You use the NextContinuationToken value\n to continue the pagination of the list by passing the value in continuation-token in the\n request to GET the next page.

\n

To use this operation, you must have permissions to perform the\n s3:GetInventoryConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about the Amazon S3 inventory feature, see Amazon S3 Inventory\n

\n

The following operations are related to\n ListBucketInventoryConfigurations:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?inventory&x-id=ListBucketInventoryConfigurations", @@ -27151,7 +27151,7 @@ "target": "com.amazonaws.s3#ListBucketMetricsConfigurationsOutput" }, "traits": { - "smithy.api#documentation": "

Lists the metrics configurations for the bucket. The metrics configurations are only for\n the request metrics of the bucket and do not provide information on daily storage metrics.\n You can have up to 1,000 configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations\n at a time. Always check the IsTruncated element in the response. If there are\n no more configurations to list, IsTruncated is set to false. If there are more\n configurations to list, IsTruncated is set to true, and there is a value in\n NextContinuationToken. You use the NextContinuationToken value\n to continue the pagination of the list by passing the value in\n continuation-token in the request to GET the next page.

\n

To use this operation, you must have permissions to perform the\n s3:GetMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For more information about metrics configurations and CloudWatch request metrics, see\n Monitoring Metrics with Amazon\n CloudWatch.

\n

The following operations are related to\n ListBucketMetricsConfigurations:

\n ", + "smithy.api#documentation": "

Lists the metrics configurations for the bucket. The metrics configurations are only for\n the request metrics of the bucket and do not provide information on daily storage metrics.\n You can have up to 1,000 configurations per bucket.

\n

This action supports list pagination and does not return more than 100 configurations at\n a time. Always check the IsTruncated element in the response. If there are no\n more configurations to list, IsTruncated is set to false. If there are more\n configurations to list, IsTruncated is set to true, and there is a value in\n NextContinuationToken. You use the NextContinuationToken value\n to continue the pagination of the list by passing the value in\n continuation-token in the request to GET the next page.

\n

To use this operation, you must have permissions to perform the\n s3:GetMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For more information about metrics configurations and CloudWatch request metrics, see\n Monitoring Metrics with Amazon CloudWatch.

\n

The following operations are related to\n ListBucketMetricsConfigurations:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?metrics&x-id=ListBucketMetricsConfigurations", @@ -27237,7 +27237,7 @@ "target": "com.amazonaws.s3#ListBucketsOutput" }, "traits": { - "smithy.api#documentation": "

Returns a list of all buckets owned by the authenticated sender of the request. To use\n this operation, you must have the s3:ListAllMyBuckets permission.

", + "smithy.api#documentation": "

Returns a list of all buckets owned by the authenticated sender of the request. To use\n this operation, you must have the s3:ListAllMyBuckets permission.

\n

For information about Amazon S3 buckets, see Creating, configuring, and\n working with Amazon S3 buckets.

", "smithy.api#http": { "method": "GET", "uri": "/", @@ -27275,7 +27275,7 @@ "target": "com.amazonaws.s3#ListMultipartUploadsOutput" }, "traits": { - "smithy.api#documentation": "

This action lists in-progress multipart uploads. An in-progress multipart upload is a\n multipart upload that has been initiated using the Initiate Multipart Upload request, but\n has not yet been completed or aborted.

\n

This action returns at most 1,000 multipart uploads in the response. 1,000 multipart\n uploads is the maximum number of uploads a response can include, which is also the default\n value. You can further limit the number of uploads in a response by specifying the\n max-uploads parameter in the response. If additional multipart uploads\n satisfy the list criteria, the response will contain an IsTruncated element\n with the value true. To list the additional multipart uploads, use the\n key-marker and upload-id-marker request parameters.

\n

In the response, the uploads are sorted by key. If your application has initiated more\n than one multipart upload using the same object key, then uploads in the response are first\n sorted by key. Additionally, uploads are sorted in ascending order within each key by the\n upload initiation time.

\n

For more information on multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information on permissions required to use the multipart upload API, see Multipart Upload and\n Permissions.

\n

The following operations are related to ListMultipartUploads:

\n ", + "smithy.api#documentation": "

This action lists in-progress multipart uploads. An in-progress multipart upload is a\n multipart upload that has been initiated using the Initiate Multipart Upload request, but\n has not yet been completed or aborted.

\n

This action returns at most 1,000 multipart uploads in the response. 1,000 multipart\n uploads is the maximum number of uploads a response can include, which is also the default\n value. You can further limit the number of uploads in a response by specifying the\n max-uploads parameter in the response. If additional multipart uploads\n satisfy the list criteria, the response will contain an IsTruncated element\n with the value true. To list the additional multipart uploads, use the\n key-marker and upload-id-marker request parameters.

\n

In the response, the uploads are sorted by key. If your application has initiated more\n than one multipart upload using the same object key, then uploads in the response are first\n sorted by key. Additionally, uploads are sorted in ascending order within each key by the\n upload initiation time.

\n

For more information on multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information on permissions required to use the multipart upload API, see Multipart Upload\n and Permissions.

\n

The following operations are related to ListMultipartUploads:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?uploads", @@ -27289,7 +27289,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the access point ARN or access point alias if used.

" + "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the\n access point ARN or access point alias if used.

" } }, "KeyMarker": { @@ -27375,7 +27375,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -27446,7 +27446,7 @@ "target": "com.amazonaws.s3#ListObjectVersionsOutput" }, "traits": { - "smithy.api#documentation": "

Returns metadata about all versions of the objects in a bucket. You can also use request\n parameters as selection criteria to return metadata about a subset of all the object\n versions.

\n \n

\n To use this operation, you must have permissions to perform the \n s3:ListBucketVersions action. Be aware of the name difference.\n

\n
\n \n

A 200 OK response can contain valid or invalid XML. Make sure to design your\n application to parse the contents of the response and handle it appropriately.

\n
\n

To use this operation, you must have READ access to the bucket.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following operations are related to\n ListObjectVersions:

\n ", + "smithy.api#documentation": "

Returns metadata about all versions of the objects in a bucket. You can also use request\n parameters as selection criteria to return metadata about a subset of all the object\n versions.

\n \n

To use this operation, you must have permissions to perform the\n s3:ListBucketVersions action. Be aware of the name difference.

\n
\n \n

A 200 OK response can contain valid or invalid XML. Make sure to design your\n application to parse the contents of the response and handle it appropriately.

\n
\n

To use this operation, you must have READ access to the bucket.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following operations are related to ListObjectVersions:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?versions", @@ -27586,7 +27586,7 @@ "target": "com.amazonaws.s3#MaxKeys", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns up\n to 1,000 key names. The response might contain fewer keys but will never contain more. If\n additional keys satisfy the search criteria, but were not returned because max-keys was\n exceeded, the response contains true. To return the\n additional keys, see key-marker and version-id-marker.

", + "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns\n up to 1,000 key names. The response might contain fewer keys but will never contain more.\n If additional keys satisfy the search criteria, but were not returned because max-keys was\n exceeded, the response contains true. To return the\n additional keys, see key-marker and version-id-marker.

", "smithy.api#httpQuery": "max-keys" } }, @@ -27695,7 +27695,7 @@ "CommonPrefixes": { "target": "com.amazonaws.s3#CommonPrefixList", "traits": { - "smithy.api#documentation": "

All of the keys (up to 1,000) rolled up in a common prefix count as a single return when calculating\n the number of returns.

\n

A response can contain CommonPrefixes only if you specify a delimiter.

\n

CommonPrefixes contains all (if there are any) keys between Prefix and the next\n occurrence of the string specified by the delimiter.

\n

CommonPrefixes lists keys that act like subdirectories in the directory specified by\n Prefix.

\n

For example, if the prefix is notes/ and the delimiter is a slash (/) as in\n notes/summer/july, the common prefix is notes/summer/. All of the keys that roll up into a\n common prefix count as a single return when calculating the number of returns.

", + "smithy.api#documentation": "

All of the keys (up to 1,000) rolled up in a common prefix count as a single return when\n calculating the number of returns.

\n

A response can contain CommonPrefixes only if you specify a delimiter.

\n

CommonPrefixes contains all (if there are any) keys between Prefix and the next\n occurrence of the string specified by the delimiter.

\n

CommonPrefixes lists keys that act like subdirectories in the directory specified by\n Prefix.

\n

For example, if the prefix is notes/ and the delimiter is a slash (/) as in\n notes/summer/july, the common prefix is notes/summer/. All of the keys that roll up into a\n common prefix count as a single return when calculating the number of returns.

", "smithy.api#xmlFlattened": {} } }, @@ -27717,7 +27717,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket containing the objects.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket containing the objects.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -27741,7 +27741,7 @@ "Marker": { "target": "com.amazonaws.s3#Marker", "traits": { - "smithy.api#documentation": "

Marker is where you want Amazon S3 to start listing from. Amazon S3 starts listing after\n this specified key. Marker can be any key in the bucket.

", + "smithy.api#documentation": "

Marker is where you want Amazon S3 to start listing from. Amazon S3 starts listing after\n this specified key. Marker can be any key in the bucket.

", "smithy.api#httpQuery": "marker" } }, @@ -27749,7 +27749,7 @@ "target": "com.amazonaws.s3#MaxKeys", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns up\n to 1,000 key names. The response might contain fewer keys but will never contain more.\n

", + "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns\n up to 1,000 key names. The response might contain fewer keys but will never contain more.\n

", "smithy.api#httpQuery": "max-keys" } }, @@ -27793,7 +27793,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns some or all (up to 1,000) of the objects in a bucket with each request. You can use\n the request parameters as selection criteria to return a subset of the objects in a bucket. A \n 200 OK response can contain valid or invalid XML. Make sure to design your\n application to parse the contents of the response and handle it appropriately. \n Objects are returned sorted in an ascending order of the respective key names in the list.\n For more information about listing objects, see Listing object keys \n programmatically\n

\n

To use this operation, you must have READ access to the bucket.

\n

To use this action in an Identity and Access Management (IAM) policy, you must\n have permissions to perform the s3:ListBucket action. The bucket owner has\n this permission by default and can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n \n

This section describes the latest revision of this action. We recommend that you use this\n revised API for application development. For backward compatibility, Amazon S3 continues to\n support the prior version of this API, ListObjects.

\n
\n

To get a list of your buckets, see ListBuckets.

\n

The following operations are related to ListObjectsV2:

\n ", + "smithy.api#documentation": "

Returns some or all (up to 1,000) of the objects in a bucket with each request. You can\n use the request parameters as selection criteria to return a subset of the objects in a\n bucket. A 200 OK response can contain valid or invalid XML. Make sure to\n design your application to parse the contents of the response and handle it appropriately.\n Objects are returned sorted in an ascending order of the respective key names in the list.\n For more information about listing objects, see Listing object keys\n programmatically\n

\n

To use this operation, you must have READ access to the bucket.

\n

To use this action in an Identity and Access Management (IAM) policy, you must have permissions to perform\n the s3:ListBucket action. The bucket owner has this permission by default and\n can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n \n

This section describes the latest revision of this action. We recommend that you use\n this revised API for application development. For backward compatibility, Amazon S3 continues\n to support the prior version of this API, ListObjects.

\n
\n

To get a list of your buckets, see ListBuckets.

\n

The following operations are related to ListObjectsV2:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?list-type=2", @@ -27826,7 +27826,7 @@ "Name": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

" } }, "Prefix": { @@ -27845,13 +27845,13 @@ "target": "com.amazonaws.s3#MaxKeys", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns up\n to 1,000 key names. The response might contain fewer keys but will never contain\n more.

" + "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns\n up to 1,000 key names. The response might contain fewer keys but will never contain\n more.

" } }, "CommonPrefixes": { "target": "com.amazonaws.s3#CommonPrefixList", "traits": { - "smithy.api#documentation": "

All of the keys (up to 1,000) rolled up into a common prefix count as a single return when calculating\n the number of returns.

\n

A response can contain CommonPrefixes only if you specify a\n delimiter.

\n

\n CommonPrefixes contains all (if there are any) keys between\n Prefix and the next occurrence of the string specified by a\n delimiter.

\n

\n CommonPrefixes lists keys that act like subdirectories in the directory\n specified by Prefix.

\n

For example, if the prefix is notes/ and the delimiter is a slash\n (/) as in notes/summer/july, the common prefix is\n notes/summer/. All of the keys that roll up into a common prefix count as a\n single return when calculating the number of returns.

", + "smithy.api#documentation": "

All of the keys (up to 1,000) rolled up into a common prefix count as a single return\n when calculating the number of returns.

\n

A response can contain CommonPrefixes only if you specify a\n delimiter.

\n

\n CommonPrefixes contains all (if there are any) keys between\n Prefix and the next occurrence of the string specified by a\n delimiter.

\n

\n CommonPrefixes lists keys that act like subdirectories in the directory\n specified by Prefix.

\n

For example, if the prefix is notes/ and the delimiter is a slash\n (/) as in notes/summer/july, the common prefix is\n notes/summer/. All of the keys that roll up into a common prefix count as a\n single return when calculating the number of returns.

", "smithy.api#xmlFlattened": {} } }, @@ -27865,7 +27865,7 @@ "target": "com.amazonaws.s3#KeyCount", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

KeyCount is the number of keys returned with this request. KeyCount will always be less\n than or equals to MaxKeys field. Say you ask for 50 keys, your result will include less than\n equals 50 keys

" + "smithy.api#documentation": "

KeyCount is the number of keys returned with this request. KeyCount will always be less\n than or equal to the MaxKeys field. Say you ask for 50 keys, your result will\n include 50 keys or fewer.

" } }, "ContinuationToken": { @@ -27898,7 +27898,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

Bucket name to list.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Bucket name to list.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -27924,7 +27924,7 @@ "target": "com.amazonaws.s3#MaxKeys", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns up\n to 1,000 key names. The response might contain fewer keys but will never contain\n more.

", + "smithy.api#documentation": "

Sets the maximum number of keys returned in the response. By default the action returns\n up to 1,000 key names. The response might contain fewer keys but will never contain\n more.

", "smithy.api#httpQuery": "max-keys" } }, @@ -27985,7 +27985,7 @@ "target": "com.amazonaws.s3#ListPartsOutput" }, "traits": { - "smithy.api#documentation": "

Lists the parts that have been uploaded for a specific multipart upload. This operation\n must include the upload ID, which you obtain by sending the initiate multipart upload\n request (see CreateMultipartUpload).\n This request returns a maximum of 1,000 uploaded parts. The default number of parts\n returned is 1,000 parts. You can restrict the number of parts returned by specifying the\n max-parts request parameter. If your multipart upload consists of more than\n 1,000 parts, the response returns an IsTruncated field with the value of true,\n and a NextPartNumberMarker element. In subsequent ListParts\n requests you can include the part-number-marker query string parameter and set its value to\n the NextPartNumberMarker field value from the previous response.

\n

If the upload was created using a checksum algorithm, you will need to have permission\n to the kms:Decrypt action for the request to succeed.\n

\n

For more information on multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information on permissions required to use the multipart upload API, see Multipart Upload and\n Permissions.

\n

The following operations are related to ListParts:

\n ", + "smithy.api#documentation": "

Lists the parts that have been uploaded for a specific multipart upload. This operation\n must include the upload ID, which you obtain by sending the initiate multipart upload\n request (see CreateMultipartUpload).\n This request returns a maximum of 1,000 uploaded parts. The default number of parts\n returned is 1,000 parts. You can restrict the number of parts returned by specifying the\n max-parts request parameter. If your multipart upload consists of more than\n 1,000 parts, the response returns an IsTruncated field with the value of true,\n and a NextPartNumberMarker element. In subsequent ListParts\n requests you can include the part-number-marker query string parameter and set its value to\n the NextPartNumberMarker field value from the previous response.

\n

If the upload was created using a checksum algorithm, you will need to have permission\n to the kms:Decrypt action for the request to succeed.

\n

For more information on multipart uploads, see Uploading Objects Using Multipart\n Upload.

\n

For information on permissions required to use the multipart upload API, see Multipart Upload\n and Permissions.

\n

The following operations are related to ListParts:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?x-id=ListParts", @@ -28005,7 +28005,7 @@ "AbortDate": { "target": "com.amazonaws.s3#AbortDate", "traits": { - "smithy.api#documentation": "

If the bucket has a lifecycle rule configured with an action to abort incomplete\n multipart uploads and the prefix in the lifecycle rule matches the object name in the\n request, then the response includes this header indicating when the initiated multipart\n upload will become eligible for abort operation. For more information, see Aborting\n Incomplete Multipart Uploads Using a Bucket Lifecycle Policy.

\n

The response will also include the x-amz-abort-rule-id header that will\n provide the ID of the lifecycle configuration rule that defines this action.

", + "smithy.api#documentation": "

If the bucket has a lifecycle rule configured with an action to abort incomplete\n multipart uploads and the prefix in the lifecycle rule matches the object name in the\n request, then the response includes this header indicating when the initiated multipart\n upload will become eligible for abort operation. For more information, see Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Configuration.

\n

The response will also include the x-amz-abort-rule-id header that will\n provide the ID of the lifecycle configuration rule that defines this action.

", "smithy.api#httpHeader": "x-amz-abort-date" } }, @@ -28019,7 +28019,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the access point ARN or access point alias if used.

" + "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated. Does not return the\n access point ARN or access point alias if used.

" } }, "Key": { @@ -28110,7 +28110,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the parts are being uploaded.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket to which the parts are being uploaded.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -28207,7 +28207,7 @@ "TargetGrants": { "target": "com.amazonaws.s3#TargetGrants", "traits": { - "smithy.api#documentation": "

Container for granting information.

\n

Buckets that use the bucket owner enforced setting for Object\n Ownership don't support target grants. For more information, see Permissions for server access log delivery in the\n Amazon S3 User Guide.

" + "smithy.api#documentation": "

Container for granting information.

\n

Buckets that use the bucket owner enforced setting for Object Ownership don't support\n target grants. For more information, see Permissions for server access log delivery in the\n Amazon S3 User Guide.

" } }, "TargetPrefix": { @@ -28396,14 +28396,14 @@ "Id": { "target": "com.amazonaws.s3#MetricsId", "traits": { - "smithy.api#documentation": "

The ID used to identify the metrics configuration.

", + "smithy.api#documentation": "

The ID used to identify the metrics configuration. The ID has a 64 character limit and\n can only contain letters, numbers, periods, dashes, and underscores.

", "smithy.api#required": {} } }, "Filter": { "target": "com.amazonaws.s3#MetricsFilter", "traits": { - "smithy.api#documentation": "

Specifies a metrics configuration filter. The metrics configuration will only include\n objects that meet the filter's criteria. A filter must be a prefix, an object tag, an access point ARN, or a conjunction\n (MetricsAndOperator).

" + "smithy.api#documentation": "

Specifies a metrics configuration filter. The metrics configuration will only include\n objects that meet the filter's criteria. A filter must be a prefix, an object tag, an\n access point ARN, or a conjunction (MetricsAndOperator).

" } } }, @@ -28446,7 +28446,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies a metrics configuration filter. The metrics configuration only includes\n objects that meet the filter's criteria. A filter must be a prefix, an object tag, an access point ARN, or a conjunction\n (MetricsAndOperator). For more information, see PutBucketMetricsConfiguration.

" + "smithy.api#documentation": "

Specifies a metrics configuration filter. The metrics configuration only includes\n objects that meet the filter's criteria. A filter must be a prefix, an object tag, an\n access point ARN, or a conjunction (MetricsAndOperator). For more information, see PutBucketMetricsConfiguration.

" } }, "com.amazonaws.s3#MetricsId": { @@ -28589,14 +28589,14 @@ "target": "com.amazonaws.s3#Days", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Specifies the number of days an object is noncurrent before Amazon S3 can perform the\n associated action. The value must be a non-zero positive integer. For information about the noncurrent days calculations, see How\n Amazon S3 Calculates When an Object Became Noncurrent in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies the number of days an object is noncurrent before Amazon S3 can perform the\n associated action. The value must be a non-zero positive integer. For information about the\n noncurrent days calculations, see How\n Amazon S3 Calculates When an Object Became Noncurrent in the\n Amazon S3 User Guide.

" } }, "NewerNoncurrentVersions": { "target": "com.amazonaws.s3#VersionCount", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Specifies how many noncurrent versions Amazon S3 will retain. If there are this many more recent\n noncurrent versions, Amazon S3 will take the associated action. For more information about noncurrent\n versions, see Lifecycle configuration elements\n in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies how many noncurrent versions Amazon S3 will retain. If there are this many more\n recent noncurrent versions, Amazon S3 will take the associated action. For more information\n about noncurrent versions, see Lifecycle configuration\n elements in the Amazon S3 User Guide.

" } } }, @@ -28624,12 +28624,12 @@ "target": "com.amazonaws.s3#VersionCount", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Specifies how many noncurrent versions Amazon S3 will retain. If there are this many more recent\n noncurrent versions, Amazon S3 will take the associated action. For more information about noncurrent\n versions, see Lifecycle configuration elements\n in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies how many noncurrent versions Amazon S3 will retain. If there are this many more\n recent noncurrent versions, Amazon S3 will take the associated action. For more information\n about noncurrent versions, see Lifecycle configuration\n elements in the Amazon S3 User Guide.

" } } }, "traits": { - "smithy.api#documentation": "

Container for the transition rule that describes when noncurrent objects transition to\n the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING,\n GLACIER_IR, GLACIER, or DEEP_ARCHIVE storage class. If your bucket is\n versioning-enabled (or versioning is suspended), you can set this action to request that\n Amazon S3 transition noncurrent object versions to the STANDARD_IA,\n ONEZONE_IA, INTELLIGENT_TIERING, GLACIER_IR, GLACIER, or\n DEEP_ARCHIVE storage class at a specific period in the object's\n lifetime.

" + "smithy.api#documentation": "

Container for the transition rule that describes when noncurrent objects transition to\n the STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING,\n GLACIER_IR, GLACIER, or DEEP_ARCHIVE storage\n class. If your bucket is versioning-enabled (or versioning is suspended), you can set this\n action to request that Amazon S3 transition noncurrent object versions to the\n STANDARD_IA, ONEZONE_IA, INTELLIGENT_TIERING,\n GLACIER_IR, GLACIER, or DEEP_ARCHIVE storage\n class at a specific period in the object's lifetime.

" } }, "com.amazonaws.s3#NoncurrentVersionTransitionList": { @@ -28695,7 +28695,7 @@ } }, "traits": { - "smithy.api#documentation": "

Specifies object key name filtering rules. For information about key name filtering, see\n Configuring\n Event Notifications in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies object key name filtering rules. For information about key name filtering, see\n Configuring event notifications using object key name filtering in the Amazon S3 User Guide.

" } }, "com.amazonaws.s3#NotificationId": { @@ -28722,7 +28722,7 @@ "ETag": { "target": "com.amazonaws.s3#ETag", "traits": { - "smithy.api#documentation": "

The entity tag is a hash of the object. The ETag reflects changes only to the contents\n of an object, not its metadata. The ETag may or may not be an MD5 digest of the object\n data. Whether or not it is depends on how the object was created and how it is encrypted as\n described below:

\n
    \n
  • \n

    Objects created by the PUT Object, POST Object, or Copy operation, or through the\n Amazon Web Services Management Console, and are encrypted by SSE-S3 or plaintext, have ETags that are\n an MD5 digest of their object data.

    \n
  • \n
  • \n

    Objects created by the PUT Object, POST Object, or Copy operation, or through the\n Amazon Web Services Management Console, and are encrypted by SSE-C or SSE-KMS, have ETags that are\n not an MD5 digest of their object data.

    \n
  • \n
  • \n

    If an object is created by either the Multipart Upload or Part Copy operation, the\n ETag is not an MD5 digest, regardless of the method of encryption. If an object\n is larger than 16 MB, the Amazon Web Services Management Console will upload or copy that object as a\n Multipart Upload, and therefore the ETag will not be an MD5 digest.

    \n
  • \n
" + "smithy.api#documentation": "

The entity tag is a hash of the object. The ETag reflects changes only to the contents\n of an object, not its metadata. The ETag may or may not be an MD5 digest of the object\n data. Whether or not it is depends on how the object was created and how it is encrypted as\n described below:

\n
    \n
  • \n

    Objects created by the PUT Object, POST Object, or Copy operation, or through the\n Amazon Web Services Management Console, and are encrypted by SSE-S3 or plaintext, have ETags that\n are an MD5 digest of their object data.

    \n
  • \n
  • \n

    Objects created by the PUT Object, POST Object, or Copy operation, or through the\n Amazon Web Services Management Console, and are encrypted by SSE-C or SSE-KMS, have ETags that are\n not an MD5 digest of their object data.

    \n
  • \n
  • \n

    If an object is created by either the Multipart Upload or Part Copy operation, the\n ETag is not an MD5 digest, regardless of the method of encryption. If an object is\n larger than 16 MB, the Amazon Web Services Management Console will upload or copy that object as a\n Multipart Upload, and therefore the ETag will not be an MD5 digest.

    \n
  • \n
" } }, "ChecksumAlgorithm": { @@ -28899,13 +28899,13 @@ "ObjectLockEnabled": { "target": "com.amazonaws.s3#ObjectLockEnabled", "traits": { - "smithy.api#documentation": "

Indicates whether this bucket has an Object Lock configuration enabled.\n Enable ObjectLockEnabled when you apply ObjectLockConfiguration\n to a bucket.

" + "smithy.api#documentation": "

Indicates whether this bucket has an Object Lock configuration enabled. Enable\n ObjectLockEnabled when you apply ObjectLockConfiguration to a\n bucket.

" } }, "Rule": { "target": "com.amazonaws.s3#ObjectLockRule", "traits": { - "smithy.api#documentation": "

Specifies the Object Lock rule for the specified object. Enable the this rule when you apply\n ObjectLockConfiguration to a bucket. Bucket settings require both a mode and a period.\n The period can be either Days or Years but you must select one.\n You cannot specify Days and Years at the same time.

" + "smithy.api#documentation": "

Specifies the Object Lock rule for the specified object. Enable the this rule when you\n apply ObjectLockConfiguration to a bucket. Bucket settings require both a mode\n and a period. The period can be either Days or Years but you must\n select one. You cannot specify Days and Years at the same\n time.

" } } }, @@ -29027,7 +29027,7 @@ "DefaultRetention": { "target": "com.amazonaws.s3#DefaultRetention", "traits": { - "smithy.api#documentation": "

The default Object Lock retention mode and period that you want to apply to new objects\n placed in the specified bucket. Bucket settings require both a mode and a period.\n The period can be either Days or Years but you must select one.\n You cannot specify Days and Years at the same time.

" + "smithy.api#documentation": "

The default Object Lock retention mode and period that you want to apply to new objects\n placed in the specified bucket. Bucket settings require both a mode and a period. The\n period can be either Days or Years but you must select one. You\n cannot specify Days and Years at the same time.

" } } }, @@ -29069,7 +29069,7 @@ } }, "traits": { - "smithy.api#documentation": "

The container element for object ownership for a bucket's ownership controls.

\n

BucketOwnerPreferred - Objects uploaded to the bucket change ownership to the bucket\n owner if the objects are uploaded with the bucket-owner-full-control canned\n ACL.

\n

ObjectWriter - The uploading account will own the object if the object is uploaded with\n the bucket-owner-full-control canned ACL.

\n

BucketOwnerEnforced - Access control lists (ACLs) are disabled and no longer affect permissions. \n The bucket owner automatically owns and has full control over every object in the bucket. The bucket only\n accepts PUT requests that don't specify an ACL or bucket owner full control\n ACLs, such as the bucket-owner-full-control canned\n ACL or an equivalent form of this ACL expressed in the XML format.

" + "smithy.api#documentation": "

The container element for object ownership for a bucket's ownership controls.

\n

BucketOwnerPreferred - Objects uploaded to the bucket change ownership to the bucket\n owner if the objects are uploaded with the bucket-owner-full-control canned\n ACL.

\n

ObjectWriter - The uploading account will own the object if the object is uploaded with\n the bucket-owner-full-control canned ACL.

\n

BucketOwnerEnforced - Access control lists (ACLs) are disabled and no longer affect\n permissions. The bucket owner automatically owns and has full control over every object in\n the bucket. The bucket only accepts PUT requests that don't specify an ACL or bucket owner\n full control ACLs, such as the bucket-owner-full-control canned ACL or an\n equivalent form of this ACL expressed in the XML format.

" } }, "com.amazonaws.s3#ObjectPart": { @@ -29326,7 +29326,7 @@ "DisplayName": { "target": "com.amazonaws.s3#DisplayName", "traits": { - "smithy.api#documentation": "

Container for the display name of the owner.

" + "smithy.api#documentation": "

Container for the display name of the owner. This value is only supported in the\n following Amazon Web Services Regions:

\n
    \n
  • \n

    US East (N. Virginia)

    \n
  • \n
  • \n

    US West (N. California)

    \n
  • \n
  • \n

    US West (Oregon)

    \n
  • \n
  • \n

    Asia Pacific (Singapore)

    \n
  • \n
  • \n

    Asia Pacific (Sydney)

    \n
  • \n
  • \n

    Asia Pacific (Tokyo)

    \n
  • \n
  • \n

    Europe (Ireland)

    \n
  • \n
  • \n

    South America (São Paulo)

    \n
  • \n
" } }, "ID": { @@ -29652,7 +29652,7 @@ "target": "com.amazonaws.s3#Setting", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should restrict public bucket policies for this bucket. Setting\n this element to TRUE restricts access to this bucket to only Amazon Web Service\n principals and authorized users within this account if the bucket has a public\n policy.

\n

Enabling this setting doesn't affect previously stored bucket policies, except that\n public and cross-account access within any public bucket policy, including non-public\n delegation to specific accounts, is blocked.

", + "smithy.api#documentation": "

Specifies whether Amazon S3 should restrict public bucket policies for this bucket. Setting\n this element to TRUE restricts access to this bucket to only Amazon Web Service principals and authorized users within this account if the bucket has\n a public policy.

\n

Enabling this setting doesn't affect previously stored bucket policies, except that\n public and cross-account access within any public bucket policy, including non-public\n delegation to specific accounts, is blocked.

", "smithy.api#xmlName": "RestrictPublicBuckets" } } @@ -29673,7 +29673,7 @@ "aws.protocols#httpChecksum": { "requestAlgorithmMember": "ChecksumAlgorithm" }, - "smithy.api#documentation": "

Sets the accelerate configuration of an existing bucket. Amazon S3 Transfer Acceleration is a\n bucket-level feature that enables you to perform faster data transfers to Amazon S3.

\n

To use this operation, you must have permission to perform the\n s3:PutAccelerateConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

The Transfer Acceleration state of a bucket can be set to one of the following two\n values:

\n
    \n
  • \n

    Enabled – Enables accelerated data transfers to the bucket.

    \n
  • \n
  • \n

    Suspended – Disables accelerated data transfers to the bucket.

    \n
  • \n
\n

The GetBucketAccelerateConfiguration action returns the transfer acceleration\n state of a bucket.

\n

After setting the Transfer Acceleration state of a bucket to Enabled, it might take up\n to thirty minutes before the data transfer rates to the bucket increase.

\n

The name of the bucket used for Transfer Acceleration must be DNS-compliant and must\n not contain periods (\".\").

\n

For more information about transfer acceleration, see Transfer Acceleration.

\n

The following operations are related to\n PutBucketAccelerateConfiguration:

\n ", + "smithy.api#documentation": "

Sets the accelerate configuration of an existing bucket. Amazon S3 Transfer Acceleration is a\n bucket-level feature that enables you to perform faster data transfers to Amazon S3.

\n

To use this operation, you must have permission to perform the\n s3:PutAccelerateConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

The Transfer Acceleration state of a bucket can be set to one of the following two\n values:

\n
    \n
  • \n

    Enabled – Enables accelerated data transfers to the bucket.

    \n
  • \n
  • \n

    Suspended – Disables accelerated data transfers to the bucket.

    \n
  • \n
\n

The GetBucketAccelerateConfiguration action returns the transfer acceleration state\n of a bucket.

\n

After setting the Transfer Acceleration state of a bucket to Enabled, it might take up\n to thirty minutes before the data transfer rates to the bucket increase.

\n

The name of the bucket used for Transfer Acceleration must be DNS-compliant and must\n not contain periods (\".\").

\n

For more information about transfer acceleration, see Transfer\n Acceleration.

\n

The following operations are related to\n PutBucketAccelerateConfiguration:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?accelerate", @@ -29736,7 +29736,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the permissions on an existing bucket using access control lists (ACL). For more\n information, see Using ACLs. To set\n the ACL of a bucket, you must have WRITE_ACP permission.

\n

You can use one of the following two ways to set a bucket's permissions:

\n
    \n
  • \n

    Specify the ACL in the request body

    \n
  • \n
  • \n

    Specify permissions using request headers

    \n
  • \n
\n \n

You cannot specify access permission using both the body and the request\n headers.

\n
\n

Depending on your application needs, you may choose to set the ACL on a bucket using\n either the request body or the headers. For example, if you have an existing application\n that updates a bucket ACL using the request body, then you can continue to use that\n approach.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, ACLs are disabled and no longer affect permissions. \n You must use policies to grant access to your bucket and the objects in it. Requests to set ACLs or update ACLs fail and \n return the AccessControlListNotSupported error code. Requests to read ACLs are still supported.\n For more information, see Controlling object ownership\n in the Amazon S3 User Guide.

\n
\n

\n Access Permissions\n

\n

You can set access permissions using one of the following methods:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports\n a set of predefined ACLs, known as canned ACLs. Each canned ACL\n has a predefined set of grantees and permissions. Specify the canned ACL name as the\n value of x-amz-acl. If you use this header, you cannot use other access\n control-specific headers in your request. For more information, see Canned ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the x-amz-grant-read,\n x-amz-grant-read-acp, x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. When using these headers, you\n specify explicit access permissions and grantees (Amazon Web Services accounts or Amazon S3 groups) who\n will receive the permission. If you use these ACL-specific headers, you cannot use\n the x-amz-acl header to set a canned ACL. These parameters map to the\n set of permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL)\n Overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-write header grants create,\n overwrite, and delete objects permission to LogDelivery group predefined by Amazon S3 and\n two Amazon Web Services accounts identified by their email addresses.

    \n

    \n x-amz-grant-write: uri=\"http://acs.amazonaws.com/groups/s3/LogDelivery\",\n id=\"111122223333\", id=\"555566667777\" \n

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You cannot do\n both.

\n

\n Grantee Values\n

\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>lt;/Grantee>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n \n

    Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

    \n
      \n
    • \n

      US East (N. Virginia)

      \n
    • \n
    • \n

      US West (N. California)

      \n
    • \n
    • \n

      US West (Oregon)

      \n
    • \n
    • \n

      Asia Pacific (Singapore)

      \n
    • \n
    • \n

      Asia Pacific (Sydney)

      \n
    • \n
    • \n

      Asia Pacific (Tokyo)

      \n
    • \n
    • \n

      Europe (Ireland)

      \n
    • \n
    • \n

      South America (São Paulo)

      \n
    • \n
    \n

    For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

    \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Sets the permissions on an existing bucket using access control lists (ACL). For more\n information, see Using ACLs. To set the ACL of a\n bucket, you must have WRITE_ACP permission.

\n

You can use one of the following two ways to set a bucket's permissions:

\n
    \n
  • \n

    Specify the ACL in the request body

    \n
  • \n
  • \n

    Specify permissions using request headers

    \n
  • \n
\n \n

You cannot specify access permission using both the body and the request\n headers.

\n
\n

Depending on your application needs, you may choose to set the ACL on a bucket using\n either the request body or the headers. For example, if you have an existing application\n that updates a bucket ACL using the request body, then you can continue to use that\n approach.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, ACLs\n are disabled and no longer affect permissions. You must use policies to grant access to\n your bucket and the objects in it. Requests to set ACLs or update ACLs fail and return\n the AccessControlListNotSupported error code. Requests to read ACLs are\n still supported. For more information, see Controlling object\n ownership in the Amazon S3 User Guide.

\n
\n
\n
Permissions
\n
\n

You can set access permissions using one of the following methods:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports\n a set of predefined ACLs, known as canned ACLs. Each canned ACL\n has a predefined set of grantees and permissions. Specify the canned ACL name as the\n value of x-amz-acl. If you use this header, you cannot use other access\n control-specific headers in your request. For more information, see Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the x-amz-grant-read,\n x-amz-grant-read-acp, x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. When using these headers, you\n specify explicit access permissions and grantees (Amazon Web Services accounts or Amazon S3 groups) who\n will receive the permission. If you use these ACL-specific headers, you cannot use\n the x-amz-acl header to set a canned ACL. These parameters map to the\n set of permissions that Amazon S3 supports in an ACL. For more information, see Access Control\n List (ACL) Overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an\n Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-write header grants create,\n overwrite, and delete objects permission to LogDelivery group predefined by Amazon S3 and\n two Amazon Web Services accounts identified by their email addresses.

    \n

    \n x-amz-grant-write: uri=\"http://acs.amazonaws.com/groups/s3/LogDelivery\",\n id=\"111122223333\", id=\"555566667777\" \n

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You cannot do\n both.

\n
\n
Grantee Values
\n
\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>&\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n \n

    Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

    \n
      \n
    • \n

      US East (N. Virginia)

      \n
    • \n
    • \n

      US West (N. California)

      \n
    • \n
    • \n

      US West (Oregon)

      \n
    • \n
    • \n

      Asia Pacific (Singapore)

      \n
    • \n
    • \n

      Asia Pacific (Sydney)

      \n
    • \n
    • \n

      Asia Pacific (Tokyo)

      \n
    • \n
    • \n

      Europe (Ireland)

      \n
    • \n
    • \n

      South America (São Paulo)

      \n
    • \n
    \n

    For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

    \n
    \n
  • \n
\n
\n
\n

The following operations are related to PutBucketAcl:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?acl", @@ -29811,7 +29811,7 @@ "GrantWrite": { "target": "com.amazonaws.s3#GrantWrite", "traits": { - "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and overwrites of those objects.

", + "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and\n overwrites of those objects.

", "smithy.api#httpHeader": "x-amz-grant-write" } }, @@ -29843,7 +29843,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Sets an analytics configuration for the bucket (specified by the analytics configuration\n ID). You can have up to 1,000 analytics configurations per bucket.

\n

You can choose to have storage class analysis export analysis reports sent to a\n comma-separated values (CSV) flat file. See the DataExport request element.\n Reports are updated daily and are based on the object filters that you configure. When\n selecting data export, you specify a destination bucket and an optional destination prefix\n where the file is written. You can export the data to a destination bucket in a different\n account. However, the destination bucket must be in the same Region as the bucket that you\n are making the PUT analytics configuration to. For more information, see Amazon S3 Analytics – Storage Class\n Analysis.

\n \n

You must create a bucket policy on the destination bucket where the exported file is\n written to grant permissions to Amazon S3 to write objects to the bucket. For an example\n policy, see Granting Permissions for Amazon S3 Inventory and Storage Class Analysis.

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

\n Special Errors\n

\n
    \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 400 Bad Request\n

      \n
    • \n
    • \n

      \n Code: InvalidArgument\n

      \n
    • \n
    • \n

      \n Cause: Invalid argument.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 400 Bad Request\n

      \n
    • \n
    • \n

      \n Code: TooManyConfigurations\n

      \n
    • \n
    • \n

      \n Cause: You are attempting to create a new configuration but have\n already reached the 1,000-configuration limit.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 403 Forbidden\n

      \n
    • \n
    • \n

      \n Code: AccessDenied\n

      \n
    • \n
    • \n

      \n Cause: You are not the owner of the specified bucket, or you do\n not have the s3:PutAnalyticsConfiguration bucket permission to set the\n configuration on the bucket.\n

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Sets an analytics configuration for the bucket (specified by the analytics configuration\n ID). You can have up to 1,000 analytics configurations per bucket.

\n

You can choose to have storage class analysis export analysis reports sent to a\n comma-separated values (CSV) flat file. See the DataExport request element.\n Reports are updated daily and are based on the object filters that you configure. When\n selecting data export, you specify a destination bucket and an optional destination prefix\n where the file is written. You can export the data to a destination bucket in a different\n account. However, the destination bucket must be in the same Region as the bucket that you\n are making the PUT analytics configuration to. For more information, see Amazon S3\n Analytics – Storage Class Analysis.

\n \n

You must create a bucket policy on the destination bucket where the exported file is\n written to grant permissions to Amazon S3 to write objects to the bucket. For an example\n policy, see Granting Permissions for Amazon S3 Inventory and Storage Class Analysis.

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutAnalyticsConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

\n PutBucketAnalyticsConfiguration has the following special errors:

\n
    \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 400 Bad Request\n

      \n
    • \n
    • \n

      \n Code: InvalidArgument\n

      \n
    • \n
    • \n

      \n Cause: Invalid argument.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 400 Bad Request\n

      \n
    • \n
    • \n

      \n Code: TooManyConfigurations\n

      \n
    • \n
    • \n

      \n Cause: You are attempting to create a new configuration but have\n already reached the 1,000-configuration limit.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n HTTP Error: HTTP 403 Forbidden\n

      \n
    • \n
    • \n

      \n Code: AccessDenied\n

      \n
    • \n
    • \n

      \n Cause: You are not the owner of the specified bucket, or you do\n not have the s3:PutAnalyticsConfiguration bucket permission to set the\n configuration on the bucket.\n

      \n
    • \n
    \n
  • \n
\n

The following operations are related to PutBucketAnalyticsConfiguration:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?analytics", @@ -29907,7 +29907,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the cors configuration for your bucket. If the configuration exists,\n Amazon S3 replaces it.

\n

To use this operation, you must be allowed to perform the s3:PutBucketCORS\n action. By default, the bucket owner has this permission and can grant it to others.

\n

You set this configuration on a bucket so that the bucket can service cross-origin\n requests. For example, you might want to enable a request whose origin is\n http://www.example.com to access your Amazon S3 bucket at\n my.example.bucket.com by using the browser's XMLHttpRequest\n capability.

\n

To enable cross-origin resource sharing (CORS) on a bucket, you add the\n cors subresource to the bucket. The cors subresource is an XML\n document in which you configure rules that identify origins and the HTTP methods that can\n be executed on your bucket. The document is limited to 64 KB in size.

\n

When Amazon S3 receives a cross-origin request (or a pre-flight OPTIONS request) against a\n bucket, it evaluates the cors configuration on the bucket and uses the first\n CORSRule rule that matches the incoming browser request to enable a\n cross-origin request. For a rule to match, the following conditions must be met:

\n
    \n
  • \n

    The request's Origin header must match AllowedOrigin\n elements.

    \n
  • \n
  • \n

    The request method (for example, GET, PUT, HEAD, and so on) or the\n Access-Control-Request-Method header in case of a pre-flight\n OPTIONS request must be one of the AllowedMethod\n elements.

    \n
  • \n
  • \n

    Every header specified in the Access-Control-Request-Headers request\n header of a pre-flight request must match an AllowedHeader element.\n

    \n
  • \n
\n

For more information about CORS, go to Enabling\n Cross-Origin Resource Sharing in the Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Sets the cors configuration for your bucket. If the configuration exists,\n Amazon S3 replaces it.

\n

To use this operation, you must be allowed to perform the s3:PutBucketCORS\n action. By default, the bucket owner has this permission and can grant it to others.

\n

You set this configuration on a bucket so that the bucket can service cross-origin\n requests. For example, you might want to enable a request whose origin is\n http://www.example.com to access your Amazon S3 bucket at\n my.example.bucket.com by using the browser's XMLHttpRequest\n capability.

\n

To enable cross-origin resource sharing (CORS) on a bucket, you add the\n cors subresource to the bucket. The cors subresource is an XML\n document in which you configure rules that identify origins and the HTTP methods that can\n be executed on your bucket. The document is limited to 64 KB in size.

\n

When Amazon S3 receives a cross-origin request (or a pre-flight OPTIONS request) against a\n bucket, it evaluates the cors configuration on the bucket and uses the first\n CORSRule rule that matches the incoming browser request to enable a\n cross-origin request. For a rule to match, the following conditions must be met:

\n
    \n
  • \n

    The request's Origin header must match AllowedOrigin\n elements.

    \n
  • \n
  • \n

    The request method (for example, GET, PUT, HEAD, and so on) or the\n Access-Control-Request-Method header in case of a pre-flight\n OPTIONS request must be one of the AllowedMethod\n elements.

    \n
  • \n
  • \n

    Every header specified in the Access-Control-Request-Headers request\n header of a pre-flight request must match an AllowedHeader element.\n

    \n
  • \n
\n

For more information about CORS, go to Enabling Cross-Origin Resource Sharing in\n the Amazon S3 User Guide.

\n

The following operations are related to PutBucketCors:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?cors", @@ -29932,7 +29932,7 @@ "CORSConfiguration": { "target": "com.amazonaws.s3#CORSConfiguration", "traits": { - "smithy.api#documentation": "

Describes the cross-origin access configuration for objects in an Amazon S3 bucket. For more\n information, see Enabling Cross-Origin Resource\n Sharing in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Describes the cross-origin access configuration for objects in an Amazon S3 bucket. For more\n information, see Enabling\n Cross-Origin Resource Sharing in the\n Amazon S3 User Guide.

", "smithy.api#httpPayload": {}, "smithy.api#required": {}, "smithy.api#xmlName": "CORSConfiguration" @@ -29977,7 +29977,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

This action uses the encryption subresource to configure default\n encryption and Amazon S3 Bucket Key for an existing bucket.

\n

Default encryption for a bucket can use server-side encryption with Amazon S3-managed keys\n (SSE-S3) or customer managed keys (SSE-KMS). If you specify default encryption\n using SSE-KMS, you can also configure Amazon S3 Bucket Key. When the default encryption is SSE-KMS, if\n you upload an object to the bucket and do not specify the KMS key to use for encryption, Amazon S3\n uses the default Amazon Web Services managed KMS key for your account. For information about default\n encryption, see Amazon S3 default bucket encryption\n in the Amazon S3 User Guide. For more information about S3 Bucket Keys,\n see Amazon S3 Bucket Keys in the Amazon S3 User Guide.

\n \n

This action requires Amazon Web Services Signature Version 4. For more information, see Authenticating Requests (Amazon Web Services Signature\n Version 4).

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This action uses the encryption subresource to configure default encryption\n and Amazon S3 Bucket Keys for an existing bucket.

\n

By default, all buckets have a default encryption configuration that\n uses server-side encryption with Amazon S3 managed keys (SSE-S3).\n You can optionally configure default encryption for a bucket by using server-side\n encryption with an Amazon Web Services KMS key (SSE-KMS) or a customer-provided key (SSE-C). If you specify default encryption by using\n SSE-KMS, you can also configure Amazon S3 Bucket Keys. For information about bucket default encryption,\n see Amazon S3\n bucket default encryption in the Amazon S3 User Guide. For more\n information about S3 Bucket Keys, see Amazon S3 Bucket Keys in the\n Amazon S3 User Guide.

\n \n

This action requires Amazon Web Services Signature Version 4. For more information, see \n Authenticating Requests (Amazon Web Services Signature Version 4).

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n

The following operations are related to PutBucketEncryption:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?encryption", @@ -29991,7 +29991,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

Specifies default encryption for a bucket using server-side encryption with Amazon S3-managed\n keys (SSE-S3) or customer managed keys (SSE-KMS). For information about\n the Amazon S3 default encryption feature, see Amazon S3 Default Bucket Encryption\n in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Specifies default encryption for a bucket using server-side encryption with different\n key options. By default, all buckets have a default encryption configuration that\n uses server-side encryption with Amazon S3 managed keys (SSE-S3). You can optionally configure default encryption for a bucket by using server-side\n encryption with an Amazon Web Services KMS key (SSE-KMS) or a customer-provided key (SSE-C). For information about the bucket default\n encryption feature, see Amazon S3 Bucket Default Encryption\n in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -30002,7 +30002,7 @@ "ContentMD5": { "target": "com.amazonaws.s3#ContentMD5", "traits": { - "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the server-side encryption configuration.

\n

For requests made using the Amazon Web Services Command Line Interface (CLI) or Amazon Web Services SDKs, this field is calculated automatically.

", + "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the server-side encryption\n configuration.

\n

For requests made using the Amazon Web Services Command Line Interface (CLI) or Amazon Web Services SDKs, this field is calculated automatically.

", "smithy.api#httpHeader": "Content-MD5" } }, @@ -30042,7 +30042,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Puts a S3 Intelligent-Tiering configuration to the specified bucket.\n You can have up to 1,000 S3 Intelligent-Tiering configurations per bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to\n PutBucketIntelligentTieringConfiguration include:

\n \n \n

You only need S3 Intelligent-Tiering enabled on a bucket if you want to automatically\n move objects stored in the S3 Intelligent-Tiering storage class to the\n Archive Access or Deep Archive Access tier.

\n
\n

\n Special Errors\n

\n
    \n
  • \n

    \n HTTP 400 Bad Request Error\n

    \n
      \n
    • \n

      \n Code: InvalidArgument

      \n
    • \n
    • \n

      \n Cause: Invalid Argument

      \n
    • \n
    \n
  • \n
  • \n

    \n HTTP 400 Bad Request Error\n

    \n
      \n
    • \n

      \n Code: TooManyConfigurations

      \n
    • \n
    • \n

      \n Cause: You are attempting to create a new configuration\n but have already reached the 1,000-configuration limit.

      \n
    • \n
    \n
  • \n
  • \n

    \n HTTP 403 Forbidden Error\n

    \n
      \n
    • \n

      \n Code: AccessDenied

      \n
    • \n
    • \n

      \n Cause: You are not the owner of the specified bucket,\n or you do not have the s3:PutIntelligentTieringConfiguration bucket\n permission to set the configuration on the bucket.

      \n
    • \n
    \n
  • \n
", + "smithy.api#documentation": "

Puts a S3 Intelligent-Tiering configuration to the specified bucket. You can have up to\n 1,000 S3 Intelligent-Tiering configurations per bucket.

\n

The S3 Intelligent-Tiering storage class is designed to optimize storage costs by automatically moving data to the most cost-effective storage access tier, without performance impact or operational overhead. S3 Intelligent-Tiering delivers automatic cost savings in three low latency and high throughput access tiers. To get the lowest storage cost on data that can be accessed in minutes to hours, you can choose to activate additional archiving capabilities.

\n

The S3 Intelligent-Tiering storage class is the ideal storage class for data with unknown, changing, or unpredictable access patterns, independent of object size or retention period. If the size of an object is less than 128 KB, it is not monitored and not eligible for auto-tiering. Smaller objects can be stored, but they are always charged at the Frequent Access tier rates in the S3 Intelligent-Tiering storage class.

\n

For more information, see Storage class for automatically optimizing frequently and infrequently accessed objects.

\n

Operations related to PutBucketIntelligentTieringConfiguration include:

\n \n \n

You only need S3 Intelligent-Tiering enabled on a bucket if you want to automatically\n move objects stored in the S3 Intelligent-Tiering storage class to the Archive Access\n or Deep Archive Access tier.

\n
\n

\n PutBucketIntelligentTieringConfiguration has the following special errors:

\n
\n
HTTP 400 Bad Request Error
\n
\n

\n Code: InvalidArgument

\n

\n Cause: Invalid Argument

\n
\n
HTTP 400 Bad Request Error
\n
\n

\n Code: TooManyConfigurations

\n

\n Cause: You are attempting to create a new configuration\n but have already reached the 1,000-configuration limit.

\n
\n
HTTP 403 Forbidden Error
\n
\n

\n Cause: You are not the owner of the specified bucket,\n or you do not have the s3:PutIntelligentTieringConfiguration\n bucket permission to set the configuration on the bucket.

\n
\n
", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?intelligent-tiering", @@ -30095,7 +30095,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

This implementation of the PUT action adds an inventory configuration\n (identified by the inventory ID) to the bucket. You can have up to 1,000 inventory\n configurations per bucket.

\n

Amazon S3 inventory generates inventories of the objects in the bucket on a daily or weekly\n basis, and the results are published to a flat file. The bucket that is inventoried is\n called the source bucket, and the bucket where the inventory flat file\n is stored is called the destination bucket. The\n destination bucket must be in the same Amazon Web Services Region as the\n source bucket.

\n

When you configure an inventory for a source bucket, you specify\n the destination bucket where you want the inventory to be stored, and\n whether to generate the inventory daily or weekly. You can also configure what object\n metadata to include and whether to inventory all object versions or only current versions.\n For more information, see Amazon S3\n Inventory in the Amazon S3 User Guide.

\n \n

You must create a bucket policy on the destination bucket to\n grant permissions to Amazon S3 to write objects to the bucket in the defined location. For an\n example policy, see \n Granting Permissions for Amazon S3 Inventory and Storage Class Analysis.

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutInventoryConfiguration action. The bucket owner has this permission\n by default and can grant this permission to others. For more information about permissions,\n see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

\n Special Errors\n

\n
    \n
  • \n

    \n HTTP 400 Bad Request Error\n

    \n
      \n
    • \n

      \n Code: InvalidArgument

      \n
    • \n
    • \n

      \n Cause: Invalid Argument

      \n
    • \n
    \n
  • \n
  • \n

    \n HTTP 400 Bad Request Error\n

    \n
      \n
    • \n

      \n Code: TooManyConfigurations

      \n
    • \n
    • \n

      \n Cause: You are attempting to create a new configuration\n but have already reached the 1,000-configuration limit.

      \n
    • \n
    \n
  • \n
  • \n

    \n HTTP 403 Forbidden Error\n

    \n
      \n
    • \n

      \n Code: AccessDenied

      \n
    • \n
    • \n

      \n Cause: You are not the owner of the specified bucket,\n or you do not have the s3:PutInventoryConfiguration bucket\n permission to set the configuration on the bucket.

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This implementation of the PUT action adds an inventory configuration\n (identified by the inventory ID) to the bucket. You can have up to 1,000 inventory\n configurations per bucket.

\n

Amazon S3 inventory generates inventories of the objects in the bucket on a daily or weekly\n basis, and the results are published to a flat file. The bucket that is inventoried is\n called the source bucket, and the bucket where the inventory flat file\n is stored is called the destination bucket. The\n destination bucket must be in the same Amazon Web Services Region as the\n source bucket.

\n

When you configure an inventory for a source bucket, you specify\n the destination bucket where you want the inventory to be stored, and\n whether to generate the inventory daily or weekly. You can also configure what object\n metadata to include and whether to inventory all object versions or only current versions.\n For more information, see Amazon S3 Inventory in the\n Amazon S3 User Guide.

\n \n

You must create a bucket policy on the destination bucket to\n grant permissions to Amazon S3 to write objects to the bucket in the defined location. For an\n example policy, see Granting Permissions for Amazon S3 Inventory and Storage Class Analysis.

\n
\n
\n
Permissions
\n
\n

To use this operation, you must have permission to perform the\n s3:PutInventoryConfiguration action. The bucket owner has this permission\n by default and can grant this permission to others.

\n

The s3:PutInventoryConfiguration permission allows a user to create an\n S3\n Inventory report that includes all object metadata fields available and to\n specify the destination bucket to store the inventory. A user with read access to objects\n in the destination bucket can also access all object metadata fields that are available in\n the inventory report.

\n

To restrict access to an inventory report, see Restricting access to an Amazon S3 Inventory report in the\n Amazon S3 User Guide. For more information about the metadata fields\n available in S3 Inventory, see Amazon S3\n Inventory lists in the Amazon S3 User Guide. For more\n information about permissions, see Permissions related to bucket subresource operations and Identity and\n access management in Amazon S3 in the Amazon S3 User Guide.

\n
\n
\n

\n PutBucketInventoryConfiguration has the following special errors:

\n
\n
HTTP 400 Bad Request Error
\n
\n

\n Code: InvalidArgument

\n

\n Cause: Invalid Argument

\n
\n
HTTP 400 Bad Request Error
\n
\n

\n Code: TooManyConfigurations

\n

\n Cause: You are attempting to create a new configuration\n but have already reached the 1,000-configuration limit.

\n
\n
HTTP 403 Forbidden Error
\n
\n

\n Cause: You are not the owner of the specified bucket,\n or you do not have the s3:PutInventoryConfiguration bucket\n permission to set the configuration on the bucket.

\n
\n
\n

The following operations are related to PutBucketInventoryConfiguration:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?inventory", @@ -30159,7 +30159,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Creates a new lifecycle configuration for the bucket or replaces an existing lifecycle\n configuration. Keep in mind that this will overwrite an existing lifecycle configuration, so if\n you want to retain any configuration details, they must be included in the new lifecycle\n configuration. For information about lifecycle configuration, see Managing your storage\n lifecycle.

\n \n

Bucket lifecycle configuration now supports specifying a lifecycle rule using an\n object key name prefix, one or more object tags, or a combination of both. Accordingly,\n this section describes the latest API. The previous version of the API supported\n filtering based only on an object key name prefix, which is supported for backward\n compatibility. For the related API description, see PutBucketLifecycle.

\n
\n

\n Rules\n

\n

You specify the lifecycle configuration in your request body. The lifecycle\n configuration is specified as XML consisting of one or more rules. An Amazon S3 Lifecycle\n configuration can have up to 1,000 rules. This limit is not adjustable. Each rule consists\n of the following:

\n
    \n
  • \n

    Filter identifying a subset of objects to which the rule applies. The filter can\n be based on a key name prefix, object tags, or a combination of both.

    \n
  • \n
  • \n

    Status whether the rule is in effect.

    \n
  • \n
  • \n

    One or more lifecycle transition and expiration actions that you want Amazon S3 to\n perform on the objects identified by the filter. If the state of your bucket is\n versioning-enabled or versioning-suspended, you can have many versions of the same\n object (one current version and zero or more noncurrent versions). Amazon S3 provides\n predefined actions that you can specify for current and noncurrent object\n versions.

    \n
  • \n
\n

For more information, see Object\n Lifecycle Management and Lifecycle Configuration Elements.

\n

\n Permissions\n

\n

By default, all Amazon S3 resources are private, including buckets, objects, and related\n subresources (for example, lifecycle configuration and website configuration). Only the\n resource owner (that is, the Amazon Web Services account that created it) can access the resource. The\n resource owner can optionally grant access permissions to others by writing an access\n policy. For this operation, a user must get the s3:PutLifecycleConfiguration\n permission.

\n

You can also explicitly deny permissions. Explicit deny also supersedes any other\n permissions. If you want to block users or accounts from removing or deleting objects from\n your bucket, you must deny them permissions for the following actions:

\n
    \n
  • \n

    \n s3:DeleteObject\n

    \n
  • \n
  • \n

    \n s3:DeleteObjectVersion\n

    \n
  • \n
  • \n

    \n s3:PutLifecycleConfiguration\n

    \n
  • \n
\n

For more information about permissions, see Managing Access Permissions to Your Amazon S3\n Resources.

\n

The following are related to PutBucketLifecycleConfiguration:

\n ", + "smithy.api#documentation": "

Creates a new lifecycle configuration for the bucket or replaces an existing lifecycle\n configuration. Keep in mind that this will overwrite an existing lifecycle configuration,\n so if you want to retain any configuration details, they must be included in the new\n lifecycle configuration. For information about lifecycle configuration, see Managing\n your storage lifecycle.

\n \n

Bucket lifecycle configuration now supports specifying a lifecycle rule using an\n object key name prefix, one or more object tags, or a combination of both. Accordingly,\n this section describes the latest API. The previous version of the API supported\n filtering based only on an object key name prefix, which is supported for backward\n compatibility. For the related API description, see PutBucketLifecycle.

\n
\n
\n
Rules
\n
\n

You specify the lifecycle configuration in your request body. The lifecycle\n configuration is specified as XML consisting of one or more rules. An Amazon S3 Lifecycle\n configuration can have up to 1,000 rules. This limit is not adjustable. Each rule consists\n of the following:

\n
    \n
  • \n

    Filter identifying a subset of objects to which the rule applies. The filter can\n be based on a key name prefix, object tags, or a combination of both.

    \n
  • \n
  • \n

    Status whether the rule is in effect.

    \n
  • \n
  • \n

    One or more lifecycle transition and expiration actions that you want Amazon S3 to\n perform on the objects identified by the filter. If the state of your bucket is\n versioning-enabled or versioning-suspended, you can have many versions of the same\n object (one current version and zero or more noncurrent versions). Amazon S3 provides\n predefined actions that you can specify for current and noncurrent object\n versions.

    \n
  • \n
\n

For more information, see Object Lifecycle Management\n and Lifecycle Configuration Elements.

\n
\n
Permissions
\n
\n

By default, all Amazon S3 resources are private, including buckets, objects, and related\n subresources (for example, lifecycle configuration and website configuration). Only the\n resource owner (that is, the Amazon Web Services account that created it) can access the resource. The\n resource owner can optionally grant access permissions to others by writing an access\n policy. For this operation, a user must get the s3:PutLifecycleConfiguration\n permission.

\n

You can also explicitly deny permissions. Explicit deny also supersedes any other\n permissions. If you want to block users or accounts from removing or deleting objects from\n your bucket, you must deny them permissions for the following actions:

\n
    \n
  • \n

    \n s3:DeleteObject\n

    \n
  • \n
  • \n

    \n s3:DeleteObjectVersion\n

    \n
  • \n
  • \n

    \n s3:PutLifecycleConfiguration\n

    \n
  • \n
\n

For more information about permissions, see Managing Access Permissions to\n Your Amazon S3 Resources.

\n
\n
\n

The following operations are related to PutBucketLifecycleConfiguration:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?lifecycle", @@ -30221,7 +30221,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Set the logging parameters for a bucket and to specify permissions for who can view and\n modify the logging parameters. All logs are saved to buckets in the same Amazon Web Services Region as the\n source bucket. To set the logging status of a bucket, you must be the bucket owner.

\n

The bucket owner is automatically granted FULL_CONTROL to all logs. You use the Grantee request element to grant access to other people. The\n Permissions request element specifies the kind of access the grantee has to\n the logs.

\n \n

If the target bucket for log delivery uses the bucket owner enforced\n setting for S3 Object Ownership, you can't use the Grantee request element\n to grant access to others. Permissions can only be granted using policies. For more information, see Permissions for server access log delivery in the\n Amazon S3 User Guide.

\n
\n

\n Grantee Values\n

\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request.

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
\n

To enable logging, you use LoggingEnabled and its children request elements. To disable\n logging, you use an empty BucketLoggingStatus request element:

\n

\n \n

\n

For more information about server access logging, see Server Access Logging in the Amazon S3 User Guide.

\n

For more information about creating a bucket, see CreateBucket. For more\n information about returning the logging status of a bucket, see GetBucketLogging.

\n

The following operations are related to PutBucketLogging:

\n ", + "smithy.api#documentation": "

Set the logging parameters for a bucket and to specify permissions for who can view and\n modify the logging parameters. All logs are saved to buckets in the same Amazon Web Services Region as\n the source bucket. To set the logging status of a bucket, you must be the bucket\n owner.

\n

The bucket owner is automatically granted FULL_CONTROL to all logs. You use the\n Grantee request element to grant access to other people. The\n Permissions request element specifies the kind of access the grantee has to\n the logs.

\n \n

If the target bucket for log delivery uses the bucket owner enforced setting for S3\n Object Ownership, you can't use the Grantee request element to grant access\n to others. Permissions can only be granted using policies. For more information, see\n Permissions for server access log delivery in the\n Amazon S3 User Guide.

\n
\n
\n
Grantee Values
\n
\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request.

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
\n
\n
\n

To enable logging, you use LoggingEnabled and its children request elements. To disable\n logging, you use an empty BucketLoggingStatus request element:

\n

\n \n

\n

For more information about server access logging, see Server Access Logging in the\n Amazon S3 User Guide.

\n

For more information about creating a bucket, see CreateBucket. For more\n information about returning the logging status of a bucket, see GetBucketLogging.

\n

The following operations are related to PutBucketLogging:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?logging", @@ -30287,7 +30287,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Sets a metrics configuration (specified by the metrics configuration ID) for the bucket.\n You can have up to 1,000 metrics configurations per bucket. If you're updating an existing\n metrics configuration, note that this is a full replacement of the existing metrics\n configuration. If you don't include the elements you want to keep, they are erased.

\n

To use this operation, you must have permissions to perform the\n s3:PutMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring Metrics with Amazon\n CloudWatch.

\n

The following operations are related to\n PutBucketMetricsConfiguration:

\n \n

\n GetBucketLifecycle has the following special error:

\n
    \n
  • \n

    Error code: TooManyConfigurations\n

    \n
      \n
    • \n

      Description: You are attempting to create a new configuration but have\n already reached the 1,000-configuration limit.

      \n
    • \n
    • \n

      HTTP Status Code: HTTP 400 Bad Request

      \n
    • \n
    \n
  • \n
", + "smithy.api#documentation": "

Sets a metrics configuration (specified by the metrics configuration ID) for the bucket.\n You can have up to 1,000 metrics configurations per bucket. If you're updating an existing\n metrics configuration, note that this is a full replacement of the existing metrics\n configuration. If you don't include the elements you want to keep, they are erased.

\n

To use this operation, you must have permissions to perform the\n s3:PutMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring\n Metrics with Amazon CloudWatch.

\n

The following operations are related to\n PutBucketMetricsConfiguration:

\n \n

\n GetBucketLifecycle has the following special error:

\n
    \n
  • \n

    Error code: TooManyConfigurations\n

    \n
      \n
    • \n

      Description: You are attempting to create a new configuration but have\n already reached the 1,000-configuration limit.

      \n
    • \n
    • \n

      HTTP Status Code: HTTP 400 Bad Request

      \n
    • \n
    \n
  • \n
", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?metrics", @@ -30312,7 +30312,7 @@ "Id": { "target": "com.amazonaws.s3#MetricsId", "traits": { - "smithy.api#documentation": "

The ID used to identify the metrics configuration.

", + "smithy.api#documentation": "

The ID used to identify the metrics configuration. The ID has a 64 character limit and\n can only contain letters, numbers, periods, dashes, and underscores.

", "smithy.api#httpQuery": "id", "smithy.api#required": {} } @@ -30347,7 +30347,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Enables notifications of specified events for a bucket. For more information about event\n notifications, see Configuring Event\n Notifications.

\n

Using this API, you can replace an existing notification configuration. The\n configuration is an XML file that defines the event types that you want Amazon S3 to publish and\n the destination where you want Amazon S3 to publish an event notification when it detects an\n event of the specified type.

\n

By default, your bucket has no event notifications configured. That is, the notification\n configuration will be an empty NotificationConfiguration.

\n

\n \n

\n

\n \n

\n

This action replaces the existing notification configuration with the configuration\n you include in the request body.

\n

After Amazon S3 receives this request, it first verifies that any Amazon Simple Notification\n Service (Amazon SNS) or Amazon Simple Queue Service (Amazon SQS) destination exists, and\n that the bucket owner has permission to publish to it by sending a test notification. In\n the case of Lambda destinations, Amazon S3 verifies that the Lambda function permissions\n grant Amazon S3 permission to invoke the function from the Amazon S3 bucket. For more information,\n see Configuring Notifications for Amazon S3\n Events.

\n

You can disable notifications by adding the empty NotificationConfiguration\n element.

\n

For more information about the number of event notification configurations that you can create per bucket, see\n Amazon S3 service quotas in Amazon Web Services General Reference.

\n

By default, only the bucket owner can configure notifications on a bucket. However,\n bucket owners can use a bucket policy to grant permission to other users to set this\n configuration with s3:PutBucketNotification permission.

\n \n

The PUT notification is an atomic operation. For example, suppose your notification\n configuration includes SNS topic, SQS queue, and Lambda function configurations. When\n you send a PUT request with this configuration, Amazon S3 sends test messages to your SNS\n topic. If the message fails, the entire PUT action will fail, and Amazon S3 will not add\n the configuration to your bucket.

\n
\n

\n Responses\n

\n

If the configuration in the request body includes only one\n TopicConfiguration specifying only the\n s3:ReducedRedundancyLostObject event type, the response will also include\n the x-amz-sns-test-message-id header containing the message ID of the test\n notification sent to the topic.

\n

The following action is related to\n PutBucketNotificationConfiguration:

\n ", + "smithy.api#documentation": "

Enables notifications of specified events for a bucket. For more information about event\n notifications, see Configuring Event\n Notifications.

\n

Using this API, you can replace an existing notification configuration. The\n configuration is an XML file that defines the event types that you want Amazon S3 to publish and\n the destination where you want Amazon S3 to publish an event notification when it detects an\n event of the specified type.

\n

By default, your bucket has no event notifications configured. That is, the notification\n configuration will be an empty NotificationConfiguration.

\n

\n \n

\n

\n \n

\n

This action replaces the existing notification configuration with the configuration you\n include in the request body.

\n

After Amazon S3 receives this request, it first verifies that any Amazon Simple Notification\n Service (Amazon SNS) or Amazon Simple Queue Service (Amazon SQS) destination exists, and\n that the bucket owner has permission to publish to it by sending a test notification. In\n the case of Lambda destinations, Amazon S3 verifies that the Lambda function permissions\n grant Amazon S3 permission to invoke the function from the Amazon S3 bucket. For more information,\n see Configuring Notifications for Amazon S3 Events.

\n

You can disable notifications by adding the empty NotificationConfiguration\n element.

\n

For more information about the number of event notification configurations that you can\n create per bucket, see Amazon S3 service quotas in Amazon Web Services\n General Reference.

\n

By default, only the bucket owner can configure notifications on a bucket. However,\n bucket owners can use a bucket policy to grant permission to other users to set this\n configuration with s3:PutBucketNotification permission.

\n \n

The PUT notification is an atomic operation. For example, suppose your notification\n configuration includes SNS topic, SQS queue, and Lambda function configurations. When\n you send a PUT request with this configuration, Amazon S3 sends test messages to your SNS\n topic. If the message fails, the entire PUT action will fail, and Amazon S3 will not add the\n configuration to your bucket.

\n
\n

If the configuration in the request body includes only one\n TopicConfiguration specifying only the\n s3:ReducedRedundancyLostObject event type, the response will also include\n the x-amz-sns-test-message-id header containing the message ID of the test\n notification sent to the topic.

\n

The following action is related to\n PutBucketNotificationConfiguration:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?notification", @@ -30388,7 +30388,7 @@ "target": "com.amazonaws.s3#SkipValidation", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Skips validation of Amazon SQS, Amazon SNS, and Lambda destinations. True or false value.

", + "smithy.api#documentation": "

Skips validation of Amazon SQS, Amazon SNS, and Lambda\n destinations. True or false value.

", "smithy.api#httpHeader": "x-amz-skip-destination-validation" } } @@ -30409,7 +30409,7 @@ "aws.protocols#httpChecksum": { "requestChecksumRequired": true }, - "smithy.api#documentation": "

Creates or modifies OwnershipControls for an Amazon S3 bucket. To use this\n operation, you must have the s3:PutBucketOwnershipControls permission. For\n more information about Amazon S3 permissions, see Specifying permissions in a policy.

\n

For information about Amazon S3 Object Ownership, see Using object ownership.

\n

The following operations are related to PutBucketOwnershipControls:

\n ", + "smithy.api#documentation": "

Creates or modifies OwnershipControls for an Amazon S3 bucket. To use this\n operation, you must have the s3:PutBucketOwnershipControls permission. For\n more information about Amazon S3 permissions, see Specifying permissions in a\n policy.

\n

For information about Amazon S3 Object Ownership, see Using object\n ownership.

\n

The following operations are related to PutBucketOwnershipControls:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?ownershipControls", @@ -30448,7 +30448,7 @@ "OwnershipControls": { "target": "com.amazonaws.s3#OwnershipControls", "traits": { - "smithy.api#documentation": "

The OwnershipControls (BucketOwnerEnforced, BucketOwnerPreferred, or ObjectWriter) that you want\n to apply to this Amazon S3 bucket.

", + "smithy.api#documentation": "

The OwnershipControls (BucketOwnerEnforced, BucketOwnerPreferred, or\n ObjectWriter) that you want to apply to this Amazon S3 bucket.

", "smithy.api#httpPayload": {}, "smithy.api#required": {}, "smithy.api#xmlName": "OwnershipControls" @@ -30472,7 +30472,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Applies an Amazon S3 bucket policy to an Amazon S3 bucket. If you are using an identity other than\n the root user of the Amazon Web Services account that owns the bucket, the calling identity must have the\n PutBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account in order to use this operation.

\n

If you don't have PutBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

As a security precaution, the root user of the Amazon Web Services account that owns a bucket can\n always use this operation, even if the policy explicitly denies the root user the\n ability to perform this action.

\n
\n

For more information, see Bucket policy examples.

\n

The following operations are related to PutBucketPolicy:

\n ", + "smithy.api#documentation": "

Applies an Amazon S3 bucket policy to an Amazon S3 bucket. If you are using an identity other than\n the root user of the Amazon Web Services account that owns the bucket, the calling identity must have the\n PutBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account in order to use this operation.

\n

If you don't have PutBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

To ensure that bucket owners don't inadvertently lock themselves out of their own\n buckets, the root principal in a bucket owner's Amazon Web Services account can perform the\n GetBucketPolicy, PutBucketPolicy, and\n DeleteBucketPolicy API actions, even if their bucket policy explicitly\n denies the root principal's access. Bucket owner root principals can only be blocked from performing \n these API actions by VPC endpoint policies and Amazon Web Services Organizations policies.

\n
\n

For more information, see Bucket policy\n examples.

\n

The following operations are related to PutBucketPolicy:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?policy", @@ -30549,7 +30549,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Creates a replication configuration or replaces an existing one. For more information,\n see Replication in the Amazon S3 User Guide.

\n

Specify the replication configuration in the request body. In the replication\n configuration, you provide the name of the destination bucket or buckets where you want\n Amazon S3 to replicate objects, the IAM role that Amazon S3 can assume to replicate objects on your\n behalf, and other relevant information.

\n

A replication configuration must include at least one rule, and can contain a maximum of\n 1,000. Each rule identifies a subset of objects to replicate by filtering the objects in\n the source bucket. To choose additional subsets of objects to replicate, add a rule for\n each subset.

\n

To specify a subset of the objects in the source bucket to apply a replication rule to,\n add the Filter element as a child of the Rule element. You can filter objects based on an\n object key prefix, one or more object tags, or both. When you add the Filter element in the\n configuration, you must also add the following elements:\n DeleteMarkerReplication, Status, and\n Priority.

\n \n

If you are using an earlier version of the replication configuration, Amazon S3 handles\n replication of delete markers differently. For more information, see Backward Compatibility.

\n
\n

For information about enabling versioning on a bucket, see Using Versioning.

\n

\n Handling Replication of Encrypted Objects\n

\n

By default, Amazon S3 doesn't replicate objects that are stored at rest using server-side\n encryption with KMS keys. To replicate Amazon Web Services KMS-encrypted objects, add the\n following: SourceSelectionCriteria, SseKmsEncryptedObjects,\n Status, EncryptionConfiguration, and\n ReplicaKmsKeyID. For information about replication configuration, see\n Replicating Objects\n Created with SSE Using KMS keys.

\n

For information on PutBucketReplication errors, see List of\n replication-related error codes\n

\n

\n Permissions\n

\n

To create a PutBucketReplication request, you must have s3:PutReplicationConfiguration \n permissions for the bucket. \n

\n

By default, a resource owner, in this case the Amazon Web Services account that created the bucket, can\n perform this operation. The resource owner can also grant others permissions to perform the\n operation. For more information about permissions, see Specifying Permissions in a Policy\n and Managing Access Permissions to Your\n Amazon S3 Resources.

\n \n

To perform this operation, the user or role performing the action must have the\n iam:PassRole permission.

\n
\n

The following operations are related to PutBucketReplication:

\n ", + "smithy.api#documentation": "

Creates a replication configuration or replaces an existing one. For more information,\n see Replication in the Amazon S3 User Guide.

\n

Specify the replication configuration in the request body. In the replication\n configuration, you provide the name of the destination bucket or buckets where you want\n Amazon S3 to replicate objects, the IAM role that Amazon S3 can assume to replicate objects on your\n behalf, and other relevant information.

\n

A replication configuration must include at least one rule, and can contain a maximum of\n 1,000. Each rule identifies a subset of objects to replicate by filtering the objects in\n the source bucket. To choose additional subsets of objects to replicate, add a rule for\n each subset.

\n

To specify a subset of the objects in the source bucket to apply a replication rule to,\n add the Filter element as a child of the Rule element. You can filter objects based on an\n object key prefix, one or more object tags, or both. When you add the Filter element in the\n configuration, you must also add the following elements:\n DeleteMarkerReplication, Status, and\n Priority.

\n \n

If you are using an earlier version of the replication configuration, Amazon S3 handles\n replication of delete markers differently. For more information, see Backward Compatibility.

\n
\n

For information about enabling versioning on a bucket, see Using Versioning.

\n
\n
Handling Replication of Encrypted Objects
\n
\n

By default, Amazon S3 doesn't replicate objects that are stored at rest using server-side\n encryption with KMS keys. To replicate Amazon Web Services KMS-encrypted objects, add the following:\n SourceSelectionCriteria, SseKmsEncryptedObjects,\n Status, EncryptionConfiguration, and\n ReplicaKmsKeyID. For information about replication configuration, see\n Replicating Objects\n Created with SSE Using KMS keys.

\n

For information on PutBucketReplication errors, see List of\n replication-related error codes\n

\n
\n
Permissions
\n
\n

To create a PutBucketReplication request, you must have\n s3:PutReplicationConfiguration permissions for the bucket.\n \n

\n

By default, a resource owner, in this case the Amazon Web Services account that created the bucket,\n can perform this operation. The resource owner can also grant others permissions to perform\n the operation. For more information about permissions, see Specifying Permissions in a\n Policy and Managing Access Permissions to\n Your Amazon S3 Resources.

\n \n

To perform this operation, the user or role performing the action must have the\n iam:PassRole permission.

\n
\n
\n
\n

The following operations are related to PutBucketReplication:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?replication", @@ -30625,7 +30625,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the request payment configuration for a bucket. By default, the bucket owner pays\n for downloads from the bucket. This configuration parameter enables the bucket owner (only)\n to specify that the person requesting the download will be charged for the download. For\n more information, see Requester Pays\n Buckets.

\n

The following operations are related to PutBucketRequestPayment:

\n ", + "smithy.api#documentation": "

Sets the request payment configuration for a bucket. By default, the bucket owner pays\n for downloads from the bucket. This configuration parameter enables the bucket owner (only)\n to specify that the person requesting the download will be charged for the download. For\n more information, see Requester Pays\n Buckets.

\n

The following operations are related to PutBucketRequestPayment:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?requestPayment", @@ -30650,7 +30650,7 @@ "ContentMD5": { "target": "com.amazonaws.s3#ContentMD5", "traits": { - "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the data. You must use this header as a\n message integrity check to verify that the request body was not corrupted in transit. For\n more information, see RFC\n 1864.

\n

For requests made using the Amazon Web Services Command Line Interface (CLI) or Amazon Web Services SDKs, this field is calculated automatically.

", + "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the data. You must use this header as a message\n integrity check to verify that the request body was not corrupted in transit. For more\n information, see RFC 1864.

\n

For requests made using the Amazon Web Services Command Line Interface (CLI) or Amazon Web Services SDKs, this field is calculated automatically.

", "smithy.api#httpHeader": "Content-MD5" } }, @@ -30695,7 +30695,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the tags for a bucket.

\n

Use tags to organize your Amazon Web Services bill to reflect your own cost structure. To do this, sign\n up to get your Amazon Web Services account bill with tag key values included. Then, to see the cost of\n combined resources, organize your billing information according to resources with the same\n tag key values. For example, you can tag several resources with a specific application\n name, and then organize your billing information to see the total cost of that application\n across several services. For more information, see Cost Allocation\n and Tagging and Using Cost Allocation in Amazon S3 Bucket\n Tags.

\n \n

\n When this operation sets the tags for a bucket, it will overwrite any current tags the \n bucket already has. You cannot use this operation to add tags to an existing list of tags.

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutBucketTagging action. The bucket owner has this permission by default\n and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources.

\n

\n PutBucketTagging has the following special errors:

\n
    \n
  • \n

    Error code: InvalidTagError\n

    \n \n
  • \n
  • \n

    Error code: MalformedXMLError\n

    \n
      \n
    • \n

      Description: The XML provided does not match the schema.

      \n
    • \n
    \n
  • \n
  • \n

    Error code: OperationAbortedError \n

    \n
      \n
    • \n

      Description: A conflicting conditional action is currently in progress\n against this resource. Please try again.

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InternalError\n

    \n
      \n
    • \n

      Description: The service was unable to apply the provided tag to the\n bucket.

      \n
    • \n
    \n
  • \n
\n

The following operations are related to PutBucketTagging:

\n ", + "smithy.api#documentation": "

Sets the tags for a bucket.

\n

Use tags to organize your Amazon Web Services bill to reflect your own cost structure. To do this,\n sign up to get your Amazon Web Services account bill with tag key values included. Then, to see the cost\n of combined resources, organize your billing information according to resources with the\n same tag key values. For example, you can tag several resources with a specific application\n name, and then organize your billing information to see the total cost of that application\n across several services. For more information, see Cost Allocation and\n Tagging and Using Cost Allocation in Amazon S3 Bucket\n Tags.

\n \n

When this operation sets the tags for a bucket, it will overwrite any current tags\n the bucket already has. You cannot use this operation to add tags to an existing list of\n tags.

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutBucketTagging action. The bucket owner has this permission by default\n and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

\n PutBucketTagging has the following special errors:

\n
    \n
  • \n

    Error code: InvalidTagError\n

    \n \n
  • \n
  • \n

    Error code: MalformedXMLError\n

    \n
      \n
    • \n

      Description: The XML provided does not match the schema.

      \n
    • \n
    \n
  • \n
  • \n

    Error code: OperationAbortedError \n

    \n
      \n
    • \n

      Description: A conflicting conditional action is currently in progress\n against this resource. Please try again.

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InternalError\n

    \n
      \n
    • \n

      Description: The service was unable to apply the provided tag to the\n bucket.

      \n
    • \n
    \n
  • \n
\n

The following operations are related to PutBucketTagging:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?tagging", @@ -30765,7 +30765,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the versioning state of an existing bucket.

\n

You can set the versioning state with one of the following values:

\n

\n Enabled—Enables versioning for the objects in the\n bucket. All objects added to the bucket receive a unique version ID.

\n

\n Suspended—Disables versioning for the objects in the\n bucket. All objects added to the bucket receive the version ID null.

\n

If the versioning state has never been set on a bucket, it has no versioning state; a\n GetBucketVersioning request does not return a versioning state value.

\n

In order to enable MFA Delete, you must be the bucket owner. If you are the bucket owner\n and want to enable MFA Delete in the bucket versioning configuration, you must\n include the x-amz-mfa request header and the\n Status and the MfaDelete request elements in a request to set\n the versioning state of the bucket.

\n \n

If you have an object expiration lifecycle policy in your non-versioned bucket and\n you want to maintain the same permanent delete behavior when you enable versioning, you\n must add a noncurrent expiration policy. The noncurrent expiration lifecycle policy will\n manage the deletes of the noncurrent object versions in the version-enabled bucket. (A\n version-enabled bucket maintains one current and zero or more noncurrent object\n versions.) For more information, see Lifecycle and Versioning.

\n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Sets the versioning state of an existing bucket.

\n

You can set the versioning state with one of the following values:

\n

\n Enabled—Enables versioning for the objects in the\n bucket. All objects added to the bucket receive a unique version ID.

\n

\n Suspended—Disables versioning for the objects in the\n bucket. All objects added to the bucket receive the version ID null.

\n

If the versioning state has never been set on a bucket, it has no versioning state; a\n GetBucketVersioning request does not return a versioning state value.

\n

In order to enable MFA Delete, you must be the bucket owner. If you are the bucket owner\n and want to enable MFA Delete in the bucket versioning configuration, you must include the\n x-amz-mfa request header and the Status and the\n MfaDelete request elements in a request to set the versioning state of the\n bucket.

\n \n

If you have an object expiration lifecycle configuration in your non-versioned bucket and\n you want to maintain the same permanent delete behavior when you enable versioning, you\n must add a noncurrent expiration policy. The noncurrent expiration lifecycle configuration will\n manage the deletes of the noncurrent object versions in the version-enabled bucket. (A\n version-enabled bucket maintains one current and zero or more noncurrent object\n versions.) For more information, see Lifecycle and Versioning.

\n
\n

The following operations are related to PutBucketVersioning:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?versioning", @@ -30911,7 +30911,7 @@ "aws.protocols#httpChecksum": { "requestAlgorithmMember": "ChecksumAlgorithm" }, - "smithy.api#documentation": "

Adds an object to a bucket. You must have WRITE permissions on a bucket to add an object\n to it.

\n

Amazon S3 never adds partial objects; if you receive a success response, Amazon S3 added the\n entire object to the bucket.

\n

Amazon S3 is a distributed system. If it receives multiple write requests for the same object\n simultaneously, it overwrites all but the last object written. Amazon S3 does not provide object\n locking; if you need this, make sure to build it into your application layer or use\n versioning instead.

\n

To ensure that data is not corrupted traversing the network, use the\n Content-MD5 header. When you use this header, Amazon S3 checks the object\n against the provided MD5 value and, if they do not match, returns an error. Additionally,\n you can calculate the MD5 while putting an object to Amazon S3 and compare the returned ETag to\n the calculated MD5 value.

\n \n
    \n
  • \n

    To successfully complete the PutObject request, you must have the \n s3:PutObject in your IAM permissions.

    \n
  • \n
  • \n

    To successfully change the objects acl of your PutObject request, \n you must have the s3:PutObjectAcl in your IAM permissions.

    \n
  • \n
  • \n

    The Content-MD5 header is required for any request to upload an object\n with a retention period configured using Amazon S3 Object Lock. For more information about\n Amazon S3 Object Lock, see Amazon S3 Object Lock Overview\n in the Amazon S3 User Guide.

    \n
  • \n
\n
\n

\n Server-side Encryption\n

\n

You can optionally request server-side encryption. With server-side encryption, Amazon S3 encrypts \n your data as it writes it to disks in its data centers and decrypts the data\n when you access it. You have the option to provide your own encryption key or use Amazon Web Services\n managed encryption keys (SSE-S3 or SSE-KMS). For more information, see Using Server-Side\n Encryption.

\n

If you request server-side encryption using Amazon Web Services Key Management Service (SSE-KMS), you can enable \n an S3 Bucket Key at the object-level. For more information, see Amazon S3 Bucket Keys in the \n Amazon S3 User Guide.

\n

\n Access Control List (ACL)-Specific Request\n Headers\n

\n

You can use headers to grant ACL- based permissions. By default, all objects are\n private. Only the owner has full access control. When adding a new object, you can grant\n permissions to individual Amazon Web Services accounts or to predefined groups defined by Amazon S3. These\n permissions are then added to the ACL on the object. For more information, see Access Control List\n (ACL) Overview and Managing ACLs Using the REST\n API.

\n

If the bucket that you're uploading objects to uses the bucket owner enforced setting\n for S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that\n use this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control canned\n ACL or an equivalent form of this ACL expressed in the XML format. PUT requests that contain other\n ACLs (for example, custom grants to certain Amazon Web Services accounts) fail and return a\n 400 error with the error code\n AccessControlListNotSupported.

\n

For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, \n all objects written to the bucket by any account will be owned by the bucket owner.

\n
\n

\n Storage Class Options\n

\n

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

\n

\n Versioning\n

\n

If you enable versioning for a bucket, Amazon S3 automatically generates a unique version ID\n for the object being stored. Amazon S3 returns this ID in the response. When you enable\n versioning for a bucket, if Amazon S3 receives multiple write requests for the same object\n simultaneously, it stores all of the objects.

\n

For more information about versioning, see Adding Objects to\n Versioning Enabled Buckets. For information about returning the versioning state\n of a bucket, see GetBucketVersioning.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Adds an object to a bucket. You must have WRITE permissions on a bucket to add an object\n to it.

\n \n

Amazon S3 never adds partial objects; if you receive a success response, Amazon S3 added the\n entire object to the bucket. You cannot use PutObject to only update a\n single piece of metadata for an existing object. You must put the entire object with\n updated metadata if you want to update some values.

\n
\n

Amazon S3 is a distributed system. If it receives multiple write requests for the same object\n simultaneously, it overwrites all but the last object written. To prevent objects from\n being deleted or overwritten, you can use Amazon S3 Object\n Lock.

\n

To ensure that data is not corrupted traversing the network, use the\n Content-MD5 header. When you use this header, Amazon S3 checks the object\n against the provided MD5 value and, if they do not match, returns an error. Additionally,\n you can calculate the MD5 while putting an object to Amazon S3 and compare the returned ETag to\n the calculated MD5 value.

\n \n
    \n
  • \n

    To successfully complete the PutObject request, you must have the\n s3:PutObject in your IAM permissions.

    \n
  • \n
  • \n

    To successfully change the objects acl of your PutObject request,\n you must have the s3:PutObjectAcl in your IAM permissions.

    \n
  • \n
  • \n

    To successfully set the tag-set with your PutObject request, you\n must have the s3:PutObjectTagging in your IAM permissions.

    \n
  • \n
  • \n

    The Content-MD5 header is required for any request to upload an\n object with a retention period configured using Amazon S3 Object Lock. For more\n information about Amazon S3 Object Lock, see Amazon S3 Object Lock\n Overview in the Amazon S3 User Guide.

    \n
  • \n
\n
\n

You have three mutually exclusive options to protect data using server-side encryption\n in Amazon S3, depending on how you choose to manage the encryption keys. Specifically, the\n encryption key options are Amazon S3 managed keys (SSE-S3), Amazon Web Services KMS keys (SSE-KMS), and\n customer-provided keys (SSE-C). Amazon S3 encrypts data with server-side encryption by using\n Amazon S3 managed keys (SSE-S3) by default. You can optionally tell Amazon S3 to encrypt data at by\n rest using server-side encryption with other key options. For more information, see Using\n Server-Side Encryption.

\n

When adding a new object, you can use headers to grant ACL-based permissions to\n individual Amazon Web Services accounts or to predefined groups defined by Amazon S3. These permissions are\n then added to the ACL on the object. By default, all objects are private. Only the owner\n has full access control. For more information, see Access Control List (ACL) Overview\n and Managing\n ACLs Using the REST API.

\n

If the bucket that you're uploading objects to uses the bucket owner enforced setting\n for S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that\n use this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control\n canned ACL or an equivalent form of this ACL expressed in the XML format. PUT requests that\n contain other ACLs (for example, custom grants to certain Amazon Web Services accounts) fail and return a\n 400 error with the error code AccessControlListNotSupported.\n For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, all\n objects written to the bucket by any account will be owned by the bucket owner.

\n
\n

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

\n

If you enable versioning for a bucket, Amazon S3 automatically generates a unique version ID\n for the object being stored. Amazon S3 returns this ID in the response. When you enable\n versioning for a bucket, if Amazon S3 receives multiple write requests for the same object\n simultaneously, it stores all of the objects. For more information about versioning, see\n Adding Objects to\n Versioning-Enabled Buckets. For information about returning the versioning state\n of a bucket, see GetBucketVersioning.

\n

For more information about related Amazon S3 APIs, see the following:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?x-id=PutObject", @@ -30937,7 +30937,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Uses the acl subresource to set the access control list (ACL) permissions\n for a new or existing object in an S3 bucket. You must have WRITE_ACP\n permission to set the ACL of an object. For more information, see What\n permissions can I grant? in the Amazon S3 User Guide.

\n

This action is not supported by Amazon S3 on Outposts.

\n

Depending on your application needs, you can choose to set\n the ACL on an object using either the request body or the headers. For example, if you have\n an existing application that updates a bucket ACL using the request body, you can continue\n to use that approach. For more information, see Access Control List (ACL) Overview in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, ACLs are disabled and no longer affect permissions. \n You must use policies to grant access to your bucket and the objects in it. Requests to set ACLs or update ACLs fail and \n return the AccessControlListNotSupported error code. Requests to read ACLs are still supported.\n For more information, see Controlling object ownership\n in the Amazon S3 User Guide.

\n
\n

\n Access Permissions\n

\n

You can set access permissions using one of the following methods:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports\n a set of predefined ACLs, known as canned ACLs. Each canned ACL has a predefined set\n of grantees and permissions. Specify the canned ACL name as the value of\n x-amz-acl. If you use this header, you cannot use other access\n control-specific headers in your request. For more information, see Canned ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the x-amz-grant-read,\n x-amz-grant-read-acp, x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. When using these headers, you\n specify explicit access permissions and grantees (Amazon Web Services accounts or Amazon S3 groups) who\n will receive the permission. If you use these ACL-specific headers, you cannot use\n x-amz-acl header to set a canned ACL. These parameters map to the set\n of permissions that Amazon S3 supports in an ACL. For more information, see Access Control List (ACL)\n Overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants list\n objects permission to the two Amazon Web Services accounts identified by their email\n addresses.

    \n

    \n x-amz-grant-read: emailAddress=\"xyz@amazon.com\",\n emailAddress=\"abc@amazon.com\" \n

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You cannot do\n both.

\n

\n Grantee Values\n

\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request.

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>lt;/Grantee>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n \n

    Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

    \n
      \n
    • \n

      US East (N. Virginia)

      \n
    • \n
    • \n

      US West (N. California)

      \n
    • \n
    • \n

      US West (Oregon)

      \n
    • \n
    • \n

      Asia Pacific (Singapore)

      \n
    • \n
    • \n

      Asia Pacific (Sydney)

      \n
    • \n
    • \n

      Asia Pacific (Tokyo)

      \n
    • \n
    • \n

      Europe (Ireland)

      \n
    • \n
    • \n

      South America (São Paulo)

      \n
    • \n
    \n

    For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

    \n
    \n
  • \n
\n

\n Versioning\n

\n

The ACL of an object is set at the object version level. By default, PUT sets the ACL of\n the current version of an object. To set the ACL of a different version, use the\n versionId subresource.

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Uses the acl subresource to set the access control list (ACL) permissions\n for a new or existing object in an S3 bucket. You must have WRITE_ACP\n permission to set the ACL of an object. For more information, see What\n permissions can I grant? in the Amazon S3 User Guide.

\n

This action is not supported by Amazon S3 on Outposts.

\n

Depending on your application needs, you can choose to set the ACL on an object using\n either the request body or the headers. For example, if you have an existing application\n that updates a bucket ACL using the request body, you can continue to use that approach.\n For more information, see Access Control List (ACL) Overview\n in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, ACLs\n are disabled and no longer affect permissions. You must use policies to grant access to\n your bucket and the objects in it. Requests to set ACLs or update ACLs fail and return\n the AccessControlListNotSupported error code. Requests to read ACLs are\n still supported. For more information, see Controlling object\n ownership in the Amazon S3 User Guide.

\n
\n
\n
Permissions
\n
\n

You can set access permissions using one of the following methods:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports\n a set of predefined ACLs, known as canned ACLs. Each canned ACL has a predefined set\n of grantees and permissions. Specify the canned ACL name as the value of\n x-amz-acl. If you use this header, you cannot use other access\n control-specific headers in your request. For more information, see Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the x-amz-grant-read,\n x-amz-grant-read-acp, x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. When using these headers, you\n specify explicit access permissions and grantees (Amazon Web Services accounts or Amazon S3 groups) who\n will receive the permission. If you use these ACL-specific headers, you cannot use\n x-amz-acl header to set a canned ACL. These parameters map to the set\n of permissions that Amazon S3 supports in an ACL. For more information, see Access Control\n List (ACL) Overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an\n Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants list\n objects permission to the two Amazon Web Services accounts identified by their email\n addresses.

    \n

    \n x-amz-grant-read: emailAddress=\"xyz@amazon.com\",\n emailAddress=\"abc@amazon.com\" \n

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You cannot do\n both.

\n
\n
Grantee Values
\n
\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request.

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>lt;/Grantee>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n \n

    Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

    \n
      \n
    • \n

      US East (N. Virginia)

      \n
    • \n
    • \n

      US West (N. California)

      \n
    • \n
    • \n

      US West (Oregon)

      \n
    • \n
    • \n

      Asia Pacific (Singapore)

      \n
    • \n
    • \n

      Asia Pacific (Sydney)

      \n
    • \n
    • \n

      Asia Pacific (Tokyo)

      \n
    • \n
    • \n

      Europe (Ireland)

      \n
    • \n
    • \n

      South America (São Paulo)

      \n
    • \n
    \n

    For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

    \n
    \n
  • \n
\n
\n
Versioning
\n
\n

The ACL of an object is set at the object version level. By default, PUT sets the ACL of\n the current version of an object. To set the ACL of a different version, use the\n versionId subresource.

\n
\n
\n

The following operations are related to PutObjectAcl:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?acl", @@ -30965,7 +30965,7 @@ "ACL": { "target": "com.amazonaws.s3#ObjectCannedACL", "traits": { - "smithy.api#documentation": "

The canned ACL to apply to the object. For more information, see Canned ACL.

", + "smithy.api#documentation": "

The canned ACL to apply to the object. For more information, see Canned\n ACL.

", "smithy.api#httpHeader": "x-amz-acl" } }, @@ -31012,7 +31012,7 @@ "GrantRead": { "target": "com.amazonaws.s3#GrantRead", "traits": { - "smithy.api#documentation": "

Allows grantee to list the objects in the\n bucket.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to list the objects in the bucket.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-read" } }, @@ -31026,21 +31026,21 @@ "GrantWrite": { "target": "com.amazonaws.s3#GrantWrite", "traits": { - "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and overwrites of those objects.

", + "smithy.api#documentation": "

Allows grantee to create new objects in the bucket.

\n

For the bucket and object owners of existing objects, also allows deletions and\n overwrites of those objects.

", "smithy.api#httpHeader": "x-amz-grant-write" } }, "GrantWriteACP": { "target": "com.amazonaws.s3#GrantWriteACP", "traits": { - "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable\n bucket.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable bucket.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-write-acp" } }, "Key": { "target": "com.amazonaws.s3#ObjectKey", "traits": { - "smithy.api#documentation": "

Key for which the PUT action was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Key for which the PUT action was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {} } @@ -31187,7 +31187,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Places an Object Lock configuration on the specified bucket. The rule specified in the\n Object Lock configuration will be applied by default to every new object placed in the\n specified bucket. For more information, see Locking Objects.\n

\n \n
    \n
  • \n

    The DefaultRetention settings require both a mode and a\n period.

    \n
  • \n
  • \n

    The DefaultRetention period can be either Days\n or Years but you must select one. You cannot specify Days\n and Years at the same time.

    \n
  • \n
  • \n

    You can only enable Object Lock for new buckets. If you want to turn on\n Object Lock for an existing bucket, contact Amazon Web Services Support.

    \n
  • \n
\n
", + "smithy.api#documentation": "

Places an Object Lock configuration on the specified bucket. The rule specified in the\n Object Lock configuration will be applied by default to every new object placed in the\n specified bucket. For more information, see Locking Objects.

\n \n
    \n
  • \n

    The DefaultRetention settings require both a mode and a\n period.

    \n
  • \n
  • \n

    The DefaultRetention period can be either Days or\n Years but you must select one. You cannot specify\n Days and Years at the same time.

    \n
  • \n
  • \n

    You can only enable Object Lock for new buckets. If you want to turn on Object\n Lock for an existing bucket, contact Amazon Web Services Support.

    \n
  • \n
\n
", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?object-lock", @@ -31318,7 +31318,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

If you specified server-side encryption either with an Amazon Web Services KMS key\n or Amazon S3-managed encryption key in your PUT request, the response includes this header. It\n confirms the encryption algorithm that Amazon S3 used to encrypt the object.

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -31346,14 +31346,14 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If x-amz-server-side-encryption is present and has the value of\n aws:kms, this header specifies the ID of the Amazon Web Services Key Management Service\n (Amazon Web Services KMS) symmetric customer managed key that was used for the\n object.

", + "smithy.api#documentation": "

If x-amz-server-side-encryption is has a valid value of\n aws:kms, this header specifies the ID of the Amazon Web Services Key Management Service\n (Amazon Web Services KMS) symmetric encryption customer managed key that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, "SSEKMSEncryptionContext": { "target": "com.amazonaws.s3#SSEKMSEncryptionContext", "traits": { - "smithy.api#documentation": "

If present, specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The\n value of this header is a base64-encoded UTF-8 string holding JSON with the encryption\n context key-value pairs.

", + "smithy.api#documentation": "

If present, specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The\n value of this header is a base64-encoded UTF-8 string holding JSON with the encryption\n context key-value pairs. This value is stored as object metadata and automatically gets passed\n on to Amazon Web Services KMS for future GetObject or CopyObject operations on\n this object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-context" } }, @@ -31361,7 +31361,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the uploaded object uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the uploaded object uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -31382,7 +31382,7 @@ "ACL": { "target": "com.amazonaws.s3#ObjectCannedACL", "traits": { - "smithy.api#documentation": "

The canned ACL to apply to the object. For more information, see Canned\n ACL.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

The canned ACL to apply to the object. For more information, see Canned\n ACL.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-acl" } }, @@ -31397,7 +31397,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name to which the PUT action was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name to which the PUT action was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -31415,14 +31415,14 @@ "ContentDisposition": { "target": "com.amazonaws.s3#ContentDisposition", "traits": { - "smithy.api#documentation": "

Specifies presentational information for the object. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1.

", + "smithy.api#documentation": "

Specifies presentational information for the object. For more information, see https://www.rfc-editor.org/rfc/rfc6266#section-4.

", "smithy.api#httpHeader": "Content-Disposition" } }, "ContentEncoding": { "target": "com.amazonaws.s3#ContentEncoding", "traits": { - "smithy.api#documentation": "

Specifies what content encodings have been applied to the object and thus what decoding\n mechanisms must be applied to obtain the media-type referenced by the Content-Type header\n field. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.

", + "smithy.api#documentation": "

Specifies what content encodings have been applied to the object and thus what decoding\n mechanisms must be applied to obtain the media-type referenced by the Content-Type header\n field. For more information, see https://www.rfc-editor.org/rfc/rfc9110.html#field.content-encoding.

", "smithy.api#httpHeader": "Content-Encoding" } }, @@ -31437,21 +31437,21 @@ "target": "com.amazonaws.s3#ContentLength", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

Size of the body in bytes. This parameter is useful when the size of the body cannot be\n determined automatically. For more information, see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13.

", + "smithy.api#documentation": "

Size of the body in bytes. This parameter is useful when the size of the body cannot be\n determined automatically. For more information, see https://www.rfc-editor.org/rfc/rfc9110.html#name-content-length.

", "smithy.api#httpHeader": "Content-Length" } }, "ContentMD5": { "target": "com.amazonaws.s3#ContentMD5", "traits": { - "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the message (without the headers) according to\n RFC 1864. This header can be used as a message integrity check to verify that the data is\n the same data that was originally sent. Although it is optional, we recommend using the\n Content-MD5 mechanism as an end-to-end integrity check. For more information about REST\n request authentication, see REST\n Authentication.

", + "smithy.api#documentation": "

The base64-encoded 128-bit MD5 digest of the message (without the headers) according to\n RFC 1864. This header can be used as a message integrity check to verify that the data is\n the same data that was originally sent. Although it is optional, we recommend using the\n Content-MD5 mechanism as an end-to-end integrity check. For more information about REST\n request authentication, see REST Authentication.

", "smithy.api#httpHeader": "Content-MD5" } }, "ContentType": { "target": "com.amazonaws.s3#ContentType", "traits": { - "smithy.api#documentation": "

A standard MIME type describing the format of the contents. For more information, see\n http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17.

", + "smithy.api#documentation": "

A standard MIME type describing the format of the contents. For more information, see\n https://www.rfc-editor.org/rfc/rfc9110.html#name-content-type.

", "smithy.api#httpHeader": "Content-Type" } }, @@ -31493,21 +31493,21 @@ "Expires": { "target": "com.amazonaws.s3#Expires", "traits": { - "smithy.api#documentation": "

The date and time at which the object is no longer cacheable. For more information, see\n http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21.

", + "smithy.api#documentation": "

The date and time at which the object is no longer cacheable. For more information, see\n https://www.rfc-editor.org/rfc/rfc7234#section-5.3.

", "smithy.api#httpHeader": "Expires" } }, "GrantFullControl": { "target": "com.amazonaws.s3#GrantFullControl", "traits": { - "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-full-control" } }, "GrantRead": { "target": "com.amazonaws.s3#GrantRead", "traits": { - "smithy.api#documentation": "

Allows grantee to read the object data and its\n metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to read the object data and its metadata.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-read" } }, @@ -31521,7 +31521,7 @@ "GrantWriteACP": { "target": "com.amazonaws.s3#GrantWriteACP", "traits": { - "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable\n object.

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Allows grantee to write the ACL for the applicable object.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#httpHeader": "x-amz-grant-write-acp" } }, @@ -31543,21 +31543,21 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", + "smithy.api#documentation": "

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-storage-class" } }, "WebsiteRedirectLocation": { "target": "com.amazonaws.s3#WebsiteRedirectLocation", "traits": { - "smithy.api#documentation": "

If the bucket is configured as a website, redirects requests for this object to another\n object in the same bucket or to an external URL. Amazon S3 stores the value of this header in\n the object metadata. For information about object metadata, see Object Key and Metadata.

\n

In the following example, the request header sets the redirect to an object\n (anotherPage.html) in the same bucket:

\n

\n x-amz-website-redirect-location: /anotherPage.html\n

\n

In the following example, the request header sets the object redirect to another\n website:

\n

\n x-amz-website-redirect-location: http://www.example.com/\n

\n

For more information about website hosting in Amazon S3, see Hosting Websites on Amazon S3 and How to Configure Website Page\n Redirects.

", + "smithy.api#documentation": "

If the bucket is configured as a website, redirects requests for this object to another\n object in the same bucket or to an external URL. Amazon S3 stores the value of this header in\n the object metadata. For information about object metadata, see Object Key and Metadata.

\n

In the following example, the request header sets the redirect to an object\n (anotherPage.html) in the same bucket:

\n

\n x-amz-website-redirect-location: /anotherPage.html\n

\n

In the following example, the request header sets the object redirect to another\n website:

\n

\n x-amz-website-redirect-location: http://www.example.com/\n

\n

For more information about website hosting in Amazon S3, see Hosting Websites on Amazon S3 and\n How to\n Configure Website Page Redirects.

", "smithy.api#httpHeader": "x-amz-website-redirect-location" } }, @@ -31585,14 +31585,14 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If x-amz-server-side-encryption is present and has the value of\n aws:kms, this header specifies the ID of the Amazon Web Services Key Management Service\n (Amazon Web Services KMS) symmetrical customer managed key that was used for the\n object. If you specify x-amz-server-side-encryption:aws:kms, but do not\n provide x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the Amazon Web Services\n managed key to protect the data. If the KMS key does not exist in the same account\n issuing the command, you must use the full ARN and not just the ID.\n

", + "smithy.api#documentation": "

If x-amz-server-side-encryption has a valid value of aws:kms,\n this header specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object. If you specify\n x-amz-server-side-encryption:aws:kms, but do not provide\n x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the Amazon Web Services managed key to\n protect the data. If the KMS key does not exist in the same account issuing the command,\n you must use the full ARN and not just the ID.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, "SSEKMSEncryptionContext": { "target": "com.amazonaws.s3#SSEKMSEncryptionContext", "traits": { - "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of this\n header is a base64-encoded UTF-8 string holding JSON with the encryption context key-value\n pairs.

", + "smithy.api#documentation": "

Specifies the Amazon Web Services KMS Encryption Context to use for object encryption. The value of\n this header is a base64-encoded UTF-8 string holding JSON with the encryption context\n key-value pairs. This value is stored as object metadata and automatically gets passed on to\n Amazon Web Services KMS for future GetObject or CopyObject operations on this\n object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-context" } }, @@ -31600,7 +31600,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with server-side encryption using AWS KMS (SSE-KMS). Setting this header to true causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with a PUT action doesn’t affect bucket-level settings for S3 Bucket Key.

", + "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using AWS KMS (SSE-KMS). Setting this header to true\n causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with a PUT action doesn’t affect bucket-level settings for S3\n Bucket Key.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -31634,7 +31634,7 @@ "ObjectLockLegalHoldStatus": { "target": "com.amazonaws.s3#ObjectLockLegalHoldStatus", "traits": { - "smithy.api#documentation": "

Specifies whether a legal hold will be applied to this object. For more information\n about S3 Object Lock, see Object\n Lock.

", + "smithy.api#documentation": "

Specifies whether a legal hold will be applied to this object. For more information\n about S3 Object Lock, see Object Lock.

", "smithy.api#httpHeader": "x-amz-object-lock-legal-hold" } }, @@ -31663,7 +31663,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Places an Object Retention configuration on an object. For more information, see Locking Objects.\n Users or accounts require the s3:PutObjectRetention permission in order to place\n an Object Retention configuration on objects. Bypassing a Governance Retention configuration\n requires the s3:BypassGovernanceRetention permission.\n

\n

This action is not supported by Amazon S3 on Outposts.

", + "smithy.api#documentation": "

Places an Object Retention configuration on an object. For more information, see Locking Objects.\n Users or accounts require the s3:PutObjectRetention permission in order to\n place an Object Retention configuration on objects. Bypassing a Governance Retention\n configuration requires the s3:BypassGovernanceRetention permission.

\n

This action is not supported by Amazon S3 on Outposts.

", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?retention", @@ -31775,7 +31775,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the supplied tag-set to an object that already exists in a bucket.

\n

A tag is a key-value pair. You can associate tags with an object by sending a PUT\n request against the tagging subresource that is associated with the object. You can\n retrieve tags by sending a GET request. For more information, see GetObjectTagging.

\n

For tagging-related restrictions related to characters and encodings, see Tag\n Restrictions. Note that Amazon S3 limits the maximum number of tags to 10 tags per\n object.

\n

To use this operation, you must have permission to perform the\n s3:PutObjectTagging action. By default, the bucket owner has this\n permission and can grant this permission to others.

\n

To put tags of any other version, use the versionId query parameter. You\n also need permission for the s3:PutObjectVersionTagging action.

\n

For information about the Amazon S3 object tagging feature, see Object Tagging.

\n

\n Special Errors\n

\n
    \n
  • \n
      \n
    • \n

      \n Code: InvalidTagError \n

      \n
    • \n
    • \n

      \n Cause: The tag provided was not a valid tag. This error can occur\n if the tag did not pass input validation. For more information, see Object Tagging.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedXMLError \n

      \n
    • \n
    • \n

      \n Cause: The XML provided does not match the schema.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: OperationAbortedError \n

      \n
    • \n
    • \n

      \n Cause: A conflicting conditional action is currently in\n progress against this resource. Please try again.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InternalError\n

      \n
    • \n
    • \n

      \n Cause: The service was unable to apply the provided tag to the\n object.\n

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Sets the supplied tag-set to an object that already exists in a bucket.

\n

A tag is a key-value pair. You can associate tags with an object by sending a PUT\n request against the tagging subresource that is associated with the object. You can\n retrieve tags by sending a GET request. For more information, see GetObjectTagging.

\n

For tagging-related restrictions related to characters and encodings, see Tag\n Restrictions. Note that Amazon S3 limits the maximum number of tags to 10 tags per\n object.

\n

To use this operation, you must have permission to perform the\n s3:PutObjectTagging action. By default, the bucket owner has this\n permission and can grant this permission to others.

\n

To put tags of any other version, use the versionId query parameter. You\n also need permission for the s3:PutObjectVersionTagging action.

\n

For information about the Amazon S3 object tagging feature, see Object Tagging.

\n

\n PutObjectTagging has the following special errors:

\n
    \n
  • \n
      \n
    • \n

      \n Code: InvalidTagError \n

      \n
    • \n
    • \n

      \n Cause: The tag provided was not a valid tag. This error can occur\n if the tag did not pass input validation. For more information, see Object\n Tagging.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedXMLError \n

      \n
    • \n
    • \n

      \n Cause: The XML provided does not match the schema.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: OperationAbortedError \n

      \n
    • \n
    • \n

      \n Cause: A conflicting conditional action is currently in progress\n against this resource. Please try again.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InternalError\n

      \n
    • \n
    • \n

      \n Cause: The service was unable to apply the provided tag to the\n object.\n

      \n
    • \n
    \n
  • \n
\n

The following operations are related to PutObjectTagging:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?tagging", @@ -31804,7 +31804,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the object.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -31881,7 +31881,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Creates or modifies the PublicAccessBlock configuration for an Amazon S3 bucket.\n To use this operation, you must have the s3:PutBucketPublicAccessBlock\n permission. For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n \n

When Amazon S3 evaluates the PublicAccessBlock configuration for a bucket or\n an object, it checks the PublicAccessBlock configuration for both the\n bucket (or the bucket that contains the object) and the bucket owner's account. If the\n PublicAccessBlock configurations are different between the bucket and\n the account, Amazon S3 uses the most restrictive combination of the bucket-level and\n account-level settings.

\n
\n

For more information about when Amazon S3 considers a bucket or an object public, see The Meaning of \"Public\".

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Creates or modifies the PublicAccessBlock configuration for an Amazon S3 bucket.\n To use this operation, you must have the s3:PutBucketPublicAccessBlock\n permission. For more information about Amazon S3 permissions, see Specifying Permissions in a\n Policy.

\n \n

When Amazon S3 evaluates the PublicAccessBlock configuration for a bucket or\n an object, it checks the PublicAccessBlock configuration for both the\n bucket (or the bucket that contains the object) and the bucket owner's account. If the\n PublicAccessBlock configurations are different between the bucket and\n the account, Amazon S3 uses the most restrictive combination of the bucket-level and\n account-level settings.

\n
\n

For more information about when Amazon S3 considers a bucket or an object public, see The Meaning of \"Public\".

\n

The following operations are related to PutPublicAccessBlock:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?publicAccessBlock", @@ -32134,7 +32134,7 @@ "Role": { "target": "com.amazonaws.s3#Role", "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the Identity and Access Management (IAM) role that\n Amazon S3 assumes when replicating objects. For more information, see How to Set Up\n Replication in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the Identity and Access Management (IAM) role that Amazon S3 assumes when\n replicating objects. For more information, see How to Set Up Replication\n in the Amazon S3 User Guide.

", "smithy.api#required": {} } }, @@ -32188,13 +32188,13 @@ "SourceSelectionCriteria": { "target": "com.amazonaws.s3#SourceSelectionCriteria", "traits": { - "smithy.api#documentation": "

A container that describes additional filters for identifying the source objects that\n you want to replicate. You can choose to enable or disable the replication of these\n objects. Currently, Amazon S3 supports only the filter that you can specify for objects created\n with server-side encryption using a customer managed key stored in Amazon Web Services Key Management\n Service (SSE-KMS).

" + "smithy.api#documentation": "

A container that describes additional filters for identifying the source objects that\n you want to replicate. You can choose to enable or disable the replication of these\n objects. Currently, Amazon S3 supports only the filter that you can specify for objects created\n with server-side encryption using a customer managed key stored in Amazon Web Services Key Management Service\n (SSE-KMS).

" } }, "ExistingObjectReplication": { "target": "com.amazonaws.s3#ExistingObjectReplication", "traits": { - "smithy.api#documentation": "

" + "smithy.api#documentation": "

Optional configuration to replicate existing source bucket objects. For more\n information, see Replicating Existing Objects in the Amazon S3 User Guide.\n

" } }, "Destination": { @@ -32471,7 +32471,7 @@ "aws.protocols#httpChecksum": { "requestAlgorithmMember": "ChecksumAlgorithm" }, - "smithy.api#documentation": "

Restores an archived copy of an object back into Amazon S3

\n

This action is not supported by Amazon S3 on Outposts.

\n

This action performs the following types of requests:

\n
    \n
  • \n

    \n select - Perform a select query on an archived object

    \n
  • \n
  • \n

    \n restore an archive - Restore an archived object

    \n
  • \n
\n

To use this operation, you must have permissions to perform the\n s3:RestoreObject action. The bucket owner has this permission by default\n and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing Access Permissions to Your Amazon S3\n Resources in the Amazon S3 User Guide.

\n

\n Querying Archives with Select Requests\n

\n

You use a select type of request to perform SQL queries on archived objects. The\n archived objects that are being queried by the select request must be formatted as\n uncompressed comma-separated values (CSV) files. You can run queries and custom analytics\n on your archived data without having to restore your data to a hotter Amazon S3 tier. For an\n overview about select requests, see Querying Archived Objects in the Amazon S3 User Guide.

\n

When making a select request, do the following:

\n
    \n
  • \n

    Define an output location for the select query's output. This must be an Amazon S3\n bucket in the same Amazon Web Services Region as the bucket that contains the archive object that is\n being queried. The Amazon Web Services account that initiates the job must have permissions to write\n to the S3 bucket. You can specify the storage class and encryption for the output\n objects stored in the bucket. For more information about output, see Querying Archived Objects\n in the Amazon S3 User Guide.

    \n

    For more information about the S3 structure in the request body, see\n the following:

    \n \n
  • \n
  • \n

    Define the SQL expression for the SELECT type of restoration for your\n query in the request body's SelectParameters structure. You can use\n expressions like the following examples.

    \n
      \n
    • \n

      The following expression returns all records from the specified\n object.

      \n

      \n SELECT * FROM Object\n

      \n
    • \n
    • \n

      Assuming that you are not using any headers for data stored in the object,\n you can specify columns with positional headers.

      \n

      \n SELECT s._1, s._2 FROM Object s WHERE s._3 > 100\n

      \n
    • \n
    • \n

      If you have headers and you set the fileHeaderInfo in the\n CSV structure in the request body to USE, you can\n specify headers in the query. (If you set the fileHeaderInfo field\n to IGNORE, the first row is skipped for the query.) You cannot mix\n ordinal positions with header column names.

      \n

      \n SELECT s.Id, s.FirstName, s.SSN FROM S3Object s\n

      \n
    • \n
    \n
  • \n
\n

For more information about using SQL with S3 Glacier Select restore, see SQL Reference for Amazon S3 Select and\n S3 Glacier Select in the Amazon S3 User Guide.

\n

When making a select request, you can also do the following:

\n
    \n
  • \n

    To expedite your queries, specify the Expedited tier. For more\n information about tiers, see \"Restoring Archives,\" later in this topic.

    \n
  • \n
  • \n

    Specify details about the data serialization format of both the input object that\n is being queried and the serialization of the CSV-encoded query results.

    \n
  • \n
\n

The following are additional important facts about the select feature:

\n
    \n
  • \n

    The output results are new Amazon S3 objects. Unlike archive retrievals, they are\n stored until explicitly deleted-manually or through a lifecycle policy.

    \n
  • \n
  • \n

    You can issue more than one select request on the same Amazon S3 object. Amazon S3 doesn't\n deduplicate requests, so avoid issuing duplicate requests.

    \n
  • \n
  • \n

    Amazon S3 accepts a select request even if the object has already been restored. A\n select request doesn’t return error response 409.

    \n
  • \n
\n

\n Restoring objects\n

\n

Objects that you archive to the S3 Glacier or\n S3 Glacier Deep Archive storage class, and S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers are not accessible in real time. For objects in\n Archive Access or Deep Archive Access tiers you must first initiate a restore request, and\n then wait until the object is moved into the Frequent Access tier. For objects in\n S3 Glacier or S3 Glacier Deep Archive storage classes you must\n first initiate a restore request, and then wait until a temporary copy of the object is\n available. To access an archived object, you must restore the object for the duration\n (number of days) that you specify.

\n

To restore a specific object version, you can provide a version ID. If you don't provide\n a version ID, Amazon S3 restores the current version.

\n

When restoring an archived object (or using a select request), you can specify one of\n the following data access tier options in the Tier element of the request\n body:

\n
    \n
  • \n

    \n Expedited - Expedited retrievals allow you to quickly access your\n data stored in the S3 Glacier storage class or S3 Intelligent-Tiering Archive\n tier when occasional urgent requests for a subset of archives are required. For all\n but the largest archived objects (250 MB+), data accessed using Expedited retrievals\n is typically made available within 1–5 minutes. Provisioned capacity ensures that\n retrieval capacity for Expedited retrievals is available when you need it. Expedited\n retrievals and provisioned capacity are not available for objects stored in the\n S3 Glacier Deep Archive storage class or S3 Intelligent-Tiering Deep Archive tier.

    \n
  • \n
  • \n

    \n Standard - Standard retrievals allow you to access any of your\n archived objects within several hours. This is the default option for retrieval\n requests that do not specify the retrieval option. Standard retrievals typically\n finish within 3–5 hours for objects stored in the S3 Glacier storage\n class or S3 Intelligent-Tiering Archive tier. They typically finish within 12 hours for\n objects stored in the S3 Glacier Deep Archive storage class or\n S3 Intelligent-Tiering Deep Archive tier. Standard retrievals are free for objects stored in\n S3 Intelligent-Tiering.

    \n
  • \n
  • \n

    \n Bulk - Bulk retrievals are the lowest-cost retrieval option in\n S3 Glacier, enabling you to retrieve large amounts, even petabytes, of data\n inexpensively. Bulk retrievals typically finish within 5–12 hours for objects stored\n in the S3 Glacier storage class or S3 Intelligent-Tiering Archive tier. They\n typically finish within 48 hours for objects stored in the\n S3 Glacier Deep Archive storage class or S3 Intelligent-Tiering Deep Archive tier. Bulk\n retrievals are free for objects stored in S3 Intelligent-Tiering.

    \n
  • \n
\n

For more information about archive retrieval options and provisioned capacity for\n Expedited data access, see Restoring Archived Objects in the Amazon S3 User Guide.

\n

You can use Amazon S3 restore speed upgrade to change the restore speed to a faster speed\n while it is in progress. For more information, see \n Upgrading the speed of an in-progress restore in the\n Amazon S3 User Guide.

\n

To get the status of object restoration, you can send a HEAD request.\n Operations return the x-amz-restore header, which provides information about\n the restoration status, in the response. You can use Amazon S3 event notifications to notify you\n when a restore is initiated or completed. For more information, see Configuring Amazon S3 Event Notifications in\n the Amazon S3 User Guide.

\n

After restoring an archived object, you can update the restoration period by reissuing\n the request with a new period. Amazon S3 updates the restoration period relative to the current\n time and charges only for the request-there are no data transfer charges. You cannot\n update the restoration period when Amazon S3 is actively processing your current restore request\n for the object.

\n

If your bucket has a lifecycle configuration with a rule that includes an expiration\n action, the object expiration overrides the life span that you specify in a restore\n request. For example, if you restore an object copy for 10 days, but the object is\n scheduled to expire in 3 days, Amazon S3 deletes the object in 3 days. For more information\n about lifecycle configuration, see PutBucketLifecycleConfiguration and Object Lifecycle Management in\n Amazon S3 User Guide.

\n

\n Responses\n

\n

A successful action returns either the 200 OK or 202\n Accepted status code.

\n
    \n
  • \n

    If the object is not previously restored, then Amazon S3 returns 202\n Accepted in the response.

    \n
  • \n
  • \n

    If the object is previously restored, Amazon S3 returns 200 OK in the\n response.

    \n
  • \n
\n

\n Special Errors\n

\n
    \n
  • \n
      \n
    • \n

      \n Code: RestoreAlreadyInProgress\n

      \n
    • \n
    • \n

      \n Cause: Object restore is already in progress. (This error does not\n apply to SELECT type requests.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: GlacierExpeditedRetrievalNotAvailable\n

      \n
    • \n
    • \n

      \n Cause: expedited retrievals are currently not available. Try again\n later. (Returned if there is insufficient capacity to process the Expedited\n request. This error applies only to Expedited retrievals and not to\n S3 Standard or Bulk retrievals.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 503\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: N/A\n

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Restores an archived copy of an object back into Amazon S3

\n

This action is not supported by Amazon S3 on Outposts.

\n

This action performs the following types of requests:

\n
    \n
  • \n

    \n select - Perform a select query on an archived object

    \n
  • \n
  • \n

    \n restore an archive - Restore an archived object

    \n
  • \n
\n

For more information about the S3 structure in the request body, see the\n following:

\n \n

Define the SQL expression for the SELECT type of restoration for your\n query in the request body's SelectParameters structure. You can use\n expressions like the following examples.

\n
    \n
  • \n

    The following expression returns all records from the specified\n object.

    \n

    \n SELECT * FROM Object\n

    \n
  • \n
  • \n

    Assuming that you are not using any headers for data stored in the object,\n you can specify columns with positional headers.

    \n

    \n SELECT s._1, s._2 FROM Object s WHERE s._3 > 100\n

    \n
  • \n
  • \n

    If you have headers and you set the fileHeaderInfo in the\n CSV structure in the request body to USE, you can\n specify headers in the query. (If you set the fileHeaderInfo field\n to IGNORE, the first row is skipped for the query.) You cannot mix\n ordinal positions with header column names.

    \n

    \n SELECT s.Id, s.FirstName, s.SSN FROM S3Object s\n

    \n
  • \n
\n

When making a select request, you can also do the following:

\n
    \n
  • \n

    To expedite your queries, specify the Expedited tier. For more\n information about tiers, see \"Restoring Archives,\" later in this topic.

    \n
  • \n
  • \n

    Specify details about the data serialization format of both the input object that\n is being queried and the serialization of the CSV-encoded query results.

    \n
  • \n
\n

The following are additional important facts about the select feature:

\n
    \n
  • \n

    The output results are new Amazon S3 objects. Unlike archive retrievals, they are\n stored until explicitly deleted-manually or through a lifecycle configuration.

    \n
  • \n
  • \n

    You can issue more than one select request on the same Amazon S3 object. Amazon S3 doesn't\n duplicate requests, so avoid issuing duplicate requests.

    \n
  • \n
  • \n

    Amazon S3 accepts a select request even if the object has already been restored. A\n select request doesn’t return error response 409.

    \n
  • \n
\n
\n
Permissions
\n
\n

To use this operation, you must have permissions to perform the\n s3:RestoreObject action. The bucket owner has this permission by default\n and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n
\n
Restoring objects
\n
\n

Objects that you archive to the S3 Glacier Flexible Retrieval or\n S3 Glacier Deep Archive storage class, and S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers, are not accessible in real time. For objects in the\n S3 Glacier Flexible Retrieval or S3 Glacier Deep Archive storage\n classes, you must first initiate a restore request, and then wait until a temporary copy of\n the object is available. If you want a permanent copy of the object, create a copy of it in\n the Amazon S3 Standard storage class in your S3 bucket. To access an archived object, you must\n restore the object for the duration (number of days) that you specify. For objects in the\n Archive Access or Deep Archive Access tiers of S3 Intelligent-Tiering, you must first\n initiate a restore request, and then wait until the object is moved into the Frequent\n Access tier.

\n

To restore a specific object version, you can provide a version ID. If you don't provide\n a version ID, Amazon S3 restores the current version.

\n

When restoring an archived object, you can specify one of the following data access tier\n options in the Tier element of the request body:

\n
    \n
  • \n

    \n Expedited - Expedited retrievals allow you to quickly access your\n data stored in the S3 Glacier Flexible Retrieval storage class or\n S3 Intelligent-Tiering Archive tier when occasional urgent requests for restoring archives\n are required. For all but the largest archived objects (250 MB+), data accessed using\n Expedited retrievals is typically made available within 1–5 minutes. Provisioned\n capacity ensures that retrieval capacity for Expedited retrievals is available when\n you need it. Expedited retrievals and provisioned capacity are not available for\n objects stored in the S3 Glacier Deep Archive storage class or\n S3 Intelligent-Tiering Deep Archive tier.

    \n
  • \n
  • \n

    \n Standard - Standard retrievals allow you to access any of your\n archived objects within several hours. This is the default option for retrieval\n requests that do not specify the retrieval option. Standard retrievals typically\n finish within 3–5 hours for objects stored in the S3 Glacier Flexible\n Retrieval storage class or S3 Intelligent-Tiering Archive tier. They typically finish within\n 12 hours for objects stored in the S3 Glacier Deep Archive storage class or\n S3 Intelligent-Tiering Deep Archive tier. Standard retrievals are free for objects stored in\n S3 Intelligent-Tiering.

    \n
  • \n
  • \n

    \n Bulk - Bulk retrievals free for objects stored in the S3 Glacier\n Flexible Retrieval and S3 Intelligent-Tiering storage classes, enabling you to\n retrieve large amounts, even petabytes, of data at no cost. Bulk retrievals typically\n finish within 5–12 hours for objects stored in the S3 Glacier\n Flexible Retrieval storage class or S3 Intelligent-Tiering Archive tier. Bulk retrievals are\n also the lowest-cost retrieval option when restoring objects from\n S3 Glacier Deep Archive. They typically finish within 48 hours for objects\n stored in the S3 Glacier Deep Archive storage class or S3 Intelligent-Tiering Deep Archive\n tier.

    \n
  • \n
\n

For more information about archive retrieval options and provisioned capacity for\n Expedited data access, see Restoring Archived Objects in\n the Amazon S3 User Guide.

\n

You can use Amazon S3 restore speed upgrade to change the restore speed to a faster speed\n while it is in progress. For more information, see Upgrading the speed of an in-progress restore in the\n Amazon S3 User Guide.

\n

To get the status of object restoration, you can send a HEAD request.\n Operations return the x-amz-restore header, which provides information about\n the restoration status, in the response. You can use Amazon S3 event notifications to notify you\n when a restore is initiated or completed. For more information, see Configuring Amazon S3\n Event Notifications in the Amazon S3 User Guide.

\n

After restoring an archived object, you can update the restoration period by reissuing\n the request with a new period. Amazon S3 updates the restoration period relative to the current\n time and charges only for the request-there are no data transfer charges. You cannot\n update the restoration period when Amazon S3 is actively processing your current restore request\n for the object.

\n

If your bucket has a lifecycle configuration with a rule that includes an expiration\n action, the object expiration overrides the life span that you specify in a restore\n request. For example, if you restore an object copy for 10 days, but the object is\n scheduled to expire in 3 days, Amazon S3 deletes the object in 3 days. For more information\n about lifecycle configuration, see PutBucketLifecycleConfiguration and Object Lifecycle Management\n in Amazon S3 User Guide.

\n
\n
Responses
\n
\n

A successful action returns either the 200 OK or 202 Accepted\n status code.

\n
    \n
  • \n

    If the object is not previously restored, then Amazon S3 returns 202\n Accepted in the response.

    \n
  • \n
  • \n

    If the object is previously restored, Amazon S3 returns 200 OK in the\n response.

    \n
  • \n
\n
    \n
  • \n

    Special errors:

    \n
      \n
    • \n

      \n Code: RestoreAlreadyInProgress\n

      \n
    • \n
    • \n

      \n Cause: Object restore is already in progress. (This error does not\n apply to SELECT type requests.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: GlacierExpeditedRetrievalNotAvailable\n

      \n
    • \n
    • \n

      \n Cause: expedited retrievals are currently not available. Try again\n later. (Returned if there is insufficient capacity to process the Expedited\n request. This error applies only to Expedited retrievals and not to\n S3 Standard or Bulk retrievals.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 503\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: N/A\n

      \n
    • \n
    \n
  • \n
\n
\n
\n

The following operations are related to RestoreObject:

\n ", "smithy.api#http": { "method": "POST", "uri": "/{Bucket}/{Key+}?restore&x-id=RestoreObject", @@ -32506,7 +32506,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name containing the object to restore.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name containing the object to restore.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -32747,7 +32747,7 @@ "KeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

Specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric customer managed key\n to use for encrypting inventory reports.

", + "smithy.api#documentation": "

Specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric encryption\n customer managed key to use for encrypting inventory reports.

", "smithy.api#required": {} } } @@ -32808,7 +32808,7 @@ "target": "com.amazonaws.s3#SelectObjectContentOutput" }, "traits": { - "smithy.api#documentation": "

This action filters the contents of an Amazon S3 object based on a simple structured query\n language (SQL) statement. In the request, along with the SQL expression, you must also\n specify a data serialization format (JSON, CSV, or Apache Parquet) of the object. Amazon S3 uses\n this format to parse object data into records, and returns only records that match the\n specified SQL expression. You must also specify the data serialization format for the\n response.

\n

This action is not supported by Amazon S3 on Outposts.

\n

For more information about Amazon S3 Select,\n see Selecting Content from\n Objects and SELECT\n Command in the Amazon S3 User Guide.

\n

For more information about using SQL with Amazon S3 Select, see SQL Reference for Amazon S3 Select\n and S3 Glacier Select in the Amazon S3 User Guide.

\n

\n

\n Permissions\n

\n

You must have s3:GetObject permission for this operation. Amazon S3 Select does\n not support anonymous access. For more information about permissions, see Specifying Permissions in a Policy\n in the Amazon S3 User Guide.

\n

\n

\n Object Data Formats\n

\n

You can use Amazon S3 Select to query objects that have the following format\n properties:

\n
    \n
  • \n

    \n CSV, JSON, and Parquet - Objects must be in CSV, JSON, or\n Parquet format.

    \n
  • \n
  • \n

    \n UTF-8 - UTF-8 is the only encoding type Amazon S3 Select\n supports.

    \n
  • \n
  • \n

    \n GZIP or BZIP2 - CSV and JSON files can be compressed using\n GZIP or BZIP2. GZIP and BZIP2 are the only compression formats that Amazon S3 Select\n supports for CSV and JSON files. Amazon S3 Select supports columnar compression for\n Parquet using GZIP or Snappy. Amazon S3 Select does not support whole-object compression\n for Parquet objects.

    \n
  • \n
  • \n

    \n Server-side encryption - Amazon S3 Select supports querying\n objects that are protected with server-side encryption.

    \n

    For objects that are encrypted with customer-provided encryption keys (SSE-C), you\n must use HTTPS, and you must use the headers that are documented in the GetObject. For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys) in the\n Amazon S3 User Guide.

    \n

    For objects that are encrypted with Amazon S3 managed encryption keys (SSE-S3) and\n Amazon Web Services KMS keys (SSE-KMS),\n server-side encryption is handled transparently, so you don't need to specify\n anything. For more information about server-side encryption, including SSE-S3 and\n SSE-KMS, see Protecting Data Using\n Server-Side Encryption in the Amazon S3 User Guide.

    \n
  • \n
\n

\n Working with the Response Body\n

\n

Given the response size is unknown, Amazon S3 Select streams the response as a series of\n messages and includes a Transfer-Encoding header with chunked as\n its value in the response. For more information, see Appendix: SelectObjectContent\n Response.

\n

\n

\n GetObject Support\n

\n

The SelectObjectContent action does not support the following\n GetObject functionality. For more information, see GetObject.

\n
    \n
  • \n

    \n Range: Although you can specify a scan range for an Amazon S3 Select request\n (see SelectObjectContentRequest - ScanRange in the request parameters),\n you cannot specify the range of bytes of an object to return.

    \n
  • \n
  • \n

    GLACIER, DEEP_ARCHIVE and REDUCED_REDUNDANCY storage classes: You cannot specify\n the GLACIER, DEEP_ARCHIVE, or REDUCED_REDUNDANCY storage classes. For\n more information, about storage classes see Storage Classes\n in the Amazon S3 User Guide.

    \n
  • \n
\n

\n

\n Special Errors\n

\n

For a list of special errors for this operation, see List of\n SELECT Object Content Error Codes\n

\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

This action filters the contents of an Amazon S3 object based on a simple structured query\n language (SQL) statement. In the request, along with the SQL expression, you must also\n specify a data serialization format (JSON, CSV, or Apache Parquet) of the object. Amazon S3 uses\n this format to parse object data into records, and returns only records that match the\n specified SQL expression. You must also specify the data serialization format for the\n response.

\n

This action is not supported by Amazon S3 on Outposts.

\n

For more information about Amazon S3 Select, see Selecting Content from\n Objects and SELECT\n Command in the Amazon S3 User Guide.

\n

\n
\n
Permissions
\n
\n

You must have s3:GetObject permission for this operation. Amazon S3 Select does\n not support anonymous access. For more information about permissions, see Specifying\n Permissions in a Policy in the Amazon S3 User Guide.

\n
\n
Object Data Formats
\n
\n

You can use Amazon S3 Select to query objects that have the following format\n properties:

\n
    \n
  • \n

    \n CSV, JSON, and Parquet - Objects must be in CSV, JSON, or\n Parquet format.

    \n
  • \n
  • \n

    \n UTF-8 - UTF-8 is the only encoding type Amazon S3 Select\n supports.

    \n
  • \n
  • \n

    \n GZIP or BZIP2 - CSV and JSON files can be compressed using\n GZIP or BZIP2. GZIP and BZIP2 are the only compression formats that Amazon S3 Select\n supports for CSV and JSON files. Amazon S3 Select supports columnar compression for\n Parquet using GZIP or Snappy. Amazon S3 Select does not support whole-object compression\n for Parquet objects.

    \n
  • \n
  • \n

    \n Server-side encryption - Amazon S3 Select supports querying\n objects that are protected with server-side encryption.

    \n

    For objects that are encrypted with customer-provided encryption keys (SSE-C), you\n must use HTTPS, and you must use the headers that are documented in the GetObject. For more information about SSE-C, see Server-Side\n Encryption (Using Customer-Provided Encryption Keys) in the\n Amazon S3 User Guide.

    \n

    For objects that are encrypted with Amazon S3 managed keys (SSE-S3) and Amazon Web Services KMS keys\n (SSE-KMS), server-side encryption is handled transparently, so you don't need to\n specify anything. For more information about server-side encryption, including SSE-S3\n and SSE-KMS, see Protecting Data Using\n Server-Side Encryption in the Amazon S3 User Guide.

    \n
  • \n
\n
\n
Working with the Response Body
\n
\n

Given the response size is unknown, Amazon S3 Select streams the response as a series of\n messages and includes a Transfer-Encoding header with chunked as\n its value in the response. For more information, see Appendix: SelectObjectContent\n Response.

\n
\n
GetObject Support
\n
\n

The SelectObjectContent action does not support the following\n GetObject functionality. For more information, see GetObject.

\n
    \n
  • \n

    \n Range: Although you can specify a scan range for an Amazon S3 Select request\n (see SelectObjectContentRequest - ScanRange in the request parameters),\n you cannot specify the range of bytes of an object to return.

    \n
  • \n
  • \n

    GLACIER, DEEP_ARCHIVE and REDUCED_REDUNDANCY storage classes: You cannot specify\n the GLACIER, DEEP_ARCHIVE, or REDUCED_REDUNDANCY storage classes. For\n more information, about storage classes see Storage\n Classes in the Amazon S3 User Guide.

    \n
  • \n
\n
\n
Special Errors
\n
\n

For a list of special errors for this operation, see List of\n SELECT Object Content Error Codes\n

\n
\n
\n

The following operations are related to SelectObjectContent:

\n ", "smithy.api#http": { "method": "POST", "uri": "/{Bucket}/{Key+}?select&select-type=2&x-id=SelectObjectContent", @@ -33032,12 +33032,12 @@ "KMSMasterKeyID": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

Amazon Web Services Key Management Service (KMS) customer Amazon Web Services KMS key ID to use for the default\n encryption. This parameter is allowed if and only if SSEAlgorithm is set to\n aws:kms.

\n

You can specify the key ID or the Amazon Resource Name (ARN) of the KMS key. However, if\n you are using encryption with cross-account or Amazon Web Services service operations you must use a fully qualified KMS\n key ARN. For more information, see Using encryption for cross-account operations.

\n

\n For example:\n

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN:\n arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n \n

Amazon S3 only supports symmetric KMS keys and not asymmetric KMS keys. For more information, see\n Using symmetric and\n asymmetric keys in the Amazon Web Services Key Management Service Developer Guide.

\n
" + "smithy.api#documentation": "

Amazon Web Services Key Management Service (KMS) customer Amazon Web Services KMS key ID to use for the default\n encryption. This parameter is allowed if and only if SSEAlgorithm is set to\n aws:kms.

\n

You can specify the key ID or the Amazon Resource Name (ARN) of the KMS key. If you use\n a key ID, you can run into a LogDestination undeliverable error when creating a VPC flow\n log.

\n

If you are using encryption with cross-account or Amazon Web Services service operations you must use\n a fully qualified KMS key ARN. For more information, see Using encryption for cross-account operations.

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN:\n arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n \n

Amazon S3 only supports symmetric encryption KMS keys. For more information, see Asymmetric keys in Amazon Web Services KMS in the Amazon Web Services Key Management Service\n Developer Guide.

\n
" } } }, "traits": { - "smithy.api#documentation": "

Describes the default server-side encryption to apply to new objects in the bucket. If a\n PUT Object request doesn't specify any server-side encryption, this default encryption will\n be applied. If you don't specify a customer managed key at configuration, Amazon S3 automatically creates \n an Amazon Web Services KMS key in your Amazon Web Services account the first time that you add an object encrypted with\n SSE-KMS to a bucket. By default, Amazon S3 uses this KMS key for SSE-KMS. For more information, see PUT Bucket encryption in\n the Amazon S3 API Reference.

" + "smithy.api#documentation": "

Describes the default server-side encryption to apply to new objects in the bucket. If a\n PUT Object request doesn't specify any server-side encryption, this default encryption will\n be applied. If you don't specify a customer managed key at configuration, Amazon S3 automatically creates\n an Amazon Web Services KMS key in your Amazon Web Services account the first time that you add an object encrypted\n with SSE-KMS to a bucket. By default, Amazon S3 uses this KMS key for SSE-KMS. For more\n information, see PUT Bucket encryption in\n the Amazon S3 API Reference.

" } }, "com.amazonaws.s3#ServerSideEncryptionConfiguration": { @@ -33070,7 +33070,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key with server-side encryption using KMS (SSE-KMS) for new objects in the bucket. Existing objects are not affected. Setting the BucketKeyEnabled element to true causes Amazon S3 to use an S3 Bucket Key. By default, S3 Bucket Key is not enabled.

\n

For more information, see Amazon S3 Bucket Keys in the Amazon S3 User Guide.

" + "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key with server-side encryption using KMS\n (SSE-KMS) for new objects in the bucket. Existing objects are not affected. Setting the\n BucketKeyEnabled element to true causes Amazon S3 to use an S3\n Bucket Key. By default, S3 Bucket Key is not enabled.

\n

For more information, see Amazon S3 Bucket Keys in the\n Amazon S3 User Guide.

" } } }, @@ -33108,7 +33108,7 @@ "SseKmsEncryptedObjects": { "target": "com.amazonaws.s3#SseKmsEncryptedObjects", "traits": { - "smithy.api#documentation": "

A container for filter information for the selection of Amazon S3 objects encrypted with Amazon Web Services\n KMS. If you include SourceSelectionCriteria in the replication configuration,\n this element is required.

" + "smithy.api#documentation": "

A container for filter information for the selection of Amazon S3 objects encrypted with\n Amazon Web Services KMS. If you include SourceSelectionCriteria in the replication\n configuration, this element is required.

" } }, "ReplicaModifications": { @@ -33119,7 +33119,7 @@ } }, "traits": { - "smithy.api#documentation": "

A container that describes additional filters for identifying the source objects that\n you want to replicate. You can choose to enable or disable the replication of these\n objects. Currently, Amazon S3 supports only the filter that you can specify for objects created\n with server-side encryption using a customer managed key stored in Amazon Web Services Key Management\n Service (SSE-KMS).

" + "smithy.api#documentation": "

A container that describes additional filters for identifying the source objects that\n you want to replicate. You can choose to enable or disable the replication of these\n objects. Currently, Amazon S3 supports only the filter that you can specify for objects created\n with server-side encryption using a customer managed key stored in Amazon Web Services Key Management Service\n (SSE-KMS).

" } }, "com.amazonaws.s3#SseKmsEncryptedObjects": { @@ -33424,7 +33424,7 @@ } }, "traits": { - "smithy.api#documentation": "

Container for granting information.

\n

Buckets that use the bucket owner enforced setting for Object\n Ownership don't support target grants. For more information, see Permissions server access log delivery in the\n Amazon S3 User Guide.

" + "smithy.api#documentation": "

Container for granting information.

\n

Buckets that use the bucket owner enforced setting for Object Ownership don't support\n target grants. For more information, see Permissions server access log delivery in the\n Amazon S3 User Guide.

" } }, "com.amazonaws.s3#TargetGrants": { @@ -33476,7 +33476,7 @@ "AccessTier": { "target": "com.amazonaws.s3#IntelligentTieringAccessTier", "traits": { - "smithy.api#documentation": "

S3 Intelligent-Tiering access tier. See Storage class for\n automatically optimizing frequently and infrequently accessed objects for a list\n of access tiers in the S3 Intelligent-Tiering storage class.

", + "smithy.api#documentation": "

S3 Intelligent-Tiering access tier. See Storage class\n for automatically optimizing frequently and infrequently accessed objects for a\n list of access tiers in the S3 Intelligent-Tiering storage class.

", "smithy.api#required": {} } } @@ -33649,7 +33649,7 @@ "aws.protocols#httpChecksum": { "requestAlgorithmMember": "ChecksumAlgorithm" }, - "smithy.api#documentation": "

Uploads a part in a multipart upload.

\n \n

In this operation, you provide part data in your request. However, you have an option\n to specify your existing Amazon S3 object as a data source for the part you are uploading. To\n upload a part from an existing object, you use the UploadPartCopy operation.\n

\n
\n

You must initiate a multipart upload (see CreateMultipartUpload)\n before you can upload any part. In response to your initiate request, Amazon S3 returns an\n upload ID, a unique identifier, that you must include in your upload part request.

\n

Part numbers can be any number from 1 to 10,000, inclusive. A part number uniquely\n identifies a part and also defines its position within the object being created. If you\n upload a new part using the same part number that was used with a previous part, the\n previously uploaded part is overwritten.

\n

For information about maximum and minimum part sizes and other multipart upload specifications, see Multipart upload limits in the Amazon S3 User Guide.

\n

To ensure that data is not corrupted when traversing the network, specify the\n Content-MD5 header in the upload part request. Amazon S3 checks the part data\n against the provided MD5 value. If they do not match, Amazon S3 returns an error.

\n

If the upload request is signed with Signature Version 4, then Amazon Web Services S3 uses the\n x-amz-content-sha256 header as a checksum instead of\n Content-MD5. For more information see Authenticating Requests: Using the Authorization Header (Amazon Web Services Signature Version\n 4).

\n

\n Note: After you initiate multipart upload and upload\n one or more parts, you must either complete or abort multipart upload in order to stop\n getting charged for storage of the uploaded parts. Only after you either complete or abort\n multipart upload, Amazon S3 frees up the parts storage and stops charging you for the parts\n storage.

\n

For more information on multipart uploads, go to Multipart Upload Overview in the\n Amazon S3 User Guide .

\n

For information on the permissions required to use the multipart upload API, go to\n Multipart Upload and\n Permissions in the Amazon S3 User Guide.

\n

You can optionally request server-side encryption where Amazon S3 encrypts your data as it\n writes it to disks in its data centers and decrypts it for you when you access it. You have\n the option of providing your own encryption key, or you can use the Amazon Web Services managed encryption\n keys. If you choose to provide your own encryption key, the request headers you provide in\n the request must match the headers you used in the request to initiate the upload by using\n CreateMultipartUpload. For more information, go to Using Server-Side Encryption in\n the Amazon S3 User Guide.

\n

Server-side encryption is supported by the S3 Multipart Upload actions. Unless you are\n using a customer-provided encryption key, you don't need to specify the encryption\n parameters in each UploadPart request. Instead, you only need to specify the server-side\n encryption parameters in the initial Initiate Multipart request. For more information, see\n CreateMultipartUpload.

\n

If you requested server-side encryption using a customer-provided encryption key in your\n initiate multipart upload request, you must provide identical encryption information in\n each part upload using the following headers.

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

\n Special Errors\n

\n
    \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload\n

      \n
    • \n
    • \n

      \n Cause: The specified multipart upload does not exist. The upload\n ID might be invalid, or the multipart upload might have been aborted or\n completed.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found \n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client\n

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Uploads a part in a multipart upload.

\n \n

In this operation, you provide part data in your request. However, you have an option\n to specify your existing Amazon S3 object as a data source for the part you are uploading. To\n upload a part from an existing object, you use the UploadPartCopy operation.\n

\n
\n

You must initiate a multipart upload (see CreateMultipartUpload)\n before you can upload any part. In response to your initiate request, Amazon S3 returns an\n upload ID, a unique identifier, that you must include in your upload part request.

\n

Part numbers can be any number from 1 to 10,000, inclusive. A part number uniquely\n identifies a part and also defines its position within the object being created. If you\n upload a new part using the same part number that was used with a previous part, the\n previously uploaded part is overwritten.

\n

For information about maximum and minimum part sizes and other multipart upload\n specifications, see Multipart upload limits in the Amazon S3 User Guide.

\n

To ensure that data is not corrupted when traversing the network, specify the\n Content-MD5 header in the upload part request. Amazon S3 checks the part data\n against the provided MD5 value. If they do not match, Amazon S3 returns an error.

\n

If the upload request is signed with Signature Version 4, then Amazon Web Services S3 uses the\n x-amz-content-sha256 header as a checksum instead of\n Content-MD5. For more information see Authenticating\n Requests: Using the Authorization Header (Amazon Web Services Signature Version 4).

\n

\n Note: After you initiate multipart upload and upload\n one or more parts, you must either complete or abort multipart upload in order to stop\n getting charged for storage of the uploaded parts. Only after you either complete or abort\n multipart upload, Amazon S3 frees up the parts storage and stops charging you for the parts\n storage.

\n

For more information on multipart uploads, go to Multipart Upload Overview in the\n Amazon S3 User Guide .

\n

For information on the permissions required to use the multipart upload API, go to\n Multipart\n Upload and Permissions in the Amazon S3 User Guide.

\n

Server-side encryption is for data encryption at rest. Amazon S3 encrypts your data as it\n writes it to disks in its data centers and decrypts it when you access it. You have three\n mutually exclusive options to protect data using server-side encryption in Amazon S3, depending\n on how you choose to manage the encryption keys. Specifically, the encryption key options\n are Amazon S3 managed keys (SSE-S3), Amazon Web Services KMS keys (SSE-KMS), and Customer-Provided Keys\n (SSE-C). Amazon S3 encrypts data with server-side encryption using Amazon S3 managed keys (SSE-S3) by\n default. You can optionally tell Amazon S3 to encrypt data at rest using server-side encryption\n with other key options. The option you use depends on whether you want to use KMS keys\n (SSE-KMS) or provide your own encryption key (SSE-C). If you choose to provide your own\n encryption key, the request headers you provide in the request must match the headers you\n used in the request to initiate the upload by using CreateMultipartUpload.\n For more information, go to Using Server-Side\n Encryption in the Amazon S3 User Guide.

\n

Server-side encryption is supported by the S3 Multipart Upload actions. Unless you are\n using a customer-provided encryption key (SSE-C), you don't need to specify the encryption\n parameters in each UploadPart request. Instead, you only need to specify the server-side\n encryption parameters in the initial Initiate Multipart request. For more information, see\n CreateMultipartUpload.

\n

If you requested server-side encryption using a customer-provided encryption key (SSE-C)\n in your initiate multipart upload request, you must provide identical encryption\n information in each part upload using the following headers.

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

\n UploadPart has the following special errors:

\n
    \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload\n

      \n
    • \n
    • \n

      \n Cause: The specified multipart upload does not exist. The upload\n ID might be invalid, or the multipart upload might have been aborted or\n completed.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found \n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client\n

      \n
    • \n
    \n
  • \n
\n

The following operations are related to UploadPart:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?x-id=UploadPart", @@ -33666,7 +33666,7 @@ "target": "com.amazonaws.s3#UploadPartCopyOutput" }, "traits": { - "smithy.api#documentation": "

Uploads a part by copying data from an existing object as data source. You specify the\n data source by adding the request header x-amz-copy-source in your request and\n a byte range by adding the request header x-amz-copy-source-range in your\n request.

\n

For information about maximum and minimum part sizes and other multipart upload specifications, see Multipart upload limits in the Amazon S3 User Guide.

\n \n

Instead of using an existing object as part data, you might use the UploadPart\n action and provide data in your request.

\n
\n

You must initiate a multipart upload before you can upload any part. In response to your\n initiate request. Amazon S3 returns a unique identifier, the upload ID, that you must include in\n your upload part request.

\n

For more information about using the UploadPartCopy operation, see the\n following:

\n
    \n
  • \n

    For conceptual information about multipart uploads, see Uploading Objects Using Multipart\n Upload in the Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about permissions required to use the multipart upload API, see\n Multipart Upload and\n Permissions in the Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about copying objects using a single atomic action vs. a multipart\n upload, see Operations on Objects in\n the Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about using server-side encryption with customer-provided\n encryption keys with the UploadPartCopy operation, see CopyObject and UploadPart.

    \n
  • \n
\n

Note the following additional considerations about the request headers\n x-amz-copy-source-if-match, x-amz-copy-source-if-none-match,\n x-amz-copy-source-if-unmodified-since, and\n x-amz-copy-source-if-modified-since:

\n

\n
    \n
  • \n

    \n Consideration 1 - If both of the\n x-amz-copy-source-if-match and\n x-amz-copy-source-if-unmodified-since headers are present in the\n request as follows:

    \n

    \n x-amz-copy-source-if-match condition evaluates to true,\n and;

    \n

    \n x-amz-copy-source-if-unmodified-since condition evaluates to\n false;

    \n

    Amazon S3 returns 200 OK and copies the data.\n

    \n
  • \n
  • \n

    \n Consideration 2 - If both of the\n x-amz-copy-source-if-none-match and\n x-amz-copy-source-if-modified-since headers are present in the\n request as follows:

    \n

    \n x-amz-copy-source-if-none-match condition evaluates to\n false, and;

    \n

    \n x-amz-copy-source-if-modified-since condition evaluates to\n true;

    \n

    Amazon S3 returns 412 Precondition Failed response code.\n

    \n
  • \n
\n

\n Versioning\n

\n

If your bucket has versioning enabled, you could have multiple versions of the same\n object. By default, x-amz-copy-source identifies the current version of the\n object to copy. If the current version is a delete marker and you don't specify a versionId\n in the x-amz-copy-source, Amazon S3 returns a 404 error, because the object does\n not exist. If you specify versionId in the x-amz-copy-source and the versionId\n is a delete marker, Amazon S3 returns an HTTP 400 error, because you are not allowed to specify\n a delete marker as a version for the x-amz-copy-source.

\n

You can optionally specify a specific version of the source object to copy by adding the\n versionId subresource as shown in the following example:

\n

\n x-amz-copy-source: /bucket/object?versionId=version id\n

\n

\n Special Errors\n

\n
    \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload\n

      \n
    • \n
    • \n

      \n Cause: The specified multipart upload does not exist. The upload\n ID might be invalid, or the multipart upload might have been aborted or\n completed.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest\n

      \n
    • \n
    • \n

      \n Cause: The specified copy source is not supported as a byte-range\n copy source.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request\n

      \n
    • \n
    \n
  • \n
\n

\n Related Resources\n

\n ", + "smithy.api#documentation": "

Uploads a part by copying data from an existing object as data source. You specify the\n data source by adding the request header x-amz-copy-source in your request and\n a byte range by adding the request header x-amz-copy-source-range in your\n request.

\n

For information about maximum and minimum part sizes and other multipart upload\n specifications, see Multipart upload limits in the Amazon S3 User Guide.

\n \n

Instead of using an existing object as part data, you might use the UploadPart\n action and provide data in your request.

\n
\n

You must initiate a multipart upload before you can upload any part. In response to your\n initiate request. Amazon S3 returns a unique identifier, the upload ID, that you must include in\n your upload part request.

\n

For more information about using the UploadPartCopy operation, see the\n following:

\n
    \n
  • \n

    For conceptual information about multipart uploads, see Uploading\n Objects Using Multipart Upload in the\n Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about permissions required to use the multipart upload API, see\n Multipart Upload and Permissions in the\n Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about copying objects using a single atomic action vs. a multipart\n upload, see Operations on Objects in\n the Amazon S3 User Guide.

    \n
  • \n
  • \n

    For information about using server-side encryption with customer-provided\n encryption keys with the UploadPartCopy operation, see CopyObject and UploadPart.

    \n
  • \n
\n

Note the following additional considerations about the request headers\n x-amz-copy-source-if-match, x-amz-copy-source-if-none-match,\n x-amz-copy-source-if-unmodified-since, and\n x-amz-copy-source-if-modified-since:

\n

\n
    \n
  • \n

    \n Consideration 1 - If both of the\n x-amz-copy-source-if-match and\n x-amz-copy-source-if-unmodified-since headers are present in the\n request as follows:

    \n

    \n x-amz-copy-source-if-match condition evaluates to true,\n and;

    \n

    \n x-amz-copy-source-if-unmodified-since condition evaluates to\n false;

    \n

    Amazon S3 returns 200 OK and copies the data.\n

    \n
  • \n
  • \n

    \n Consideration 2 - If both of the\n x-amz-copy-source-if-none-match and\n x-amz-copy-source-if-modified-since headers are present in the\n request as follows:

    \n

    \n x-amz-copy-source-if-none-match condition evaluates to\n false, and;

    \n

    \n x-amz-copy-source-if-modified-since condition evaluates to\n true;

    \n

    Amazon S3 returns 412 Precondition Failed response code.\n

    \n
  • \n
\n
\n
Versioning
\n
\n

If your bucket has versioning enabled, you could have multiple versions of the same\n object. By default, x-amz-copy-source identifies the current version of the\n object to copy. If the current version is a delete marker and you don't specify a versionId\n in the x-amz-copy-source, Amazon S3 returns a 404 error, because the object does\n not exist. If you specify versionId in the x-amz-copy-source and the versionId\n is a delete marker, Amazon S3 returns an HTTP 400 error, because you are not allowed to specify\n a delete marker as a version for the x-amz-copy-source.

\n

You can optionally specify a specific version of the source object to copy by adding the\n versionId subresource as shown in the following example:

\n

\n x-amz-copy-source: /bucket/object?versionId=version id\n

\n
\n
Special errors
\n
\n
    \n
  • \n
      \n
    • \n

      \n Code: NoSuchUpload\n

      \n
    • \n
    • \n

      \n Cause: The specified multipart upload does not exist. The upload\n ID might be invalid, or the multipart upload might have been aborted or\n completed.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 404 Not Found\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InvalidRequest\n

      \n
    • \n
    • \n

      \n Cause: The specified copy source is not supported as a byte-range\n copy source.\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 400 Bad Request\n

      \n
    • \n
    \n
  • \n
\n
\n
\n

The following operations are related to UploadPartCopy:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?x-id=UploadPartCopy", @@ -33694,7 +33694,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -33715,7 +33715,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -33723,7 +33723,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -33744,7 +33744,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The bucket name.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -33893,7 +33893,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -33949,7 +33949,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n customer managed key was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -33957,7 +33957,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -33986,7 +33986,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When using this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When using this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts bucket ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see Using Amazon S3 on Outposts in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

The name of the bucket to which the multipart upload was initiated.

\n

When using this action with an access point, you must direct requests to the access point hostname. The access point hostname takes the form AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com. When using this action with an access point through the Amazon Web Services SDKs, you provide the access point ARN in place of the bucket name. For more information about access point ARNs, see Using access points in the Amazon S3 User Guide.

\n

When you use this action with Amazon S3 on Outposts, you must direct requests to the S3 on Outposts hostname. The S3 on Outposts hostname takes the form \n AccessPointName-AccountId.outpostID.s3-outposts.Region.amazonaws.com. When you use this action with S3 on Outposts through the Amazon Web Services SDKs, you provide the Outposts access point ARN in place of the bucket name. For more information about S3 on Outposts ARNs, see What is S3 on Outposts in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -34012,7 +34012,7 @@ "ChecksumAlgorithm": { "target": "com.amazonaws.s3#ChecksumAlgorithm", "traits": { - "smithy.api#documentation": "

Indicates the algorithm used to create the checksum for the object when using the SDK. This header will not provide any\n additional functionality if not using the SDK. When sending this header, there must be a corresponding x-amz-checksum or\n x-amz-trailer header sent. Otherwise, Amazon S3 fails the request with the HTTP status code 400 Bad Request. For more\n information, see Checking object integrity in\n the Amazon S3 User Guide.

\n

If you provide an individual checksum, Amazon S3 ignores any provided\n ChecksumAlgorithm parameter.

\n

This checksum algorithm must be the same for all parts and it match the checksum\n value supplied in the CreateMultipartUpload request.

", + "smithy.api#documentation": "

Indicates the algorithm used to create the checksum for the object when using the SDK. This header will not provide any\n additional functionality if not using the SDK. When sending this header, there must be a corresponding x-amz-checksum or\n x-amz-trailer header sent. Otherwise, Amazon S3 fails the request with the HTTP status code 400 Bad Request. For more\n information, see Checking object integrity in\n the Amazon S3 User Guide.

\n

If you provide an individual checksum, Amazon S3 ignores any provided\n ChecksumAlgorithm parameter.

\n

This checksum algorithm must be the same for all parts and it match the checksum value\n supplied in the CreateMultipartUpload request.

", "smithy.api#httpHeader": "x-amz-sdk-checksum-algorithm" } }, @@ -34198,7 +34198,7 @@ "smithy.api#auth": [ "aws.auth#sigv4" ], - "smithy.api#documentation": "

Passes transformed\n objects to a GetObject operation when using Object Lambda access points. For information about\n Object Lambda access points, see Transforming objects with\n Object Lambda access points in the Amazon S3 User Guide.

\n

This operation supports metadata that can be returned by GetObject, in addition to\n RequestRoute, RequestToken, StatusCode,\n ErrorCode, and ErrorMessage. The GetObject\n response metadata is supported so that the WriteGetObjectResponse caller,\n typically an Lambda function, can provide the same metadata when it internally invokes\n GetObject. When WriteGetObjectResponse is called by a\n customer-owned Lambda function, the metadata returned to the end user\n GetObject call might differ from what Amazon S3 would normally return.

\n

You can include any number of metadata headers. When including a metadata header, it should be\n prefaced with x-amz-meta. For example, x-amz-meta-my-custom-header: MyCustomValue.\n The primary use case for this is to forward GetObject metadata.

\n

Amazon Web Services provides some prebuilt Lambda functions that you can use with S3 Object Lambda to detect and redact\n personally identifiable information (PII) and decompress S3 objects. These Lambda functions\n are available in the Amazon Web Services Serverless Application Repository, and can be selected through the Amazon Web Services Management Console when you create your\n Object Lambda access point.

\n

Example 1: PII Access Control - This Lambda function uses Amazon Comprehend, a natural language processing (NLP) service using machine learning to find insights and relationships in text. It automatically detects personally identifiable information (PII) such as names, addresses, dates, credit card numbers, and social security numbers from documents in your Amazon S3 bucket.

\n

Example 2: PII Redaction - This Lambda function uses Amazon Comprehend, a natural language processing (NLP) service using machine learning to find insights and relationships in text. It automatically redacts personally identifiable information (PII) such as names, addresses, dates, credit card numbers, and social security numbers from documents in your Amazon S3 bucket.

\n

Example 3: Decompression - The Lambda function S3ObjectLambdaDecompression, is equipped to decompress objects stored in S3 in one of six compressed file formats including bzip2, gzip, snappy, zlib, zstandard and ZIP.

\n

For information on how to view and use these functions, see Using Amazon Web Services built Lambda functions in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Passes transformed objects to a GetObject operation when using Object Lambda access points. For\n information about Object Lambda access points, see Transforming objects with\n Object Lambda access points in the Amazon S3 User Guide.

\n

This operation supports metadata that can be returned by GetObject, in addition to\n RequestRoute, RequestToken, StatusCode,\n ErrorCode, and ErrorMessage. The GetObject\n response metadata is supported so that the WriteGetObjectResponse caller,\n typically an Lambda function, can provide the same metadata when it internally invokes\n GetObject. When WriteGetObjectResponse is called by a\n customer-owned Lambda function, the metadata returned to the end user\n GetObject call might differ from what Amazon S3 would normally return.

\n

You can include any number of metadata headers. When including a metadata header, it\n should be prefaced with x-amz-meta. For example,\n x-amz-meta-my-custom-header: MyCustomValue. The primary use case for this\n is to forward GetObject metadata.

\n

Amazon Web Services provides some prebuilt Lambda functions that you can use with S3 Object Lambda to\n detect and redact personally identifiable information (PII) and decompress S3 objects.\n These Lambda functions are available in the Amazon Web Services Serverless Application Repository, and\n can be selected through the Amazon Web Services Management Console when you create your Object Lambda access point.

\n

Example 1: PII Access Control - This Lambda function uses Amazon Comprehend, a\n natural language processing (NLP) service using machine learning to find insights and\n relationships in text. It automatically detects personally identifiable information (PII)\n such as names, addresses, dates, credit card numbers, and social security numbers from\n documents in your Amazon S3 bucket.

\n

Example 2: PII Redaction - This Lambda function uses Amazon Comprehend, a natural\n language processing (NLP) service using machine learning to find insights and relationships\n in text. It automatically redacts personally identifiable information (PII) such as names,\n addresses, dates, credit card numbers, and social security numbers from documents in your\n Amazon S3 bucket.

\n

Example 3: Decompression - The Lambda function S3ObjectLambdaDecompression, is\n equipped to decompress objects stored in S3 in one of six compressed file formats including\n bzip2, gzip, snappy, zlib, zstandard and ZIP.

\n

For information on how to view and use these functions, see Using Amazon Web Services built Lambda\n functions in the Amazon S3 User Guide.

", "smithy.api#endpoint": { "hostPrefix": "{RequestRoute}." }, @@ -34246,7 +34246,7 @@ "target": "com.amazonaws.s3#GetObjectResponseStatusCode", "traits": { "smithy.api#default": 0, - "smithy.api#documentation": "

The integer status code for an HTTP response of a corresponding GetObject\n request.

\n

\n Status Codes\n

\n
    \n
  • \n

    \n 200 - OK\n

    \n
  • \n
  • \n

    \n 206 - Partial Content\n

    \n
  • \n
  • \n

    \n 304 - Not Modified\n

    \n
  • \n
  • \n

    \n 400 - Bad Request\n

    \n
  • \n
  • \n

    \n 401 - Unauthorized\n

    \n
  • \n
  • \n

    \n 403 - Forbidden\n

    \n
  • \n
  • \n

    \n 404 - Not Found\n

    \n
  • \n
  • \n

    \n 405 - Method Not Allowed\n

    \n
  • \n
  • \n

    \n 409 - Conflict\n

    \n
  • \n
  • \n

    \n 411 - Length Required\n

    \n
  • \n
  • \n

    \n 412 - Precondition Failed\n

    \n
  • \n
  • \n

    \n 416 - Range Not Satisfiable\n

    \n
  • \n
  • \n

    \n 500 - Internal Server Error\n

    \n
  • \n
  • \n

    \n 503 - Service Unavailable\n

    \n
  • \n
", + "smithy.api#documentation": "

The integer status code for an HTTP response of a corresponding GetObject\n request. The following is a list of status codes.

\n
    \n
  • \n

    \n 200 - OK\n

    \n
  • \n
  • \n

    \n 206 - Partial Content\n

    \n
  • \n
  • \n

    \n 304 - Not Modified\n

    \n
  • \n
  • \n

    \n 400 - Bad Request\n

    \n
  • \n
  • \n

    \n 401 - Unauthorized\n

    \n
  • \n
  • \n

    \n 403 - Forbidden\n

    \n
  • \n
  • \n

    \n 404 - Not Found\n

    \n
  • \n
  • \n

    \n 405 - Method Not Allowed\n

    \n
  • \n
  • \n

    \n 409 - Conflict\n

    \n
  • \n
  • \n

    \n 411 - Length Required\n

    \n
  • \n
  • \n

    \n 412 - Precondition Failed\n

    \n
  • \n
  • \n

    \n 416 - Range Not Satisfiable\n

    \n
  • \n
  • \n

    \n 500 - Internal Server Error\n

    \n
  • \n
  • \n

    \n 503 - Service Unavailable\n

    \n
  • \n
", "smithy.api#httpHeader": "x-amz-fwd-status" } }, @@ -34288,7 +34288,7 @@ "ContentEncoding": { "target": "com.amazonaws.s3#ContentEncoding", "traits": { - "smithy.api#documentation": "

Specifies what content encodings have been applied to the object and thus what decoding\n mechanisms must be applied to obtain the media-type referenced by the Content-Type header\n field.

", + "smithy.api#documentation": "

Specifies what content encodings have been applied to the object and thus what decoding\n mechanisms must be applied to obtain the media-type referenced by the Content-Type header\n field.

", "smithy.api#httpHeader": "x-amz-fwd-header-Content-Encoding" } }, @@ -34324,28 +34324,28 @@ "ChecksumCRC32": { "target": "com.amazonaws.s3#ChecksumCRC32", "traits": { - "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is the\n same data that was originally sent. This specifies the base64-encoded, 32-bit CRC32 checksum\n of the object returned by the Object Lambda function. This may not match the checksum for the\n object stored in Amazon S3. Amazon S3 will perform validation of the checksum values only when the original\n GetObject request required checksum validation. For more information about checksums, see\n Checking\n object integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple\n checksum headers, this request will fail.

\n

", + "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is\n the same data that was originally sent. This specifies the base64-encoded, 32-bit CRC32\n checksum of the object returned by the Object Lambda function. This may not match the\n checksum for the object stored in Amazon S3. Amazon S3 will perform validation of the checksum values\n only when the original GetObject request required checksum validation. For\n more information about checksums, see Checking object\n integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple checksum\n headers, this request will fail.

\n

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-checksum-crc32" } }, "ChecksumCRC32C": { "target": "com.amazonaws.s3#ChecksumCRC32C", "traits": { - "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is the\n same data that was originally sent. This specifies the base64-encoded, 32-bit CRC32C checksum\n of the object returned by the Object Lambda function. This may not match the checksum for the\n object stored in Amazon S3. Amazon S3 will perform validation of the checksum values only when the original\n GetObject request required checksum validation. For more information about checksums, see\n Checking\n object integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple\n checksum headers, this request will fail.

", + "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is\n the same data that was originally sent. This specifies the base64-encoded, 32-bit CRC32C\n checksum of the object returned by the Object Lambda function. This may not match the\n checksum for the object stored in Amazon S3. Amazon S3 will perform validation of the checksum values\n only when the original GetObject request required checksum validation. For\n more information about checksums, see Checking object\n integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple checksum\n headers, this request will fail.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-checksum-crc32c" } }, "ChecksumSHA1": { "target": "com.amazonaws.s3#ChecksumSHA1", "traits": { - "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is the\n same data that was originally sent. This specifies the base64-encoded, 160-bit SHA-1 digest\n of the object returned by the Object Lambda function. This may not match the checksum for the\n object stored in Amazon S3. Amazon S3 will perform validation of the checksum values only when the original\n GetObject request required checksum validation. For more information about checksums, see\n Checking\n object integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple\n checksum headers, this request will fail.

", + "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is\n the same data that was originally sent. This specifies the base64-encoded, 160-bit SHA-1\n digest of the object returned by the Object Lambda function. This may not match the\n checksum for the object stored in Amazon S3. Amazon S3 will perform validation of the checksum values\n only when the original GetObject request required checksum validation. For\n more information about checksums, see Checking object\n integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple checksum\n headers, this request will fail.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-checksum-sha1" } }, "ChecksumSHA256": { "target": "com.amazonaws.s3#ChecksumSHA256", "traits": { - "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is the\n same data that was originally sent. This specifies the base64-encoded, 256-bit SHA-256 digest\n of the object returned by the Object Lambda function. This may not match the checksum for the\n object stored in Amazon S3. Amazon S3 will perform validation of the checksum values only when the original\n GetObject request required checksum validation. For more information about checksums, see\n Checking\n object integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple\n checksum headers, this request will fail.

", + "smithy.api#documentation": "

This header can be used as a data integrity check to verify that the data received is\n the same data that was originally sent. This specifies the base64-encoded, 256-bit SHA-256\n digest of the object returned by the Object Lambda function. This may not match the\n checksum for the object stored in Amazon S3. Amazon S3 will perform validation of the checksum values\n only when the original GetObject request required checksum validation. For\n more information about checksums, see Checking object\n integrity in the Amazon S3 User Guide.

\n

Only one checksum header can be specified at a time. If you supply multiple checksum\n headers, this request will fail.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-checksum-sha256" } }, @@ -34374,7 +34374,7 @@ "Expiration": { "target": "com.amazonaws.s3#Expiration", "traits": { - "smithy.api#documentation": "

If the object expiration is configured (see PUT Bucket lifecycle), the response\n includes this header. It includes the expiry-date and rule-id\n key-value pairs that provide the object expiration information. The value of the\n rule-id is URL-encoded.

", + "smithy.api#documentation": "

If the object expiration is configured (see PUT Bucket lifecycle), the response includes\n this header. It includes the expiry-date and rule-id key-value\n pairs that provide the object expiration information. The value of the rule-id\n is URL-encoded.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-expiration" } }, @@ -34403,7 +34403,7 @@ "ObjectLockMode": { "target": "com.amazonaws.s3#ObjectLockMode", "traits": { - "smithy.api#documentation": "

Indicates whether an object stored in Amazon S3 has Object Lock enabled. For more\n information about S3 Object Lock, see Object Lock.

", + "smithy.api#documentation": "

Indicates whether an object stored in Amazon S3 has Object Lock enabled. For more information\n about S3 Object Lock, see Object Lock.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-object-lock-mode" } }, @@ -34432,7 +34432,7 @@ "ReplicationStatus": { "target": "com.amazonaws.s3#ReplicationStatus", "traits": { - "smithy.api#documentation": "

Indicates if request involves bucket that is either a source or destination in a Replication rule. For more\n information about S3 Replication, see Replication.

", + "smithy.api#documentation": "

Indicates if request involves bucket that is either a source or destination in a\n Replication rule. For more information about S3 Replication, see Replication.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-replication-status" } }, @@ -34445,28 +34445,28 @@ "Restore": { "target": "com.amazonaws.s3#Restore", "traits": { - "smithy.api#documentation": "

Provides information about object restoration operation and expiration time of the\n restored object copy.

", + "smithy.api#documentation": "

Provides information about object restoration operation and expiration time of the\n restored object copy.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-restore" } }, "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing requested object in Amazon S3 (for example, AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing requested object in Amazon S3 (for\n example, AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-server-side-encryption" } }, "SSECustomerAlgorithm": { "target": "com.amazonaws.s3#SSECustomerAlgorithm", "traits": { - "smithy.api#documentation": "

Encryption algorithm used if server-side encryption with a customer-provided encryption key was specified for object stored in Amazon S3.

", + "smithy.api#documentation": "

Encryption algorithm used if server-side encryption with a customer-provided encryption\n key was specified for object stored in Amazon S3.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-server-side-encryption-customer-algorithm" } }, "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric customer managed key that was used for stored in Amazon S3 object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for stored in Amazon S3 object.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -34480,7 +34480,7 @@ "StorageClass": { "target": "com.amazonaws.s3#StorageClass", "traits": { - "smithy.api#documentation": "

Provides storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage\n Classes.

", + "smithy.api#documentation": "

Provides storage class information of the object. Amazon S3 returns this header for all\n objects except for S3 Standard storage class objects.

\n

For more information, see Storage Classes.

", "smithy.api#httpHeader": "x-amz-fwd-header-x-amz-storage-class" } }, diff --git a/aws/sdk/aws-models/sdk-endpoints.json b/aws/sdk/aws-models/sdk-endpoints.json index c57d7fd0ff..b94964b8f8 100644 --- a/aws/sdk/aws-models/sdk-endpoints.json +++ b/aws/sdk/aws-models/sdk-endpoints.json @@ -41,6 +41,9 @@ "ap-south-1" : { "description" : "Asia Pacific (Mumbai)" }, + "ap-south-2" : { + "description" : "Asia Pacific (Hyderabad)" + }, "ap-southeast-1" : { "description" : "Asia Pacific (Singapore)" }, @@ -50,18 +53,27 @@ "ap-southeast-3" : { "description" : "Asia Pacific (Jakarta)" }, + "ap-southeast-4" : { + "description" : "Asia Pacific (Melbourne)" + }, "ca-central-1" : { "description" : "Canada (Central)" }, "eu-central-1" : { "description" : "Europe (Frankfurt)" }, + "eu-central-2" : { + "description" : "Europe (Zurich)" + }, "eu-north-1" : { "description" : "Europe (Stockholm)" }, "eu-south-1" : { "description" : "Europe (Milan)" }, + "eu-south-2" : { + "description" : "Europe (Spain)" + }, "eu-west-1" : { "description" : "Europe (Ireland)" }, @@ -107,9 +119,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "access-analyzer-fips.ca-central-1.amazonaws.com", @@ -117,8 +131,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -206,9 +222,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "acm-fips.ca-central-1.amazonaws.com", @@ -223,8 +241,10 @@ "hostname" : "acm-fips.ca-central-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -296,9 +316,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "acm-pca-fips.ca-central-1.amazonaws.com", @@ -306,8 +328,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -346,6 +370,7 @@ "deprecated" : true, "hostname" : "acm-pca-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -426,6 +451,7 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -447,6 +473,7 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -458,6 +485,18 @@ "us-west-2" : { } } }, + "aoss" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, "api.detective" : { "defaults" : { "protocols" : [ "https" ] @@ -577,6 +616,12 @@ }, "hostname" : "api.ecr.ap-south-1.amazonaws.com" }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "api.ecr.ap-south-2.amazonaws.com" + }, "ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -595,6 +640,12 @@ }, "hostname" : "api.ecr.ap-southeast-3.amazonaws.com" }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "api.ecr.ap-southeast-4.amazonaws.com" + }, "ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -647,6 +698,12 @@ }, "hostname" : "api.ecr.eu-central-1.amazonaws.com" }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "api.ecr.eu-central-2.amazonaws.com" + }, "eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -659,6 +716,12 @@ }, "hostname" : "api.ecr.eu-south-1.amazonaws.com" }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "api.ecr.eu-south-2.amazonaws.com" + }, "eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -793,6 +856,22 @@ } } }, + "api.ecr-public" : { + "endpoints" : { + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "api.ecr-public.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "api.ecr-public.us-west-2.amazonaws.com" + } + } + }, "api.elastic-inference" : { "endpoints" : { "ap-northeast-1" : { @@ -956,12 +1035,15 @@ }, "api.mediatailor" : { "endpoints" : { + "af-south-1" : { }, "ap-northeast-1" : { }, + "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "eu-central-1" : { }, "eu-west-1" : { }, "us-east-1" : { }, + "us-east-2" : { }, "us-west-2" : { } } }, @@ -990,16 +1072,20 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -1116,6 +1202,7 @@ "deprecated" : true, "hostname" : "api.tunneling.iot-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -1152,9 +1239,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "apigateway-fips.ca-central-1.amazonaws.com", @@ -1162,8 +1251,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -1253,13 +1344,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -1280,15 +1375,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -1328,13 +1429,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -1357,6 +1462,7 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, @@ -1576,6 +1682,9 @@ "apprunner" : { "endpoints" : { "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, "eu-west-1" : { }, "fips-us-east-1" : { "credentialScope" : { @@ -1642,6 +1751,7 @@ "deprecated" : true, "hostname" : "appstream2-fips.us-west-2.amazonaws.com" }, + "sa-east-1" : { }, "us-east-1" : { "variants" : [ { "hostname" : "appstream2-fips.us-east-1.amazonaws.com", @@ -1679,16 +1789,20 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -1703,14 +1817,49 @@ }, "endpoints" : { "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, + "arc-zonal-shift" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, "us-east-1" : { }, "us-east-2" : { }, + "us-west-1" : { }, "us-west-2" : { } } }, @@ -1856,6 +2005,9 @@ "variants" : [ { "hostname" : "athena-fips.us-east-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] }, { "hostname" : "athena.us-east-1.api.aws", "tags" : [ "dualstack" ] @@ -1865,6 +2017,9 @@ "variants" : [ { "hostname" : "athena-fips.us-east-2.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-east-2.api.aws", + "tags" : [ "dualstack", "fips" ] }, { "hostname" : "athena.us-east-2.api.aws", "tags" : [ "dualstack" ] @@ -1874,6 +2029,9 @@ "variants" : [ { "hostname" : "athena-fips.us-west-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] }, { "hostname" : "athena.us-west-1.api.aws", "tags" : [ "dualstack" ] @@ -1883,6 +2041,9 @@ "variants" : [ { "hostname" : "athena-fips.us-west-2.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-west-2.api.aws", + "tags" : [ "dualstack", "fips" ] }, { "hostname" : "athena.us-west-2.api.aws", "tags" : [ "dualstack" ] @@ -1917,13 +2078,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -1973,16 +2138,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -2038,13 +2208,16 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -2076,6 +2249,7 @@ "deprecated" : true, "hostname" : "fips.batch.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -2138,8 +2312,27 @@ }, "cases" : { "endpoints" : { - "us-east-1" : { }, - "us-west-2" : { } + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-west-2" : { }, + "fips-us-east-1" : { + "deprecated" : true + }, + "fips-us-west-2" : { + "deprecated" : true + }, + "us-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + } } }, "cassandra" : { @@ -2221,6 +2414,21 @@ "isRegionalized" : false, "partitionEndpoint" : "aws-global" }, + "cleanrooms" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, "cloud9" : { "endpoints" : { "af-south-1" : { }, @@ -2254,9 +2462,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "cloudcontrolapi-fips.ca-central-1.amazonaws.com", @@ -2264,8 +2474,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -2354,13 +2566,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -2457,6 +2673,7 @@ "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, "eu-west-1" : { }, @@ -2493,13 +2710,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -2560,6 +2781,32 @@ } } }, + "cloudtrail-data" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "codeartifact" : { "endpoints" : { "ap-northeast-1" : { }, @@ -2585,16 +2832,20 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -2651,6 +2902,15 @@ } } }, + "codecatalyst" : { + "endpoints" : { + "aws-global" : { + "hostname" : "codecatalyst.global.api.aws" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, "codecommit" : { "endpoints" : { "af-south-1" : { }, @@ -2659,8 +2919,10 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "codecommit-fips.ca-central-1.amazonaws.com", @@ -2687,6 +2949,7 @@ "deprecated" : true, "hostname" : "codecommit-fips.ca-central-1.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -2751,13 +3014,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -2834,6 +3101,7 @@ }, "codepipeline" : { "endpoints" : { + "af-south-1" : { }, "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, @@ -2847,6 +3115,7 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, "eu-west-1" : { }, @@ -2887,6 +3156,7 @@ "deprecated" : true, "hostname" : "codepipeline-fips.us-west-2.amazonaws.com" }, + "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { "variants" : [ { @@ -2983,6 +3253,7 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -3040,6 +3311,7 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -3354,13 +3626,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -3431,13 +3707,38 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-west-2" : { }, - "us-east-1" : { }, - "us-west-2" : { } + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "connect-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "connect-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "connect-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "connect-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "connect-campaigns" : { "endpoints" : { "ap-southeast-2" : { }, + "ca-central-1" : { }, "eu-west-2" : { }, "fips-us-east-1" : { "credentialScope" : { @@ -3481,11 +3782,15 @@ }, "controltower" : { "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, + "ap-northeast-3" : { }, "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "controltower-fips.ca-central-1.amazonaws.com", @@ -3501,9 +3806,11 @@ }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { "variants" : [ { @@ -3531,6 +3838,19 @@ "deprecated" : true, "hostname" : "controltower-fips.us-east-2.amazonaws.com" }, + "us-west-1" : { + "variants" : [ { + "hostname" : "controltower-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1-fips" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "controltower-fips.us-west-1.amazonaws.com" + }, "us-west-2" : { "variants" : [ { "hostname" : "controltower-fips.us-west-2.amazonaws.com", @@ -3926,6 +4246,7 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, @@ -3936,8 +4257,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -3976,6 +4299,7 @@ "deprecated" : true, "hostname" : "datasync-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -4036,12 +4360,24 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "ca-central-1" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "devops-guru-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, "eu-north-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "devops-guru-fips.ca-central-1.amazonaws.com" + }, "fips-us-east-1" : { "credentialScope" : { "region" : "us-east-1" @@ -4056,6 +4392,13 @@ "deprecated" : true, "hostname" : "devops-guru-fips.us-east-2.amazonaws.com" }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "devops-guru-fips.us-west-1.amazonaws.com" + }, "fips-us-west-2" : { "credentialScope" : { "region" : "us-west-2" @@ -4076,7 +4419,12 @@ "tags" : [ "fips" ] } ] }, - "us-west-1" : { }, + "us-west-1" : { + "variants" : [ { + "hostname" : "devops-guru-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "us-west-2" : { "variants" : [ { "hostname" : "devops-guru-fips.us-west-2.amazonaws.com", @@ -4093,13 +4441,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4182,13 +4534,17 @@ "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -4205,9 +4561,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "dms" : { "credentialScope" : { @@ -4227,8 +4585,10 @@ "hostname" : "dms-fips.us-west-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4411,9 +4771,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "ds-fips.ca-central-1.amazonaws.com", @@ -4421,8 +4783,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4461,6 +4825,7 @@ "deprecated" : true, "hostname" : "ds-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -4500,9 +4865,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "dynamodb-fips.ca-central-1.amazonaws.com", @@ -4517,8 +4884,10 @@ "hostname" : "dynamodb-fips.ca-central-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4594,9 +4963,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "ebs-fips.ca-central-1.amazonaws.com", @@ -4604,8 +4975,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4644,6 +5017,7 @@ "deprecated" : true, "hostname" : "ebs-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -4688,9 +5062,11 @@ "tags" : [ "dualstack" ] } ] }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "ec2-fips.ca-central-1.amazonaws.com", @@ -4698,8 +5074,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { "variants" : [ { "hostname" : "ec2.eu-west-1.api.aws", @@ -4794,13 +5172,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4886,13 +5268,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -4961,13 +5347,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -5147,6 +5537,12 @@ "tags" : [ "fips" ] } ] }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ap-southeast-1" : { "variants" : [ { "hostname" : "elasticfilesystem-fips.ap-southeast-1.amazonaws.com", @@ -5165,6 +5561,12 @@ "tags" : [ "fips" ] } ] }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ca-central-1" : { "variants" : [ { "hostname" : "elasticfilesystem-fips.ca-central-1.amazonaws.com", @@ -5177,18 +5579,30 @@ "tags" : [ "fips" ] } ] }, - "eu-north-1" : { + "eu-central-2" : { "variants" : [ { - "hostname" : "elasticfilesystem-fips.eu-north-1.amazonaws.com", + "hostname" : "elasticfilesystem-fips.eu-central-2.amazonaws.com", "tags" : [ "fips" ] } ] }, - "eu-south-1" : { + "eu-north-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-north-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-south-1" : { "variants" : [ { "hostname" : "elasticfilesystem-fips.eu-south-1.amazonaws.com", "tags" : [ "fips" ] } ] }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-west-1" : { "variants" : [ { "hostname" : "elasticfilesystem-fips.eu-west-1.amazonaws.com", @@ -5249,6 +5663,13 @@ "deprecated" : true, "hostname" : "elasticfilesystem-fips.ap-south-1.amazonaws.com" }, + "fips-ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-south-2.amazonaws.com" + }, "fips-ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -5270,6 +5691,13 @@ "deprecated" : true, "hostname" : "elasticfilesystem-fips.ap-southeast-3.amazonaws.com" }, + "fips-ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.ap-southeast-4.amazonaws.com" + }, "fips-ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -5284,6 +5712,13 @@ "deprecated" : true, "hostname" : "elasticfilesystem-fips.eu-central-1.amazonaws.com" }, + "fips-eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-central-2.amazonaws.com" + }, "fips-eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -5298,6 +5733,13 @@ "deprecated" : true, "hostname" : "elasticfilesystem-fips.eu-south-1.amazonaws.com" }, + "fips-eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.eu-south-2.amazonaws.com" + }, "fips-eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -5423,13 +5865,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -5502,9 +5948,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "elasticmapreduce-fips.ca-central-1.amazonaws.com", @@ -5514,8 +5962,10 @@ "eu-central-1" : { "sslCommonName" : "{service}.{region}.{dnsSuffix}" }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -5605,6 +6055,7 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, @@ -5646,6 +6097,7 @@ }, "emr-containers" : { "endpoints" : { + "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, "ap-south-1" : { }, @@ -5697,6 +6149,7 @@ "deprecated" : true, "hostname" : "emr-containers-fips.us-west-2.amazonaws.com" }, + "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { "variants" : [ { @@ -5726,6 +6179,7 @@ }, "emr-serverless" : { "endpoints" : { + "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, "ap-south-1" : { }, @@ -5777,6 +6231,7 @@ "deprecated" : true, "hostname" : "emr-serverless-fips.us-west-2.amazonaws.com" }, + "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { "variants" : [ { @@ -5822,13 +6277,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -5904,13 +6363,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -6028,13 +6491,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -6131,6 +6598,7 @@ "tags" : [ "fips" ] } ] }, + "ap-south-2" : { }, "ap-southeast-1" : { "variants" : [ { "hostname" : "fms-fips.ap-southeast-1.amazonaws.com", @@ -6143,6 +6611,7 @@ "tags" : [ "fips" ] } ] }, + "ap-southeast-3" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "fms-fips.ca-central-1.amazonaws.com", @@ -6155,6 +6624,7 @@ "tags" : [ "fips" ] } ] }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { "variants" : [ { @@ -6162,6 +6632,7 @@ "tags" : [ "fips" ] } ] }, + "eu-south-2" : { }, "eu-west-1" : { "variants" : [ { "hostname" : "fms-fips.eu-west-1.amazonaws.com", @@ -6313,6 +6784,7 @@ "deprecated" : true, "hostname" : "fms-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { "variants" : [ { "hostname" : "fms-fips.me-south-1.amazonaws.com", @@ -6469,6 +6941,7 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, @@ -6479,8 +6952,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -6554,6 +7029,7 @@ "deprecated" : true, "hostname" : "fsx-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "prod-ca-central-1" : { "credentialScope" : { @@ -6659,17 +7135,22 @@ }, "gamesparks" : { "endpoints" : { + "ap-northeast-1" : { }, "us-east-1" : { } } }, "geo" : { "endpoints" : { "ap-northeast-1" : { }, + "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, "eu-west-1" : { }, + "eu-west-2" : { }, + "sa-east-1" : { }, "us-east-1" : { }, "us-east-2" : { }, "us-west-2" : { } @@ -6777,8 +7258,10 @@ "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -6810,6 +7293,7 @@ "deprecated" : true, "hostname" : "glue-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -6912,13 +7396,61 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "ca-central-1" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "greengrass-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, - "us-east-1" : { }, - "us-east-2" : { }, - "us-west-2" : { } + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "greengrass-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "greengrass-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "greengrass-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "greengrass-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } }, "isRegionalized" : true }, @@ -6985,13 +7517,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -7091,6 +7627,7 @@ "protocols" : [ "https" ] }, "endpoints" : { + "ap-south-1" : { }, "us-east-1" : { }, "us-east-2" : { }, "us-west-2" : { } @@ -7161,16 +7698,22 @@ }, "identitystore" : { "endpoints" : { + "af-south-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, + "ap-northeast-3" : { }, "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, "us-east-1" : { }, "us-east-2" : { }, "us-west-2" : { } @@ -7339,6 +7882,78 @@ "us-west-2" : { } } }, + "internetmonitor" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "hostname" : "internetmonitor.af-south-1.api.aws" + }, + "ap-east-1" : { + "hostname" : "internetmonitor.ap-east-1.api.aws" + }, + "ap-northeast-1" : { + "hostname" : "internetmonitor.ap-northeast-1.api.aws" + }, + "ap-northeast-2" : { + "hostname" : "internetmonitor.ap-northeast-2.api.aws" + }, + "ap-south-1" : { + "hostname" : "internetmonitor.ap-south-1.api.aws" + }, + "ap-southeast-1" : { + "hostname" : "internetmonitor.ap-southeast-1.api.aws" + }, + "ap-southeast-2" : { + "hostname" : "internetmonitor.ap-southeast-2.api.aws" + }, + "ca-central-1" : { + "hostname" : "internetmonitor.ca-central-1.api.aws" + }, + "eu-central-1" : { + "hostname" : "internetmonitor.eu-central-1.api.aws" + }, + "eu-north-1" : { + "hostname" : "internetmonitor.eu-north-1.api.aws" + }, + "eu-south-1" : { + "hostname" : "internetmonitor.eu-south-1.api.aws" + }, + "eu-west-1" : { + "hostname" : "internetmonitor.eu-west-1.api.aws" + }, + "eu-west-2" : { + "hostname" : "internetmonitor.eu-west-2.api.aws" + }, + "eu-west-3" : { + "hostname" : "internetmonitor.eu-west-3.api.aws" + }, + "me-south-1" : { + "hostname" : "internetmonitor.me-south-1.api.aws" + }, + "sa-east-1" : { + "hostname" : "internetmonitor.sa-east-1.api.aws" + }, + "us-east-1" : { + "hostname" : "internetmonitor.us-east-1.api.aws" + }, + "us-east-2" : { + "hostname" : "internetmonitor.us-east-2.api.aws" + }, + "us-west-1" : { + "hostname" : "internetmonitor.us-west-1.api.aws" + }, + "us-west-2" : { + "hostname" : "internetmonitor.us-west-2.api.aws" + } + } + }, "iot" : { "defaults" : { "credentialScope" : { @@ -7629,6 +8244,12 @@ "us-east-1" : { } } }, + "iotroborunner" : { + "endpoints" : { + "eu-central-1" : { }, + "us-east-1" : { } + } + }, "iotsecuredtunneling" : { "defaults" : { "variants" : [ { @@ -7801,8 +8422,32 @@ "ap-southeast-2" : { }, "eu-central-1" : { }, "eu-west-1" : { }, - "us-east-1" : { }, - "us-west-2" : { } + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "iottwinmaker-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "iottwinmaker-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "iottwinmaker-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "iottwinmaker-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "iotwireless" : { @@ -7861,6 +8506,17 @@ "us-west-2" : { } } }, + "ivsrealtime" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, "kafka" : { "endpoints" : { "af-south-1" : { }, @@ -7869,22 +8525,86 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, - "ca-central-1" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "kafka-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "kafka-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "kafka-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "kafka-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "kafka-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "kafka-fips.us-west-2.amazonaws.com" + }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, - "us-east-1" : { }, - "us-east-2" : { }, - "us-west-1" : { }, - "us-west-2" : { } + "us-east-1" : { + "variants" : [ { + "hostname" : "kafka-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "kafka-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "kafka-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "kafka-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "kafkaconnect" : { @@ -7909,6 +8629,8 @@ }, "kendra" : { "endpoints" : { + "ap-northeast-1" : { }, + "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ca-central-1" : { }, @@ -7954,21 +8676,112 @@ } } }, - "kinesis" : { - "endpoints" : { - "af-south-1" : { }, - "ap-east-1" : { }, - "ap-northeast-1" : { }, + "kendra-ranking" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "af-south-1" : { + "hostname" : "kendra-ranking.af-south-1.api.aws" + }, + "ap-east-1" : { + "hostname" : "kendra-ranking.ap-east-1.api.aws" + }, + "ap-northeast-1" : { + "hostname" : "kendra-ranking.ap-northeast-1.api.aws" + }, + "ap-northeast-2" : { + "hostname" : "kendra-ranking.ap-northeast-2.api.aws" + }, + "ap-northeast-3" : { + "hostname" : "kendra-ranking.ap-northeast-3.api.aws" + }, + "ap-south-1" : { + "hostname" : "kendra-ranking.ap-south-1.api.aws" + }, + "ap-south-2" : { + "hostname" : "kendra-ranking.ap-south-2.api.aws" + }, + "ap-southeast-1" : { + "hostname" : "kendra-ranking.ap-southeast-1.api.aws" + }, + "ap-southeast-2" : { + "hostname" : "kendra-ranking.ap-southeast-2.api.aws" + }, + "ap-southeast-3" : { + "hostname" : "kendra-ranking.ap-southeast-3.api.aws" + }, + "ap-southeast-4" : { + "hostname" : "kendra-ranking.ap-southeast-4.api.aws" + }, + "ca-central-1" : { + "hostname" : "kendra-ranking.ca-central-1.api.aws" + }, + "eu-central-2" : { + "hostname" : "kendra-ranking.eu-central-2.api.aws" + }, + "eu-north-1" : { + "hostname" : "kendra-ranking.eu-north-1.api.aws" + }, + "eu-south-1" : { + "hostname" : "kendra-ranking.eu-south-1.api.aws" + }, + "eu-south-2" : { + "hostname" : "kendra-ranking.eu-south-2.api.aws" + }, + "eu-west-1" : { + "hostname" : "kendra-ranking.eu-west-1.api.aws" + }, + "eu-west-3" : { + "hostname" : "kendra-ranking.eu-west-3.api.aws" + }, + "me-central-1" : { + "hostname" : "kendra-ranking.me-central-1.api.aws" + }, + "me-south-1" : { + "hostname" : "kendra-ranking.me-south-1.api.aws" + }, + "sa-east-1" : { + "hostname" : "kendra-ranking.sa-east-1.api.aws" + }, + "us-east-1" : { + "hostname" : "kendra-ranking.us-east-1.api.aws" + }, + "us-east-2" : { + "hostname" : "kendra-ranking.us-east-2.api.aws" + }, + "us-west-1" : { + "hostname" : "kendra-ranking.us-west-1.api.aws" + }, + "us-west-2" : { + "hostname" : "kendra-ranking.us-west-2.api.aws" + } + } + }, + "kinesis" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -8037,16 +8850,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -8162,6 +8980,12 @@ "deprecated" : true, "hostname" : "kms-fips.ap-south-1.amazonaws.com" }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "kms-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ap-south-2-fips" : { "credentialScope" : { "region" : "ap-south-2" @@ -8208,6 +9032,19 @@ "deprecated" : true, "hostname" : "kms-fips.ap-southeast-3.amazonaws.com" }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "kms-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ap-southeast-4-fips" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "kms-fips.ap-southeast-4.amazonaws.com" + }, "ca-central-1" : { "variants" : [ { "hostname" : "kms-fips.ca-central-1.amazonaws.com", @@ -8234,6 +9071,12 @@ "deprecated" : true, "hostname" : "kms-fips.eu-central-1.amazonaws.com" }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "kms-fips.eu-central-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-2-fips" : { "credentialScope" : { "region" : "eu-central-2" @@ -8267,6 +9110,12 @@ "deprecated" : true, "hostname" : "kms-fips.eu-south-1.amazonaws.com" }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "kms-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-south-2-fips" : { "credentialScope" : { "region" : "eu-south-2" @@ -8313,6 +9162,12 @@ "deprecated" : true, "hostname" : "kms-fips.eu-west-3.amazonaws.com" }, + "il-central-1-fips" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "kms-fips.il-central-1.amazonaws.com" + }, "me-central-1" : { "variants" : [ { "hostname" : "kms-fips.me-central-1.amazonaws.com", @@ -8416,10 +9271,13 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -8451,6 +9309,7 @@ "deprecated" : true, "hostname" : "lakeformation-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -8517,6 +9376,12 @@ "tags" : [ "dualstack" ] } ] }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "lambda.ap-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, "ap-southeast-1" : { "variants" : [ { "hostname" : "lambda.ap-southeast-1.api.aws", @@ -8535,6 +9400,12 @@ "tags" : [ "dualstack" ] } ] }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "lambda.ap-southeast-4.api.aws", + "tags" : [ "dualstack" ] + } ] + }, "ca-central-1" : { "variants" : [ { "hostname" : "lambda.ca-central-1.api.aws", @@ -8547,6 +9418,12 @@ "tags" : [ "dualstack" ] } ] }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "lambda.eu-central-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, "eu-north-1" : { "variants" : [ { "hostname" : "lambda.eu-north-1.api.aws", @@ -8559,6 +9436,12 @@ "tags" : [ "dualstack" ] } ] }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "lambda.eu-south-2.api.aws", + "tags" : [ "dualstack" ] + } ] + }, "eu-west-1" : { "variants" : [ { "hostname" : "lambda.eu-west-1.api.aws", @@ -8735,6 +9618,85 @@ } } }, + "license-manager-linux-subscriptions" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "license-manager-linux-subscriptions-fips.us-west-2.amazonaws.com" + }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "license-manager-linux-subscriptions-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, "license-manager-user-subscriptions" : { "endpoints" : { "af-south-1" : { }, @@ -8834,13 +9796,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -8934,13 +9900,56 @@ }, "m2" : { "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "ca-central-1" : { }, + "ca-central-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "deprecated" : true + }, + "fips-us-east-1" : { + "deprecated" : true + }, + "fips-us-east-2" : { + "deprecated" : true + }, + "fips-us-west-1" : { + "deprecated" : true + }, + "fips-us-west-2" : { + "deprecated" : true + }, "sa-east-1" : { }, - "us-east-1" : { }, - "us-west-2" : { } + "us-east-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "tags" : [ "fips" ] + } ] + } } }, "machinelearning" : { @@ -9121,6 +10130,7 @@ }, "mediaconvert" : { "endpoints" : { + "af-south-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, "ap-south-1" : { }, @@ -9347,8 +10357,10 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, + "eu-west-3" : { }, "fips" : { "credentialScope" : { "region" : "us-west-1" @@ -9393,13 +10405,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -9412,18 +10428,7 @@ "us-west-2" : { } } }, - "mgh" : { - "endpoints" : { - "ap-northeast-1" : { }, - "ap-southeast-2" : { }, - "eu-central-1" : { }, - "eu-west-1" : { }, - "eu-west-2" : { }, - "us-east-1" : { }, - "us-west-2" : { } - } - }, - "mgn" : { + "metrics.sagemaker" : { "endpoints" : { "af-south-1" : { }, "ap-east-1" : { }, @@ -9431,16 +10436,20 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, - "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -9449,7 +10458,7 @@ "us-west-2" : { } } }, - "migrationhub-orchestrator" : { + "mgh" : { "endpoints" : { "ap-northeast-1" : { }, "ap-southeast-2" : { }, @@ -9460,20 +10469,106 @@ "us-west-2" : { } } }, - "migrationhub-strategy" : { + "mgn" : { "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { }, "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, - "us-east-1" : { }, - "us-west-2" : { } - } - }, - "mobileanalytics" : { - "endpoints" : { - "us-east-1" : { } + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-west-2.amazonaws.com" + }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "mgn-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "mgn-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "migrationhub-orchestrator" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "migrationhub-strategy" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-west-2" : { } + } + }, + "mobileanalytics" : { + "endpoints" : { + "us-east-1" : { } } }, "models-v2-lex" : { @@ -9547,13 +10642,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -9622,13 +10721,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -9660,6 +10763,7 @@ "deprecated" : true, "hostname" : "mq-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -9867,6 +10971,7 @@ "deprecated" : true, "hostname" : "network-firewall-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -9917,8 +11022,45 @@ "us-west-2" : { } } }, + "oam" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "oidc" : { "endpoints" : { + "af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "oidc.af-south-1.amazonaws.com" + }, "ap-east-1" : { "credentialScope" : { "region" : "ap-east-1" @@ -9961,6 +11103,12 @@ }, "hostname" : "oidc.ap-southeast-2.amazonaws.com" }, + "ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "hostname" : "oidc.ap-southeast-3.amazonaws.com" + }, "ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -10027,6 +11175,12 @@ }, "hostname" : "oidc.us-east-2.amazonaws.com" }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "oidc.us-west-1.amazonaws.com" + }, "us-west-2" : { "credentialScope" : { "region" : "us-west-2" @@ -10035,6 +11189,68 @@ } } }, + "omics" : { + "endpoints" : { + "ap-southeast-1" : { + "credentialScope" : { + "region" : "ap-southeast-1" + }, + "hostname" : "omics.ap-southeast-1.amazonaws.com" + }, + "eu-central-1" : { + "credentialScope" : { + "region" : "eu-central-1" + }, + "hostname" : "omics.eu-central-1.amazonaws.com" + }, + "eu-west-1" : { + "credentialScope" : { + "region" : "eu-west-1" + }, + "hostname" : "omics.eu-west-1.amazonaws.com" + }, + "eu-west-2" : { + "credentialScope" : { + "region" : "eu-west-2" + }, + "hostname" : "omics.eu-west-2.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "omics-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "omics-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "hostname" : "omics.us-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "omics-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "hostname" : "omics.us-west-2.amazonaws.com", + "variants" : [ { + "hostname" : "omics-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, "opsworks" : { "endpoints" : { "ap-northeast-1" : { }, @@ -10090,6 +11306,20 @@ "isRegionalized" : false, "partitionEndpoint" : "aws-global" }, + "osis" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "outposts" : { "endpoints" : { "af-south-1" : { }, @@ -10237,13 +11467,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -10341,12 +11575,40 @@ } } }, + "pipes" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "polly" : { "endpoints" : { "af-south-1" : { }, "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, + "ap-northeast-3" : { }, "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, @@ -10414,6 +11676,12 @@ }, "portal.sso" : { "endpoints" : { + "af-south-1" : { + "credentialScope" : { + "region" : "af-south-1" + }, + "hostname" : "portal.sso.af-south-1.amazonaws.com" + }, "ap-east-1" : { "credentialScope" : { "region" : "ap-east-1" @@ -10456,6 +11724,12 @@ }, "hostname" : "portal.sso.ap-southeast-2.amazonaws.com" }, + "ap-southeast-3" : { + "credentialScope" : { + "region" : "ap-southeast-3" + }, + "hostname" : "portal.sso.ap-southeast-3.amazonaws.com" + }, "ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -10522,6 +11796,12 @@ }, "hostname" : "portal.sso.us-east-2.amazonaws.com" }, + "us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "hostname" : "portal.sso.us-west-1.amazonaws.com" + }, "us-west-2" : { "credentialScope" : { "region" : "us-west-2" @@ -10537,11 +11817,47 @@ "ap-northeast-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "ca-central-1" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "profile-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, "eu-west-2" : { }, - "us-east-1" : { }, - "us-west-2" : { } + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "profile-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "profile-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "profile-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "profile-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "profile-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "projects.iot1click" : { @@ -10558,7 +11874,13 @@ "proton" : { "endpoints" : { "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, "eu-west-1" : { }, + "eu-west-2" : { }, "us-east-1" : { }, "us-east-2" : { }, "us-west-2" : { } @@ -10634,11 +11956,12 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "api" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-north-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, + "eu-west-3" : { }, "sa-east-1" : { }, "us-east-1" : { }, "us-east-2" : { }, @@ -10653,9 +11976,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "ram-fips.ca-central-1.amazonaws.com", @@ -10663,8 +11988,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -10740,9 +12067,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "rbin-fips.ca-central-1.amazonaws.com", @@ -10750,8 +12079,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -10827,9 +12158,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "rds-fips.ca-central-1.amazonaws.com", @@ -10844,8 +12177,10 @@ "hostname" : "rds-fips.ca-central-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -11066,9 +12401,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "redshift-fips.ca-central-1.amazonaws.com", @@ -11076,8 +12413,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -11149,14 +12488,18 @@ "endpoints" : { "ap-northeast-1" : { }, "ap-northeast-2" : { }, + "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, + "eu-west-3" : { }, "us-east-1" : { }, "us-east-2" : { }, + "us-west-1" : { }, "us-west-2" : { } } }, @@ -11348,6 +12691,7 @@ }, "resource-explorer-2" : { "defaults" : { + "dnsSuffix" : "api.aws", "variants" : [ { "dnsSuffix" : "api.aws", "hostname" : "{service}-fips.{region}.{dnsSuffix}", @@ -11355,12 +12699,6 @@ } ] }, "endpoints" : { - "af-south-1" : { - "hostname" : "resource-explorer-2.af-south-1.api.aws" - }, - "ap-east-1" : { - "hostname" : "resource-explorer-2.ap-east-1.api.aws" - }, "ap-northeast-1" : { "hostname" : "resource-explorer-2.ap-northeast-1.api.aws" }, @@ -11373,18 +12711,27 @@ "ap-south-1" : { "hostname" : "resource-explorer-2.ap-south-1.api.aws" }, + "ap-south-2" : { + "hostname" : "resource-explorer-2.ap-south-2.api.aws" + }, "ap-southeast-1" : { "hostname" : "resource-explorer-2.ap-southeast-1.api.aws" }, "ap-southeast-2" : { "hostname" : "resource-explorer-2.ap-southeast-2.api.aws" }, + "ap-southeast-4" : { + "hostname" : "resource-explorer-2.ap-southeast-4.api.aws" + }, "ca-central-1" : { "hostname" : "resource-explorer-2.ca-central-1.api.aws" }, "eu-central-1" : { "hostname" : "resource-explorer-2.eu-central-1.api.aws" }, + "eu-central-2" : { + "hostname" : "resource-explorer-2.eu-central-2.api.aws" + }, "eu-north-1" : { "hostname" : "resource-explorer-2.eu-north-1.api.aws" }, @@ -11422,13 +12769,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -11460,6 +12811,7 @@ "deprecated" : true, "hostname" : "resource-groups-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -11574,16 +12926,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -11680,16 +13037,20 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -11799,6 +13160,12 @@ "tags" : [ "dualstack" ] } ] }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, "ap-southeast-1" : { "hostname" : "s3.ap-southeast-1.amazonaws.com", "signatureVersions" : [ "s3", "s3v4" ], @@ -11821,6 +13188,12 @@ "tags" : [ "dualstack" ] } ] }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "s3.dualstack.ap-southeast-4.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, "aws-global" : { "credentialScope" : { "region" : "us-east-1" @@ -11846,6 +13219,12 @@ "tags" : [ "dualstack" ] } ] }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "s3.dualstack.eu-central-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, "eu-north-1" : { "variants" : [ { "hostname" : "s3.dualstack.eu-north-1.amazonaws.com", @@ -11858,6 +13237,12 @@ "tags" : [ "dualstack" ] } ] }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "s3.dualstack.eu-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, "eu-west-1" : { "hostname" : "s3.eu-west-1.amazonaws.com", "signatureVersions" : [ "s3", "s3v4" ], @@ -12333,6 +13718,11 @@ } } }, + "sagemaker-geospatial" : { + "endpoints" : { + "us-west-2" : { } + } + }, "savingsplans" : { "endpoints" : { "aws-global" : { @@ -12345,6 +13735,37 @@ "isRegionalized" : false, "partitionEndpoint" : "aws-global" }, + "scheduler" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "schemas" : { "endpoints" : { "ap-east-1" : { }, @@ -12392,9 +13813,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "secretsmanager-fips.ca-central-1.amazonaws.com", @@ -12409,8 +13832,10 @@ "hostname" : "secretsmanager-fips.ca-central-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -12479,13 +13904,16 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -12517,6 +13945,7 @@ "deprecated" : true, "hostname" : "securityhub-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -12545,6 +13974,20 @@ } } }, + "securitylake" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, "serverlessrepo" : { "defaults" : { "protocols" : [ "https" ] @@ -12614,16 +14057,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -12738,6 +14186,7 @@ "deprecated" : true, "hostname" : "servicecatalog-appregistry-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -12768,19 +14217,79 @@ }, "servicediscovery" : { "endpoints" : { - "af-south-1" : { }, - "ap-east-1" : { }, - "ap-northeast-1" : { }, - "ap-northeast-2" : { }, - "ap-northeast-3" : { }, - "ap-south-1" : { }, - "ap-southeast-1" : { }, - "ap-southeast-2" : { }, - "ap-southeast-3" : { }, + "af-south-1" : { + "variants" : [ { + "hostname" : "servicediscovery.af-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-east-1" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-1" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-northeast-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-2" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-northeast-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-northeast-3" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-northeast-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-1" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-south-2" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-1" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-2" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-3" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "ap-southeast-4" : { + "variants" : [ { + "hostname" : "servicediscovery.ap-southeast-4.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, "ca-central-1" : { "variants" : [ { "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.ca-central-1.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, "ca-central-1-fips" : { @@ -12790,15 +14299,72 @@ "deprecated" : true, "hostname" : "servicediscovery-fips.ca-central-1.amazonaws.com" }, - "eu-central-1" : { }, - "eu-north-1" : { }, - "eu-south-1" : { }, - "eu-west-1" : { }, - "eu-west-2" : { }, - "eu-west-3" : { }, - "me-central-1" : { }, - "me-south-1" : { }, - "sa-east-1" : { }, + "eu-central-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-central-2" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-central-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-north-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-north-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-south-2" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-south-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-1" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-west-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-2" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-west-2.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "eu-west-3" : { + "variants" : [ { + "hostname" : "servicediscovery.eu-west-3.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-central-1" : { + "variants" : [ { + "hostname" : "servicediscovery.me-central-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "me-south-1" : { + "variants" : [ { + "hostname" : "servicediscovery.me-south-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, + "sa-east-1" : { + "variants" : [ { + "hostname" : "servicediscovery.sa-east-1.amazonaws.com", + "tags" : [ "dualstack" ] + } ] + }, "servicediscovery" : { "credentialScope" : { "region" : "ca-central-1" @@ -12820,6 +14386,9 @@ "variants" : [ { "hostname" : "servicediscovery-fips.us-east-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-east-1.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, "us-east-1-fips" : { @@ -12833,6 +14402,9 @@ "variants" : [ { "hostname" : "servicediscovery-fips.us-east-2.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-east-2.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, "us-east-2-fips" : { @@ -12846,6 +14418,9 @@ "variants" : [ { "hostname" : "servicediscovery-fips.us-west-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-west-1.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, "us-west-1-fips" : { @@ -12859,6 +14434,9 @@ "variants" : [ { "hostname" : "servicediscovery-fips.us-west-2.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-west-2.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, "us-west-2-fips" : { @@ -12881,16 +14459,21 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { }, @@ -12966,16 +14549,100 @@ "tags" : [ "fips" ] } ] }, - "fips-aws-global" : { - "credentialScope" : { - "region" : "us-east-1" - }, - "deprecated" : true, - "hostname" : "shield-fips.us-east-1.amazonaws.com" + "fips-aws-global" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "shield-fips.us-east-1.amazonaws.com" + } + }, + "isRegionalized" : false, + "partitionEndpoint" : "aws-global" + }, + "signer" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "signer-fips.us-west-2.amazonaws.com" + }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "signer-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "signer-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "signer-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "signer-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] } - }, - "isRegionalized" : false, - "partitionEndpoint" : "aws-global" + } + }, + "simspaceweaver" : { + "endpoints" : { + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } }, "sms" : { "endpoints" : { @@ -13055,12 +14722,48 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, - "ca-central-1" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, - "us-east-1" : { }, - "us-west-2" : { } + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-east-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-west-2.amazonaws.com" + }, + "us-east-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "snowball" : { @@ -13248,6 +14951,7 @@ "deprecated" : true, "hostname" : "snowball-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "sa-east-1" : { "variants" : [ { "hostname" : "snowball-fips.sa-east-1.amazonaws.com", @@ -13291,13 +14995,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13370,13 +15078,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13446,9 +15158,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "ssm-fips.ca-central-1.amazonaws.com", @@ -13456,8 +15170,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13545,8 +15261,95 @@ "us-west-2" : { } } }, + "ssm-sap" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "ssm-sap-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "ssm-sap-fips.ca-central-1.amazonaws.com" + }, + "fips-us-east-1" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "ssm-sap-fips.us-east-1.amazonaws.com" + }, + "fips-us-east-2" : { + "credentialScope" : { + "region" : "us-east-2" + }, + "deprecated" : true, + "hostname" : "ssm-sap-fips.us-east-2.amazonaws.com" + }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "ssm-sap-fips.us-west-1.amazonaws.com" + }, + "fips-us-west-2" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "ssm-sap-fips.us-west-2.amazonaws.com" + }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "ssm-sap-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-2" : { + "variants" : [ { + "hostname" : "ssm-sap-fips.us-east-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-1" : { + "variants" : [ { + "hostname" : "ssm-sap-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "ssm-sap-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, "sso" : { "endpoints" : { + "af-south-1" : { }, "ap-east-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, @@ -13554,6 +15357,7 @@ "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ap-southeast-3" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, @@ -13565,6 +15369,7 @@ "sa-east-1" : { }, "us-east-1" : { }, "us-east-2" : { }, + "us-west-1" : { }, "us-west-2" : { } } }, @@ -13576,13 +15381,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13651,9 +15460,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "storagegateway-fips.ca-central-1.amazonaws.com", @@ -13668,8 +15479,10 @@ "hostname" : "storagegateway-fips.ca-central-1.amazonaws.com" }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13680,6 +15493,7 @@ "deprecated" : true, "hostname" : "storagegateway-fips.ca-central-1.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -13750,13 +15564,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13784,9 +15602,11 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "aws-global" : { "credentialScope" : { "region" : "us-east-1" @@ -13795,8 +15615,10 @@ }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13884,13 +15706,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13959,13 +15785,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -14034,13 +15864,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -14361,6 +16195,7 @@ "deprecated" : true, "hostname" : "transfer-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, "us-east-1" : { @@ -14448,31 +16283,114 @@ } } }, + "voice-chime" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-southeast-1" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "voice-chime-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "ca-central-1-fips" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "voice-chime-fips.ca-central-1.amazonaws.com" + }, + "eu-central-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "us-east-1" : { + "variants" : [ { + "hostname" : "voice-chime-fips.us-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-east-1-fips" : { + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "voice-chime-fips.us-east-1.amazonaws.com" + }, + "us-west-2" : { + "variants" : [ { + "hostname" : "voice-chime-fips.us-west-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-west-2-fips" : { + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "voice-chime-fips.us-west-2.amazonaws.com" + } + } + }, "voiceid" : { "endpoints" : { "ap-northeast-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, + "ca-central-1" : { + "variants" : [ { + "hostname" : "voiceid-fips.ca-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-central-1" : { }, "eu-west-2" : { }, + "fips-ca-central-1" : { + "credentialScope" : { + "region" : "ca-central-1" + }, + "deprecated" : true, + "hostname" : "voiceid-fips.ca-central-1.amazonaws.com" + }, "fips-us-east-1" : { - "deprecated" : true + "credentialScope" : { + "region" : "us-east-1" + }, + "deprecated" : true, + "hostname" : "voiceid-fips.us-east-1.amazonaws.com" }, "fips-us-west-2" : { - "deprecated" : true + "credentialScope" : { + "region" : "us-west-2" + }, + "deprecated" : true, + "hostname" : "voiceid-fips.us-west-2.amazonaws.com" }, "us-east-1" : { "variants" : [ { + "hostname" : "voiceid-fips.us-east-1.amazonaws.com", "tags" : [ "fips" ] } ] }, "us-west-2" : { "variants" : [ { + "hostname" : "voiceid-fips.us-west-2.amazonaws.com", "tags" : [ "fips" ] } ] } } }, + "vpc-lattice" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-west-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-2" : { } + } + }, "waf" : { "endpoints" : { "aws" : { @@ -14575,6 +16493,16 @@ "tags" : [ "fips" ] } ] }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "waf-regional.ap-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -14605,6 +16533,16 @@ "tags" : [ "fips" ] } ] }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "waf-regional.ap-southeast-4.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -14625,6 +16563,16 @@ "tags" : [ "fips" ] } ] }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "waf-regional.eu-central-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-central-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -14645,6 +16593,16 @@ "tags" : [ "fips" ] } ] }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "waf-regional.eu-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -14717,6 +16675,13 @@ "deprecated" : true, "hostname" : "waf-regional-fips.ap-south-1.amazonaws.com" }, + "fips-ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-south-2.amazonaws.com" + }, "fips-ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -14738,6 +16703,13 @@ "deprecated" : true, "hostname" : "waf-regional-fips.ap-southeast-3.amazonaws.com" }, + "fips-ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.ap-southeast-4.amazonaws.com" + }, "fips-ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -14752,6 +16724,13 @@ "deprecated" : true, "hostname" : "waf-regional-fips.eu-central-1.amazonaws.com" }, + "fips-eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-central-2.amazonaws.com" + }, "fips-eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -14766,6 +16745,13 @@ "deprecated" : true, "hostname" : "waf-regional-fips.eu-south-1.amazonaws.com" }, + "fips-eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.eu-south-2.amazonaws.com" + }, "fips-eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -14787,6 +16773,19 @@ "deprecated" : true, "hostname" : "waf-regional-fips.eu-west-3.amazonaws.com" }, + "fips-il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "waf-regional-fips.il-central-1.amazonaws.com" + }, + "fips-me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "deprecated" : true, + "hostname" : "waf-regional-fips.me-central-1.amazonaws.com" + }, "fips-me-south-1" : { "credentialScope" : { "region" : "me-south-1" @@ -14829,6 +16828,16 @@ "deprecated" : true, "hostname" : "waf-regional-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "hostname" : "waf-regional.me-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "waf-regional-fips.me-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "me-south-1" : { "credentialScope" : { "region" : "me-south-1" @@ -14953,6 +16962,16 @@ "tags" : [ "fips" ] } ] }, + "ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "hostname" : "wafv2.ap-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -14983,6 +17002,16 @@ "tags" : [ "fips" ] } ] }, + "ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "hostname" : "wafv2.ap-southeast-4.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.ap-southeast-4.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -15003,6 +17032,16 @@ "tags" : [ "fips" ] } ] }, + "eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "hostname" : "wafv2.eu-central-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-central-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -15023,6 +17062,16 @@ "tags" : [ "fips" ] } ] }, + "eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "hostname" : "wafv2.eu-south-2.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.eu-south-2.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -15095,6 +17144,13 @@ "deprecated" : true, "hostname" : "wafv2-fips.ap-south-1.amazonaws.com" }, + "fips-ap-south-2" : { + "credentialScope" : { + "region" : "ap-south-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-south-2.amazonaws.com" + }, "fips-ap-southeast-1" : { "credentialScope" : { "region" : "ap-southeast-1" @@ -15116,6 +17172,13 @@ "deprecated" : true, "hostname" : "wafv2-fips.ap-southeast-3.amazonaws.com" }, + "fips-ap-southeast-4" : { + "credentialScope" : { + "region" : "ap-southeast-4" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.ap-southeast-4.amazonaws.com" + }, "fips-ca-central-1" : { "credentialScope" : { "region" : "ca-central-1" @@ -15130,6 +17193,13 @@ "deprecated" : true, "hostname" : "wafv2-fips.eu-central-1.amazonaws.com" }, + "fips-eu-central-2" : { + "credentialScope" : { + "region" : "eu-central-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-central-2.amazonaws.com" + }, "fips-eu-north-1" : { "credentialScope" : { "region" : "eu-north-1" @@ -15144,6 +17214,13 @@ "deprecated" : true, "hostname" : "wafv2-fips.eu-south-1.amazonaws.com" }, + "fips-eu-south-2" : { + "credentialScope" : { + "region" : "eu-south-2" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.eu-south-2.amazonaws.com" + }, "fips-eu-west-1" : { "credentialScope" : { "region" : "eu-west-1" @@ -15165,6 +17242,19 @@ "deprecated" : true, "hostname" : "wafv2-fips.eu-west-3.amazonaws.com" }, + "fips-il-central-1" : { + "credentialScope" : { + "region" : "il-central-1" + }, + "hostname" : "wafv2-fips.il-central-1.amazonaws.com" + }, + "fips-me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "deprecated" : true, + "hostname" : "wafv2-fips.me-central-1.amazonaws.com" + }, "fips-me-south-1" : { "credentialScope" : { "region" : "me-south-1" @@ -15207,6 +17297,16 @@ "deprecated" : true, "hostname" : "wafv2-fips.us-west-2.amazonaws.com" }, + "me-central-1" : { + "credentialScope" : { + "region" : "me-central-1" + }, + "hostname" : "wafv2.me-central-1.amazonaws.com", + "variants" : [ { + "hostname" : "wafv2-fips.me-central-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "me-south-1" : { "credentialScope" : { "region" : "me-south-1" @@ -15428,13 +17528,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -15552,6 +17656,12 @@ "cn-northwest-1" : { } } }, + "airflow" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "api.ecr" : { "endpoints" : { "cn-north-1" : { @@ -15637,8 +17747,18 @@ }, "athena" : { "endpoints" : { - "cn-north-1" : { }, - "cn-northwest-1" : { } + "cn-north-1" : { + "variants" : [ { + "hostname" : "athena.cn-north-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "athena.cn-northwest-1.api.amazonwebservices.com.cn", + "tags" : [ "dualstack" ] + } ] + } } }, "autoscaling" : { @@ -15796,7 +17916,10 @@ "protocols" : [ "https" ] }, "endpoints" : { - "cn-north-1" : { }, + "cn-north-1" : { + "hostname" : "data.ats.iot.cn-north-1.amazonaws.com.cn", + "protocols" : [ "https" ] + }, "cn-northwest-1" : { } } }, @@ -15824,6 +17947,12 @@ "cn-northwest-1" : { } } }, + "datasync" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "dax" : { "endpoints" : { "cn-north-1" : { }, @@ -15969,6 +18098,12 @@ "cn-northwest-1" : { } } }, + "emr-serverless" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "es" : { "endpoints" : { "cn-north-1" : { }, @@ -16080,6 +18215,24 @@ "isRegionalized" : false, "partitionEndpoint" : "aws-cn-global" }, + "internetmonitor" : { + "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "variants" : [ { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "internetmonitor.cn-north-1.api.amazonwebservices.com.cn" + }, + "cn-northwest-1" : { + "hostname" : "internetmonitor.cn-northwest-1.api.amazonwebservices.com.cn" + } + } + }, "iot" : { "defaults" : { "credentialScope" : { @@ -16128,6 +18281,24 @@ "cn-northwest-1" : { } } }, + "kendra-ranking" : { + "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "variants" : [ { + "dnsSuffix" : "api.amazonwebservices.com.cn", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "cn-north-1" : { + "hostname" : "kendra-ranking.cn-north-1.api.amazonwebservices.com.cn" + }, + "cn-northwest-1" : { + "hostname" : "kendra-ranking.cn-northwest-1.api.amazonwebservices.com.cn" + } + } + }, "kinesis" : { "endpoints" : { "cn-north-1" : { }, @@ -16140,6 +18311,11 @@ "cn-northwest-1" : { } } }, + "kinesisvideo" : { + "endpoints" : { + "cn-north-1" : { } + } + }, "kms" : { "endpoints" : { "cn-north-1" : { }, @@ -16174,6 +18350,12 @@ "cn-northwest-1" : { } } }, + "license-manager-linux-subscriptions" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "logs" : { "endpoints" : { "cn-north-1" : { }, @@ -16196,6 +18378,12 @@ "cn-northwest-1" : { } } }, + "metrics.sagemaker" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "monitoring" : { "defaults" : { "protocols" : [ "http", "https" ] @@ -16227,6 +18415,12 @@ } } }, + "oam" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "organizations" : { "endpoints" : { "aws-cn-global" : { @@ -16281,6 +18475,7 @@ }, "resource-explorer-2" : { "defaults" : { + "dnsSuffix" : "api.amazonwebservices.com.cn", "variants" : [ { "dnsSuffix" : "api.amazonwebservices.com.cn", "hostname" : "{service}-fips.{region}.{dnsSuffix}", @@ -16302,6 +18497,12 @@ "cn-northwest-1" : { } } }, + "rolesanywhere" : { + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, "route53" : { "endpoints" : { "aws-cn-global" : { @@ -16421,6 +18622,31 @@ } }, "servicediscovery" : { + "endpoints" : { + "cn-north-1" : { + "variants" : [ { + "hostname" : "servicediscovery.cn-north-1.amazonaws.com.cn", + "tags" : [ "dualstack" ] + } ] + }, + "cn-northwest-1" : { + "variants" : [ { + "hostname" : "servicediscovery.cn-northwest-1.amazonaws.com.cn", + "tags" : [ "dualstack" ] + } ] + } + } + }, + "servicequotas" : { + "defaults" : { + "protocols" : [ "https" ] + }, + "endpoints" : { + "cn-north-1" : { }, + "cn-northwest-1" : { } + } + }, + "signer" : { "endpoints" : { "cn-north-1" : { }, "cn-northwest-1" : { } @@ -16703,12 +18929,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "access-analyzer.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "access-analyzer.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "access-analyzer.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "access-analyzer.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "access-analyzer.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "access-analyzer.us-gov-west-1.amazonaws.com" } } @@ -16891,6 +19139,7 @@ } ] }, "endpoints" : { + "us-gov-east-1" : { }, "us-gov-west-1" : { "variants" : [ { "hostname" : "api-fips.sagemaker.us-gov-west-1.amazonaws.com", @@ -17007,9 +19256,29 @@ }, "endpoints" : { "us-gov-east-1" : { + "hostname" : "application-autoscaling.us-gov-east-1.amazonaws.com", + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "application-autoscaling.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "deprecated" : true, + "hostname" : "application-autoscaling.us-gov-east-1.amazonaws.com", "protocols" : [ "http", "https" ] }, "us-gov-west-1" : { + "hostname" : "application-autoscaling.us-gov-west-1.amazonaws.com", + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "application-autoscaling.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "deprecated" : true, + "hostname" : "application-autoscaling.us-gov-west-1.amazonaws.com", "protocols" : [ "http", "https" ] } } @@ -17045,6 +19314,19 @@ "deprecated" : true, "hostname" : "appstream2-fips.us-gov-west-1.amazonaws.com" }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "appstream2-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "appstream2-fips.us-gov-east-1.amazonaws.com" + }, "us-gov-west-1" : { "variants" : [ { "hostname" : "appstream2-fips.us-gov-west-1.amazonaws.com", @@ -17080,12 +19362,24 @@ "variants" : [ { "hostname" : "athena-fips.us-gov-east-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-gov-east-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.us-gov-east-1.api.aws", + "tags" : [ "dualstack" ] } ] }, "us-gov-west-1" : { "variants" : [ { "hostname" : "athena-fips.us-gov-west-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "athena-fips.us-gov-west-1.api.aws", + "tags" : [ "dualstack", "fips" ] + }, { + "hostname" : "athena.us-gov-west-1.api.aws", + "tags" : [ "dualstack" ] } ] } } @@ -17173,12 +19467,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "cassandra.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "cassandra.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "cassandra.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "cassandra.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "cassandra.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "cassandra.us-gov-west-1.amazonaws.com" } } @@ -17215,7 +19531,19 @@ }, "clouddirectory" : { "endpoints" : { - "us-gov-west-1" : { } + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "clouddirectory.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "clouddirectory.us-gov-west-1.amazonaws.com" + } } }, "cloudformation" : { @@ -17224,12 +19552,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "cloudformation.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "cloudformation.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "cloudformation.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "cloudformation.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "cloudformation.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "cloudformation.us-gov-west-1.amazonaws.com" } } @@ -17392,6 +19742,7 @@ "deprecated" : true, "hostname" : "codepipeline-fips.us-gov-west-1.amazonaws.com" }, + "us-gov-east-1" : { }, "us-gov-west-1" : { "variants" : [ { "hostname" : "codepipeline-fips.us-gov-west-1.amazonaws.com", @@ -17471,6 +19822,22 @@ } } }, + "compute-optimizer" : { + "endpoints" : { + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "compute-optimizer-fips.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "compute-optimizer-fips.us-gov-west-1.amazonaws.com" + } + } + }, "config" : { "defaults" : { "variants" : [ { @@ -17509,7 +19876,19 @@ }, "connect" : { "endpoints" : { - "us-gov-west-1" : { } + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "connect.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "connect.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "controltower" : { @@ -17622,7 +20001,19 @@ }, "databrew" : { "endpoints" : { - "us-gov-west-1" : { } + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "databrew.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "databrew.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "datasync" : { @@ -17673,8 +20064,32 @@ }, "dlm" : { "endpoints" : { - "us-gov-east-1" : { }, - "us-gov-west-1" : { } + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "dlm.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "dlm.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "dlm.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "dlm.us-gov-west-1.amazonaws.com" + } } }, "dms" : { @@ -17946,12 +20361,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "elasticbeanstalk.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "elasticbeanstalk.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "elasticbeanstalk.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "elasticbeanstalk.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "elasticbeanstalk.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "elasticbeanstalk.us-gov-west-1.amazonaws.com" } } @@ -18077,6 +20514,12 @@ } } }, + "emr-containers" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, "es" : { "endpoints" : { "fips" : { @@ -18273,18 +20716,32 @@ }, "glacier" : { "endpoints" : { - "us-gov-east-1" : { + "fips-us-gov-east-1" : { "credentialScope" : { "region" : "us-gov-east-1" }, + "deprecated" : true, "hostname" : "glacier.us-gov-east-1.amazonaws.com" }, - "us-gov-west-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, - "hostname" : "glacier.us-gov-west-1.amazonaws.com", - "protocols" : [ "http", "https" ] + "deprecated" : true, + "hostname" : "glacier.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "glacier.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "protocols" : [ "http", "https" ], + "variants" : [ { + "hostname" : "glacier.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } } }, @@ -18340,23 +20797,26 @@ "region" : "us-gov-east-1" }, "deprecated" : true, - "hostname" : "greengrass-fips.us-gov-east-1.amazonaws.com" + "hostname" : "greengrass.us-gov-east-1.amazonaws.com" }, - "us-gov-east-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { - "region" : "us-gov-east-1" + "region" : "us-gov-west-1" }, - "hostname" : "greengrass.us-gov-east-1.amazonaws.com", + "deprecated" : true, + "hostname" : "greengrass.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { "variants" : [ { - "hostname" : "greengrass-fips.us-gov-east-1.amazonaws.com", + "hostname" : "greengrass.us-gov-east-1.amazonaws.com", "tags" : [ "fips" ] } ] }, "us-gov-west-1" : { - "credentialScope" : { - "region" : "us-gov-west-1" - }, - "hostname" : "greengrass.us-gov-west-1.amazonaws.com" + "variants" : [ { + "hostname" : "greengrass.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } }, "isRegionalized" : true @@ -18496,6 +20956,23 @@ } } }, + "ingest.timestream" : { + "endpoints" : { + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "ingest.timestream.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "ingest.timestream.us-gov-west-1.amazonaws.com" + } + } + }, "inspector" : { "endpoints" : { "fips-us-gov-east-1" : { @@ -18526,6 +21003,30 @@ } } }, + "inspector2" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, + "internetmonitor" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "hostname" : "internetmonitor.us-gov-east-1.api.aws" + }, + "us-gov-west-1" : { + "hostname" : "internetmonitor.us-gov-west-1.api.aws" + } + } + }, "iot" : { "defaults" : { "credentialScope" : { @@ -18652,10 +21153,59 @@ } } }, + "iottwinmaker" : { + "endpoints" : { + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "iottwinmaker-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "iottwinmaker-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, "kafka" : { "endpoints" : { - "us-gov-east-1" : { }, - "us-gov-west-1" : { } + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "kafka.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "kafka.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "kafka.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "kafka.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "kafka.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "kafka.us-gov-west-1.amazonaws.com" + } } }, "kendra" : { @@ -18668,26 +21218,66 @@ "hostname" : "kendra-fips.us-gov-west-1.amazonaws.com" }, "us-gov-west-1" : { - "variants" : [ { - "hostname" : "kendra-fips.us-gov-west-1.amazonaws.com", - "tags" : [ "fips" ] - } ] + "variants" : [ { + "hostname" : "kendra-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, + "kendra-ranking" : { + "defaults" : { + "dnsSuffix" : "api.aws", + "variants" : [ { + "dnsSuffix" : "api.aws", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "endpoints" : { + "us-gov-east-1" : { + "hostname" : "kendra-ranking.us-gov-east-1.api.aws" + }, + "us-gov-west-1" : { + "hostname" : "kendra-ranking.us-gov-west-1.api.aws" } } }, "kinesis" : { "endpoints" : { - "us-gov-east-1" : { + "fips-us-gov-east-1" : { "credentialScope" : { "region" : "us-gov-east-1" }, + "deprecated" : true, "hostname" : "kinesis.us-gov-east-1.amazonaws.com" }, - "us-gov-west-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "deprecated" : true, "hostname" : "kinesis.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "hostname" : "kinesis.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "kinesis.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "hostname" : "kinesis.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "kinesis.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } } }, @@ -18867,11 +21457,18 @@ }, "mediaconvert" : { "endpoints" : { - "us-gov-west-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "deprecated" : true, "hostname" : "mediaconvert.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "mediaconvert.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } } }, @@ -18916,6 +21513,12 @@ "us-gov-west-1" : { } } }, + "metrics.sagemaker" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, "models.lex" : { "defaults" : { "credentialScope" : { @@ -19107,30 +21710,57 @@ }, "outposts" : { "endpoints" : { - "us-gov-east-1" : { + "fips-us-gov-east-1" : { "credentialScope" : { "region" : "us-gov-east-1" }, + "deprecated" : true, "hostname" : "outposts.us-gov-east-1.amazonaws.com" }, - "us-gov-west-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "deprecated" : true, "hostname" : "outposts.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "outposts.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "outposts.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } } }, "participant.connect" : { "endpoints" : { - "us-gov-west-1" : { + "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "deprecated" : true, "hostname" : "participant.connect.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "participant.connect.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] } } }, + "pi" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, "pinpoint" : { "defaults" : { "credentialScope" : { @@ -19202,12 +21832,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "ram.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "ram.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "ram.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "ram.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "ram.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "ram.us-gov-west-1.amazonaws.com" } } @@ -19344,6 +21996,7 @@ }, "resource-explorer-2" : { "defaults" : { + "dnsSuffix" : "api.aws", "variants" : [ { "dnsSuffix" : "api.aws", "hostname" : "{service}-fips.{region}.{dnsSuffix}", @@ -19425,8 +22078,26 @@ }, "route53resolver" : { "endpoints" : { - "us-gov-east-1" : { }, - "us-gov-west-1" : { } + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "route53resolver.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "deprecated" : true, + "hostname" : "route53resolver.us-gov-east-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "route53resolver.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "deprecated" : true, + "hostname" : "route53resolver.us-gov-west-1.amazonaws.com" + } } }, "runtime.lex" : { @@ -19463,6 +22134,7 @@ } ] }, "endpoints" : { + "us-gov-east-1" : { }, "us-gov-west-1" : { "variants" : [ { "hostname" : "runtime.sagemaker.us-gov-west-1.amazonaws.com", @@ -19683,18 +22355,32 @@ }, "endpoints" : { "us-gov-east-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "serverlessrepo.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { "credentialScope" : { "region" : "us-gov-east-1" }, - "hostname" : "serverlessrepo.us-gov-east-1.amazonaws.com", - "protocols" : [ "https" ] + "deprecated" : true, + "hostname" : "serverlessrepo.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { + "protocols" : [ "https" ], + "variants" : [ { + "hostname" : "serverlessrepo.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { "credentialScope" : { "region" : "us-gov-west-1" }, - "hostname" : "serverlessrepo.us-gov-west-1.amazonaws.com", - "protocols" : [ "https" ] + "deprecated" : true, + "hostname" : "serverlessrepo.us-gov-west-1.amazonaws.com" } } }, @@ -19763,6 +22449,9 @@ "variants" : [ { "hostname" : "servicediscovery-fips.us-gov-east-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-gov-east-1.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, "us-gov-east-1-fips" : { @@ -19776,6 +22465,9 @@ "variants" : [ { "hostname" : "servicediscovery-fips.us-gov-west-1.amazonaws.com", "tags" : [ "fips" ] + }, { + "hostname" : "servicediscovery.us-gov-west-1.amazonaws.com", + "tags" : [ "dualstack" ] } ] }, "us-gov-west-1-fips" : { @@ -19856,7 +22548,19 @@ }, "sms-voice" : { "endpoints" : { - "us-gov-west-1" : { } + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "sms-voice-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "sms-voice-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } } }, "snowball" : { @@ -19912,7 +22616,7 @@ } ] }, "us-gov-west-1" : { - "protocols" : [ "http", "https" ], + "protocols" : [ "https" ], "variants" : [ { "hostname" : "sns.us-gov-west-1.amazonaws.com", "tags" : [ "fips" ] @@ -20172,12 +22876,34 @@ "credentialScope" : { "region" : "us-gov-east-1" }, + "hostname" : "swf.us-gov-east-1.amazonaws.com", + "variants" : [ { + "hostname" : "swf.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-east-1-fips" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, "hostname" : "swf.us-gov-east-1.amazonaws.com" }, "us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" }, + "hostname" : "swf.us-gov-west-1.amazonaws.com", + "variants" : [ { + "hostname" : "swf.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1-fips" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, "hostname" : "swf.us-gov-west-1.amazonaws.com" } } @@ -20432,6 +23158,7 @@ "deprecated" : true, "hostname" : "workspaces-fips.us-gov-west-1.amazonaws.com" }, + "us-gov-east-1" : { }, "us-gov-west-1" : { "variants" : [ { "hostname" : "workspaces-fips.us-gov-west-1.amazonaws.com", @@ -20542,6 +23269,11 @@ "us-iso-west-1" : { } } }, + "athena" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, "autoscaling" : { "endpoints" : { "us-iso-east-1" : { @@ -20550,6 +23282,12 @@ "us-iso-west-1" : { } } }, + "cloudcontrolapi" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, "cloudformation" : { "endpoints" : { "us-iso-east-1" : { }, @@ -20700,11 +23438,24 @@ "deprecated" : true, "hostname" : "elasticfilesystem-fips.us-iso-east-1.c2s.ic.gov" }, + "fips-us-iso-west-1" : { + "credentialScope" : { + "region" : "us-iso-west-1" + }, + "deprecated" : true, + "hostname" : "elasticfilesystem-fips.us-iso-west-1.c2s.ic.gov" + }, "us-iso-east-1" : { "variants" : [ { "hostname" : "elasticfilesystem-fips.us-iso-east-1.c2s.ic.gov", "tags" : [ "fips" ] } ] + }, + "us-iso-west-1" : { + "variants" : [ { + "hostname" : "elasticfilesystem-fips.us-iso-west-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] } } }, @@ -20738,7 +23489,8 @@ }, "firehose" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } }, "glacier" : { @@ -20749,6 +23501,11 @@ "us-iso-west-1" : { } } }, + "glue" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, "health" : { "endpoints" : { "us-iso-east-1" : { } @@ -20817,7 +23574,8 @@ }, "license-manager" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } }, "logs" : { @@ -20836,6 +23594,11 @@ "us-iso-east-1" : { } } }, + "metrics.sagemaker" : { + "endpoints" : { + "us-iso-east-1" : { } + } + }, "monitoring" : { "endpoints" : { "us-iso-east-1" : { }, @@ -20853,6 +23616,23 @@ "us-iso-west-1" : { } } }, + "rbin" : { + "endpoints" : { + "fips-us-iso-east-1" : { + "credentialScope" : { + "region" : "us-iso-east-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-iso-east-1.c2s.ic.gov" + }, + "us-iso-east-1" : { + "variants" : [ { + "hostname" : "rbin-fips.us-iso-east-1.c2s.ic.gov", + "tags" : [ "fips" ] + } ] + } + } + }, "rds" : { "endpoints" : { "us-iso-east-1" : { }, @@ -20901,7 +23681,8 @@ }, "secretsmanager" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } }, "snowball" : { @@ -21005,7 +23786,8 @@ }, "workspaces" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } } } @@ -21091,6 +23873,11 @@ "us-isob-east-1" : { } } }, + "dlm" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, "dms" : { "defaults" : { "variants" : [ { @@ -21290,6 +24077,11 @@ "us-isob-east-1" : { } } }, + "metrics.sagemaker" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, "monitoring" : { "endpoints" : { "us-isob-east-1" : { } @@ -21300,6 +24092,23 @@ "us-isob-east-1" : { } } }, + "rbin" : { + "endpoints" : { + "fips-us-isob-east-1" : { + "credentialScope" : { + "region" : "us-isob-east-1" + }, + "deprecated" : true, + "hostname" : "rbin-fips.us-isob-east-1.sc2s.sgov.gov" + }, + "us-isob-east-1" : { + "variants" : [ { + "hostname" : "rbin-fips.us-isob-east-1.sc2s.sgov.gov", + "tags" : [ "fips" ] + } ] + } + } + }, "rds" : { "endpoints" : { "us-isob-east-1" : { } @@ -21341,6 +24150,11 @@ "us-isob-east-1" : { } } }, + "secretsmanager" : { + "endpoints" : { + "us-isob-east-1" : { } + } + }, "snowball" : { "endpoints" : { "us-isob-east-1" : { } @@ -21421,6 +24235,23 @@ } } } + }, { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "cloud.adc-e.uk", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "dnsSuffix" : "cloud.adc-e.uk", + "partition" : "aws-iso-e", + "partitionName" : "AWS ISOE (Europe)", + "regionRegex" : "^eu\\-isoe\\-\\w+\\-\\d+$", + "regions" : { }, + "services" : { } } ], "version" : 3 } diff --git a/aws/sdk/aws-models/sts.json b/aws/sdk/aws-models/sts.json index 2543c7a989..ddc251e8fa 100644 --- a/aws/sdk/aws-models/sts.json +++ b/aws/sdk/aws-models/sts.json @@ -70,7 +70,7 @@ "name": "sts" }, "aws.protocols#awsQuery": {}, - "smithy.api#documentation": "Security Token Service\n

Security Token Service (STS) enables you to request temporary, limited-privilege \n credentials for Identity and Access Management (IAM) users or for users that you \n authenticate (federated users). This guide provides descriptions of the STS API. For \n more information about using this service, see Temporary Security Credentials.

", + "smithy.api#documentation": "Security Token Service\n

Security Token Service (STS) enables you to request temporary, limited-privilege \n credentials for users. This guide provides descriptions of the STS API. For \n more information about using this service, see Temporary Security Credentials.

", "smithy.api#title": "AWS Security Token Service", "smithy.api#xmlNamespace": { "uri": "https://sts.amazonaws.com/doc/2011-06-15/" @@ -1701,9 +1701,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1720,10 +1720,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "ap-northeast-1", - "UseGlobalEndpoint": true + "Region": "ap-northeast-1" } }, { @@ -1733,9 +1733,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1752,10 +1752,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "ap-south-1", - "UseGlobalEndpoint": true + "Region": "ap-south-1" } }, { @@ -1765,9 +1765,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1784,10 +1784,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "ap-southeast-1", - "UseGlobalEndpoint": true + "Region": "ap-southeast-1" } }, { @@ -1797,9 +1797,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1816,10 +1816,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "ap-southeast-2", - "UseGlobalEndpoint": true + "Region": "ap-southeast-2" } }, { @@ -1829,9 +1829,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1848,10 +1848,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "aws-global", - "UseGlobalEndpoint": true + "Region": "aws-global" } }, { @@ -1861,9 +1861,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1880,10 +1880,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "ca-central-1", - "UseGlobalEndpoint": true + "Region": "ca-central-1" } }, { @@ -1893,9 +1893,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1912,10 +1912,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "eu-central-1", - "UseGlobalEndpoint": true + "Region": "eu-central-1" } }, { @@ -1925,9 +1925,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1944,10 +1944,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "eu-north-1", - "UseGlobalEndpoint": true + "Region": "eu-north-1" } }, { @@ -1957,9 +1957,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -1976,10 +1976,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "eu-west-1", - "UseGlobalEndpoint": true + "Region": "eu-west-1" } }, { @@ -1989,9 +1989,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2008,10 +2008,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "eu-west-2", - "UseGlobalEndpoint": true + "Region": "eu-west-2" } }, { @@ -2021,9 +2021,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2040,10 +2040,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "eu-west-3", - "UseGlobalEndpoint": true + "Region": "eu-west-3" } }, { @@ -2053,9 +2053,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2072,10 +2072,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "sa-east-1", - "UseGlobalEndpoint": true + "Region": "sa-east-1" } }, { @@ -2085,9 +2085,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2104,10 +2104,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "us-east-1", - "UseGlobalEndpoint": true + "Region": "us-east-1" } }, { @@ -2117,9 +2117,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2136,10 +2136,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "us-east-2", - "UseGlobalEndpoint": true + "Region": "us-east-2" } }, { @@ -2149,9 +2149,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2168,10 +2168,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "us-west-1", - "UseGlobalEndpoint": true + "Region": "us-west-1" } }, { @@ -2181,9 +2181,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-1", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-1" + "name": "sigv4" } ] }, @@ -2200,10 +2200,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "us-west-2", - "UseGlobalEndpoint": true + "Region": "us-west-2" } }, { @@ -2213,9 +2213,9 @@ "properties": { "authSchemes": [ { + "signingRegion": "us-east-3", "signingName": "sts", - "name": "sigv4", - "signingRegion": "us-east-3" + "name": "sigv4" } ] }, @@ -2232,10 +2232,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, - "Region": "us-east-3", - "UseGlobalEndpoint": true + "Region": "us-east-3" } }, { @@ -2256,10 +2256,10 @@ } ], "params": { + "UseGlobalEndpoint": true, "UseDualStack": false, "UseFIPS": false, "Region": "us-west-1", - "UseGlobalEndpoint": true, "Endpoint": "https://example.com" } }, @@ -2271,10 +2271,10 @@ } }, "params": { - "UseDualStack": false, - "UseFIPS": false, "Endpoint": "https://example.com", - "UseGlobalEndpoint": false + "UseGlobalEndpoint": false, + "UseDualStack": false, + "UseFIPS": false } } ], @@ -2305,7 +2305,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a set of temporary security credentials that you can use to access Amazon Web Services\n resources. These temporary credentials consist of an access key ID, a secret access key,\n and a security token. Typically, you use AssumeRole within your account or for\n cross-account access. For a comparison of AssumeRole with other API operations\n that produce temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n

\n Permissions\n

\n

The temporary security credentials created by AssumeRole can be used to\n make API calls to any Amazon Web Services service with the following exception: You cannot call the\n Amazon Web Services STS GetFederationToken or GetSessionToken API\n operations.

\n

(Optional) You can pass inline or managed session policies to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters. Passing policies to this operation returns new \n temporary credentials. The resulting session's permissions are the intersection of the \n role's identity-based policy and the session policies. You can use the role's temporary \n credentials in subsequent Amazon Web Services API calls to access resources in the account that owns \n the role. You cannot use session policies to grant more permissions than those allowed \n by the identity-based policy of the role that is being assumed. For more information, see\n Session\n Policies in the IAM User Guide.

\n

When you create a role, you create two policies: A role trust policy that specifies\n who can assume the role and a permissions policy that specifies\n what can be done with the role. You specify the trusted principal\n who is allowed to assume the role in the role trust policy.

\n

To assume a role from a different account, your Amazon Web Services account must be trusted by the\n role. The trust relationship is defined in the role's trust policy when the role is\n created. That trust policy states which accounts are allowed to delegate that access to\n users in the account.

\n

A user who wants to access a role in a different account must also have permissions that\n are delegated from the user account administrator. The administrator must attach a policy\n that allows the user to call AssumeRole for the ARN of the role in the other\n account.

\n

To allow a user to assume a role in the same account, you can do either of the\n following:

\n
    \n
  • \n

    Attach a policy to the user that allows the user to call AssumeRole\n (as long as the role's trust policy trusts the account).

    \n
  • \n
  • \n

    Add the user as a principal directly in the role's trust policy.

    \n
  • \n
\n

You can do either because the role’s trust policy acts as an IAM resource-based\n policy. When a resource-based policy grants access to a principal in the same account, no\n additional identity-based policy is required. For more information about trust policies and\n resource-based policies, see IAM Policies in the\n IAM User Guide.

\n

\n Tags\n

\n

(Optional) You can pass tag key-value pairs to your session. These tags are called\n session tags. For more information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

You can set the session tags as transitive. Transitive tags persist during role\n chaining. For more information, see Chaining Roles\n with Session Tags in the IAM User Guide.

\n

\n Using MFA with AssumeRole\n

\n

(Optional) You can include multi-factor authentication (MFA) information when you call\n AssumeRole. This is useful for cross-account scenarios to ensure that the\n user that assumes the role has been authenticated with an Amazon Web Services MFA device. In that\n scenario, the trust policy of the role being assumed includes a condition that tests for\n MFA authentication. If the caller does not include valid MFA information, the request to\n assume the role is denied. The condition in a trust policy that tests for MFA\n authentication might look like the following example.

\n

\n \"Condition\": {\"Bool\": {\"aws:MultiFactorAuthPresent\": true}}\n

\n

For more information, see Configuring MFA-Protected API Access\n in the IAM User Guide guide.

\n

To use MFA with AssumeRole, you pass values for the\n SerialNumber and TokenCode parameters. The\n SerialNumber value identifies the user's hardware or virtual MFA device.\n The TokenCode is the time-based one-time password (TOTP) that the MFA device\n produces.

" + "smithy.api#documentation": "

Returns a set of temporary security credentials that you can use to access Amazon Web Services\n resources. These temporary credentials consist of an access key ID, a secret access key,\n and a security token. Typically, you use AssumeRole within your account or for\n cross-account access. For a comparison of AssumeRole with other API operations\n that produce temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n

\n Permissions\n

\n

The temporary security credentials created by AssumeRole can be used to\n make API calls to any Amazon Web Services service with the following exception: You cannot call the\n Amazon Web Services STS GetFederationToken or GetSessionToken API\n operations.

\n

(Optional) You can pass inline or managed session policies to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters. Passing policies to this operation returns new \n temporary credentials. The resulting session's permissions are the intersection of the \n role's identity-based policy and the session policies. You can use the role's temporary \n credentials in subsequent Amazon Web Services API calls to access resources in the account that owns \n the role. You cannot use session policies to grant more permissions than those allowed \n by the identity-based policy of the role that is being assumed. For more information, see\n Session\n Policies in the IAM User Guide.

\n

When you create a role, you create two policies: a role trust policy that specifies\n who can assume the role, and a permissions policy that specifies\n what can be done with the role. You specify the trusted principal\n that is allowed to assume the role in the role trust policy.

\n

To assume a role from a different account, your Amazon Web Services account must be trusted by the\n role. The trust relationship is defined in the role's trust policy when the role is\n created. That trust policy states which accounts are allowed to delegate that access to\n users in the account.

\n

A user who wants to access a role in a different account must also have permissions that\n are delegated from the account administrator. The administrator must attach a policy\n that allows the user to call AssumeRole for the ARN of the role in the other\n account.

\n

To allow a user to assume a role in the same account, you can do either of the\n following:

\n
    \n
  • \n

    Attach a policy to the user that allows the user to call AssumeRole\n (as long as the role's trust policy trusts the account).

    \n
  • \n
  • \n

    Add the user as a principal directly in the role's trust policy.

    \n
  • \n
\n

You can do either because the role’s trust policy acts as an IAM resource-based\n policy. When a resource-based policy grants access to a principal in the same account, no\n additional identity-based policy is required. For more information about trust policies and\n resource-based policies, see IAM Policies in the\n IAM User Guide.

\n

\n Tags\n

\n

(Optional) You can pass tag key-value pairs to your session. These tags are called\n session tags. For more information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

You can set the session tags as transitive. Transitive tags persist during role\n chaining. For more information, see Chaining Roles\n with Session Tags in the IAM User Guide.

\n

\n Using MFA with AssumeRole\n

\n

(Optional) You can include multi-factor authentication (MFA) information when you call\n AssumeRole. This is useful for cross-account scenarios to ensure that the\n user that assumes the role has been authenticated with an Amazon Web Services MFA device. In that\n scenario, the trust policy of the role being assumed includes a condition that tests for\n MFA authentication. If the caller does not include valid MFA information, the request to\n assume the role is denied. The condition in a trust policy that tests for MFA\n authentication might look like the following example.

\n

\n \"Condition\": {\"Bool\": {\"aws:MultiFactorAuthPresent\": true}}\n

\n

For more information, see Configuring MFA-Protected API Access\n in the IAM User Guide guide.

\n

To use MFA with AssumeRole, you pass values for the\n SerialNumber and TokenCode parameters. The\n SerialNumber value identifies the user's hardware or virtual MFA device.\n The TokenCode is the time-based one-time password (TOTP) that the MFA device\n produces.

" } }, "com.amazonaws.sts#AssumeRoleRequest": { @@ -2544,7 +2544,7 @@ "NameQualifier": { "target": "com.amazonaws.sts#NameQualifier", "traits": { - "smithy.api#documentation": "

A hash value based on the concatenation of the following:

\n
    \n
  • \n

    The Issuer response value.

    \n
  • \n
  • \n

    The Amazon Web Services account ID.

    \n
  • \n
  • \n

    The friendly name (the last part of the ARN) of the SAML provider in IAM.

    \n
  • \n
\n

The combination of NameQualifier and Subject can be used to\n uniquely identify a federated user.

\n

The following pseudocode shows how the hash value is calculated:

\n

\n BASE64 ( SHA1 ( \"https://example.com/saml\" + \"123456789012\" + \"/MySAMLIdP\" ) )\n

" + "smithy.api#documentation": "

A hash value based on the concatenation of the following:

\n
    \n
  • \n

    The Issuer response value.

    \n
  • \n
  • \n

    The Amazon Web Services account ID.

    \n
  • \n
  • \n

    The friendly name (the last part of the ARN) of the SAML provider in IAM.

    \n
  • \n
\n

The combination of NameQualifier and Subject can be used to\n uniquely identify a user.

\n

The following pseudocode shows how the hash value is calculated:

\n

\n BASE64 ( SHA1 ( \"https://example.com/saml\" + \"123456789012\" + \"/MySAMLIdP\" ) )\n

" } }, "SourceIdentity": { @@ -2591,7 +2591,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a set of temporary security credentials for users who have been authenticated in\n a mobile or web application with a web identity provider. Example providers include the\n OAuth 2.0 providers Login with Amazon and Facebook, or any OpenID Connect-compatible\n identity provider such as Google or Amazon Cognito federated identities.

\n \n

For mobile applications, we recommend that you use Amazon Cognito. You can use Amazon Cognito with the\n Amazon Web Services SDK for iOS Developer Guide and the Amazon Web Services SDK for Android Developer Guide to uniquely\n identify a user. You can also supply the user with a consistent identity throughout the\n lifetime of an application.

\n

To learn more about Amazon Cognito, see Amazon Cognito Overview in\n Amazon Web Services SDK for Android Developer Guide and Amazon Cognito Overview in the\n Amazon Web Services SDK for iOS Developer Guide.

\n
\n

Calling AssumeRoleWithWebIdentity does not require the use of Amazon Web Services\n security credentials. Therefore, you can distribute an application (for example, on mobile\n devices) that requests temporary security credentials without including long-term Amazon Web Services\n credentials in the application. You also don't need to deploy server-based proxy services\n that use long-term Amazon Web Services credentials. Instead, the identity of the caller is validated by\n using a token from the web identity provider. For a comparison of\n AssumeRoleWithWebIdentity with the other API operations that produce\n temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n

The temporary security credentials returned by this API consist of an access key ID, a\n secret access key, and a security token. Applications can use these temporary security\n credentials to sign calls to Amazon Web Services service API operations.

\n

\n Session Duration\n

\n

By default, the temporary security credentials created by\n AssumeRoleWithWebIdentity last for one hour. However, you can use the\n optional DurationSeconds parameter to specify the duration of your session.\n You can provide a value from 900 seconds (15 minutes) up to the maximum session duration\n setting for the role. This setting can have a value from 1 hour to 12 hours. To learn how\n to view the maximum value for your role, see View the\n Maximum Session Duration Setting for a Role in the\n IAM User Guide. The maximum session duration limit applies when\n you use the AssumeRole* API operations or the assume-role* CLI\n commands. However the limit does not apply when you use those operations to create a\n console URL. For more information, see Using IAM Roles in the\n IAM User Guide.

\n

\n Permissions\n

\n

The temporary security credentials created by AssumeRoleWithWebIdentity can\n be used to make API calls to any Amazon Web Services service with the following exception: you cannot\n call the STS GetFederationToken or GetSessionToken API\n operations.

\n

(Optional) You can pass inline or managed session policies to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters. Passing policies to this operation returns new \n temporary credentials. The resulting session's permissions are the intersection of the \n role's identity-based policy and the session policies. You can use the role's temporary \n credentials in subsequent Amazon Web Services API calls to access resources in the account that owns \n the role. You cannot use session policies to grant more permissions than those allowed \n by the identity-based policy of the role that is being assumed. For more information, see\n Session\n Policies in the IAM User Guide.

\n

\n Tags\n

\n

(Optional) You can configure your IdP to pass attributes into your web identity token as\n session tags. Each session tag consists of a key name and an associated value. For more\n information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n

You can pass up to 50 session tags. The plaintext session tag keys can’t exceed 128\n characters and the values can’t exceed 256 characters. For these and additional limits, see\n IAM\n and STS Character Limits in the IAM User Guide.

\n \n

An Amazon Web Services conversion compresses the passed inline session policy, managed policy ARNs,\n and session tags into a packed binary format that has a separate limit. Your request can\n fail for this limit even if your plaintext meets the other requirements. The\n PackedPolicySize response element indicates by percentage how close the\n policies and tags for your request are to the upper size limit.

\n
\n

You can pass a session tag with the same key as a tag that is attached to the role. When\n you do, the session tag overrides the role tag with the same key.

\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

You can set the session tags as transitive. Transitive tags persist during role\n chaining. For more information, see Chaining Roles\n with Session Tags in the IAM User Guide.

\n

\n Identities\n

\n

Before your application can call AssumeRoleWithWebIdentity, you must have\n an identity token from a supported identity provider and create a role that the application\n can assume. The role that your application assumes must trust the identity provider that is\n associated with the identity token. In other words, the identity provider must be specified\n in the role's trust policy.

\n \n

Calling AssumeRoleWithWebIdentity can result in an entry in your\n CloudTrail logs. The entry includes the Subject of\n the provided web identity token. We recommend that you avoid using any personally\n identifiable information (PII) in this field. For example, you could instead use a GUID\n or a pairwise identifier, as suggested\n in the OIDC specification.

\n
\n

For more information about how to use web identity federation and the\n AssumeRoleWithWebIdentity API, see the following resources:

\n " + "smithy.api#documentation": "

Returns a set of temporary security credentials for users who have been authenticated in\n a mobile or web application with a web identity provider. Example providers include the\n OAuth 2.0 providers Login with Amazon and Facebook, or any OpenID Connect-compatible\n identity provider such as Google or Amazon Cognito federated identities.

\n \n

For mobile applications, we recommend that you use Amazon Cognito. You can use Amazon Cognito with the\n Amazon Web Services SDK for iOS Developer Guide and the Amazon Web Services SDK for Android Developer Guide to uniquely\n identify a user. You can also supply the user with a consistent identity throughout the\n lifetime of an application.

\n

To learn more about Amazon Cognito, see Amazon Cognito identity pools in\n Amazon Cognito Developer Guide.

\n
\n

Calling AssumeRoleWithWebIdentity does not require the use of Amazon Web Services\n security credentials. Therefore, you can distribute an application (for example, on mobile\n devices) that requests temporary security credentials without including long-term Amazon Web Services\n credentials in the application. You also don't need to deploy server-based proxy services\n that use long-term Amazon Web Services credentials. Instead, the identity of the caller is validated by\n using a token from the web identity provider. For a comparison of\n AssumeRoleWithWebIdentity with the other API operations that produce\n temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n

The temporary security credentials returned by this API consist of an access key ID, a\n secret access key, and a security token. Applications can use these temporary security\n credentials to sign calls to Amazon Web Services service API operations.

\n

\n Session Duration\n

\n

By default, the temporary security credentials created by\n AssumeRoleWithWebIdentity last for one hour. However, you can use the\n optional DurationSeconds parameter to specify the duration of your session.\n You can provide a value from 900 seconds (15 minutes) up to the maximum session duration\n setting for the role. This setting can have a value from 1 hour to 12 hours. To learn how\n to view the maximum value for your role, see View the\n Maximum Session Duration Setting for a Role in the\n IAM User Guide. The maximum session duration limit applies when\n you use the AssumeRole* API operations or the assume-role* CLI\n commands. However the limit does not apply when you use those operations to create a\n console URL. For more information, see Using IAM Roles in the\n IAM User Guide.

\n

\n Permissions\n

\n

The temporary security credentials created by AssumeRoleWithWebIdentity can\n be used to make API calls to any Amazon Web Services service with the following exception: you cannot\n call the STS GetFederationToken or GetSessionToken API\n operations.

\n

(Optional) You can pass inline or managed session policies to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters. Passing policies to this operation returns new \n temporary credentials. The resulting session's permissions are the intersection of the \n role's identity-based policy and the session policies. You can use the role's temporary \n credentials in subsequent Amazon Web Services API calls to access resources in the account that owns \n the role. You cannot use session policies to grant more permissions than those allowed \n by the identity-based policy of the role that is being assumed. For more information, see\n Session\n Policies in the IAM User Guide.

\n

\n Tags\n

\n

(Optional) You can configure your IdP to pass attributes into your web identity token as\n session tags. Each session tag consists of a key name and an associated value. For more\n information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n

You can pass up to 50 session tags. The plaintext session tag keys can’t exceed 128\n characters and the values can’t exceed 256 characters. For these and additional limits, see\n IAM\n and STS Character Limits in the IAM User Guide.

\n \n

An Amazon Web Services conversion compresses the passed inline session policy, managed policy ARNs,\n and session tags into a packed binary format that has a separate limit. Your request can\n fail for this limit even if your plaintext meets the other requirements. The\n PackedPolicySize response element indicates by percentage how close the\n policies and tags for your request are to the upper size limit.

\n
\n

You can pass a session tag with the same key as a tag that is attached to the role. When\n you do, the session tag overrides the role tag with the same key.

\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

You can set the session tags as transitive. Transitive tags persist during role\n chaining. For more information, see Chaining Roles\n with Session Tags in the IAM User Guide.

\n

\n Identities\n

\n

Before your application can call AssumeRoleWithWebIdentity, you must have\n an identity token from a supported identity provider and create a role that the application\n can assume. The role that your application assumes must trust the identity provider that is\n associated with the identity token. In other words, the identity provider must be specified\n in the role's trust policy.

\n \n

Calling AssumeRoleWithWebIdentity can result in an entry in your\n CloudTrail logs. The entry includes the Subject of\n the provided web identity token. We recommend that you avoid using any personally\n identifiable information (PII) in this field. For example, you could instead use a GUID\n or a pairwise identifier, as suggested\n in the OIDC specification.

\n
\n

For more information about how to use web identity federation and the\n AssumeRoleWithWebIdentity API, see the following resources:

\n " } }, "com.amazonaws.sts#AssumeRoleWithWebIdentityRequest": { @@ -2895,7 +2895,7 @@ "target": "com.amazonaws.sts#GetCallerIdentityResponse" }, "traits": { - "smithy.api#documentation": "

Returns details about the IAM user or role whose credentials are used to call the\n operation.

\n \n

No permissions are required to perform this operation. If an administrator adds a\n policy to your IAM user or role that explicitly denies access to the\n sts:GetCallerIdentity action, you can still perform this operation.\n Permissions are not required because the same information is returned when an IAM user\n or role is denied access. To view an example response, see I Am Not Authorized to Perform: iam:DeleteVirtualMFADevice in the\n IAM User Guide.

\n
" + "smithy.api#documentation": "

Returns details about the IAM user or role whose credentials are used to call the operation.

\n \n

No permissions are required to perform this operation. If an administrator\n attaches a policy to your identity that explicitly denies access to the\n sts:GetCallerIdentity action, you can still perform this operation.\n Permissions are not required because the same information is returned when access is denied. To view an example response, see I Am Not Authorized to Perform: iam:DeleteVirtualMFADevice in the\n IAM User Guide.

\n
" } }, "com.amazonaws.sts#GetCallerIdentityRequest": { @@ -2952,7 +2952,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a set of temporary security credentials (consisting of an access key ID, a\n secret access key, and a security token) for a federated user. A typical use is in a proxy\n application that gets temporary security credentials on behalf of distributed applications\n inside a corporate network. You must call the GetFederationToken operation\n using the long-term security credentials of an IAM user. As a result, this call is\n appropriate in contexts where those credentials can be safely stored, usually in a\n server-based application. For a comparison of GetFederationToken with the\n other API operations that produce temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n \n

You can create a mobile-based or browser-based app that can authenticate users using\n a web identity provider like Login with Amazon, Facebook, Google, or an OpenID\n Connect-compatible identity provider. In this case, we recommend that you use Amazon Cognito or\n AssumeRoleWithWebIdentity. For more information, see Federation Through a Web-based Identity Provider in the\n IAM User Guide.

\n
\n

You can also call GetFederationToken using the security credentials of an\n Amazon Web Services account root user, but we do not recommend it. Instead, we recommend that you create\n an IAM user for the purpose of the proxy application. Then attach a policy to the IAM\n user that limits federated users to only the actions and resources that they need to\n access. For more information, see IAM Best Practices in the\n IAM User Guide.

\n

\n Session duration\n

\n

The temporary credentials are valid for the specified duration, from 900 seconds (15\n minutes) up to a maximum of 129,600 seconds (36 hours). The default session duration is\n 43,200 seconds (12 hours). Temporary credentials obtained by using the Amazon Web Services account root\n user credentials have a maximum duration of 3,600 seconds (1 hour).

\n

\n Permissions\n

\n

You can use the temporary credentials created by GetFederationToken in any\n Amazon Web Services service with the following exceptions:

\n
    \n
  • \n

    You cannot call any IAM operations using the CLI or the Amazon Web Services API. This limitation does not apply to console sessions.

    \n
  • \n
  • \n

    You cannot call any STS operations except GetCallerIdentity.

    \n
  • \n
\n

You can use temporary credentials for single sign-on (SSO) to the console.

\n

You must pass an inline or managed session policy to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters.

\n

Though the session policy parameters are optional, if you do not pass a policy, then the\n resulting federated user session has no permissions. When you pass session policies, the\n session permissions are the intersection of the IAM user policies and the session\n policies that you pass. This gives you a way to further restrict the permissions for a\n federated user. You cannot use session policies to grant more permissions than those that\n are defined in the permissions policy of the IAM user. For more information, see Session\n Policies in the IAM User Guide. For information about\n using GetFederationToken to create temporary security credentials, see GetFederationToken—Federation Through a Custom Identity Broker.

\n

You can use the credentials to access a resource that has a resource-based policy. If\n that policy specifically references the federated user session in the\n Principal element of the policy, the session has the permissions allowed by\n the policy. These permissions are granted in addition to the permissions granted by the\n session policies.

\n

\n Tags\n

\n

(Optional) You can pass tag key-value pairs to your session. These are called session\n tags. For more information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n \n

You can create a mobile-based or browser-based app that can authenticate users using\n a web identity provider like Login with Amazon, Facebook, Google, or an OpenID\n Connect-compatible identity provider. In this case, we recommend that you use Amazon Cognito or\n AssumeRoleWithWebIdentity. For more information, see Federation Through a Web-based Identity Provider in the\n IAM User Guide.

\n
\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

Tag key–value pairs are not case sensitive, but case is preserved. This means that you\n cannot have separate Department and department tag keys. Assume\n that the user that you are federating has the\n Department=Marketing tag and you pass the\n department=engineering session tag. Department\n and department are not saved as separate tags, and the session tag passed in\n the request takes precedence over the user tag.

" + "smithy.api#documentation": "

Returns a set of temporary security credentials (consisting of an access key ID, a\n secret access key, and a security token) for a user. A typical use is in a proxy\n application that gets temporary security credentials on behalf of distributed applications\n inside a corporate network.

\n

You must call the GetFederationToken operation\n using the long-term security credentials of an IAM user. As a result, this call is\n appropriate in contexts where those credentials can be safeguarded, usually in a\n server-based application. For a comparison of GetFederationToken with the\n other API operations that produce temporary credentials, see Requesting Temporary Security\n Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n

Although it is possible to call GetFederationToken using the security credentials of an\n Amazon Web Services account root user rather than an IAM user that you create for the purpose of a proxy application, we do not recommend it. For more information, see Safeguard your root user credentials and don't use them for everyday tasks in the\n IAM User Guide.

\n \n

You can create a mobile-based or browser-based app that can authenticate users using\n a web identity provider like Login with Amazon, Facebook, Google, or an OpenID\n Connect-compatible identity provider. In this case, we recommend that you use Amazon Cognito or\n AssumeRoleWithWebIdentity. For more information, see Federation Through a Web-based Identity Provider in the\n IAM User Guide.

\n
\n

\n Session duration\n

\n

The temporary credentials are valid for the specified duration, from 900 seconds (15\n minutes) up to a maximum of 129,600 seconds (36 hours). The default session duration is\n 43,200 seconds (12 hours). Temporary credentials obtained by using the root user credentials have a maximum duration of 3,600 seconds (1 hour).

\n

\n Permissions\n

\n

You can use the temporary credentials created by GetFederationToken in any\n Amazon Web Services service with the following exceptions:

\n
    \n
  • \n

    You cannot call any IAM operations using the CLI or the Amazon Web Services API. This limitation does not apply to console sessions.

    \n
  • \n
  • \n

    You cannot call any STS operations except GetCallerIdentity.

    \n
  • \n
\n

You can use temporary credentials for single sign-on (SSO) to the console.

\n

You must pass an inline or managed session policy to\n this operation. You can pass a single JSON policy document to use as an inline session\n policy. You can also specify up to 10 managed policy Amazon Resource Names (ARNs) to use as\n managed session policies. The plaintext that you use for both inline and managed session\n policies can't exceed 2,048 characters.

\n

Though the session policy parameters are optional, if you do not pass a policy, then the\n resulting federated user session has no permissions. When you pass session policies, the\n session permissions are the intersection of the IAM user policies and the session\n policies that you pass. This gives you a way to further restrict the permissions for a\n federated user. You cannot use session policies to grant more permissions than those that\n are defined in the permissions policy of the IAM user. For more information, see Session\n Policies in the IAM User Guide. For information about\n using GetFederationToken to create temporary security credentials, see GetFederationToken—Federation Through a Custom Identity Broker.

\n

You can use the credentials to access a resource that has a resource-based policy. If\n that policy specifically references the federated user session in the\n Principal element of the policy, the session has the permissions allowed by\n the policy. These permissions are granted in addition to the permissions granted by the\n session policies.

\n

\n Tags\n

\n

(Optional) You can pass tag key-value pairs to your session. These are called session\n tags. For more information about session tags, see Passing Session Tags in STS in the\n IAM User Guide.

\n \n

You can create a mobile-based or browser-based app that can authenticate users using\n a web identity provider like Login with Amazon, Facebook, Google, or an OpenID\n Connect-compatible identity provider. In this case, we recommend that you use Amazon Cognito or\n AssumeRoleWithWebIdentity. For more information, see Federation Through a Web-based Identity Provider in the\n IAM User Guide.

\n
\n

An administrator must grant you the permissions necessary to pass session tags. The\n administrator can also create granular permissions to allow you to pass only specific\n session tags. For more information, see Tutorial: Using Tags\n for Attribute-Based Access Control in the\n IAM User Guide.

\n

Tag key–value pairs are not case sensitive, but case is preserved. This means that you\n cannot have separate Department and department tag keys. Assume\n that the user that you are federating has the\n Department=Marketing tag and you pass the\n department=engineering session tag. Department\n and department are not saved as separate tags, and the session tag passed in\n the request takes precedence over the user tag.

" } }, "com.amazonaws.sts#GetFederationTokenRequest": { @@ -2980,7 +2980,7 @@ "DurationSeconds": { "target": "com.amazonaws.sts#durationSecondsType", "traits": { - "smithy.api#documentation": "

The duration, in seconds, that the session should last. Acceptable durations for\n federation sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with\n 43,200 seconds (12 hours) as the default. Sessions obtained using Amazon Web Services account root user\n credentials are restricted to a maximum of 3,600 seconds (one hour). If the specified\n duration is longer than one hour, the session obtained by using root user credentials\n defaults to one hour.

" + "smithy.api#documentation": "

The duration, in seconds, that the session should last. Acceptable durations for\n federation sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with\n 43,200 seconds (12 hours) as the default. Sessions obtained using root user\n credentials are restricted to a maximum of 3,600 seconds (one hour). If the specified\n duration is longer than one hour, the session obtained by using root user credentials\n defaults to one hour.

" } }, "Tags": { @@ -3035,7 +3035,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns a set of temporary credentials for an Amazon Web Services account or IAM user. The\n credentials consist of an access key ID, a secret access key, and a security token.\n Typically, you use GetSessionToken if you want to use MFA to protect\n programmatic calls to specific Amazon Web Services API operations like Amazon EC2 StopInstances.\n MFA-enabled IAM users would need to call GetSessionToken and submit an MFA\n code that is associated with their MFA device. Using the temporary security credentials\n that are returned from the call, IAM users can then make programmatic calls to API\n operations that require MFA authentication. If you do not supply a correct MFA code, then\n the API returns an access denied error. For a comparison of GetSessionToken\n with the other API operations that produce temporary credentials, see Requesting\n Temporary Security Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n \n

No permissions are required for users to perform this operation. The purpose of the\n sts:GetSessionToken operation is to authenticate the user using MFA. You\n cannot use policies to control authentication operations. For more information, see\n Permissions for GetSessionToken in the\n IAM User Guide.

\n
\n

\n Session Duration\n

\n

The GetSessionToken operation must be called by using the long-term Amazon Web Services\n security credentials of the Amazon Web Services account root user or an IAM user. Credentials that are\n created by IAM users are valid for the duration that you specify. This duration can range\n from 900 seconds (15 minutes) up to a maximum of 129,600 seconds (36 hours), with a default\n of 43,200 seconds (12 hours). Credentials based on account credentials can range from 900\n seconds (15 minutes) up to 3,600 seconds (1 hour), with a default of 1 hour.

\n

\n Permissions\n

\n

The temporary security credentials created by GetSessionToken can be used\n to make API calls to any Amazon Web Services service with the following exceptions:

\n
    \n
  • \n

    You cannot call any IAM API operations unless MFA authentication information is\n included in the request.

    \n
  • \n
  • \n

    You cannot call any STS API except\n AssumeRole or GetCallerIdentity.

    \n
  • \n
\n \n

We recommend that you do not call GetSessionToken with Amazon Web Services account\n root user credentials. Instead, follow our best practices by\n creating one or more IAM users, giving them the necessary permissions, and using IAM\n users for everyday interaction with Amazon Web Services.

\n
\n

The credentials that are returned by GetSessionToken are based on\n permissions associated with the user whose credentials were used to call the operation. If\n GetSessionToken is called using Amazon Web Services account root user credentials, the\n temporary credentials have root user permissions. Similarly, if\n GetSessionToken is called using the credentials of an IAM user, the\n temporary credentials have the same permissions as the IAM user.

\n

For more information about using GetSessionToken to create temporary\n credentials, go to Temporary\n Credentials for Users in Untrusted Environments in the\n IAM User Guide.

" + "smithy.api#documentation": "

Returns a set of temporary credentials for an Amazon Web Services account or IAM user. The\n credentials consist of an access key ID, a secret access key, and a security token.\n Typically, you use GetSessionToken if you want to use MFA to protect\n programmatic calls to specific Amazon Web Services API operations like Amazon EC2 StopInstances.

\n

MFA-enabled IAM users must call GetSessionToken and submit an MFA\n code that is associated with their MFA device. Using the temporary security credentials\n that the call returns, IAM users can then make programmatic calls to API\n operations that require MFA authentication. An incorrect MFA code causes the API to return an access denied error. For a comparison of GetSessionToken\n with the other API operations that produce temporary credentials, see Requesting\n Temporary Security Credentials and Comparing the\n Amazon Web Services STS API operations in the IAM User Guide.

\n \n

No permissions are required for users to perform this operation. The purpose of the\n sts:GetSessionToken operation is to authenticate the user using MFA. You\n cannot use policies to control authentication operations. For more information, see\n Permissions for GetSessionToken in the\n IAM User Guide.

\n
\n

\n Session Duration\n

\n

The GetSessionToken operation must be called by using the long-term Amazon Web Services\n security credentials of an IAM user. Credentials that are\n created by IAM users are valid for the duration that you specify. This duration can range\n from 900 seconds (15 minutes) up to a maximum of 129,600 seconds (36 hours), with a default\n of 43,200 seconds (12 hours). Credentials based on account credentials can range from 900\n seconds (15 minutes) up to 3,600 seconds (1 hour), with a default of 1 hour.

\n

\n Permissions\n

\n

The temporary security credentials created by GetSessionToken can be used\n to make API calls to any Amazon Web Services service with the following exceptions:

\n
    \n
  • \n

    You cannot call any IAM API operations unless MFA authentication information is\n included in the request.

    \n
  • \n
  • \n

    You cannot call any STS API except\n AssumeRole or GetCallerIdentity.

    \n
  • \n
\n

The credentials that GetSessionToken returns are based on\n permissions associated with the IAM user whose credentials were used to call the operation. The\n temporary credentials have the same permissions as the IAM user.

\n \n

Although it is possible to call GetSessionToken using the security credentials of an\n Amazon Web Services account root user rather than an IAM user, we do not recommend it. If\n GetSessionToken is called using root user credentials, the\n temporary credentials have root user permissions. For more information, see Safeguard your root user credentials and don't use them for everyday tasks in the\n IAM User Guide\n

\n
\n

For more information about using GetSessionToken to create temporary\n credentials, see Temporary\n Credentials for Users in Untrusted Environments in the\n IAM User Guide.

" } }, "com.amazonaws.sts#GetSessionTokenRequest": { @@ -3224,7 +3224,8 @@ "smithy.api#length": { "min": 4, "max": 100000 - } + }, + "smithy.api#sensitive": {} } }, "com.amazonaws.sts#Subject": { @@ -3266,7 +3267,10 @@ } }, "com.amazonaws.sts#accessKeySecretType": { - "type": "string" + "type": "string", + "traits": { + "smithy.api#sensitive": {} + } }, "com.amazonaws.sts#accountType": { "type": "string" @@ -3297,7 +3301,8 @@ "smithy.api#length": { "min": 4, "max": 20000 - } + }, + "smithy.api#sensitive": {} } }, "com.amazonaws.sts#dateType": { diff --git a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs index 2de72f3279..77c1940ae1 100644 --- a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs +++ b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs @@ -21,14 +21,14 @@ fn validate_sensitive_trait() { let builder = GenerateRandomOutput::builder().plaintext(Blob::new("some output")); assert_eq!( format!("{:?}", builder), - "GenerateRandomOutputBuilder { plaintext: \"*** Sensitive Data Redacted ***\", _request_id: None }" + "GenerateRandomOutputBuilder { plaintext: \"*** Sensitive Data Redacted ***\", ciphertext_for_recipient: None, _request_id: None }" ); let output = GenerateRandomOutput::builder() .plaintext(Blob::new("some output")) .build(); assert_eq!( format!("{:?}", output), - "GenerateRandomOutput { plaintext: \"*** Sensitive Data Redacted ***\", _request_id: None }" + "GenerateRandomOutput { plaintext: \"*** Sensitive Data Redacted ***\", ciphertext_for_recipient: None, _request_id: None }" ); } From 9bfe936fbcc507bd6970de0f095ee0c797a6f0a0 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 19 May 2023 09:58:28 -0700 Subject: [PATCH 102/253] Make the SDK ad hoc tests pass against the orchestrator (#2708) ## Motivation and Context This PR refactors the client protocol test generator machinery to use a client instead of calling `make_operation` directly, and then fixes the ad hoc tests for the orchestrator. The ad hoc tests revealed that overriding the signing region/service via endpoint config was lost when porting SigV4 signing to the orchestrator, so this PR updates the SigV4 `HttpRequestSigner` implementation to restore this functionality. It is doing this in the signer directly rather than via an interceptor since it should only run this logic when SigV4 is the selected auth scheme. Other notable changes: - Adds `--no-fail-fast` arg to `cargoTest` targets so that all Rust tests run in CI rather than stopping on the first failure - Changes `EndpointResolver::resolve_and_apply_endpoint` to just `resolve_endpoint` so that the orchestrator can place the endpoint config into the request state, which is required for the signer to make use of it - Adds a `set_region` method to SDK service configs - Deletes the API Gateway model and integration test from the SDK smoke test since it is covered by the ad hoc tests - Adds a comment explaining where the API Gateway model comes from in the ad hoc tests - Adds a `smithy.runtime.mode` Gradle property to `aws:sdk` and `aws:sdk-adhoc-test` to trivially switch between middleware and orchestrator when testing/generating locally ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-runtime/src/auth.rs | 215 +- aws/sdk-adhoc-test/build.gradle.kts | 4 + .../models/apigateway-rules.smithy | 8 + .../rustsdk/AwsFluentClientDecorator.kt | 25 + .../amazon/smithy/rustsdk/RegionDecorator.kt | 8 +- .../rustsdk/RetryClassifierDecorator.kt | 31 +- .../smithy/rustsdk/SigV4AuthDecorator.kt | 4 +- .../customize/ServiceSpecificDecorator.kt | 8 + .../endpoints/OperationInputTestGenerator.kt | 4 +- aws/sdk/aws-models/apigateway.json | 12865 ---------------- aws/sdk/build.gradle.kts | 3 +- aws/sdk/integration-tests/Cargo.toml | 1 - .../integration-tests/apigateway/Cargo.toml | 17 - .../apigateway/tests/accept_header.rs | 32 - buildSrc/src/main/kotlin/CodegenTestCommon.kt | 2 +- .../client/smithy/ClientCodegenVisitor.kt | 30 +- .../customizations/EndpointPrefixGenerator.kt | 19 +- .../customize/ClientCodegenDecorator.kt | 16 + .../generators/EndpointTestGenerator.kt | 9 +- .../smithy/generators/ClientInstantiator.kt | 38 +- .../EndpointTraitBindingGenerator.kt | 49 +- .../protocol/ProtocolTestGenerator.kt | 94 +- .../protocol/ResponseDeserializerGenerator.kt | 2 +- .../generators/ClientInstantiatorTest.kt | 4 +- .../core/smithy/generators/Instantiator.kt | 23 +- .../aws-smithy-runtime-api/src/client/auth.rs | 31 + .../src/client/orchestrator.rs | 9 +- .../src/client/auth/http.rs | 36 +- .../src/client/orchestrator/auth.rs | 197 +- .../src/client/orchestrator/endpoints.rs | 105 +- .../client/runtime_plugin/anonymous_auth.rs | 3 +- rust-runtime/aws-smithy-types/src/document.rs | 138 +- tools/ci-scripts/check-aws-sdk-adhoc-tests | 15 +- 33 files changed, 924 insertions(+), 13121 deletions(-) delete mode 100644 aws/sdk/aws-models/apigateway.json delete mode 100644 aws/sdk/integration-tests/apigateway/Cargo.toml delete mode 100644 aws/sdk/integration-tests/apigateway/tests/accept_header.rs diff --git a/aws/rust-runtime/aws-runtime/src/auth.rs b/aws/rust-runtime/aws-runtime/src/auth.rs index 1da617abdd..f436a5f028 100644 --- a/aws/rust-runtime/aws-runtime/src/auth.rs +++ b/aws/rust-runtime/aws-runtime/src/auth.rs @@ -11,12 +11,18 @@ pub mod sigv4 { SignableRequest, SignatureLocation, SigningParams, SigningSettings, UriPathNormalizationMode, }; - use aws_smithy_runtime_api::client::auth::{AuthSchemeId, HttpAuthScheme, HttpRequestSigner}; + use aws_smithy_runtime_api::client::auth::{ + AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, + }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpRequest}; use aws_smithy_runtime_api::config_bag::ConfigBag; - use aws_types::region::SigningRegion; + use aws_smithy_types::Document; + use aws_types::region::{Region, SigningRegion}; use aws_types::SigningService; + use std::borrow::Cow; + use std::error::Error as StdError; + use std::fmt; use std::time::{Duration, SystemTime}; const EXPIRATION_WARNING: &str = "Presigned request will expire before the given \ @@ -25,6 +31,53 @@ pub mod sigv4 { /// Auth scheme ID for SigV4. pub const SCHEME_ID: AuthSchemeId = AuthSchemeId::new("sigv4"); + struct EndpointAuthSchemeConfig { + signing_region_override: Option, + signing_service_override: Option, + } + + #[derive(Debug)] + enum SigV4SigningError { + MissingOperationSigningConfig, + MissingSigningRegion, + MissingSigningService, + WrongIdentityType(Identity), + BadTypeInEndpointAuthSchemeConfig(&'static str), + } + + impl fmt::Display for SigV4SigningError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use SigV4SigningError::*; + let mut w = |s| f.write_str(s); + match self { + MissingOperationSigningConfig => w("missing operation signing config for SigV4"), + MissingSigningRegion => w("missing signing region for SigV4 signing"), + MissingSigningService => w("missing signing service for SigV4 signing"), + WrongIdentityType(identity) => { + write!(f, "wrong identity type for SigV4: {identity:?}") + } + BadTypeInEndpointAuthSchemeConfig(field_name) => { + write!( + f, + "unexpected type for `{field_name}` in endpoint auth scheme config", + ) + } + } + } + } + + impl StdError for SigV4SigningError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Self::MissingOperationSigningConfig => None, + Self::MissingSigningRegion => None, + Self::MissingSigningService => None, + Self::WrongIdentityType(_) => None, + Self::BadTypeInEndpointAuthSchemeConfig(_) => None, + } + } + } + /// SigV4 auth scheme. #[derive(Debug, Default)] pub struct SigV4HttpAuthScheme { @@ -111,9 +164,9 @@ pub mod sigv4 { #[derive(Clone, Debug, PartialEq, Eq)] pub struct SigV4OperationSigningConfig { /// AWS Region to sign for. - pub region: SigningRegion, + pub region: Option, /// AWS Service to sign for. - pub service: SigningService, + pub service: Option, /// Signing options. pub signing_options: SigningOptions, } @@ -165,7 +218,7 @@ pub mod sigv4 { credentials: &'a Credentials, operation_config: &'a SigV4OperationSigningConfig, request_timestamp: SystemTime, - ) -> SigningParams<'a> { + ) -> Result, SigV4SigningError> { if let Some(expires_in) = settings.expires_in { if let Some(creds_expires_time) = credentials.expiry() { let presigned_expires_time = request_timestamp + expires_in; @@ -178,12 +231,75 @@ pub mod sigv4 { let mut builder = SigningParams::builder() .access_key(credentials.access_key_id()) .secret_key(credentials.secret_access_key()) - .region(operation_config.region.as_ref()) - .service_name(operation_config.service.as_ref()) + .region( + operation_config + .region + .as_ref() + .ok_or(SigV4SigningError::MissingSigningRegion)? + .as_ref(), + ) + .service_name( + operation_config + .service + .as_ref() + .ok_or(SigV4SigningError::MissingSigningService)? + .as_ref(), + ) .time(request_timestamp) .settings(settings); builder.set_security_token(credentials.session_token()); - builder.build().expect("all required fields set") + Ok(builder.build().expect("all required fields set")) + } + + fn extract_operation_config<'a>( + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'a>, + config_bag: &'a ConfigBag, + ) -> Result, SigV4SigningError> { + let operation_config = config_bag + .get::() + .ok_or(SigV4SigningError::MissingOperationSigningConfig)?; + + let EndpointAuthSchemeConfig { + signing_region_override, + signing_service_override, + } = Self::extract_endpoint_auth_scheme_config(auth_scheme_endpoint_config)?; + + match (signing_region_override, signing_service_override) { + (None, None) => Ok(Cow::Borrowed(operation_config)), + (region, service) => { + let mut operation_config = operation_config.clone(); + if region.is_some() { + operation_config.region = region; + } + if service.is_some() { + operation_config.service = service; + } + Ok(Cow::Owned(operation_config)) + } + } + } + + fn extract_endpoint_auth_scheme_config( + endpoint_config: AuthSchemeEndpointConfig<'_>, + ) -> Result { + let (mut signing_region_override, mut signing_service_override) = (None, None); + if let Some(config) = endpoint_config.config().and_then(Document::as_object) { + use SigV4SigningError::BadTypeInEndpointAuthSchemeConfig as UnexpectedType; + signing_region_override = match config.get("signingRegion") { + Some(Document::String(s)) => Some(SigningRegion::from(Region::new(s.clone()))), + None => None, + _ => return Err(UnexpectedType("signingRegion")), + }; + signing_service_override = match config.get("signingName") { + Some(Document::String(s)) => Some(SigningService::from(s.to_string())), + None => None, + _ => return Err(UnexpectedType("signingName")), + }; + } + Ok(EndpointAuthSchemeConfig { + signing_region_override, + signing_service_override, + }) } } @@ -192,11 +308,11 @@ pub mod sigv4 { &self, request: &mut HttpRequest, identity: &Identity, + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, config_bag: &ConfigBag, ) -> Result<(), BoxError> { - let operation_config = config_bag - .get::() - .ok_or("missing operation signing config for SigV4")?; + let operation_config = + Self::extract_operation_config(auth_scheme_endpoint_config, config_bag)?; let request_time = config_bag.request_time().unwrap_or_default().system_time(); let credentials = if let Some(creds) = identity.data::() { @@ -205,12 +321,12 @@ pub mod sigv4 { tracing::debug!("skipped SigV4 signing since signing is optional for this operation and there are no credentials"); return Ok(()); } else { - return Err(format!("wrong identity type for SigV4: {identity:?}").into()); + return Err(SigV4SigningError::WrongIdentityType(identity.clone()).into()); }; - let settings = Self::settings(operation_config); + let settings = Self::settings(&operation_config); let signing_params = - Self::signing_params(settings, credentials, operation_config, request_time); + Self::signing_params(settings, credentials, &operation_config, request_time)?; let (signing_instructions, _signature) = { // A body that is already in memory can be signed directly. A body that is not in memory @@ -250,6 +366,9 @@ pub mod sigv4 { use super::*; use aws_credential_types::Credentials; use aws_sigv4::http_request::SigningSettings; + use aws_types::region::SigningRegion; + use aws_types::SigningService; + use std::collections::HashMap; use std::time::{Duration, SystemTime}; use tracing_test::traced_test; @@ -270,8 +389,8 @@ pub mod sigv4 { "test", ); let operation_config = SigV4OperationSigningConfig { - region: SigningRegion::from_static("test"), - service: SigningService::from_static("test"), + region: Some(SigningRegion::from_static("test")), + service: Some(SigningService::from_static("test")), signing_options: SigningOptions { double_uri_encode: true, content_sha256_header: true, @@ -283,14 +402,74 @@ pub mod sigv4 { payload_override: None, }, }; - SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now); + SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now) + .unwrap(); assert!(!logs_contain(EXPIRATION_WARNING)); let mut settings = SigningSettings::default(); settings.expires_in = Some(creds_expire_in + Duration::from_secs(10)); - SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now); + SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now) + .unwrap(); assert!(logs_contain(EXPIRATION_WARNING)); } + + #[test] + fn endpoint_config_overrides_region_and_service() { + let mut cfg = ConfigBag::base(); + cfg.put(SigV4OperationSigningConfig { + region: Some(SigningRegion::from(Region::new("override-this-region"))), + service: Some(SigningService::from_static("override-this-service")), + signing_options: Default::default(), + }); + let config = Document::Object({ + let mut out = HashMap::new(); + out.insert("name".to_string(), "sigv4".to_string().into()); + out.insert( + "signingName".to_string(), + "qldb-override".to_string().into(), + ); + out.insert( + "signingRegion".to_string(), + "us-east-override".to_string().into(), + ); + out + }); + let config = AuthSchemeEndpointConfig::new(Some(&config)); + + let result = + SigV4HttpRequestSigner::extract_operation_config(config, &cfg).expect("success"); + + assert_eq!( + result.region, + Some(SigningRegion::from(Region::new("us-east-override"))) + ); + assert_eq!( + result.service, + Some(SigningService::from_static("qldb-override")) + ); + assert!(matches!(result, Cow::Owned(_))); + } + + #[test] + fn endpoint_config_supports_fallback_when_region_or_service_are_unset() { + let mut cfg = ConfigBag::base(); + cfg.put(SigV4OperationSigningConfig { + region: Some(SigningRegion::from(Region::new("us-east-1"))), + service: Some(SigningService::from_static("qldb")), + signing_options: Default::default(), + }); + let config = AuthSchemeEndpointConfig::empty(); + + let result = + SigV4HttpRequestSigner::extract_operation_config(config, &cfg).expect("success"); + + assert_eq!( + result.region, + Some(SigningRegion::from(Region::new("us-east-1"))) + ); + assert_eq!(result.service, Some(SigningService::from_static("qldb"))); + assert!(matches!(result, Cow::Borrowed(_))); + } } } diff --git a/aws/sdk-adhoc-test/build.gradle.kts b/aws/sdk-adhoc-test/build.gradle.kts index fbd40ae1ca..3a631a5bb2 100644 --- a/aws/sdk-adhoc-test/build.gradle.kts +++ b/aws/sdk-adhoc-test/build.gradle.kts @@ -37,6 +37,8 @@ dependencies { implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") } +fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "middleware" + val allCodegenTests = listOf( CodegenTest( "com.amazonaws.apigateway#BackplaneControlService", @@ -46,6 +48,7 @@ val allCodegenTests = listOf( , "codegen": { "includeFluentClient": false, + "enableNewSmithyRuntime": "${getSmithyRuntimeMode()}" }, "customizationConfig": { "awsSdk": { @@ -62,6 +65,7 @@ val allCodegenTests = listOf( , "codegen": { "includeFluentClient": false, + "enableNewSmithyRuntime": "${getSmithyRuntimeMode()}" }, "customizationConfig": { "awsSdk": { diff --git a/aws/sdk-adhoc-test/models/apigateway-rules.smithy b/aws/sdk-adhoc-test/models/apigateway-rules.smithy index fa609c9860..3e5bb310d3 100644 --- a/aws/sdk-adhoc-test/models/apigateway-rules.smithy +++ b/aws/sdk-adhoc-test/models/apigateway-rules.smithy @@ -1,8 +1,16 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +// The API Gateway model is coming from Smithy's protocol tests, and includes an `Accept` header test: +// https://github.com/awslabs/smithy/blob/2f6553ff39e6bba9edc644ef5832661821785319/smithy-aws-protocol-tests/model/restJson1/services/apigateway.smithy#L30-L43 + $version: "1.0" namespace com.amazonaws.apigateway use smithy.rules#endpointRuleSet + +// Add an endpoint ruleset to the Smithy protocol test API Gateway model so that the code generator doesn't fail apply BackplaneControlService @endpointRuleSet({ "version": "1.0", "rules": [{ diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index d0639dbb81..b0963eaea9 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -14,6 +14,8 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.client.Fluen import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerics import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientSection import software.amazon.smithy.rust.codegen.client.smithy.generators.client.NoClientGenerics +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.DefaultProtocolTestGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.rustlang.GenericTypeArg @@ -96,6 +98,29 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { } } } + + override fun protocolTestGenerator( + codegenContext: ClientCodegenContext, + baseGenerator: ProtocolTestGenerator, + ): ProtocolTestGenerator = DefaultProtocolTestGenerator( + codegenContext, + baseGenerator.protocolSupport, + baseGenerator.operationShape, + renderClientCreation = { params -> + rustTemplate( + """ + // If the test case was missing endpoint parameters, default a region so it doesn't fail + let mut ${params.configBuilderName} = ${params.configBuilderName}; + if ${params.configBuilderName}.region.is_none() { + ${params.configBuilderName}.set_region(Some(crate::config::Region::new("us-east-1"))); + } + let config = ${params.configBuilderName}.http_connector(${params.connectorName}).build(); + let ${params.clientName} = #{Client}::from_conf(config); + """, + "Client" to ClientRustModule.root.toType().resolve("Client"), + ) + }, + ) } private class AwsFluentClientExtensions(types: Types) { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt index bf116c9269..af0953bdcd 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt @@ -171,7 +171,7 @@ class RegionProviderConfig(codegenContext: CodegenContext) : ConfigCustomization ) ServiceConfig.BuilderStruct -> - rustTemplate("region: Option<#{Region}>,", *codegenScope) + rustTemplate("pub(crate) region: Option<#{Region}>,", *codegenScope) ServiceConfig.BuilderImpl -> rustTemplate( @@ -191,6 +191,12 @@ class RegionProviderConfig(codegenContext: CodegenContext) : ConfigCustomization self.region = region.into(); self } + + /// Sets the AWS region to use when making requests. + pub fn set_region(&mut self, region: Option<#{Region}>) -> &mut Self { + self.region = region; + self + } """, *codegenScope, ) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt index ad95b54e2b..78b288c92a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt @@ -73,6 +73,7 @@ class OperationRetryClassifiersFeature( "ClassifyRetry" to smithyRuntimeApi.resolve("client::retries::ClassifyRetry"), "RetryClassifiers" to smithyRuntimeApi.resolve("client::retries::RetryClassifiers"), "OperationError" to codegenContext.symbolProvider.symbolForOperationError(operationShape), + "OrchestratorError" to smithyRuntimeApi.resolve("client::orchestrator::OrchestratorError"), "SdkError" to RuntimeType.smithyHttp(runtimeConfig).resolve("result::SdkError"), "ErasedError" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypeErasedError"), ) @@ -89,9 +90,9 @@ class OperationRetryClassifiersFeature( } } impl #{ClassifyRetry} for HttpStatusCodeClassifier { - fn classify_retry(&self, error: &#{ErasedError}) -> Option<#{RetryReason}> { - let error = error.downcast_ref::<#{SdkError}<#{OperationError}>>().expect("The error type is always known"); - self.0.classify_error(error) + fn classify_retry(&self, _error: &#{OrchestratorError}<#{ErasedError}>) -> Option<#{RetryReason}> { + // TODO(enableNewSmithyRuntime): classify the error with self.0 + None } } """, @@ -108,9 +109,9 @@ class OperationRetryClassifiersFeature( } } impl #{ClassifyRetry} for AwsErrorCodeClassifier { - fn classify_retry(&self, error: &#{ErasedError}) -> Option<#{RetryReason}> { - let error = error.downcast_ref::<#{SdkError}<#{OperationError}>>().expect("The error type is always known"); - self.0.classify_error(error) + fn classify_retry(&self, _error: &#{OrchestratorError}<#{ErasedError}>) -> Option<#{RetryReason}> { + // TODO(enableNewSmithyRuntime): classify the error with self.0 + None } } """, @@ -127,9 +128,9 @@ class OperationRetryClassifiersFeature( } } impl #{ClassifyRetry} for ModeledAsRetryableClassifier { - fn classify_retry(&self, error: &#{ErasedError}) -> Option<#{RetryReason}> { - let error = error.downcast_ref::<#{SdkError}<#{OperationError}>>().expect("The error type is always known"); - self.0.classify_error(error) + fn classify_retry(&self, _error: &#{OrchestratorError}<#{ErasedError}>) -> Option<#{RetryReason}> { + // TODO(enableNewSmithyRuntime): classify the error with self.0 + None } } """, @@ -146,9 +147,9 @@ class OperationRetryClassifiersFeature( } } impl #{ClassifyRetry} for AmzRetryAfterHeaderClassifier { - fn classify_retry(&self, error: &#{ErasedError}) -> Option<#{RetryReason}> { - let error = error.downcast_ref::<#{SdkError}<#{OperationError}>>().expect("The error type is always known"); - self.0.classify_error(error) + fn classify_retry(&self, _error: &#{OrchestratorError}<#{ErasedError}>) -> Option<#{RetryReason}> { + // TODO(enableNewSmithyRuntime): classify the error with self.0 + None } } """, @@ -165,9 +166,9 @@ class OperationRetryClassifiersFeature( } } impl #{ClassifyRetry} for SmithyErrorClassifier { - fn classify_retry(&self, error: &#{ErasedError}) -> Option<#{RetryReason}> { - let error = error.downcast_ref::<#{SdkError}<#{OperationError}>>().expect("The error type is always known"); - self.0.classify_error(error) + fn classify_retry(&self, _error: &#{OrchestratorError}<#{ErasedError}>) -> Option<#{RetryReason}> { + // TODO(enableNewSmithyRuntime): classify the error with self.0 + None } } """, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 6131e50c79..3ca470fd0e 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -126,8 +126,8 @@ private class AuthOperationRuntimePluginCustomization(private val codegenContext val signingOptional = section.operationShape.hasTrait() rustTemplate( """ - let signing_region = cfg.get::<#{SigningRegion}>().expect("region required for signing").clone(); - let signing_service = cfg.get::<#{SigningService}>().expect("service required for signing").clone(); + let signing_region = cfg.get::<#{SigningRegion}>().cloned(); + let signing_service = cfg.get::<#{SigningService}>().cloned(); let mut signing_options = #{SigningOptions}::default(); signing_options.double_uri_encode = $doubleUriEncode; signing_options.content_sha256_header = $contentSha256Header; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index 921990fbd6..f540152a25 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustom import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization @@ -138,4 +139,11 @@ class ServiceSpecificDecorator( ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { delegateTo.serviceRuntimePluginCustomizations(codegenContext, baseCustomizations) } + + override fun protocolTestGenerator( + codegenContext: ClientCodegenContext, + baseGenerator: ProtocolTestGenerator, + ): ProtocolTestGenerator = baseGenerator.maybeApply(codegenContext.serviceShape) { + delegateTo.protocolTestGenerator(codegenContext, baseGenerator) + } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt index 9d620e11cb..698e732623 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt @@ -14,7 +14,7 @@ import software.amazon.smithy.rulesengine.traits.EndpointTestOperationInput import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointTypesGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.clientInstantiator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientInstantiator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.AttributeKind import software.amazon.smithy.rust.codegen.core.rustlang.escape @@ -120,7 +120,7 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test: private val moduleName = ctx.moduleUseName() private val endpointCustomizations = ctx.rootDecorator.endpointCustomizations(ctx) private val model = ctx.model - private val instantiator = clientInstantiator(ctx) + private val instantiator = ClientInstantiator(ctx) private fun EndpointTestOperationInput.operationId() = ShapeId.fromOptionalNamespace(ctx.serviceShape.id.namespace, operationName) diff --git a/aws/sdk/aws-models/apigateway.json b/aws/sdk/aws-models/apigateway.json deleted file mode 100644 index dbd9d381df..0000000000 --- a/aws/sdk/aws-models/apigateway.json +++ /dev/null @@ -1,12865 +0,0 @@ -{ - "smithy": "2.0", - "metadata": { - "suppressions": [ - { - "id": "HttpMethodSemantics", - "namespace": "*" - }, - { - "id": "HttpResponseCodeSemantics", - "namespace": "*" - }, - { - "id": "PaginatedTrait", - "namespace": "*" - }, - { - "id": "HttpHeaderTrait", - "namespace": "*" - }, - { - "id": "HttpUriConflict", - "namespace": "*" - }, - { - "id": "Service", - "namespace": "*" - } - ] - }, - "shapes": { - "com.amazonaws.apigateway#AccessLogSettings": { - "type": "structure", - "members": { - "format": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A single line format of the access logs of data, as specified by selected $context variables. The format must include at least $context.requestId.

" - } - }, - "destinationArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the CloudWatch Logs log group or Kinesis Data Firehose delivery stream to receive access logs. If you specify a Kinesis Data Firehose delivery stream, the stream name must begin with amazon-apigateway-.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Access log settings, including the access log format and access log destination ARN.

" - } - }, - "com.amazonaws.apigateway#Account": { - "type": "structure", - "members": { - "cloudwatchRoleArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ARN of an Amazon CloudWatch role for the current Account.

" - } - }, - "throttleSettings": { - "target": "com.amazonaws.apigateway#ThrottleSettings", - "traits": { - "smithy.api#documentation": "

Specifies the API request limits configured for the current Account.

" - } - }, - "features": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of features supported for the account. When usage plans are enabled, the features list will include an entry of \"UsagePlans\".

" - } - }, - "apiKeyVersion": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The version of the API keys used for the account.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents an AWS account that is associated with API Gateway.

" - } - }, - "com.amazonaws.apigateway#ApiKey": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the API Key.

" - } - }, - "value": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The value of the API Key.

" - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the API Key.

" - } - }, - "customerId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

An AWS Marketplace customer identifier , when integrating with the AWS SaaS Marketplace.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the API Key.

" - } - }, - "enabled": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether the API Key can be used by callers.

" - } - }, - "createdDate": { - "target": "com.amazonaws.apigateway#Timestamp", - "traits": { - "smithy.api#documentation": "

The timestamp when the API Key was created.

" - } - }, - "lastUpdatedDate": { - "target": "com.amazonaws.apigateway#Timestamp", - "traits": { - "smithy.api#documentation": "

The timestamp when the API Key was last updated.

" - } - }, - "stageKeys": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of Stage resources that are associated with the ApiKey resource.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A resource that can be distributed to callers for executing Method resources that require an API key. API keys can be mapped to any Stage on any RestApi, which indicates that the callers with the API key can make requests to that stage.

" - } - }, - "com.amazonaws.apigateway#ApiKeyIds": { - "type": "structure", - "members": { - "ids": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of all the ApiKey identifiers.

" - } - }, - "warnings": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of warning messages.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

The identifier of an ApiKey used in a UsagePlan.

" - } - }, - "com.amazonaws.apigateway#ApiKeySourceType": { - "type": "enum", - "members": { - "HEADER": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "HEADER" - } - }, - "AUTHORIZER": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "AUTHORIZER" - } - } - } - }, - "com.amazonaws.apigateway#ApiKeys": { - "type": "structure", - "members": { - "warnings": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of warning messages logged during the import of API keys when the failOnWarnings option is set to true.

" - } - }, - "items": { - "target": "com.amazonaws.apigateway#ListOfApiKey", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a collection of API keys as represented by an ApiKeys resource.

" - } - }, - "com.amazonaws.apigateway#ApiKeysFormat": { - "type": "enum", - "members": { - "csv": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "csv" - } - } - } - }, - "com.amazonaws.apigateway#ApiStage": { - "type": "structure", - "members": { - "apiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

API Id of the associated API stage in a usage plan.

" - } - }, - "stage": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

API stage name of the associated API stage in a usage plan.

" - } - }, - "throttle": { - "target": "com.amazonaws.apigateway#MapOfApiStageThrottleSettings", - "traits": { - "smithy.api#documentation": "

Map containing method level throttling information for API stage in a usage plan.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

API stage name of the associated API stage in a usage plan.

" - } - }, - "com.amazonaws.apigateway#Authorizer": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier for the authorizer resource.

" - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the authorizer.

" - } - }, - "type": { - "target": "com.amazonaws.apigateway#AuthorizerType", - "traits": { - "smithy.api#documentation": "

The authorizer type. Valid values are TOKEN for a Lambda function using a single authorization token submitted in a custom header, REQUEST for a Lambda function using incoming request parameters, and COGNITO_USER_POOLS for using an Amazon Cognito user pool.

" - } - }, - "providerARNs": { - "target": "com.amazonaws.apigateway#ListOfARNs", - "traits": { - "smithy.api#documentation": "

A list of the Amazon Cognito user pool ARNs for the COGNITO_USER_POOLS authorizer. Each element is of this format: arn:aws:cognito-idp:{region}:{account_id}:userpool/{user_pool_id}. For a TOKEN or REQUEST authorizer, this is not defined.

" - } - }, - "authType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Optional customer-defined field, used in OpenAPI imports and exports without functional impact.

" - } - }, - "authorizerUri": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the authorizer's Uniform Resource Identifier (URI). For TOKEN or REQUEST authorizers, this must be a well-formed Lambda function URI, for example, arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:{account_id}:function:{lambda_function_name}/invocations. In general, the URI has this form arn:aws:apigateway:{region}:lambda:path/{service_api}, where {region} is the same as the region hosting the Lambda function, path indicates that the remaining substring in the URI should be treated as the path to the resource, including the initial /. For Lambda functions, this is usually of the form /2015-03-31/functions/[FunctionARN]/invocations.

" - } - }, - "authorizerCredentials": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the required credentials as an IAM role for API Gateway to invoke the authorizer. To specify an IAM role for API Gateway to assume, use the role's Amazon Resource Name (ARN). To use resource-based permissions on the Lambda function, specify null.

" - } - }, - "identitySource": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identity source for which authorization is requested. For a TOKEN or\n COGNITO_USER_POOLS authorizer, this is required and specifies the request\n header mapping expression for the custom header holding the authorization token submitted by\n the client. For example, if the token header name is Auth, the header mapping expression is\n method.request.header.Auth. For the REQUEST authorizer, this is required when authorization\n caching is enabled. The value is a comma-separated string of one or more mapping expressions\n of the specified request parameters. For example, if an Auth header, a Name query string\n parameter are defined as identity sources, this value is method.request.header.Auth,\n method.request.querystring.Name. These parameters will be used to derive the authorization\n caching key and to perform runtime validation of the REQUEST authorizer by verifying all of\n the identity-related request parameters are present, not null and non-empty. Only when this is\n true does the authorizer invoke the authorizer Lambda function, otherwise, it returns a 401\n Unauthorized response without calling the Lambda function. The valid value is a string of\n comma-separated mapping expressions of the specified request parameters. When the\n authorization caching is not enabled, this property is optional.

" - } - }, - "identityValidationExpression": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A validation expression for the incoming identity token. For TOKEN authorizers, this value is a regular expression. For COGNITO_USER_POOLS authorizers, API Gateway will match the aud field of the incoming token from the client against the specified regular expression. It will invoke the authorizer's Lambda function when there is a match. Otherwise, it will return a 401 Unauthorized response without calling the Lambda function. The validation expression does not apply to the REQUEST authorizer.

" - } - }, - "authorizerResultTtlInSeconds": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The TTL in seconds of cached authorizer results. If it equals 0, authorization caching is disabled. If it is greater than 0, API Gateway will cache authorizer responses. If this field is not set, the default value is 300. The maximum value is 3600, or 1 hour.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents an authorization layer for methods. If enabled on a method, API Gateway will activate the authorizer when a client calls the method.

" - } - }, - "com.amazonaws.apigateway#AuthorizerType": { - "type": "enum", - "members": { - "TOKEN": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "TOKEN" - } - }, - "REQUEST": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "REQUEST" - } - }, - "COGNITO_USER_POOLS": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "COGNITO_USER_POOLS" - } - } - }, - "traits": { - "smithy.api#documentation": "

The authorizer type. Valid values are TOKEN for a Lambda function using a single authorization token submitted in a custom header, REQUEST for a Lambda function using incoming request parameters, and COGNITO_USER_POOLS for using an Amazon Cognito user pool.

" - } - }, - "com.amazonaws.apigateway#Authorizers": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfAuthorizer", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a collection of Authorizer resources.

" - } - }, - "com.amazonaws.apigateway#BackplaneControlService": { - "type": "service", - "version": "2015-07-09", - "operations": [ - { - "target": "com.amazonaws.apigateway#CreateApiKey" - }, - { - "target": "com.amazonaws.apigateway#CreateAuthorizer" - }, - { - "target": "com.amazonaws.apigateway#CreateBasePathMapping" - }, - { - "target": "com.amazonaws.apigateway#CreateDeployment" - }, - { - "target": "com.amazonaws.apigateway#CreateDocumentationPart" - }, - { - "target": "com.amazonaws.apigateway#CreateDocumentationVersion" - }, - { - "target": "com.amazonaws.apigateway#CreateDomainName" - }, - { - "target": "com.amazonaws.apigateway#CreateModel" - }, - { - "target": "com.amazonaws.apigateway#CreateRequestValidator" - }, - { - "target": "com.amazonaws.apigateway#CreateResource" - }, - { - "target": "com.amazonaws.apigateway#CreateRestApi" - }, - { - "target": "com.amazonaws.apigateway#CreateStage" - }, - { - "target": "com.amazonaws.apigateway#CreateUsagePlan" - }, - { - "target": "com.amazonaws.apigateway#CreateUsagePlanKey" - }, - { - "target": "com.amazonaws.apigateway#CreateVpcLink" - }, - { - "target": "com.amazonaws.apigateway#DeleteApiKey" - }, - { - "target": "com.amazonaws.apigateway#DeleteAuthorizer" - }, - { - "target": "com.amazonaws.apigateway#DeleteBasePathMapping" - }, - { - "target": "com.amazonaws.apigateway#DeleteClientCertificate" - }, - { - "target": "com.amazonaws.apigateway#DeleteDeployment" - }, - { - "target": "com.amazonaws.apigateway#DeleteDocumentationPart" - }, - { - "target": "com.amazonaws.apigateway#DeleteDocumentationVersion" - }, - { - "target": "com.amazonaws.apigateway#DeleteDomainName" - }, - { - "target": "com.amazonaws.apigateway#DeleteGatewayResponse" - }, - { - "target": "com.amazonaws.apigateway#DeleteIntegration" - }, - { - "target": "com.amazonaws.apigateway#DeleteIntegrationResponse" - }, - { - "target": "com.amazonaws.apigateway#DeleteMethod" - }, - { - "target": "com.amazonaws.apigateway#DeleteMethodResponse" - }, - { - "target": "com.amazonaws.apigateway#DeleteModel" - }, - { - "target": "com.amazonaws.apigateway#DeleteRequestValidator" - }, - { - "target": "com.amazonaws.apigateway#DeleteResource" - }, - { - "target": "com.amazonaws.apigateway#DeleteRestApi" - }, - { - "target": "com.amazonaws.apigateway#DeleteStage" - }, - { - "target": "com.amazonaws.apigateway#DeleteUsagePlan" - }, - { - "target": "com.amazonaws.apigateway#DeleteUsagePlanKey" - }, - { - "target": "com.amazonaws.apigateway#DeleteVpcLink" - }, - { - "target": "com.amazonaws.apigateway#FlushStageAuthorizersCache" - }, - { - "target": "com.amazonaws.apigateway#FlushStageCache" - }, - { - "target": "com.amazonaws.apigateway#GenerateClientCertificate" - }, - { - "target": "com.amazonaws.apigateway#GetAccount" - }, - { - "target": "com.amazonaws.apigateway#GetApiKey" - }, - { - "target": "com.amazonaws.apigateway#GetApiKeys" - }, - { - "target": "com.amazonaws.apigateway#GetAuthorizer" - }, - { - "target": "com.amazonaws.apigateway#GetAuthorizers" - }, - { - "target": "com.amazonaws.apigateway#GetBasePathMapping" - }, - { - "target": "com.amazonaws.apigateway#GetBasePathMappings" - }, - { - "target": "com.amazonaws.apigateway#GetClientCertificate" - }, - { - "target": "com.amazonaws.apigateway#GetClientCertificates" - }, - { - "target": "com.amazonaws.apigateway#GetDeployment" - }, - { - "target": "com.amazonaws.apigateway#GetDeployments" - }, - { - "target": "com.amazonaws.apigateway#GetDocumentationPart" - }, - { - "target": "com.amazonaws.apigateway#GetDocumentationParts" - }, - { - "target": "com.amazonaws.apigateway#GetDocumentationVersion" - }, - { - "target": "com.amazonaws.apigateway#GetDocumentationVersions" - }, - { - "target": "com.amazonaws.apigateway#GetDomainName" - }, - { - "target": "com.amazonaws.apigateway#GetDomainNames" - }, - { - "target": "com.amazonaws.apigateway#GetExport" - }, - { - "target": "com.amazonaws.apigateway#GetGatewayResponse" - }, - { - "target": "com.amazonaws.apigateway#GetGatewayResponses" - }, - { - "target": "com.amazonaws.apigateway#GetIntegration" - }, - { - "target": "com.amazonaws.apigateway#GetIntegrationResponse" - }, - { - "target": "com.amazonaws.apigateway#GetMethod" - }, - { - "target": "com.amazonaws.apigateway#GetMethodResponse" - }, - { - "target": "com.amazonaws.apigateway#GetModel" - }, - { - "target": "com.amazonaws.apigateway#GetModels" - }, - { - "target": "com.amazonaws.apigateway#GetModelTemplate" - }, - { - "target": "com.amazonaws.apigateway#GetRequestValidator" - }, - { - "target": "com.amazonaws.apigateway#GetRequestValidators" - }, - { - "target": "com.amazonaws.apigateway#GetResource" - }, - { - "target": "com.amazonaws.apigateway#GetResources" - }, - { - "target": "com.amazonaws.apigateway#GetRestApi" - }, - { - "target": "com.amazonaws.apigateway#GetRestApis" - }, - { - "target": "com.amazonaws.apigateway#GetSdk" - }, - { - "target": "com.amazonaws.apigateway#GetSdkType" - }, - { - "target": "com.amazonaws.apigateway#GetSdkTypes" - }, - { - "target": "com.amazonaws.apigateway#GetStage" - }, - { - "target": "com.amazonaws.apigateway#GetStages" - }, - { - "target": "com.amazonaws.apigateway#GetTags" - }, - { - "target": "com.amazonaws.apigateway#GetUsage" - }, - { - "target": "com.amazonaws.apigateway#GetUsagePlan" - }, - { - "target": "com.amazonaws.apigateway#GetUsagePlanKey" - }, - { - "target": "com.amazonaws.apigateway#GetUsagePlanKeys" - }, - { - "target": "com.amazonaws.apigateway#GetUsagePlans" - }, - { - "target": "com.amazonaws.apigateway#GetVpcLink" - }, - { - "target": "com.amazonaws.apigateway#GetVpcLinks" - }, - { - "target": "com.amazonaws.apigateway#ImportApiKeys" - }, - { - "target": "com.amazonaws.apigateway#ImportDocumentationParts" - }, - { - "target": "com.amazonaws.apigateway#ImportRestApi" - }, - { - "target": "com.amazonaws.apigateway#PutGatewayResponse" - }, - { - "target": "com.amazonaws.apigateway#PutIntegration" - }, - { - "target": "com.amazonaws.apigateway#PutIntegrationResponse" - }, - { - "target": "com.amazonaws.apigateway#PutMethod" - }, - { - "target": "com.amazonaws.apigateway#PutMethodResponse" - }, - { - "target": "com.amazonaws.apigateway#PutRestApi" - }, - { - "target": "com.amazonaws.apigateway#TagResource" - }, - { - "target": "com.amazonaws.apigateway#TestInvokeAuthorizer" - }, - { - "target": "com.amazonaws.apigateway#TestInvokeMethod" - }, - { - "target": "com.amazonaws.apigateway#UntagResource" - }, - { - "target": "com.amazonaws.apigateway#UpdateAccount" - }, - { - "target": "com.amazonaws.apigateway#UpdateApiKey" - }, - { - "target": "com.amazonaws.apigateway#UpdateAuthorizer" - }, - { - "target": "com.amazonaws.apigateway#UpdateBasePathMapping" - }, - { - "target": "com.amazonaws.apigateway#UpdateClientCertificate" - }, - { - "target": "com.amazonaws.apigateway#UpdateDeployment" - }, - { - "target": "com.amazonaws.apigateway#UpdateDocumentationPart" - }, - { - "target": "com.amazonaws.apigateway#UpdateDocumentationVersion" - }, - { - "target": "com.amazonaws.apigateway#UpdateDomainName" - }, - { - "target": "com.amazonaws.apigateway#UpdateGatewayResponse" - }, - { - "target": "com.amazonaws.apigateway#UpdateIntegration" - }, - { - "target": "com.amazonaws.apigateway#UpdateIntegrationResponse" - }, - { - "target": "com.amazonaws.apigateway#UpdateMethod" - }, - { - "target": "com.amazonaws.apigateway#UpdateMethodResponse" - }, - { - "target": "com.amazonaws.apigateway#UpdateModel" - }, - { - "target": "com.amazonaws.apigateway#UpdateRequestValidator" - }, - { - "target": "com.amazonaws.apigateway#UpdateResource" - }, - { - "target": "com.amazonaws.apigateway#UpdateRestApi" - }, - { - "target": "com.amazonaws.apigateway#UpdateStage" - }, - { - "target": "com.amazonaws.apigateway#UpdateUsage" - }, - { - "target": "com.amazonaws.apigateway#UpdateUsagePlan" - }, - { - "target": "com.amazonaws.apigateway#UpdateVpcLink" - } - ], - "traits": { - "aws.api#service": { - "sdkId": "API Gateway", - "arnNamespace": "apigateway", - "cloudFormationName": "ApiGateway", - "cloudTrailEventSource": "apigateway.amazonaws.com", - "endpointPrefix": "apigateway" - }, - "aws.auth#sigv4": { - "name": "apigateway" - }, - "aws.protocols#restJson1": {}, - "smithy.api#documentation": "Amazon API Gateway\n

Amazon API Gateway helps developers deliver robust, secure, and scalable mobile and web application back ends. API Gateway allows developers to securely connect mobile and web applications to APIs that run on AWS Lambda, Amazon EC2, or other publicly addressable web services that are hosted outside of AWS.

", - "smithy.api#title": "Amazon API Gateway", - "smithy.rules#endpointRuleSet": { - "version": "1.0", - "parameters": { - "Region": { - "builtIn": "AWS::Region", - "required": false, - "documentation": "The AWS region used to dispatch the request.", - "type": "String" - }, - "UseDualStack": { - "builtIn": "AWS::UseDualStack", - "required": true, - "default": false, - "documentation": "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.", - "type": "Boolean" - }, - "UseFIPS": { - "builtIn": "AWS::UseFIPS", - "required": true, - "default": false, - "documentation": "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.", - "type": "Boolean" - }, - "Endpoint": { - "builtIn": "SDK::Endpoint", - "required": false, - "documentation": "Override the endpoint used to send this request", - "type": "String" - } - }, - "rules": [ - { - "conditions": [ - { - "fn": "isSet", - "argv": [ - { - "ref": "Endpoint" - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "error": "Invalid Configuration: FIPS and custom endpoint are not supported", - "type": "error" - }, - { - "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", - "type": "error" - }, - { - "conditions": [], - "endpoint": { - "url": { - "ref": "Endpoint" - }, - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - } - ] - }, - { - "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "isSet", - "argv": [ - { - "ref": "Region" - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "aws.partition", - "argv": [ - { - "ref": "Region" - } - ], - "assign": "PartitionResult" - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - }, - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - }, - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://apigateway-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - } - ] - }, - { - "conditions": [], - "error": "FIPS and DualStack are enabled, but this partition does not support one or both", - "type": "error" - } - ] - }, - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseFIPS" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsFIPS" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://apigateway-fips.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - } - ] - }, - { - "conditions": [], - "error": "FIPS is enabled but this partition does not support FIPS", - "type": "error" - } - ] - }, - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - { - "ref": "UseDualStack" - }, - true - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [ - { - "fn": "booleanEquals", - "argv": [ - true, - { - "fn": "getAttr", - "argv": [ - { - "ref": "PartitionResult" - }, - "supportsDualStack" - ] - } - ] - } - ], - "type": "tree", - "rules": [ - { - "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://apigateway.{Region}.{PartitionResult#dualStackDnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - } - ] - }, - { - "conditions": [], - "error": "DualStack is enabled but this partition does not support DualStack", - "type": "error" - } - ] - }, - { - "conditions": [], - "type": "tree", - "rules": [ - { - "conditions": [], - "endpoint": { - "url": "https://apigateway.{Region}.{PartitionResult#dnsSuffix}", - "properties": {}, - "headers": {} - }, - "type": "endpoint" - } - ] - } - ] - } - ] - }, - { - "conditions": [], - "error": "Invalid Configuration: Missing Region", - "type": "error" - } - ] - } - ] - }, - "smithy.rules#endpointTests": { - "testCases": [ - { - "documentation": "For region af-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.af-south-1.amazonaws.com" - } - }, - "params": { - "Region": "af-south-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region ap-east-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.ap-east-1.amazonaws.com" - } - }, - "params": { - "Region": "ap-east-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region ap-northeast-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.ap-northeast-1.amazonaws.com" - } - }, - "params": { - "Region": "ap-northeast-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region ap-northeast-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.ap-northeast-2.amazonaws.com" - } - }, - "params": { - "Region": "ap-northeast-2", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region ap-northeast-3 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.ap-northeast-3.amazonaws.com" - } - }, - "params": { - "Region": "ap-northeast-3", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region ap-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.ap-south-1.amazonaws.com" - } - }, - "params": { - "Region": "ap-south-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region ap-southeast-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.ap-southeast-1.amazonaws.com" - } - }, - "params": { - "Region": "ap-southeast-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region ap-southeast-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.ap-southeast-2.amazonaws.com" - } - }, - "params": { - "Region": "ap-southeast-2", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region ca-central-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.ca-central-1.amazonaws.com" - } - }, - "params": { - "Region": "ca-central-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region eu-central-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.eu-central-1.amazonaws.com" - } - }, - "params": { - "Region": "eu-central-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region eu-north-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.eu-north-1.amazonaws.com" - } - }, - "params": { - "Region": "eu-north-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region eu-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.eu-south-1.amazonaws.com" - } - }, - "params": { - "Region": "eu-south-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region eu-west-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.eu-west-1.amazonaws.com" - } - }, - "params": { - "Region": "eu-west-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region eu-west-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.eu-west-2.amazonaws.com" - } - }, - "params": { - "Region": "eu-west-2", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region eu-west-3 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.eu-west-3.amazonaws.com" - } - }, - "params": { - "Region": "eu-west-3", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region me-south-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.me-south-1.amazonaws.com" - } - }, - "params": { - "Region": "me-south-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region sa-east-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.sa-east-1.amazonaws.com" - } - }, - "params": { - "Region": "sa-east-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.us-east-1.amazonaws.com" - } - }, - "params": { - "Region": "us-east-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region us-east-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.us-east-2.amazonaws.com" - } - }, - "params": { - "Region": "us-east-2", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region us-west-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.us-west-1.amazonaws.com" - } - }, - "params": { - "Region": "us-west-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region us-west-2 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.us-west-2.amazonaws.com" - } - }, - "params": { - "Region": "us-west-2", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://apigateway-fips.us-east-1.api.aws" - } - }, - "params": { - "Region": "us-east-1", - "UseFIPS": true, - "UseDualStack": true - } - }, - { - "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway-fips.us-east-1.amazonaws.com" - } - }, - "params": { - "Region": "us-east-1", - "UseFIPS": true, - "UseDualStack": false - } - }, - { - "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://apigateway.us-east-1.api.aws" - } - }, - "params": { - "Region": "us-east-1", - "UseFIPS": false, - "UseDualStack": true - } - }, - { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.cn-north-1.amazonaws.com.cn" - } - }, - "params": { - "Region": "cn-north-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region cn-northwest-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.cn-northwest-1.amazonaws.com.cn" - } - }, - "params": { - "Region": "cn-northwest-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://apigateway-fips.cn-north-1.api.amazonwebservices.com.cn" - } - }, - "params": { - "Region": "cn-north-1", - "UseFIPS": true, - "UseDualStack": true - } - }, - { - "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway-fips.cn-north-1.amazonaws.com.cn" - } - }, - "params": { - "Region": "cn-north-1", - "UseFIPS": true, - "UseDualStack": false - } - }, - { - "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://apigateway.cn-north-1.api.amazonwebservices.com.cn" - } - }, - "params": { - "Region": "cn-north-1", - "UseFIPS": false, - "UseDualStack": true - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.us-gov-east-1.amazonaws.com" - } - }, - "params": { - "Region": "us-gov-east-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region us-gov-west-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.us-gov-west-1.amazonaws.com" - } - }, - "params": { - "Region": "us-gov-west-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://apigateway-fips.us-gov-east-1.api.aws" - } - }, - "params": { - "Region": "us-gov-east-1", - "UseFIPS": true, - "UseDualStack": true - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway-fips.us-gov-east-1.amazonaws.com" - } - }, - "params": { - "Region": "us-gov-east-1", - "UseFIPS": true, - "UseDualStack": false - } - }, - { - "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", - "expect": { - "endpoint": { - "url": "https://apigateway.us-gov-east-1.api.aws" - } - }, - "params": { - "Region": "us-gov-east-1", - "UseFIPS": false, - "UseDualStack": true - } - }, - { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.us-iso-east-1.c2s.ic.gov" - } - }, - "params": { - "Region": "us-iso-east-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", - "expect": { - "error": "FIPS and DualStack are enabled, but this partition does not support one or both" - }, - "params": { - "Region": "us-iso-east-1", - "UseFIPS": true, - "UseDualStack": true - } - }, - { - "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway-fips.us-iso-east-1.c2s.ic.gov" - } - }, - "params": { - "Region": "us-iso-east-1", - "UseFIPS": true, - "UseDualStack": false - } - }, - { - "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", - "expect": { - "error": "DualStack is enabled but this partition does not support DualStack" - }, - "params": { - "Region": "us-iso-east-1", - "UseFIPS": false, - "UseDualStack": true - } - }, - { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", - "expect": { - "error": "FIPS and DualStack are enabled, but this partition does not support one or both" - }, - "params": { - "Region": "us-isob-east-1", - "UseFIPS": true, - "UseDualStack": true - } - }, - { - "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway-fips.us-isob-east-1.sc2s.sgov.gov" - } - }, - "params": { - "Region": "us-isob-east-1", - "UseFIPS": true, - "UseDualStack": false - } - }, - { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", - "expect": { - "error": "DualStack is enabled but this partition does not support DualStack" - }, - "params": { - "Region": "us-isob-east-1", - "UseFIPS": false, - "UseDualStack": true - } - }, - { - "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", - "expect": { - "endpoint": { - "url": "https://apigateway.us-isob-east-1.sc2s.sgov.gov" - } - }, - "params": { - "Region": "us-isob-east-1", - "UseFIPS": false, - "UseDualStack": false - } - }, - { - "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", - "expect": { - "endpoint": { - "url": "https://example.com" - } - }, - "params": { - "Region": "us-east-1", - "UseFIPS": false, - "UseDualStack": false, - "Endpoint": "https://example.com" - } - }, - { - "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", - "expect": { - "endpoint": { - "url": "https://example.com" - } - }, - "params": { - "UseFIPS": false, - "UseDualStack": false, - "Endpoint": "https://example.com" - } - }, - { - "documentation": "For custom endpoint with fips enabled and dualstack disabled", - "expect": { - "error": "Invalid Configuration: FIPS and custom endpoint are not supported" - }, - "params": { - "Region": "us-east-1", - "UseFIPS": true, - "UseDualStack": false, - "Endpoint": "https://example.com" - } - }, - { - "documentation": "For custom endpoint with fips disabled and dualstack enabled", - "expect": { - "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" - }, - "params": { - "Region": "us-east-1", - "UseFIPS": false, - "UseDualStack": true, - "Endpoint": "https://example.com" - } - }, - { - "documentation": "Missing region", - "expect": { - "error": "Invalid Configuration: Missing Region" - } - } - ], - "version": "1.0" - } - } - }, - "com.amazonaws.apigateway#BadRequestException": { - "type": "structure", - "members": { - "message": { - "target": "com.amazonaws.apigateway#String" - } - }, - "traits": { - "smithy.api#documentation": "

The submitted request is not valid, for example, the input is incomplete or incorrect. See the accompanying error message for details.

", - "smithy.api#error": "client", - "smithy.api#httpError": 400 - } - }, - "com.amazonaws.apigateway#BasePathMapping": { - "type": "structure", - "members": { - "basePath": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The base path name that callers of the API must provide as part of the URL after the domain name.

" - } - }, - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

" - } - }, - "stage": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the associated stage.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents the base path that callers of the API must provide as part of the URL after the domain name.

" - } - }, - "com.amazonaws.apigateway#BasePathMappings": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfBasePathMapping", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a collection of BasePathMapping resources.

" - } - }, - "com.amazonaws.apigateway#Blob": { - "type": "blob" - }, - "com.amazonaws.apigateway#Boolean": { - "type": "boolean", - "traits": { - "smithy.api#default": false - } - }, - "com.amazonaws.apigateway#CacheClusterSize": { - "type": "enum", - "members": { - "SIZE_0_POINT_5_GB": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "0.5" - } - }, - "SIZE_1_POINT_6_GB": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "1.6" - } - }, - "SIZE_6_POINT_1_GB": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "6.1" - } - }, - "SIZE_13_POINT_5_GB": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "13.5" - } - }, - "SIZE_28_POINT_4_GB": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "28.4" - } - }, - "SIZE_58_POINT_2_GB": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "58.2" - } - }, - "SIZE_118_GB": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "118" - } - }, - "SIZE_237_GB": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "237" - } - } - }, - "traits": { - "smithy.api#documentation": "

Returns the size of the CacheCluster.

" - } - }, - "com.amazonaws.apigateway#CacheClusterStatus": { - "type": "enum", - "members": { - "CREATE_IN_PROGRESS": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "CREATE_IN_PROGRESS" - } - }, - "AVAILABLE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "AVAILABLE" - } - }, - "DELETE_IN_PROGRESS": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "DELETE_IN_PROGRESS" - } - }, - "NOT_AVAILABLE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "NOT_AVAILABLE" - } - }, - "FLUSH_IN_PROGRESS": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "FLUSH_IN_PROGRESS" - } - } - }, - "traits": { - "smithy.api#documentation": "

Returns the status of the CacheCluster.

" - } - }, - "com.amazonaws.apigateway#CanarySettings": { - "type": "structure", - "members": { - "percentTraffic": { - "target": "com.amazonaws.apigateway#Double", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

The percent (0-100) of traffic diverted to a canary deployment.

" - } - }, - "deploymentId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ID of the canary deployment.

" - } - }, - "stageVariableOverrides": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Stage variables overridden for a canary release deployment, including new stage variables introduced in the canary. These stage variables are represented as a string-to-string map between stage variable names and their values.

" - } - }, - "useStageCache": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A Boolean flag to indicate whether the canary deployment uses the stage cache or not.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Configuration settings of a canary deployment.

" - } - }, - "com.amazonaws.apigateway#ClientCertificate": { - "type": "structure", - "members": { - "clientCertificateId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the client certificate.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the client certificate.

" - } - }, - "pemEncodedCertificate": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The PEM-encoded public key of the client certificate, which can be used to configure certificate authentication in the integration endpoint .

" - } - }, - "createdDate": { - "target": "com.amazonaws.apigateway#Timestamp", - "traits": { - "smithy.api#documentation": "

The timestamp when the client certificate was created.

" - } - }, - "expirationDate": { - "target": "com.amazonaws.apigateway#Timestamp", - "traits": { - "smithy.api#documentation": "

The timestamp when the client certificate will expire.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a client certificate used to configure client-side SSL authentication while sending requests to the integration endpoint.

" - } - }, - "com.amazonaws.apigateway#ClientCertificates": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfClientCertificate", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a collection of ClientCertificate resources.

" - } - }, - "com.amazonaws.apigateway#ConflictException": { - "type": "structure", - "members": { - "message": { - "target": "com.amazonaws.apigateway#String" - } - }, - "traits": { - "smithy.api#documentation": "

The request configuration has conflicts. For details, see the accompanying error message.

", - "smithy.api#error": "client", - "smithy.api#httpError": 409 - } - }, - "com.amazonaws.apigateway#ConnectionType": { - "type": "enum", - "members": { - "INTERNET": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "INTERNET" - } - }, - "VPC_LINK": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "VPC_LINK" - } - } - } - }, - "com.amazonaws.apigateway#ContentHandlingStrategy": { - "type": "enum", - "members": { - "CONVERT_TO_BINARY": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "CONVERT_TO_BINARY" - } - }, - "CONVERT_TO_TEXT": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "CONVERT_TO_TEXT" - } - } - } - }, - "com.amazonaws.apigateway#CreateApiKey": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateApiKeyRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#ApiKey" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Create an ApiKey resource.

", - "smithy.api#http": { - "method": "POST", - "uri": "/apikeys", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateApiKeyRequest": { - "type": "structure", - "members": { - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the ApiKey.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the ApiKey.

" - } - }, - "enabled": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether the ApiKey can be used by callers.

" - } - }, - "generateDistinctId": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether (true) or not (false) the key identifier is distinct from the created API key value. This parameter is deprecated and should not be used.

" - } - }, - "value": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a value of the API key.

" - } - }, - "stageKeys": { - "target": "com.amazonaws.apigateway#ListOfStageKeys", - "traits": { - "smithy.api#documentation": "

DEPRECATED FOR USAGE PLANS - Specifies stages associated with the API key.

" - } - }, - "customerId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

An AWS Marketplace customer identifier , when integrating with the AWS SaaS Marketplace.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to create an ApiKey resource.

" - } - }, - "com.amazonaws.apigateway#CreateAuthorizer": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateAuthorizerRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Authorizer" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Adds a new Authorizer resource to an existing RestApi resource.

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis/{restApiId}/authorizers", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateAuthorizerRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the authorizer.

", - "smithy.api#required": {} - } - }, - "type": { - "target": "com.amazonaws.apigateway#AuthorizerType", - "traits": { - "smithy.api#documentation": "

The authorizer type. Valid values are TOKEN for a Lambda function using a single authorization token submitted in a custom header, REQUEST for a Lambda function using incoming request parameters, and COGNITO_USER_POOLS for using an Amazon Cognito user pool.

", - "smithy.api#required": {} - } - }, - "providerARNs": { - "target": "com.amazonaws.apigateway#ListOfARNs", - "traits": { - "smithy.api#documentation": "

A list of the Amazon Cognito user pool ARNs for the COGNITO_USER_POOLS authorizer. Each element is of this format: arn:aws:cognito-idp:{region}:{account_id}:userpool/{user_pool_id}. For a TOKEN or REQUEST authorizer, this is not defined.

" - } - }, - "authType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Optional customer-defined field, used in OpenAPI imports and exports without functional impact.

" - } - }, - "authorizerUri": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the authorizer's Uniform Resource Identifier (URI). For TOKEN or REQUEST authorizers, this must be a well-formed Lambda function URI, for example, arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:{account_id}:function:{lambda_function_name}/invocations. In general, the URI has this form arn:aws:apigateway:{region}:lambda:path/{service_api}, where {region} is the same as the region hosting the Lambda function, path indicates that the remaining substring in the URI should be treated as the path to the resource, including the initial /. For Lambda functions, this is usually of the form /2015-03-31/functions/[FunctionARN]/invocations.

" - } - }, - "authorizerCredentials": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the required credentials as an IAM role for API Gateway to invoke the authorizer. To specify an IAM role for API Gateway to assume, use the role's Amazon Resource Name (ARN). To use resource-based permissions on the Lambda function, specify null.

" - } - }, - "identitySource": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identity source for which authorization is requested. For a TOKEN or\n COGNITO_USER_POOLS authorizer, this is required and specifies the request\n header mapping expression for the custom header holding the authorization token submitted by\n the client. For example, if the token header name is Auth, the header mapping\n expression is method.request.header.Auth. For the REQUEST\n authorizer, this is required when authorization caching is enabled. The value is a\n comma-separated string of one or more mapping expressions of the specified request parameters.\n For example, if an Auth header, a Name query string parameter are\n defined as identity sources, this value is method.request.header.Auth,\n method.request.querystring.Name. These parameters will be used to derive the\n authorization caching key and to perform runtime validation of the REQUEST\n authorizer by verifying all of the identity-related request parameters are present, not null\n and non-empty. Only when this is true does the authorizer invoke the authorizer Lambda\n function, otherwise, it returns a 401 Unauthorized response without calling the Lambda\n function. The valid value is a string of comma-separated mapping expressions of the specified\n request parameters. When the authorization caching is not enabled, this property is\n optional.

" - } - }, - "identityValidationExpression": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A validation expression for the incoming identity token. For TOKEN authorizers, this value is a regular expression. For COGNITO_USER_POOLS authorizers, API Gateway will match the aud field of the incoming token from the client against the specified regular expression. It will invoke the authorizer's Lambda function when there is a match. Otherwise, it will return a 401 Unauthorized response without calling the Lambda function. The validation expression does not apply to the REQUEST authorizer.

" - } - }, - "authorizerResultTtlInSeconds": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The TTL in seconds of cached authorizer results. If it equals 0, authorization caching is disabled. If it is greater than 0, API Gateway will cache authorizer responses. If this field is not set, the default value is 300. The maximum value is 3600, or 1 hour.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to add a new Authorizer to an existing RestApi resource.

" - } - }, - "com.amazonaws.apigateway#CreateBasePathMapping": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateBasePathMappingRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#BasePathMapping" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a new BasePathMapping resource.

", - "smithy.api#http": { - "method": "POST", - "uri": "/domainnames/{domainName}/basepathmappings", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateBasePathMappingRequest": { - "type": "structure", - "members": { - "domainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The domain name of the BasePathMapping resource to create.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "basePath": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The base path name that callers of the API must provide as part of the URL after the domain name. This value must be unique for all of the mappings across a single API. Specify '(none)' if you do not want callers to specify a base path name after the domain name.

" - } - }, - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#required": {} - } - }, - "stage": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the API's stage that you want to use for this mapping. Specify '(none)' if you want callers to explicitly specify the stage name after any base path name.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to create a new BasePathMapping resource.

" - } - }, - "com.amazonaws.apigateway#CreateDeployment": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateDeploymentRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Deployment" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#ServiceUnavailableException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a Deployment resource, which makes a specified RestApi callable over the internet.

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis/{restApiId}/deployments", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateDeploymentRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the Stage resource for the Deployment resource to create.

" - } - }, - "stageDescription": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the Stage resource for the Deployment resource to create.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description for the Deployment resource to create.

" - } - }, - "cacheClusterEnabled": { - "target": "com.amazonaws.apigateway#NullableBoolean", - "traits": { - "smithy.api#documentation": "

Enables a cache cluster for the Stage resource specified in the input.

" - } - }, - "cacheClusterSize": { - "target": "com.amazonaws.apigateway#CacheClusterSize", - "traits": { - "smithy.api#documentation": "

The stage's cache capacity in GB. For more information about choosing a cache size, see Enabling API caching to enhance responsiveness.

" - } - }, - "variables": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A map that defines the stage variables for the Stage resource that is associated\n with the new deployment. Variable names can have alphanumeric and underscore characters, and the values\n must match [A-Za-z0-9-._~:/?#&=,]+.

" - } - }, - "canarySettings": { - "target": "com.amazonaws.apigateway#DeploymentCanarySettings", - "traits": { - "smithy.api#documentation": "

The input configuration for the canary deployment when the deployment is a canary release deployment.

" - } - }, - "tracingEnabled": { - "target": "com.amazonaws.apigateway#NullableBoolean", - "traits": { - "smithy.api#documentation": "

Specifies whether active tracing with X-ray is enabled for the Stage.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to create a Deployment resource.

" - } - }, - "com.amazonaws.apigateway#CreateDocumentationPart": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateDocumentationPartRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DocumentationPart" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a documentation part.

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis/{restApiId}/documentation/parts", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateDocumentationPartRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "location": { - "target": "com.amazonaws.apigateway#DocumentationPartLocation", - "traits": { - "smithy.api#documentation": "

The location of the targeted API entity of the to-be-created documentation part.

", - "smithy.api#required": {} - } - }, - "properties": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The new documentation content map of the targeted API entity. Enclosed key-value pairs are API-specific, but only OpenAPI-compliant key-value pairs can be exported and, hence, published.

", - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Creates a new documentation part of a given API.

" - } - }, - "com.amazonaws.apigateway#CreateDocumentationVersion": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateDocumentationVersionRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DocumentationVersion" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a documentation version

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis/{restApiId}/documentation/versions", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateDocumentationVersionRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "documentationVersion": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The version identifier of the new snapshot.

", - "smithy.api#required": {} - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The stage name to be associated with the new documentation snapshot.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A description about the new documentation snapshot.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Creates a new documentation version of a given API.

" - } - }, - "com.amazonaws.apigateway#CreateDomainName": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateDomainNameRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DomainName" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a new domain name.

", - "smithy.api#http": { - "method": "POST", - "uri": "/domainnames", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateDomainNameRequest": { - "type": "structure", - "members": { - "domainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the DomainName resource.

", - "smithy.api#required": {} - } - }, - "certificateName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The user-friendly name of the certificate that will be used by edge-optimized endpoint for this domain name.

" - } - }, - "certificateBody": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

[Deprecated] The body of the server certificate that will be used by edge-optimized endpoint for this domain name provided by your certificate authority.

" - } - }, - "certificatePrivateKey": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

[Deprecated] Your edge-optimized endpoint's domain name certificate's private key.

" - } - }, - "certificateChain": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

[Deprecated] The intermediate certificates and optionally the root certificate, one after the other without any blank lines, used by an edge-optimized endpoint for this domain name. If you include the root certificate, your certificate chain must start with intermediate certificates and end with the root certificate. Use the intermediate certificates that were provided by your certificate authority. Do not include any intermediaries that are not in the chain of trust path.

" - } - }, - "certificateArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The reference to an AWS-managed certificate that will be used by edge-optimized endpoint for this domain name. AWS Certificate Manager is the only supported source.

" - } - }, - "regionalCertificateName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The user-friendly name of the certificate that will be used by regional endpoint for this domain name.

" - } - }, - "regionalCertificateArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The reference to an AWS-managed certificate that will be used by regional endpoint for this domain name. AWS Certificate Manager is the only supported source.

" - } - }, - "endpointConfiguration": { - "target": "com.amazonaws.apigateway#EndpointConfiguration", - "traits": { - "smithy.api#documentation": "

The endpoint configuration of this DomainName showing the endpoint types of the domain name.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" - } - }, - "securityPolicy": { - "target": "com.amazonaws.apigateway#SecurityPolicy", - "traits": { - "smithy.api#documentation": "

The Transport Layer Security (TLS) version + cipher suite for this DomainName. The valid values are TLS_1_0 and TLS_1_2.

" - } - }, - "mutualTlsAuthentication": { - "target": "com.amazonaws.apigateway#MutualTlsAuthenticationInput" - }, - "ownershipVerificationCertificateArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ARN of the public certificate issued by ACM to validate ownership of your custom\n domain. Only required when configuring mutual TLS and using an ACM imported or private CA\n certificate ARN as the regionalCertificateArn.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to create a new domain name.

" - } - }, - "com.amazonaws.apigateway#CreateModel": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateModelRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Model" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Adds a new Model resource to an existing RestApi resource.

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis/{restApiId}/models", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateModelRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The RestApi identifier under which the Model will be created.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the model. Must be alphanumeric.

", - "smithy.api#required": {} - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the model.

" - } - }, - "schema": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The schema for the model. For application/json models, this should be JSON schema draft 4 model.

" - } - }, - "contentType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The content-type for the model.

", - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to add a new Model to an existing RestApi resource.

" - } - }, - "com.amazonaws.apigateway#CreateRequestValidator": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateRequestValidatorRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#RequestValidator" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a RequestValidator of a given RestApi.

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis/{restApiId}/requestvalidators", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateRequestValidatorRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the to-be-created RequestValidator.

" - } - }, - "validateRequestBody": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A Boolean flag to indicate whether to validate request body according to the configured model schema for the method (true) or not (false).

" - } - }, - "validateRequestParameters": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A Boolean flag to indicate whether to validate request parameters, true, or not false.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Creates a RequestValidator of a given RestApi.

" - } - }, - "com.amazonaws.apigateway#CreateResource": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateResourceRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Resource" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a Resource resource.

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis/{restApiId}/resources/{parentId}", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateResourceRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "parentId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The parent resource's identifier.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "pathPart": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The last path segment for this resource.

", - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to create a Resource resource.

" - } - }, - "com.amazonaws.apigateway#CreateRestApi": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateRestApiRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#RestApi" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a new RestApi resource.

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateRestApiRequest": { - "type": "structure", - "members": { - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the RestApi.

", - "smithy.api#required": {} - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the RestApi.

" - } - }, - "version": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A version identifier for the API.

" - } - }, - "cloneFrom": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ID of the RestApi that you want to clone from.

" - } - }, - "binaryMediaTypes": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

The list of binary media types supported by the RestApi. By default, the RestApi supports only UTF-8-encoded text payloads.

" - } - }, - "minimumCompressionSize": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

A nullable integer that is used to enable compression (with non-negative between 0 and 10485760 (10M) bytes, inclusive) or disable compression (with a null value) on an API. When compression is enabled, compression or decompression is not applied on the payload if the payload size is smaller than this value. Setting it to zero allows compression for any payload size.

" - } - }, - "apiKeySource": { - "target": "com.amazonaws.apigateway#ApiKeySourceType", - "traits": { - "smithy.api#documentation": "

The source of the API key for metering requests according to a usage plan. Valid values\n are: >HEADER to read the API key from the X-API-Key header of a\n request. AUTHORIZER to read the API key from the UsageIdentifierKey\n from a custom authorizer.

" - } - }, - "endpointConfiguration": { - "target": "com.amazonaws.apigateway#EndpointConfiguration", - "traits": { - "smithy.api#documentation": "

The endpoint configuration of this RestApi showing the endpoint types of the API.

" - } - }, - "policy": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A stringified JSON policy document that applies to this RestApi regardless of the caller and Method configuration.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" - } - }, - "disableExecuteApiEndpoint": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether clients can invoke your API by using the default execute-api endpoint.\n By default, clients can invoke your API with the default\n https://{api_id}.execute-api.{region}.amazonaws.com endpoint. To require that clients use a\n custom domain name to invoke your API, disable the default endpoint

" - } - } - }, - "traits": { - "smithy.api#documentation": "

The POST Request to add a new RestApi resource to your collection.

" - } - }, - "com.amazonaws.apigateway#CreateStage": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateStageRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Stage" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a new Stage resource that references a pre-existing Deployment for the API.

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis/{restApiId}/stages", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateStageRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name for the Stage resource. Stage names can only contain alphanumeric characters, hyphens, and underscores. Maximum length is 128 characters.

", - "smithy.api#required": {} - } - }, - "deploymentId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the Deployment resource for the Stage resource.

", - "smithy.api#required": {} - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the Stage resource.

" - } - }, - "cacheClusterEnabled": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Whether cache clustering is enabled for the stage.

" - } - }, - "cacheClusterSize": { - "target": "com.amazonaws.apigateway#CacheClusterSize", - "traits": { - "smithy.api#documentation": "

The stage's cache capacity in GB. For more information about choosing a cache size, see Enabling API caching to enhance responsiveness.

" - } - }, - "variables": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A map that defines the stage variables for the new Stage resource. Variable names\n can have alphanumeric and underscore characters, and the values must match\n [A-Za-z0-9-._~:/?#&=,]+.

" - } - }, - "documentationVersion": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The version of the associated API documentation.

" - } - }, - "canarySettings": { - "target": "com.amazonaws.apigateway#CanarySettings", - "traits": { - "smithy.api#documentation": "

The canary deployment settings of this stage.

" - } - }, - "tracingEnabled": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether active tracing with X-ray is enabled for the Stage.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to create a Stage resource.

" - } - }, - "com.amazonaws.apigateway#CreateUsagePlan": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateUsagePlanRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#UsagePlan" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a usage plan with the throttle and quota limits, as well as the associated API stages, specified in the payload.

", - "smithy.api#http": { - "method": "POST", - "uri": "/usageplans", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateUsagePlanKey": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateUsagePlanKeyRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#UsagePlanKey" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a usage plan key for adding an existing API key to a usage plan.

", - "smithy.api#http": { - "method": "POST", - "uri": "/usageplans/{usagePlanId}/keys", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#CreateUsagePlanKeyRequest": { - "type": "structure", - "members": { - "usagePlanId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Id of the UsagePlan resource representing the usage plan containing the to-be-created UsagePlanKey resource representing a plan customer.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "keyId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of a UsagePlanKey resource for a plan customer.

", - "smithy.api#required": {} - } - }, - "keyType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The type of a UsagePlanKey resource for a plan customer.

", - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

The POST request to create a usage plan key for adding an existing API key to a usage plan.

" - } - }, - "com.amazonaws.apigateway#CreateUsagePlanRequest": { - "type": "structure", - "members": { - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the usage plan.

", - "smithy.api#required": {} - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the usage plan.

" - } - }, - "apiStages": { - "target": "com.amazonaws.apigateway#ListOfApiStage", - "traits": { - "smithy.api#documentation": "

The associated API stages of the usage plan.

" - } - }, - "throttle": { - "target": "com.amazonaws.apigateway#ThrottleSettings", - "traits": { - "smithy.api#documentation": "

The throttling limits of the usage plan.

" - } - }, - "quota": { - "target": "com.amazonaws.apigateway#QuotaSettings", - "traits": { - "smithy.api#documentation": "

The quota of the usage plan.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

The POST request to create a usage plan with the name, description, throttle limits and quota limits, as well as the associated API stages, specified in the payload.

" - } - }, - "com.amazonaws.apigateway#CreateVpcLink": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#CreateVpcLinkRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#VpcLink" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a VPC link, under the caller's account in a selected region, in an asynchronous operation that typically takes 2-4 minutes to complete and become operational. The caller must have permissions to create and update VPC Endpoint services.

", - "smithy.api#http": { - "method": "POST", - "uri": "/vpclinks", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#CreateVpcLinkRequest": { - "type": "structure", - "members": { - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name used to label and identify the VPC link.

", - "smithy.api#required": {} - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the VPC link.

" - } - }, - "targetArns": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

The ARN of the network load balancer of the VPC targeted by the VPC link. The network load balancer must be owned by the same AWS account of the API owner.

", - "smithy.api#required": {} - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Creates a VPC link, under the caller's account in a selected region, in an asynchronous operation that typically takes 2-4 minutes to complete and become operational. The caller must have permissions to create and update VPC Endpoint services.

" - } - }, - "com.amazonaws.apigateway#DeleteApiKey": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteApiKeyRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes the ApiKey resource.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/apikeys/{apiKey}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteApiKeyRequest": { - "type": "structure", - "members": { - "apiKey": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the ApiKey resource to be deleted.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to delete the ApiKey resource.

" - } - }, - "com.amazonaws.apigateway#DeleteAuthorizer": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteAuthorizerRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes an existing Authorizer resource.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/authorizers/{authorizerId}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteAuthorizerRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "authorizerId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the Authorizer resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to delete an existing Authorizer resource.

" - } - }, - "com.amazonaws.apigateway#DeleteBasePathMapping": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteBasePathMappingRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes the BasePathMapping resource.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/domainnames/{domainName}/basepathmappings/{basePath}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteBasePathMappingRequest": { - "type": "structure", - "members": { - "domainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The domain name of the BasePathMapping resource to delete.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "basePath": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The base path name of the BasePathMapping resource to delete.

\n

To specify an empty base path, set this parameter to '(none)'.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to delete the BasePathMapping resource.

" - } - }, - "com.amazonaws.apigateway#DeleteClientCertificate": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteClientCertificateRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes the ClientCertificate resource.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/clientcertificates/{clientCertificateId}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteClientCertificateRequest": { - "type": "structure", - "members": { - "clientCertificateId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the ClientCertificate resource to be deleted.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to delete the ClientCertificate resource.

" - } - }, - "com.amazonaws.apigateway#DeleteDeployment": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteDeploymentRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes a Deployment resource. Deleting a deployment will only succeed if there are no Stage resources associated with it.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/deployments/{deploymentId}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteDeploymentRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "deploymentId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the Deployment resource to delete.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to delete a Deployment resource.

" - } - }, - "com.amazonaws.apigateway#DeleteDocumentationPart": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteDocumentationPartRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes a documentation part

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/documentation/parts/{documentationPartId}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteDocumentationPartRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "documentationPartId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the to-be-deleted documentation part.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Deletes an existing documentation part of an API.

" - } - }, - "com.amazonaws.apigateway#DeleteDocumentationVersion": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteDocumentationVersionRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes a documentation version.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/documentation/versions/{documentationVersion}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteDocumentationVersionRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "documentationVersion": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The version identifier of a to-be-deleted documentation snapshot.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Deletes an existing documentation version of an API.

" - } - }, - "com.amazonaws.apigateway#DeleteDomainName": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteDomainNameRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes the DomainName resource.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/domainnames/{domainName}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteDomainNameRequest": { - "type": "structure", - "members": { - "domainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the DomainName resource to be deleted.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to delete the DomainName resource.

" - } - }, - "com.amazonaws.apigateway#DeleteGatewayResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteGatewayResponseRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Clears any customization of a GatewayResponse of a specified response type on the given RestApi and resets it with the default settings.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/gatewayresponses/{responseType}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteGatewayResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "responseType": { - "target": "com.amazonaws.apigateway#GatewayResponseType", - "traits": { - "smithy.api#documentation": "

The response type of the associated GatewayResponse.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Clears any customization of a GatewayResponse of a specified response type on the given RestApi and resets it with the default settings.

" - } - }, - "com.amazonaws.apigateway#DeleteIntegration": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteIntegrationRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Represents a delete integration.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration", - "code": 204 - } - } - }, - "com.amazonaws.apigateway#DeleteIntegrationRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a delete integration request's resource identifier.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a delete integration request's HTTP method.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a delete integration request.

" - } - }, - "com.amazonaws.apigateway#DeleteIntegrationResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteIntegrationResponseRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Represents a delete integration response.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration/responses/{statusCode}", - "code": 204 - } - } - }, - "com.amazonaws.apigateway#DeleteIntegrationResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a delete integration response request's resource identifier.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a delete integration response request's HTTP method.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

Specifies a delete integration response request's status code.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a delete integration response request.

" - } - }, - "com.amazonaws.apigateway#DeleteMethod": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteMethodRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes an existing Method resource.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}", - "code": 204 - } - } - }, - "com.amazonaws.apigateway#DeleteMethodRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Resource identifier for the Method resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The HTTP verb of the Method resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to delete an existing Method resource.

" - } - }, - "com.amazonaws.apigateway#DeleteMethodResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteMethodResponseRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes an existing MethodResponse resource.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/responses/{statusCode}", - "code": 204 - } - } - }, - "com.amazonaws.apigateway#DeleteMethodResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Resource identifier for the MethodResponse resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The HTTP verb of the Method resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

The status code identifier for the MethodResponse resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to delete an existing MethodResponse resource.

" - } - }, - "com.amazonaws.apigateway#DeleteModel": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteModelRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes a model.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/models/{modelName}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteModelRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "modelName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the model to delete.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to delete an existing model in an existing RestApi resource.

" - } - }, - "com.amazonaws.apigateway#DeleteRequestValidator": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteRequestValidatorRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes a RequestValidator of a given RestApi.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/requestvalidators/{requestValidatorId}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteRequestValidatorRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "requestValidatorId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the RequestValidator to be deleted.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Deletes a specified RequestValidator of a given RestApi.

" - } - }, - "com.amazonaws.apigateway#DeleteResource": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteResourceRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes a Resource resource.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/resources/{resourceId}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteResourceRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the Resource resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to delete a Resource.

" - } - }, - "com.amazonaws.apigateway#DeleteRestApi": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteRestApiRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes the specified API.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteRestApiRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to delete the specified API from your collection.

" - } - }, - "com.amazonaws.apigateway#DeleteStage": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteStageRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes a Stage resource.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/stages/{stageName}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteStageRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the Stage resource to delete.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to delete a Stage resource.

" - } - }, - "com.amazonaws.apigateway#DeleteUsagePlan": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteUsagePlanRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes a usage plan of a given plan Id.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/usageplans/{usagePlanId}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteUsagePlanKey": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteUsagePlanKeyRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes a usage plan key and remove the underlying API key from the associated usage plan.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/usageplans/{usagePlanId}/keys/{keyId}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteUsagePlanKeyRequest": { - "type": "structure", - "members": { - "usagePlanId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Id of the UsagePlan resource representing the usage plan containing the to-be-deleted UsagePlanKey resource representing a plan customer.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "keyId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Id of the UsagePlanKey resource to be deleted.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

The DELETE request to delete a usage plan key and remove the underlying API key from the associated usage plan.

" - } - }, - "com.amazonaws.apigateway#DeleteUsagePlanRequest": { - "type": "structure", - "members": { - "usagePlanId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Id of the to-be-deleted usage plan.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

The DELETE request to delete a usage plan of a given plan Id.

" - } - }, - "com.amazonaws.apigateway#DeleteVpcLink": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#DeleteVpcLinkRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Deletes an existing VpcLink of a specified identifier.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/vpclinks/{vpcLinkId}", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#DeleteVpcLinkRequest": { - "type": "structure", - "members": { - "vpcLinkId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the VpcLink. It is used in an Integration to reference this VpcLink.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Deletes an existing VpcLink of a specified identifier.

" - } - }, - "com.amazonaws.apigateway#Deployment": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier for the deployment resource.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description for the deployment resource.

" - } - }, - "createdDate": { - "target": "com.amazonaws.apigateway#Timestamp", - "traits": { - "smithy.api#documentation": "

The date and time that the deployment resource was created.

" - } - }, - "apiSummary": { - "target": "com.amazonaws.apigateway#PathToMapOfMethodSnapshot", - "traits": { - "smithy.api#documentation": "

A summary of the RestApi at the date and time that the deployment resource was created.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

An immutable representation of a RestApi resource that can be called by users using Stages. A deployment must be associated with a Stage for it to be callable over the Internet.

" - } - }, - "com.amazonaws.apigateway#DeploymentCanarySettings": { - "type": "structure", - "members": { - "percentTraffic": { - "target": "com.amazonaws.apigateway#Double", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

The percentage (0.0-100.0) of traffic routed to the canary deployment.

" - } - }, - "stageVariableOverrides": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A stage variable overrides used for the canary release deployment. They can override existing stage variables or add new stage variables for the canary release deployment. These stage variables are represented as a string-to-string map between stage variable names and their values.

" - } - }, - "useStageCache": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A Boolean flag to indicate whether the canary release deployment uses the stage cache or not.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

The input configuration for a canary deployment.

" - } - }, - "com.amazonaws.apigateway#Deployments": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfDeployment", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a collection resource that contains zero or more references to your existing deployments, and links that guide you on how to interact with your collection. The collection offers a paginated view of the contained deployments.

" - } - }, - "com.amazonaws.apigateway#DocumentationPart": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The DocumentationPart identifier, generated by API Gateway when the DocumentationPart is created.

" - } - }, - "location": { - "target": "com.amazonaws.apigateway#DocumentationPartLocation", - "traits": { - "smithy.api#documentation": "

The location of the API entity to which the documentation applies. Valid fields depend on the targeted API entity type. All the valid location fields are not required. If not explicitly specified, a valid location field is treated as a wildcard and associated documentation content may be inherited by matching entities, unless overridden.

" - } - }, - "properties": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A content map of API-specific key-value pairs describing the targeted API entity. The map must be encoded as a JSON string, e.g., \"{ \\\"description\\\": \\\"The API does ...\\\" }\". Only OpenAPI-compliant documentation-related fields from the properties map are exported and, hence, published as part of the API entity definitions, while the original documentation parts are exported in a OpenAPI extension of x-amazon-apigateway-documentation.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A documentation part for a targeted API entity.

" - } - }, - "com.amazonaws.apigateway#DocumentationPartIds": { - "type": "structure", - "members": { - "ids": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of the returned documentation part identifiers.

" - } - }, - "warnings": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of warning messages reported during import of documentation parts.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A collection of the imported DocumentationPart identifiers.

" - } - }, - "com.amazonaws.apigateway#DocumentationPartLocation": { - "type": "structure", - "members": { - "type": { - "target": "com.amazonaws.apigateway#DocumentationPartType", - "traits": { - "smithy.api#documentation": "

The type of API entity to which the documentation content applies. Valid values are API, AUTHORIZER, MODEL, RESOURCE, METHOD, PATH_PARAMETER, QUERY_PARAMETER, REQUEST_HEADER, REQUEST_BODY, RESPONSE, RESPONSE_HEADER, and RESPONSE_BODY. Content inheritance does not apply to any entity of the API, AUTHORIZER, METHOD, MODEL, REQUEST_BODY, or RESOURCE type.

", - "smithy.api#required": {} - } - }, - "path": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The URL path of the target. It is a valid field for the API entity types of RESOURCE, METHOD, PATH_PARAMETER, QUERY_PARAMETER, REQUEST_HEADER, REQUEST_BODY, RESPONSE, RESPONSE_HEADER, and RESPONSE_BODY. The default value is / for the root resource. When an applicable child entity inherits the content of another entity of the same type with more general specifications of the other location attributes, the child entity's path attribute must match that of the parent entity as a prefix.

" - } - }, - "method": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The HTTP verb of a method. It is a valid field for the API entity types of METHOD, PATH_PARAMETER, QUERY_PARAMETER, REQUEST_HEADER, REQUEST_BODY, RESPONSE, RESPONSE_HEADER, and RESPONSE_BODY. The default value is * for any method. When an applicable child entity inherits the content of an entity of the same type with more general specifications of the other location attributes, the child entity's method attribute must match that of the parent entity exactly.

" - } - }, - "statusCode": { - "target": "com.amazonaws.apigateway#DocumentationPartLocationStatusCode", - "traits": { - "smithy.api#documentation": "

The HTTP status code of a response. It is a valid field for the API entity types of RESPONSE, RESPONSE_HEADER, and RESPONSE_BODY. The default value is * for any status code. When an applicable child entity inherits the content of an entity of the same type with more general specifications of the other location attributes, the child entity's statusCode attribute must match that of the parent entity exactly.

" - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the targeted API entity. It is a valid and required field for the API entity types of AUTHORIZER, MODEL, PATH_PARAMETER, QUERY_PARAMETER, REQUEST_HEADER, REQUEST_BODY and RESPONSE_HEADER. It is an invalid field for any other entity type.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Specifies the target API entity to which the documentation applies.

" - } - }, - "com.amazonaws.apigateway#DocumentationPartLocationStatusCode": { - "type": "string", - "traits": { - "smithy.api#pattern": "^([1-5]\\d\\d|\\*|\\s*)$" - } - }, - "com.amazonaws.apigateway#DocumentationPartType": { - "type": "enum", - "members": { - "API": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "API" - } - }, - "AUTHORIZER": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "AUTHORIZER" - } - }, - "MODEL": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "MODEL" - } - }, - "RESOURCE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "RESOURCE" - } - }, - "METHOD": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "METHOD" - } - }, - "PATH_PARAMETER": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "PATH_PARAMETER" - } - }, - "QUERY_PARAMETER": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "QUERY_PARAMETER" - } - }, - "REQUEST_HEADER": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "REQUEST_HEADER" - } - }, - "REQUEST_BODY": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "REQUEST_BODY" - } - }, - "RESPONSE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "RESPONSE" - } - }, - "RESPONSE_HEADER": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "RESPONSE_HEADER" - } - }, - "RESPONSE_BODY": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "RESPONSE_BODY" - } - } - } - }, - "com.amazonaws.apigateway#DocumentationParts": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfDocumentationPart", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

The collection of documentation parts of an API.

" - } - }, - "com.amazonaws.apigateway#DocumentationVersion": { - "type": "structure", - "members": { - "version": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The version identifier of the API documentation snapshot.

" - } - }, - "createdDate": { - "target": "com.amazonaws.apigateway#Timestamp", - "traits": { - "smithy.api#documentation": "

The date when the API documentation snapshot is created.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the API documentation snapshot.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A snapshot of the documentation of an API.

" - } - }, - "com.amazonaws.apigateway#DocumentationVersions": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfDocumentationVersion", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

The collection of documentation snapshots of an API.

" - } - }, - "com.amazonaws.apigateway#DomainName": { - "type": "structure", - "members": { - "domainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The custom domain name as an API host name, for example, my-api.example.com.

" - } - }, - "certificateName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the certificate that will be used by edge-optimized endpoint for this domain name.

" - } - }, - "certificateArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The reference to an AWS-managed certificate that will be used by edge-optimized endpoint for this domain name. AWS Certificate Manager is the only supported source.

" - } - }, - "certificateUploadDate": { - "target": "com.amazonaws.apigateway#Timestamp", - "traits": { - "smithy.api#documentation": "

The timestamp when the certificate that was used by edge-optimized endpoint for this domain name was uploaded.

" - } - }, - "regionalDomainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The domain name associated with the regional endpoint for this custom domain name. You set up this association by adding a DNS record that points the custom domain name to this regional domain name. The regional domain name is returned by API Gateway when you create a regional endpoint.

" - } - }, - "regionalHostedZoneId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The region-specific Amazon Route 53 Hosted Zone ID of the regional endpoint. For more information, see Set up a Regional Custom Domain Name and AWS Regions and Endpoints for API Gateway.

" - } - }, - "regionalCertificateName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the certificate that will be used for validating the regional domain name.

" - } - }, - "regionalCertificateArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The reference to an AWS-managed certificate that will be used for validating the regional domain name. AWS Certificate Manager is the only supported source.

" - } - }, - "distributionDomainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The domain name of the Amazon CloudFront distribution associated with this custom domain name for an edge-optimized endpoint. You set up this association when adding a DNS record pointing the custom domain name to this distribution name. For more information about CloudFront distributions, see the Amazon CloudFront documentation.

" - } - }, - "distributionHostedZoneId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The region-agnostic Amazon Route 53 Hosted Zone ID of the edge-optimized endpoint. The valid value is Z2FDTNDATAQYW2 for all the regions. For more information, see Set up a Regional Custom Domain Name and AWS Regions and Endpoints for API Gateway.

" - } - }, - "endpointConfiguration": { - "target": "com.amazonaws.apigateway#EndpointConfiguration", - "traits": { - "smithy.api#documentation": "

The endpoint configuration of this DomainName showing the endpoint types of the domain name.

" - } - }, - "domainNameStatus": { - "target": "com.amazonaws.apigateway#DomainNameStatus", - "traits": { - "smithy.api#documentation": "

The status of the DomainName migration. The valid values are AVAILABLE and UPDATING. If the status is UPDATING, the domain cannot be modified further until the existing operation is complete. If it is AVAILABLE, the domain can be updated.

" - } - }, - "domainNameStatusMessage": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

An optional text message containing detailed information about status of the DomainName migration.

" - } - }, - "securityPolicy": { - "target": "com.amazonaws.apigateway#SecurityPolicy", - "traits": { - "smithy.api#documentation": "

The Transport Layer Security (TLS) version + cipher suite for this DomainName. The valid values are TLS_1_0 and TLS_1_2.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" - } - }, - "mutualTlsAuthentication": { - "target": "com.amazonaws.apigateway#MutualTlsAuthentication", - "traits": { - "smithy.api#documentation": "

The mutual TLS authentication configuration for a custom domain name. If specified, API Gateway\n performs two-way authentication between the client and the server. Clients must present a\n trusted certificate to access your API.

" - } - }, - "ownershipVerificationCertificateArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ARN of the public certificate issued by ACM to validate ownership of your custom\n domain. Only required when configuring mutual TLS and using an ACM imported or private CA\n certificate ARN as the regionalCertificateArn.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a custom domain name as a user-friendly host name of an API (RestApi).

" - } - }, - "com.amazonaws.apigateway#DomainNameStatus": { - "type": "enum", - "members": { - "AVAILABLE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "AVAILABLE" - } - }, - "UPDATING": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "UPDATING" - } - }, - "PENDING": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "PENDING" - } - }, - "PENDING_CERTIFICATE_REIMPORT": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "PENDING_CERTIFICATE_REIMPORT" - } - }, - "PENDING_OWNERSHIP_VERIFICATION": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "PENDING_OWNERSHIP_VERIFICATION" - } - } - } - }, - "com.amazonaws.apigateway#DomainNames": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfDomainName", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a collection of DomainName resources.

" - } - }, - "com.amazonaws.apigateway#Double": { - "type": "double", - "traits": { - "smithy.api#default": 0 - } - }, - "com.amazonaws.apigateway#EndpointConfiguration": { - "type": "structure", - "members": { - "types": { - "target": "com.amazonaws.apigateway#ListOfEndpointType", - "traits": { - "smithy.api#documentation": "

A list of endpoint types of an API (RestApi) or its custom domain name (DomainName). For an edge-optimized API and its custom domain name, the endpoint type is \"EDGE\". For a regional API and its custom domain name, the endpoint type is REGIONAL. For a private API, the endpoint type is PRIVATE.

" - } - }, - "vpcEndpointIds": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of VpcEndpointIds of an API (RestApi) against which to create Route53 ALIASes. It is only supported for PRIVATE endpoint type.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

The endpoint configuration to indicate the types of endpoints an API (RestApi) or its custom domain name (DomainName) has.

" - } - }, - "com.amazonaws.apigateway#EndpointType": { - "type": "enum", - "members": { - "REGIONAL": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "REGIONAL" - } - }, - "EDGE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "EDGE" - } - }, - "PRIVATE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "PRIVATE" - } - } - }, - "traits": { - "smithy.api#documentation": "

The endpoint type. The valid values are EDGE for edge-optimized API setup, most suitable for mobile applications; REGIONAL for regional API endpoint setup, most suitable for calling from AWS Region; and PRIVATE for private APIs.

" - } - }, - "com.amazonaws.apigateway#ExportResponse": { - "type": "structure", - "members": { - "contentType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The content-type header value in the HTTP response. This will correspond to a valid 'accept' type in the request.

", - "smithy.api#httpHeader": "Content-Type" - } - }, - "contentDisposition": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The content-disposition header value in the HTTP response.

", - "smithy.api#httpHeader": "Content-Disposition" - } - }, - "body": { - "target": "com.amazonaws.apigateway#Blob", - "traits": { - "smithy.api#documentation": "

The binary blob response to GetExport, which contains the export.

", - "smithy.api#httpPayload": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

The binary blob response to GetExport, which contains the generated SDK.

" - } - }, - "com.amazonaws.apigateway#FlushStageAuthorizersCache": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#FlushStageAuthorizersCacheRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Flushes all authorizer cache entries on a stage.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/stages/{stageName}/cache/authorizers", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#FlushStageAuthorizersCacheRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the stage to flush.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to flush authorizer cache entries on a specified stage.

" - } - }, - "com.amazonaws.apigateway#FlushStageCache": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#FlushStageCacheRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Flushes a stage's cache.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/restapis/{restApiId}/stages/{stageName}/cache/data", - "code": 202 - } - } - }, - "com.amazonaws.apigateway#FlushStageCacheRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the stage to flush its cache.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to flush a stage's cache.

" - } - }, - "com.amazonaws.apigateway#GatewayResponse": { - "type": "structure", - "members": { - "responseType": { - "target": "com.amazonaws.apigateway#GatewayResponseType", - "traits": { - "smithy.api#documentation": "

The response type of the associated GatewayResponse.

" - } - }, - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

The HTTP status code for this GatewayResponse.

" - } - }, - "responseParameters": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Response parameters (paths, query strings and headers) of the GatewayResponse as a\n string-to-string map of key-value pairs.

" - } - }, - "responseTemplates": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Response templates of the GatewayResponse as a string-to-string map of key-value pairs.

" - } - }, - "defaultResponse": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A Boolean flag to indicate whether this GatewayResponse is the default gateway response (true) or not (false). A default gateway response is one generated by API Gateway without any customization by an API developer.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A gateway response of a given response type and status code, with optional response parameters and mapping templates.

" - } - }, - "com.amazonaws.apigateway#GatewayResponseType": { - "type": "enum", - "members": { - "DEFAULT_4XX": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "DEFAULT_4XX" - } - }, - "DEFAULT_5XX": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "DEFAULT_5XX" - } - }, - "RESOURCE_NOT_FOUND": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "RESOURCE_NOT_FOUND" - } - }, - "UNAUTHORIZED": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "UNAUTHORIZED" - } - }, - "INVALID_API_KEY": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "INVALID_API_KEY" - } - }, - "ACCESS_DENIED": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "ACCESS_DENIED" - } - }, - "AUTHORIZER_FAILURE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "AUTHORIZER_FAILURE" - } - }, - "AUTHORIZER_CONFIGURATION_ERROR": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "AUTHORIZER_CONFIGURATION_ERROR" - } - }, - "INVALID_SIGNATURE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "INVALID_SIGNATURE" - } - }, - "EXPIRED_TOKEN": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "EXPIRED_TOKEN" - } - }, - "MISSING_AUTHENTICATION_TOKEN": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "MISSING_AUTHENTICATION_TOKEN" - } - }, - "INTEGRATION_FAILURE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "INTEGRATION_FAILURE" - } - }, - "INTEGRATION_TIMEOUT": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "INTEGRATION_TIMEOUT" - } - }, - "API_CONFIGURATION_ERROR": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "API_CONFIGURATION_ERROR" - } - }, - "UNSUPPORTED_MEDIA_TYPE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "UNSUPPORTED_MEDIA_TYPE" - } - }, - "BAD_REQUEST_PARAMETERS": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "BAD_REQUEST_PARAMETERS" - } - }, - "BAD_REQUEST_BODY": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "BAD_REQUEST_BODY" - } - }, - "REQUEST_TOO_LARGE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "REQUEST_TOO_LARGE" - } - }, - "THROTTLED": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "THROTTLED" - } - }, - "QUOTA_EXCEEDED": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "QUOTA_EXCEEDED" - } - }, - "WAF_FILTERED": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "WAF_FILTERED" - } - } - } - }, - "com.amazonaws.apigateway#GatewayResponses": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfGatewayResponse", - "traits": { - "smithy.api#documentation": "

Returns the entire collection, because of no pagination support.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set. The GatewayResponse collection does not support pagination and the position does not apply here.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

The collection of the GatewayResponse instances of a RestApi as a responseType-to-GatewayResponse object map of key-value pairs. As such, pagination is not supported for querying this collection.

" - } - }, - "com.amazonaws.apigateway#GenerateClientCertificate": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GenerateClientCertificateRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#ClientCertificate" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Generates a ClientCertificate resource.

", - "smithy.api#http": { - "method": "POST", - "uri": "/clientcertificates", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#GenerateClientCertificateRequest": { - "type": "structure", - "members": { - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the ClientCertificate.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to generate a ClientCertificate resource.

" - } - }, - "com.amazonaws.apigateway#GetAccount": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetAccountRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Account" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets information about the current Account resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/account", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetAccountRequest": { - "type": "structure", - "members": {}, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to get information about the current Account resource.

" - } - }, - "com.amazonaws.apigateway#GetApiKey": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetApiKeyRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#ApiKey" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets information about the current ApiKey resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/apikeys/{apiKey}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetApiKeyRequest": { - "type": "structure", - "members": { - "apiKey": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the ApiKey resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "includeValue": { - "target": "com.amazonaws.apigateway#NullableBoolean", - "traits": { - "smithy.api#documentation": "

A boolean flag to specify whether (true) or not (false) the result contains the key value.

", - "smithy.api#httpQuery": "includeValue" - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to get information about the current ApiKey resource.

" - } - }, - "com.amazonaws.apigateway#GetApiKeys": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetApiKeysRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#ApiKeys" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets information about the current ApiKeys resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/apikeys", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetApiKeysRequest": { - "type": "structure", - "members": { - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - }, - "nameQuery": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of queried API keys.

", - "smithy.api#httpQuery": "name" - } - }, - "customerId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of a customer in AWS Marketplace or an external system, such as a developer portal.

", - "smithy.api#httpQuery": "customerId" - } - }, - "includeValues": { - "target": "com.amazonaws.apigateway#NullableBoolean", - "traits": { - "smithy.api#documentation": "

A boolean flag to specify whether (true) or not (false) the result contains key values.

", - "smithy.api#httpQuery": "includeValues" - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to get information about the current ApiKeys resource.

" - } - }, - "com.amazonaws.apigateway#GetAuthorizer": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetAuthorizerRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Authorizer" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Describe an existing Authorizer resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/authorizers/{authorizerId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetAuthorizerRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "authorizerId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the Authorizer resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to describe an existing Authorizer resource.

" - } - }, - "com.amazonaws.apigateway#GetAuthorizers": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetAuthorizersRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Authorizers" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Describe an existing Authorizers resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/authorizers", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetAuthorizersRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to describe an existing Authorizers resource.

" - } - }, - "com.amazonaws.apigateway#GetBasePathMapping": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetBasePathMappingRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#BasePathMapping" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Describe a BasePathMapping resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/domainnames/{domainName}/basepathmappings/{basePath}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetBasePathMappingRequest": { - "type": "structure", - "members": { - "domainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The domain name of the BasePathMapping resource to be described.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "basePath": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The base path name that callers of the API must provide as part of the URL after the domain name. This value must be unique for all of the mappings across a single API. Specify '(none)' if you do not want callers to specify any base path name after the domain name.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to describe a BasePathMapping resource.

" - } - }, - "com.amazonaws.apigateway#GetBasePathMappings": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetBasePathMappingsRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#BasePathMappings" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Represents a collection of BasePathMapping resources.

", - "smithy.api#http": { - "method": "GET", - "uri": "/domainnames/{domainName}/basepathmappings", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetBasePathMappingsRequest": { - "type": "structure", - "members": { - "domainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The domain name of a BasePathMapping resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to get information about a collection of BasePathMapping resources.

" - } - }, - "com.amazonaws.apigateway#GetClientCertificate": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetClientCertificateRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#ClientCertificate" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets information about the current ClientCertificate resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/clientcertificates/{clientCertificateId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetClientCertificateRequest": { - "type": "structure", - "members": { - "clientCertificateId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the ClientCertificate resource to be described.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to get information about the current ClientCertificate resource.

" - } - }, - "com.amazonaws.apigateway#GetClientCertificates": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetClientCertificatesRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#ClientCertificates" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets a collection of ClientCertificate resources.

", - "smithy.api#http": { - "method": "GET", - "uri": "/clientcertificates", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetClientCertificatesRequest": { - "type": "structure", - "members": { - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to get information about a collection of ClientCertificate resources.

" - } - }, - "com.amazonaws.apigateway#GetDeployment": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetDeploymentRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Deployment" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#ServiceUnavailableException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets information about a Deployment resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/deployments/{deploymentId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetDeploymentRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "deploymentId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the Deployment resource to get information about.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "embed": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A query parameter to retrieve the specified embedded resources of the returned Deployment resource in the response. In a REST API call, this embed parameter value is a list of comma-separated strings, as in GET /restapis/{restapi_id}/deployments/{deployment_id}?embed=var1,var2. The SDK and other platform-dependent libraries might use a different format for the list. Currently, this request supports only retrieval of the embedded API summary this way. Hence, the parameter value must be a single-valued list containing only the \"apisummary\" string. For example, GET /restapis/{restapi_id}/deployments/{deployment_id}?embed=apisummary.

", - "smithy.api#httpQuery": "embed" - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to get information about a Deployment resource.

" - } - }, - "com.amazonaws.apigateway#GetDeployments": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetDeploymentsRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Deployments" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#ServiceUnavailableException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets information about a Deployments collection.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/deployments", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetDeploymentsRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to get information about a Deployments collection.

" - } - }, - "com.amazonaws.apigateway#GetDocumentationPart": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetDocumentationPartRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DocumentationPart" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets a documentation part.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/documentation/parts/{documentationPartId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetDocumentationPartRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "documentationPartId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Gets a specified documentation part of a given API.

" - } - }, - "com.amazonaws.apigateway#GetDocumentationParts": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetDocumentationPartsRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DocumentationParts" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets documentation parts.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/documentation/parts", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetDocumentationPartsRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "type": { - "target": "com.amazonaws.apigateway#DocumentationPartType", - "traits": { - "smithy.api#documentation": "

The type of API entities of the to-be-retrieved documentation parts.

", - "smithy.api#httpQuery": "type" - } - }, - "nameQuery": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of API entities of the to-be-retrieved documentation parts.

", - "smithy.api#httpQuery": "name" - } - }, - "path": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The path of API entities of the to-be-retrieved documentation parts.

", - "smithy.api#httpQuery": "path" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - }, - "locationStatus": { - "target": "com.amazonaws.apigateway#LocationStatusType", - "traits": { - "smithy.api#documentation": "

The status of the API documentation parts to retrieve. Valid values are DOCUMENTED for retrieving DocumentationPart resources with content and UNDOCUMENTED for DocumentationPart resources without content.

", - "smithy.api#httpQuery": "locationStatus" - } - } - }, - "traits": { - "smithy.api#documentation": "

Gets the documentation parts of an API. The result may be filtered by the type, name, or path of API entities (targets).

" - } - }, - "com.amazonaws.apigateway#GetDocumentationVersion": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetDocumentationVersionRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DocumentationVersion" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets a documentation version.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/documentation/versions/{documentationVersion}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetDocumentationVersionRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "documentationVersion": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The version identifier of the to-be-retrieved documentation snapshot.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Gets a documentation snapshot of an API.

" - } - }, - "com.amazonaws.apigateway#GetDocumentationVersions": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetDocumentationVersionsRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DocumentationVersions" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets documentation versions.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/documentation/versions", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetDocumentationVersionsRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

Gets the documentation versions of an API.

" - } - }, - "com.amazonaws.apigateway#GetDomainName": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetDomainNameRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DomainName" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Represents a domain name that is contained in a simpler, more intuitive URL that can be called.

", - "smithy.api#http": { - "method": "GET", - "uri": "/domainnames/{domainName}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetDomainNameRequest": { - "type": "structure", - "members": { - "domainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the DomainName resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to get the name of a DomainName resource.

" - } - }, - "com.amazonaws.apigateway#GetDomainNames": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetDomainNamesRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DomainNames" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Represents a collection of DomainName resources.

", - "smithy.api#http": { - "method": "GET", - "uri": "/domainnames", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetDomainNamesRequest": { - "type": "structure", - "members": { - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to describe a collection of DomainName resources.

" - } - }, - "com.amazonaws.apigateway#GetExport": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetExportRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#ExportResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Exports a deployed version of a RestApi in a specified format.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/stages/{stageName}/exports/{exportType}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetExportRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the Stage that will be exported.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "exportType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The type of export. Acceptable values are 'oas30' for OpenAPI 3.0.x and 'swagger' for Swagger/OpenAPI 2.0.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "parameters": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map of query string parameters that specify properties of the export, depending on the requested exportType. For exportType\n oas30 and swagger, any combination of the following parameters are supported: extensions='integrations' or extensions='apigateway' will export the API with x-amazon-apigateway-integration extensions. extensions='authorizers' will export the API with x-amazon-apigateway-authorizer extensions. postman will export the API with Postman extensions, allowing for import to the Postman tool

", - "smithy.api#httpQueryParams": {} - } - }, - "accepts": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The content-type of the export, for example application/json. Currently application/json and application/yaml are supported for exportType ofoas30 and swagger. This should be specified in the Accept header for direct API requests.

", - "smithy.api#httpHeader": "Accept" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request a new export of a RestApi for a particular Stage.

" - } - }, - "com.amazonaws.apigateway#GetGatewayResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetGatewayResponseRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#GatewayResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets a GatewayResponse of a specified response type on the given RestApi.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/gatewayresponses/{responseType}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetGatewayResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "responseType": { - "target": "com.amazonaws.apigateway#GatewayResponseType", - "traits": { - "smithy.api#documentation": "

The response type of the associated GatewayResponse.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Gets a GatewayResponse of a specified response type on the given RestApi.

" - } - }, - "com.amazonaws.apigateway#GetGatewayResponses": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetGatewayResponsesRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#GatewayResponses" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets the GatewayResponses collection on the given RestApi. If an API developer has not added any definitions for gateway responses, the result will be the API Gateway-generated default GatewayResponses collection for the supported response types.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/gatewayresponses", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetGatewayResponsesRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set. The GatewayResponse collection does not support pagination and the position does not apply here.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500. The GatewayResponses collection does not support pagination and the limit does not apply here.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

Gets the GatewayResponses collection on the given RestApi. If an API developer has not added any definitions for gateway responses, the result will be the API Gateway-generated default GatewayResponses collection for the supported response types.

" - } - }, - "com.amazonaws.apigateway#GetIntegration": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetIntegrationRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Integration" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Get the integration settings.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetIntegrationRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a get integration request's resource identifier

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a get integration request's HTTP method.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a request to get the integration configuration.

" - } - }, - "com.amazonaws.apigateway#GetIntegrationResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetIntegrationResponseRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#IntegrationResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Represents a get integration response.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration/responses/{statusCode}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetIntegrationResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a get integration response request's resource identifier.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a get integration response request's HTTP method.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

Specifies a get integration response request's status code.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a get integration response request.

" - } - }, - "com.amazonaws.apigateway#GetMethod": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetMethodRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Method" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Describe an existing Method resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetMethodRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Resource identifier for the Method resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the method request's HTTP method type.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to describe an existing Method resource.

" - } - }, - "com.amazonaws.apigateway#GetMethodResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetMethodResponseRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#MethodResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Describes a MethodResponse resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/responses/{statusCode}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetMethodResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Resource identifier for the MethodResponse resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The HTTP verb of the Method resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

The status code for the MethodResponse resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to describe a MethodResponse resource.

" - } - }, - "com.amazonaws.apigateway#GetModel": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetModelRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Model" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Describes an existing model defined for a RestApi resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/models/{modelName}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetModelRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The RestApi identifier under which the Model exists.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "modelName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the model as an identifier.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "flatten": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A query parameter of a Boolean value to resolve (true) all external model references and returns a flattened model schema or not (false) The default is false.

", - "smithy.api#httpQuery": "flatten" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to list information about a model in an existing RestApi resource.

" - } - }, - "com.amazonaws.apigateway#GetModelTemplate": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetModelTemplateRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Template" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Generates a sample mapping template that can be used to transform a payload into the structure of a model.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/models/{modelName}/default_template", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetModelTemplateRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "modelName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the model for which to generate a template.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to generate a sample mapping template used to transform the payload.

" - } - }, - "com.amazonaws.apigateway#GetModels": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetModelsRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Models" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Describes existing Models defined for a RestApi resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/models", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetModelsRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to list existing Models defined for a RestApi resource.

" - } - }, - "com.amazonaws.apigateway#GetRequestValidator": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetRequestValidatorRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#RequestValidator" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets a RequestValidator of a given RestApi.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/requestvalidators/{requestValidatorId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetRequestValidatorRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "requestValidatorId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the RequestValidator to be retrieved.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Gets a RequestValidator of a given RestApi.

" - } - }, - "com.amazonaws.apigateway#GetRequestValidators": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetRequestValidatorsRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#RequestValidators" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets the RequestValidators collection of a given RestApi.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/requestvalidators", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetRequestValidatorsRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

Gets the RequestValidators collection of a given RestApi.

" - } - }, - "com.amazonaws.apigateway#GetResource": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetResourceRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Resource" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Lists information about a resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/resources/{resourceId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetResourceRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier for the Resource resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "embed": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A query parameter to retrieve the specified resources embedded in the returned Resource representation in the response. This embed parameter value is a list of comma-separated strings. Currently, the request supports only retrieval of the embedded Method resources this way. The query parameter value must be a single-valued list and contain the \"methods\" string. For example, GET /restapis/{restapi_id}/resources/{resource_id}?embed=methods.

", - "smithy.api#httpQuery": "embed" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to list information about a resource.

" - } - }, - "com.amazonaws.apigateway#GetResources": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetResourcesRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Resources" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Lists information about a collection of Resource resources.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/resources", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetResourcesRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - }, - "embed": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A query parameter used to retrieve the specified resources embedded in the returned Resources resource in the response. This embed parameter value is a list of comma-separated strings. Currently, the request supports only retrieval of the embedded Method resources this way. The query parameter value must be a single-valued list and contain the \"methods\" string. For example, GET /restapis/{restapi_id}/resources?embed=methods.

", - "smithy.api#httpQuery": "embed" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to list information about a collection of resources.

" - } - }, - "com.amazonaws.apigateway#GetRestApi": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetRestApiRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#RestApi" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Lists the RestApi resource in the collection.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetRestApiRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

The GET request to list an existing RestApi defined for your collection.

" - } - }, - "com.amazonaws.apigateway#GetRestApis": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetRestApisRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#RestApis" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Lists the RestApis resources for your collection.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetRestApisRequest": { - "type": "structure", - "members": { - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

The GET request to list existing RestApis defined for your collection.

" - } - }, - "com.amazonaws.apigateway#GetSdk": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetSdkRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#SdkResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Generates a client SDK for a RestApi and Stage.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/stages/{stageName}/sdks/{sdkType}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetSdkRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the Stage that the SDK will use.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "sdkType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The language for the generated SDK. Currently java, javascript, android, objectivec (for iOS), swift (for iOS), and ruby are supported.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "parameters": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A string-to-string key-value map of query parameters sdkType-dependent properties of the SDK. For sdkType of objectivec or swift, a parameter named classPrefix is required. For sdkType of android, parameters named groupId, artifactId, artifactVersion, and invokerPackage are required. For sdkType of java, parameters named serviceName and javaPackageName are required.

", - "smithy.api#httpQueryParams": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Request a new generated client SDK for a RestApi and Stage.

" - } - }, - "com.amazonaws.apigateway#GetSdkType": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetSdkTypeRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#SdkType" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets an SDK type.

", - "smithy.api#http": { - "method": "GET", - "uri": "/sdktypes/{id}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetSdkTypeRequest": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the queried SdkType instance.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Get an SdkType instance.

" - } - }, - "com.amazonaws.apigateway#GetSdkTypes": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetSdkTypesRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#SdkTypes" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets SDK types

", - "smithy.api#http": { - "method": "GET", - "uri": "/sdktypes", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetSdkTypesRequest": { - "type": "structure", - "members": { - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

Get the SdkTypes collection.

" - } - }, - "com.amazonaws.apigateway#GetStage": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetStageRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Stage" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets information about a Stage resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/stages/{stageName}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetStageRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the Stage resource to get information about.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to get information about a Stage resource.

" - } - }, - "com.amazonaws.apigateway#GetStages": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetStagesRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Stages" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets information about one or more Stage resources.

", - "smithy.api#http": { - "method": "GET", - "uri": "/restapis/{restApiId}/stages", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetStagesRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "deploymentId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The stages' deployment identifiers.

", - "smithy.api#httpQuery": "deploymentId" - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to get information about one or more Stage resources.

" - } - }, - "com.amazonaws.apigateway#GetTags": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetTagsRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Tags" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets the Tags collection for a given resource.

", - "smithy.api#http": { - "method": "GET", - "uri": "/tags/{resourceArn}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetTagsRequest": { - "type": "structure", - "members": { - "resourceArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ARN of a resource that can be tagged.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

(Not currently supported) The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

(Not currently supported) The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

Gets the Tags collection for a given resource.

" - } - }, - "com.amazonaws.apigateway#GetUsage": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetUsageRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Usage" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets the usage data of a usage plan in a specified time interval.

", - "smithy.api#http": { - "method": "GET", - "uri": "/usageplans/{usagePlanId}/usage", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetUsagePlan": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetUsagePlanRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#UsagePlan" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets a usage plan of a given plan identifier.

", - "smithy.api#http": { - "method": "GET", - "uri": "/usageplans/{usagePlanId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetUsagePlanKey": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetUsagePlanKeyRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#UsagePlanKey" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets a usage plan key of a given key identifier.

", - "smithy.api#http": { - "method": "GET", - "uri": "/usageplans/{usagePlanId}/keys/{keyId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetUsagePlanKeyRequest": { - "type": "structure", - "members": { - "usagePlanId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Id of the UsagePlan resource representing the usage plan containing the to-be-retrieved UsagePlanKey resource representing a plan customer.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "keyId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The key Id of the to-be-retrieved UsagePlanKey resource representing a plan customer.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

The GET request to get a usage plan key of a given key identifier.

" - } - }, - "com.amazonaws.apigateway#GetUsagePlanKeys": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetUsagePlanKeysRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#UsagePlanKeys" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets all the usage plan keys representing the API keys added to a specified usage plan.

", - "smithy.api#http": { - "method": "GET", - "uri": "/usageplans/{usagePlanId}/keys", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetUsagePlanKeysRequest": { - "type": "structure", - "members": { - "usagePlanId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Id of the UsagePlan resource representing the usage plan containing the to-be-retrieved UsagePlanKey resource representing a plan customer.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - }, - "nameQuery": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A query parameter specifying the name of the to-be-returned usage plan keys.

", - "smithy.api#httpQuery": "name" - } - } - }, - "traits": { - "smithy.api#documentation": "

The GET request to get all the usage plan keys representing the API keys added to a specified usage plan.

" - } - }, - "com.amazonaws.apigateway#GetUsagePlanRequest": { - "type": "structure", - "members": { - "usagePlanId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the UsagePlan resource to be retrieved.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

The GET request to get a usage plan of a given plan identifier.

" - } - }, - "com.amazonaws.apigateway#GetUsagePlans": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetUsagePlansRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#UsagePlans" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets all the usage plans of the caller's account.

", - "smithy.api#http": { - "method": "GET", - "uri": "/usageplans", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetUsagePlansRequest": { - "type": "structure", - "members": { - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "keyId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the API key associated with the usage plans.

", - "smithy.api#httpQuery": "keyId" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

The GET request to get all the usage plans of the caller's account.

" - } - }, - "com.amazonaws.apigateway#GetUsageRequest": { - "type": "structure", - "members": { - "usagePlanId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Id of the usage plan associated with the usage data.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "keyId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Id of the API key associated with the resultant usage data.

", - "smithy.api#httpQuery": "keyId" - } - }, - "startDate": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The starting date (e.g., 2016-01-01) of the usage data.

", - "smithy.api#httpQuery": "startDate", - "smithy.api#required": {} - } - }, - "endDate": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ending date (e.g., 2016-12-31) of the usage data.

", - "smithy.api#httpQuery": "endDate", - "smithy.api#required": {} - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

The GET request to get the usage data of a usage plan in a specified time interval.

" - } - }, - "com.amazonaws.apigateway#GetVpcLink": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetVpcLinkRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#VpcLink" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets a specified VPC link under the caller's account in a region.

", - "smithy.api#http": { - "method": "GET", - "uri": "/vpclinks/{vpcLinkId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#GetVpcLinkRequest": { - "type": "structure", - "members": { - "vpcLinkId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the VpcLink. It is used in an Integration to reference this VpcLink.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Gets a specified VPC link under the caller's account in a region.

" - } - }, - "com.amazonaws.apigateway#GetVpcLinks": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#GetVpcLinksRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#VpcLinks" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Gets the VpcLinks collection under the caller's account in a selected region.

", - "smithy.api#http": { - "method": "GET", - "uri": "/vpclinks", - "code": 200 - }, - "smithy.api#paginated": { - "inputToken": "position", - "outputToken": "position", - "items": "items", - "pageSize": "limit" - } - } - }, - "com.amazonaws.apigateway#GetVpcLinksRequest": { - "type": "structure", - "members": { - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - }, - "limit": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

The maximum number of returned results per page. The default value is 25 and the maximum value is 500.

", - "smithy.api#httpQuery": "limit" - } - } - }, - "traits": { - "smithy.api#documentation": "

Gets the VpcLinks collection under the caller's account in a selected region.

" - } - }, - "com.amazonaws.apigateway#ImportApiKeys": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#ImportApiKeysRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#ApiKeyIds" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Import API keys from an external source, such as a CSV-formatted file.

", - "smithy.api#http": { - "method": "POST", - "uri": "/apikeys?mode=import", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#ImportApiKeysRequest": { - "type": "structure", - "members": { - "body": { - "target": "com.amazonaws.apigateway#Blob", - "traits": { - "smithy.api#documentation": "

The payload of the POST request to import API keys. For the payload format, see API Key File Format.

", - "smithy.api#httpPayload": {}, - "smithy.api#required": {} - } - }, - "format": { - "target": "com.amazonaws.apigateway#ApiKeysFormat", - "traits": { - "smithy.api#documentation": "

A query parameter to specify the input format to imported API keys. Currently, only the csv format is supported.

", - "smithy.api#httpQuery": "format", - "smithy.api#required": {} - } - }, - "failOnWarnings": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A query parameter to indicate whether to rollback ApiKey importation (true) or not (false) when error is encountered.

", - "smithy.api#httpQuery": "failonwarnings" - } - } - }, - "traits": { - "smithy.api#documentation": "

The POST request to import API keys from an external source, such as a CSV-formatted file.

" - } - }, - "com.amazonaws.apigateway#ImportDocumentationParts": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#ImportDocumentationPartsRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DocumentationPartIds" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Imports documentation parts

", - "smithy.api#http": { - "method": "PUT", - "uri": "/restapis/{restApiId}/documentation/parts", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#ImportDocumentationPartsRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "mode": { - "target": "com.amazonaws.apigateway#PutMode", - "traits": { - "smithy.api#documentation": "

A query parameter to indicate whether to overwrite (OVERWRITE) any existing DocumentationParts definition or to merge (MERGE) the new definition into the existing one. The default value is MERGE.

", - "smithy.api#httpQuery": "mode" - } - }, - "failOnWarnings": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A query parameter to specify whether to rollback the documentation importation (true) or not (false) when a warning is encountered. The default value is false.

", - "smithy.api#httpQuery": "failonwarnings" - } - }, - "body": { - "target": "com.amazonaws.apigateway#Blob", - "traits": { - "smithy.api#documentation": "

Raw byte array representing the to-be-imported documentation parts. To import from an OpenAPI file, this is a JSON object.

", - "smithy.api#httpPayload": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Import documentation parts from an external (e.g., OpenAPI) definition file.

" - } - }, - "com.amazonaws.apigateway#ImportRestApi": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#ImportRestApiRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#RestApi" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

A feature of the API Gateway control service for creating a new API from an external API definition file.

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis?mode=import", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#ImportRestApiRequest": { - "type": "structure", - "members": { - "failOnWarnings": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A query parameter to indicate whether to rollback the API creation (true) or not (false)\n when a warning is encountered. The default value is false.

", - "smithy.api#httpQuery": "failonwarnings" - } - }, - "parameters": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map of context-specific query string parameters specifying the behavior of different API importing operations. The following shows operation-specific parameters and their supported values.

\n

To exclude DocumentationParts from the import, set parameters as ignore=documentation.

\n

To configure the endpoint type, set parameters as endpointConfigurationTypes=EDGE, endpointConfigurationTypes=REGIONAL, or endpointConfigurationTypes=PRIVATE. The default endpoint type is EDGE.

\n

To handle imported basepath, set parameters as basepath=ignore, basepath=prepend or basepath=split.

\n

For example, the AWS CLI command to exclude documentation from the imported API is:

\n

The AWS CLI command to set the regional endpoint on the imported API is:

", - "smithy.api#httpQueryParams": {} - } - }, - "body": { - "target": "com.amazonaws.apigateway#Blob", - "traits": { - "smithy.api#documentation": "

The POST request body containing external API definitions. Currently, only OpenAPI definition JSON/YAML files are supported. The maximum size of the API definition file is 6MB.

", - "smithy.api#httpPayload": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

A POST request to import an API to API Gateway using an input of an API definition file.

" - } - }, - "com.amazonaws.apigateway#Integer": { - "type": "integer", - "traits": { - "smithy.api#default": 0 - } - }, - "com.amazonaws.apigateway#Integration": { - "type": "structure", - "members": { - "type": { - "target": "com.amazonaws.apigateway#IntegrationType", - "traits": { - "smithy.api#documentation": "

Specifies an API method integration type. The valid value is one of the following:

\n

For the HTTP and HTTP proxy integrations, each integration can specify a protocol (http/https), port and path. Standard 80 and 443 ports are supported as well as custom ports above 1024. An HTTP or HTTP proxy integration with a connectionType of VPC_LINK is referred to as a private integration and uses a VpcLink to connect API Gateway to a network load balancer of a VPC.

" - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the integration's HTTP method type.

" - } - }, - "uri": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies Uniform Resource Identifier (URI) of the integration endpoint.

\n

For HTTP or HTTP_PROXY integrations, the URI must be a fully formed, encoded HTTP(S) URL\n according to the RFC-3986 specification, for either standard integration, where connectionType\n is not VPC_LINK, or private integration, where connectionType is VPC_LINK. For a private HTTP\n integration, the URI is not used for routing. For AWS or AWS_PROXY integrations, the URI is of\n the form arn:aws:apigateway:{region}:{subdomain.service|service}:path|action/{service_api}.\n Here, {Region} is the API Gateway region (e.g., us-east-1); {service} is the name of the\n integrated Amazon Web Services service (e.g., s3); and {subdomain} is a designated subdomain supported by\n certain Amazon Web Services service for fast host-name lookup. action can be used for an Amazon Web Services service\n action-based API, using an Action={name}&{p1}={v1}&p2={v2}... query string. The ensuing\n {service_api} refers to a supported action {name} plus any required input parameters.\n Alternatively, path can be used for an AWS service path-based API. The ensuing service_api\n refers to the path to an Amazon Web Services service resource, including the region of the integrated Amazon Web Services \n service, if applicable. For example, for integration with the S3 API of GetObject, the uri can\n be either arn:aws:apigateway:us-west-2:s3:action/GetObject&Bucket={bucket}&Key={key} or\n arn:aws:apigateway:us-west-2:s3:path/{bucket}/{key}\n

" - } - }, - "connectionType": { - "target": "com.amazonaws.apigateway#ConnectionType", - "traits": { - "smithy.api#documentation": "

The type of the network connection to the integration endpoint. The valid value is INTERNET for connections through the public routable internet or VPC_LINK for private connections between API Gateway and a network load balancer in a VPC. The default value is INTERNET.

" - } - }, - "connectionId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ID of the VpcLink used for the integration when connectionType=VPC_LINK and undefined, otherwise.

" - } - }, - "credentials": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the credentials required for the integration, if any. For AWS integrations, three options are available. To specify an IAM Role for API Gateway to assume, use the role's Amazon Resource Name (ARN). To require that the caller's identity be passed through from the request, specify the string arn:aws:iam::\\*:user/\\*. To use resource-based permissions on supported AWS services, specify null.

" - } - }, - "requestParameters": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map specifying request parameters that are passed from the method request to the back end. The key is an integration request parameter name and the associated value is a method request parameter value or static value that must be enclosed within single quotes and pre-encoded as required by the back end. The method request parameter value must match the pattern of method.request.{location}.{name}, where location is querystring, path, or header and name must be a valid and unique method request parameter name.

" - } - }, - "requestTemplates": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Represents a map of Velocity templates that are applied on the request payload based on the value of the Content-Type header sent by the client. The content type value is the key in this map, and the template (as a String) is the value.

" - } - }, - "passthroughBehavior": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies how the method request body of an unmapped content type will be passed through\n the integration request to the back end without transformation. A content type is unmapped if\n no mapping template is defined in the integration or the content type does not match any of\n the mapped content types, as specified in requestTemplates. The valid value is one of the\n following: WHEN_NO_MATCH: passes the method request body through the integration request to\n the back end without transformation when the method request content type does not match any\n content type associated with the mapping templates defined in the integration request.\n WHEN_NO_TEMPLATES: passes the method request body through the integration request to the back\n end without transformation when no mapping template is defined in the integration request. If\n a template is defined when this option is selected, the method request of an unmapped\n content-type will be rejected with an HTTP 415 Unsupported Media Type response. NEVER: rejects\n the method request with an HTTP 415 Unsupported Media Type response when either the method\n request content type does not match any content type associated with the mapping templates\n defined in the integration request or no mapping template is defined in the integration\n request.

" - } - }, - "contentHandling": { - "target": "com.amazonaws.apigateway#ContentHandlingStrategy", - "traits": { - "smithy.api#documentation": "

Specifies how to handle request payload content type conversions. Supported values are CONVERT_TO_BINARY and CONVERT_TO_TEXT, with the following behaviors:

\n

If this property is not defined, the request payload will be passed through from the method request to integration request without modification, provided that the passthroughBehavior is configured to support payload pass-through.

" - } - }, - "timeoutInMillis": { - "target": "com.amazonaws.apigateway#Integer", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

Custom timeout between 50 and 29,000 milliseconds. The default value is 29,000 milliseconds or 29 seconds.

" - } - }, - "cacheNamespace": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a group of related cached parameters. By default, API Gateway uses the resource ID as the cacheNamespace. You can specify the same cacheNamespace across resources to return the same cached data for requests to different resources.

" - } - }, - "cacheKeyParameters": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of request parameters whose values API Gateway caches. To be valid values for cacheKeyParameters, these parameters must also be specified for Method requestParameters.

" - } - }, - "integrationResponses": { - "target": "com.amazonaws.apigateway#MapOfIntegrationResponse", - "traits": { - "smithy.api#documentation": "

Specifies the integration's responses.

" - } - }, - "tlsConfig": { - "target": "com.amazonaws.apigateway#TlsConfig", - "traits": { - "smithy.api#documentation": "

Specifies the TLS configuration for an integration.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents an HTTP, HTTP_PROXY, AWS, AWS_PROXY, or Mock integration.

" - } - }, - "com.amazonaws.apigateway#IntegrationResponse": { - "type": "structure", - "members": { - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

Specifies the status code that is used to map the integration response to an existing MethodResponse.

" - } - }, - "selectionPattern": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the regular expression (regex) pattern used to choose an integration response based on the response from the back end. For example, if the success response returns nothing and the error response returns some string, you could use the .+ regex to match error response. However, make sure that the error response does not contain any newline (\\n) character in such cases. If the back end is an AWS Lambda function, the AWS Lambda function error header is matched. For all other HTTP and AWS back ends, the HTTP status code is matched.

" - } - }, - "responseParameters": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map specifying response parameters that are passed to the method response from the back end.\n The key is a method response header parameter name and the mapped value is an integration response header value, a static value enclosed within a pair of single quotes, or a JSON expression from the integration response body. The mapping key must match the pattern of method.response.header.{name}, where name is a valid and unique header name. The mapped non-static value must match the pattern of integration.response.header.{name} or integration.response.body.{JSON-expression}, where name is a valid and unique response header name and JSON-expression is a valid JSON expression without the $ prefix.

" - } - }, - "responseTemplates": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Specifies the templates used to transform the integration response body. Response templates are represented as a key/value map, with a content-type as the key and a template as the value.

" - } - }, - "contentHandling": { - "target": "com.amazonaws.apigateway#ContentHandlingStrategy", - "traits": { - "smithy.api#documentation": "

Specifies how to handle response payload content type conversions. Supported values are CONVERT_TO_BINARY and CONVERT_TO_TEXT, with the following behaviors:

\n

If this property is not defined, the response payload will be passed through from the integration response to the method response without modification.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents an integration response. The status code must map to an existing MethodResponse, and parameters and templates can be used to transform the back-end response.

" - } - }, - "com.amazonaws.apigateway#IntegrationType": { - "type": "enum", - "members": { - "HTTP": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "HTTP" - } - }, - "AWS": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "AWS" - } - }, - "MOCK": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "MOCK" - } - }, - "HTTP_PROXY": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "HTTP_PROXY" - } - }, - "AWS_PROXY": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "AWS_PROXY" - } - } - }, - "traits": { - "smithy.api#documentation": "

The integration type. The valid value is HTTP for integrating an API method with an HTTP backend; AWS with any AWS service endpoints; MOCK for testing without actually invoking the backend; HTTP_PROXY for integrating with the HTTP proxy integration; AWS_PROXY for integrating with the Lambda proxy integration.

" - } - }, - "com.amazonaws.apigateway#LimitExceededException": { - "type": "structure", - "members": { - "retryAfterSeconds": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#httpHeader": "Retry-After" - } - }, - "message": { - "target": "com.amazonaws.apigateway#String" - } - }, - "traits": { - "smithy.api#documentation": "

The request exceeded the rate limit. Retry after the specified time period.

", - "smithy.api#error": "client", - "smithy.api#httpError": 429 - } - }, - "com.amazonaws.apigateway#ListOfARNs": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#ProviderARN" - } - }, - "com.amazonaws.apigateway#ListOfApiKey": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#ApiKey" - } - }, - "com.amazonaws.apigateway#ListOfApiStage": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#ApiStage" - } - }, - "com.amazonaws.apigateway#ListOfAuthorizer": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#Authorizer" - } - }, - "com.amazonaws.apigateway#ListOfBasePathMapping": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#BasePathMapping" - } - }, - "com.amazonaws.apigateway#ListOfClientCertificate": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#ClientCertificate" - } - }, - "com.amazonaws.apigateway#ListOfDeployment": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#Deployment" - } - }, - "com.amazonaws.apigateway#ListOfDocumentationPart": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#DocumentationPart" - } - }, - "com.amazonaws.apigateway#ListOfDocumentationVersion": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#DocumentationVersion" - } - }, - "com.amazonaws.apigateway#ListOfDomainName": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#DomainName" - } - }, - "com.amazonaws.apigateway#ListOfEndpointType": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#EndpointType" - } - }, - "com.amazonaws.apigateway#ListOfGatewayResponse": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#GatewayResponse" - } - }, - "com.amazonaws.apigateway#ListOfLong": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#Long" - } - }, - "com.amazonaws.apigateway#ListOfModel": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#Model" - } - }, - "com.amazonaws.apigateway#ListOfPatchOperation": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#PatchOperation" - }, - "traits": { - "smithy.api#documentation": "

A list of operations describing the updates to apply to the specified resource. The patches are applied\n in the order specified in the list.

" - } - }, - "com.amazonaws.apigateway#ListOfRequestValidator": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#RequestValidator" - } - }, - "com.amazonaws.apigateway#ListOfResource": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#Resource" - } - }, - "com.amazonaws.apigateway#ListOfRestApi": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#RestApi" - } - }, - "com.amazonaws.apigateway#ListOfSdkConfigurationProperty": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#SdkConfigurationProperty" - } - }, - "com.amazonaws.apigateway#ListOfSdkType": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#SdkType" - } - }, - "com.amazonaws.apigateway#ListOfStage": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#Stage" - } - }, - "com.amazonaws.apigateway#ListOfStageKeys": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#StageKey" - } - }, - "com.amazonaws.apigateway#ListOfString": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#String" - } - }, - "com.amazonaws.apigateway#ListOfUsage": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#ListOfLong" - } - }, - "com.amazonaws.apigateway#ListOfUsagePlan": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#UsagePlan" - } - }, - "com.amazonaws.apigateway#ListOfUsagePlanKey": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#UsagePlanKey" - } - }, - "com.amazonaws.apigateway#ListOfVpcLink": { - "type": "list", - "member": { - "target": "com.amazonaws.apigateway#VpcLink" - } - }, - "com.amazonaws.apigateway#LocationStatusType": { - "type": "enum", - "members": { - "DOCUMENTED": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "DOCUMENTED" - } - }, - "UNDOCUMENTED": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "UNDOCUMENTED" - } - } - } - }, - "com.amazonaws.apigateway#Long": { - "type": "long", - "traits": { - "smithy.api#default": 0 - } - }, - "com.amazonaws.apigateway#MapOfApiStageThrottleSettings": { - "type": "map", - "key": { - "target": "com.amazonaws.apigateway#String" - }, - "value": { - "target": "com.amazonaws.apigateway#ThrottleSettings" - } - }, - "com.amazonaws.apigateway#MapOfIntegrationResponse": { - "type": "map", - "key": { - "target": "com.amazonaws.apigateway#String" - }, - "value": { - "target": "com.amazonaws.apigateway#IntegrationResponse" - } - }, - "com.amazonaws.apigateway#MapOfKeyUsages": { - "type": "map", - "key": { - "target": "com.amazonaws.apigateway#String" - }, - "value": { - "target": "com.amazonaws.apigateway#ListOfUsage" - } - }, - "com.amazonaws.apigateway#MapOfMethod": { - "type": "map", - "key": { - "target": "com.amazonaws.apigateway#String" - }, - "value": { - "target": "com.amazonaws.apigateway#Method" - } - }, - "com.amazonaws.apigateway#MapOfMethodResponse": { - "type": "map", - "key": { - "target": "com.amazonaws.apigateway#String" - }, - "value": { - "target": "com.amazonaws.apigateway#MethodResponse" - } - }, - "com.amazonaws.apigateway#MapOfMethodSettings": { - "type": "map", - "key": { - "target": "com.amazonaws.apigateway#String" - }, - "value": { - "target": "com.amazonaws.apigateway#MethodSetting" - } - }, - "com.amazonaws.apigateway#MapOfMethodSnapshot": { - "type": "map", - "key": { - "target": "com.amazonaws.apigateway#String" - }, - "value": { - "target": "com.amazonaws.apigateway#MethodSnapshot" - } - }, - "com.amazonaws.apigateway#MapOfStringToBoolean": { - "type": "map", - "key": { - "target": "com.amazonaws.apigateway#String" - }, - "value": { - "target": "com.amazonaws.apigateway#NullableBoolean" - } - }, - "com.amazonaws.apigateway#MapOfStringToList": { - "type": "map", - "key": { - "target": "com.amazonaws.apigateway#String" - }, - "value": { - "target": "com.amazonaws.apigateway#ListOfString" - } - }, - "com.amazonaws.apigateway#MapOfStringToString": { - "type": "map", - "key": { - "target": "com.amazonaws.apigateway#String" - }, - "value": { - "target": "com.amazonaws.apigateway#String" - } - }, - "com.amazonaws.apigateway#Method": { - "type": "structure", - "members": { - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The method's HTTP verb.

" - } - }, - "authorizationType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The method's authorization type. Valid values are NONE for open access, AWS_IAM for using AWS IAM permissions, CUSTOM for using a custom authorizer, or COGNITO_USER_POOLS for using a Cognito user pool.

" - } - }, - "authorizerId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of an Authorizer to use on this method. The authorizationType must be CUSTOM.

" - } - }, - "apiKeyRequired": { - "target": "com.amazonaws.apigateway#NullableBoolean", - "traits": { - "smithy.api#documentation": "

A boolean flag specifying whether a valid ApiKey is required to invoke this method.

" - } - }, - "requestValidatorId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of a RequestValidator for request validation.

" - } - }, - "operationName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A human-friendly operation identifier for the method. For example, you can assign the operationName of ListPets for the GET /pets method in the PetStore example.

" - } - }, - "requestParameters": { - "target": "com.amazonaws.apigateway#MapOfStringToBoolean", - "traits": { - "smithy.api#documentation": "

A key-value map defining required or optional method request parameters that can be accepted by API Gateway. A key is a method request parameter name matching the pattern of method.request.{location}.{name}, where location is querystring, path, or header and name is a valid and unique parameter name. The value associated with the key is a Boolean flag indicating whether the parameter is required (true) or optional (false). The method request parameter names defined here are available in Integration to be mapped to integration request parameters or templates.

" - } - }, - "requestModels": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map specifying data schemas, represented by Model resources, (as the mapped value) of the request payloads of given content types (as the mapping key).

" - } - }, - "methodResponses": { - "target": "com.amazonaws.apigateway#MapOfMethodResponse", - "traits": { - "smithy.api#documentation": "

Gets a method response associated with a given HTTP status code.

" - } - }, - "methodIntegration": { - "target": "com.amazonaws.apigateway#Integration", - "traits": { - "smithy.api#documentation": "

Gets the method's integration responsible for passing the client-submitted request to the back end and performing necessary transformations to make the request compliant with the back end.

" - } - }, - "authorizationScopes": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of authorization scopes configured on the method. The scopes are used with a COGNITO_USER_POOLS authorizer to authorize the method invocation. The authorization works by matching the method scopes against the scopes parsed from the access token in the incoming request. The method invocation is authorized if any method scopes matches a claimed scope in the access token. Otherwise, the invocation is not authorized. When the method scope is configured, the client must provide an access token instead of an identity token for authorization purposes.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

\n Represents a client-facing interface by which the client calls the API to access back-end resources. A Method resource is\n integrated with an Integration resource. Both consist of a request and one or more responses. The method request takes\n the client input that is passed to the back end through the integration request. A method response returns the output from\n the back end to the client through an integration response. A method request is embodied in a Method resource, whereas\n an integration request is embodied in an Integration resource. On the other hand, a method response is represented\n by a MethodResponse resource, whereas an integration response is represented by an IntegrationResponse resource.\n

" - } - }, - "com.amazonaws.apigateway#MethodResponse": { - "type": "structure", - "members": { - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

The method response's status code.

" - } - }, - "responseParameters": { - "target": "com.amazonaws.apigateway#MapOfStringToBoolean", - "traits": { - "smithy.api#documentation": "

A key-value map specifying required or optional response parameters that API Gateway can send back to the caller. A key defines a method response header and the value specifies whether the associated method response header is required or not. The expression of the key must match the pattern method.response.header.{name}, where name is a valid and unique header name. API Gateway passes certain integration response data to the method response headers specified here according to the mapping you prescribe in the API's IntegrationResponse. The integration response data that can be mapped include an integration response header expressed in integration.response.header.{name}, a static value enclosed within a pair of single quotes (e.g., 'application/json'), or a JSON expression from the back-end response payload in the form of integration.response.body.{JSON-expression}, where JSON-expression is a valid JSON expression without the $ prefix.)

" - } - }, - "responseModels": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Specifies the Model resources used for the response's content-type. Response models are represented as a key/value map, with a content-type as the key and a Model name as the value.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a method response of a given HTTP status code returned to the client. The method response is passed from the back end through the associated integration response that can be transformed using a mapping template.

" - } - }, - "com.amazonaws.apigateway#MethodSetting": { - "type": "structure", - "members": { - "metricsEnabled": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon CloudWatch metrics are enabled for this method. The PATCH path for this setting is /{method_setting_key}/metrics/enabled, and the value is a Boolean.

" - } - }, - "loggingLevel": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the logging level for this method, which affects the log entries pushed to Amazon CloudWatch Logs. The PATCH path for this setting is /{method_setting_key}/logging/loglevel, and the available levels are OFF, ERROR, and INFO. Choose ERROR to write only error-level entries to CloudWatch Logs, or choose INFO to include all ERROR events as well as extra informational events.

" - } - }, - "dataTraceEnabled": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether data trace logging is enabled for this method, which affects the log entries pushed to Amazon CloudWatch Logs. The PATCH path for this setting is /{method_setting_key}/logging/dataTrace, and the value is a Boolean.

" - } - }, - "throttlingBurstLimit": { - "target": "com.amazonaws.apigateway#Integer", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

Specifies the throttling burst limit. The PATCH path for this setting is /{method_setting_key}/throttling/burstLimit, and the value is an integer.

" - } - }, - "throttlingRateLimit": { - "target": "com.amazonaws.apigateway#Double", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

Specifies the throttling rate limit. The PATCH path for this setting is /{method_setting_key}/throttling/rateLimit, and the value is a double.

" - } - }, - "cachingEnabled": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether responses should be cached and returned for requests. A cache cluster must be enabled on the stage for responses to be cached. The PATCH path for this setting is /{method_setting_key}/caching/enabled, and the value is a Boolean.

" - } - }, - "cacheTtlInSeconds": { - "target": "com.amazonaws.apigateway#Integer", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

Specifies the time to live (TTL), in seconds, for cached responses. The higher the TTL, the longer the response will be cached. The PATCH path for this setting is /{method_setting_key}/caching/ttlInSeconds, and the value is an integer.

" - } - }, - "cacheDataEncrypted": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether the cached responses are encrypted. The PATCH path for this setting is /{method_setting_key}/caching/dataEncrypted, and the value is a Boolean.

" - } - }, - "requireAuthorizationForCacheControl": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether authorization is required for a cache invalidation request. The PATCH path for this setting is /{method_setting_key}/caching/requireAuthorizationForCacheControl, and the value is a Boolean.

" - } - }, - "unauthorizedCacheControlHeaderStrategy": { - "target": "com.amazonaws.apigateway#UnauthorizedCacheControlHeaderStrategy", - "traits": { - "smithy.api#documentation": "

Specifies how to handle unauthorized requests for cache invalidation. The PATCH path for this setting is /{method_setting_key}/caching/unauthorizedCacheControlHeaderStrategy, and the available values are FAIL_WITH_403, SUCCEED_WITH_RESPONSE_HEADER, SUCCEED_WITHOUT_RESPONSE_HEADER.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Specifies the method setting properties.

" - } - }, - "com.amazonaws.apigateway#MethodSnapshot": { - "type": "structure", - "members": { - "authorizationType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The method's authorization type. Valid values are NONE for open access, AWS_IAM for using AWS IAM permissions, CUSTOM for using a custom authorizer, or COGNITO_USER_POOLS for using a Cognito user pool.

" - } - }, - "apiKeyRequired": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether the method requires a valid ApiKey.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a summary of a Method resource, given a particular date and time.

" - } - }, - "com.amazonaws.apigateway#Model": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier for the model resource.

" - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the model. Must be an alphanumeric string.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the model.

" - } - }, - "schema": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The schema for the model. For application/json models, this should be JSON schema draft 4 model. Do not include \"\\*/\" characters in the description of any properties because such \"\\*/\" characters may be interpreted as the closing marker for comments in some languages, such as Java or JavaScript, causing the installation of your API's SDK generated by API Gateway to fail.

" - } - }, - "contentType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The content-type for the model.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents the data structure of a method's request or response payload.

" - } - }, - "com.amazonaws.apigateway#Models": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfModel", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a collection of Model resources.

" - } - }, - "com.amazonaws.apigateway#MutualTlsAuthentication": { - "type": "structure", - "members": { - "truststoreUri": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

An Amazon S3 URL that specifies the truststore for mutual TLS authentication, for example\n s3://bucket-name/key-name. The truststore can contain certificates from public or private\n certificate authorities. To update the truststore, upload a new version to S3, and then update\n your custom domain name to use the new version. To update the truststore, you must have\n permissions to access the S3 object.

" - } - }, - "truststoreVersion": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The version of the S3 object that contains your truststore. To specify a version, you must have versioning enabled for the S3 bucket.

" - } - }, - "truststoreWarnings": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of warnings that API Gateway returns while processing your truststore. Invalid\n certificates produce warnings. Mutual TLS is still enabled, but some clients might not be able\n to access your API. To resolve warnings, upload a new truststore to S3, and then update you\n domain name to use the new version.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

The mutual TLS authentication configuration for a custom domain name. If specified, API Gateway\n performs two-way authentication between the client and the server. Clients must present a\n trusted certificate to access your API.

" - } - }, - "com.amazonaws.apigateway#MutualTlsAuthenticationInput": { - "type": "structure", - "members": { - "truststoreUri": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

An Amazon S3 URL that specifies the truststore for mutual TLS authentication, for example\n s3://bucket-name/key-name. The truststore can contain certificates from public or private\n certificate authorities. To update the truststore, upload a new version to S3, and then update\n your custom domain name to use the new version. To update the truststore, you must have\n permissions to access the S3 object.

" - } - }, - "truststoreVersion": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The version of the S3 object that contains your truststore. To specify a version, you must have versioning enabled for the S3 bucket

" - } - } - }, - "traits": { - "smithy.api#documentation": "

The mutual TLS authentication configuration for a custom domain name. If specified, API Gateway\n performs two-way authentication between the client and the server. Clients must present a\n trusted certificate to access your API.

" - } - }, - "com.amazonaws.apigateway#NotFoundException": { - "type": "structure", - "members": { - "message": { - "target": "com.amazonaws.apigateway#String" - } - }, - "traits": { - "smithy.api#documentation": "

The requested resource is not found. Make sure that the request URI is correct.

", - "smithy.api#error": "client", - "smithy.api#httpError": 404 - } - }, - "com.amazonaws.apigateway#NullableBoolean": { - "type": "boolean" - }, - "com.amazonaws.apigateway#NullableInteger": { - "type": "integer" - }, - "com.amazonaws.apigateway#Op": { - "type": "enum", - "members": { - "add": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "add" - } - }, - "remove": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "remove" - } - }, - "replace": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "replace" - } - }, - "move": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "move" - } - }, - "copy": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "copy" - } - }, - "test": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "test" - } - } - } - }, - "com.amazonaws.apigateway#PatchOperation": { - "type": "structure", - "members": { - "op": { - "target": "com.amazonaws.apigateway#Op", - "traits": { - "smithy.api#documentation": "

An update operation to be performed with this PATCH request. The valid value can be\n add, remove, replace or copy. Not all valid operations are supported for a given\n resource. Support of the operations depends on specific operational contexts. Attempts\n to apply an unsupported operation on a resource will return an error message..

" - } - }, - "path": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The op operation's target, as identified by a JSON Pointer value that references a\n location within the targeted resource. For example, if the target resource has an\n updateable property of {\"name\":\"value\"}, the path for this property is /name. If the\n name property value is a JSON object (e.g., {\"name\": {\"child/name\": \"child-value\"}}),\n the path for the child/name property will be /name/child~1name. Any slash (\"/\")\n character appearing in path names must be escaped with \"~1\", as shown in the example\n above. Each op operation can have only one path associated with it.

" - } - }, - "value": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The new target value of the update operation. It is applicable for the add or replace\n operation. When using AWS CLI to update a property of a JSON value, enclose the JSON\n object with a pair of single quotes in a Linux shell, e.g., '{\"a\": ...}'.

" - } - }, - "from": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The copy update operation's source as identified by a JSON-Pointer value referencing\n the location within the targeted resource to copy the value from. For example, to\n promote a canary deployment, you copy the canary deployment ID to the affiliated\n deployment ID by calling a PATCH request on a Stage resource with \"op\":\"copy\",\n \"from\":\"/canarySettings/deploymentId\" and \"path\":\"/deploymentId\".

" - } - } - }, - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - }, - "com.amazonaws.apigateway#PathToMapOfMethodSnapshot": { - "type": "map", - "key": { - "target": "com.amazonaws.apigateway#String" - }, - "value": { - "target": "com.amazonaws.apigateway#MapOfMethodSnapshot" - } - }, - "com.amazonaws.apigateway#ProviderARN": { - "type": "string" - }, - "com.amazonaws.apigateway#PutGatewayResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#PutGatewayResponseRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#GatewayResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Creates a customization of a GatewayResponse of a specified response type and status code on the given RestApi.

", - "smithy.api#http": { - "method": "PUT", - "uri": "/restapis/{restApiId}/gatewayresponses/{responseType}", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#PutGatewayResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "responseType": { - "target": "com.amazonaws.apigateway#GatewayResponseType", - "traits": { - "smithy.api#documentation": "

The response type of the associated GatewayResponse

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

The HTTP status code of the GatewayResponse.

" - } - }, - "responseParameters": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Response parameters (paths, query strings and headers) of the GatewayResponse as a string-to-string map of key-value pairs.

" - } - }, - "responseTemplates": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Response templates of the GatewayResponse as a string-to-string map of key-value pairs.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Creates a customization of a GatewayResponse of a specified response type and status code on the given RestApi.

" - } - }, - "com.amazonaws.apigateway#PutIntegration": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#PutIntegrationRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Integration" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Sets up a method's integration.

", - "smithy.api#http": { - "method": "PUT", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#PutIntegrationRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a put integration request's resource ID.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the HTTP method for the integration.

", - "smithy.api#httpLabel": {}, - "smithy.api#jsonName": "requestHttpMethod", - "smithy.api#required": {} - } - }, - "type": { - "target": "com.amazonaws.apigateway#IntegrationType", - "traits": { - "smithy.api#documentation": "

Specifies a put integration input's type.

", - "smithy.api#required": {} - } - }, - "integrationHttpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The HTTP method for the integration.

", - "smithy.api#jsonName": "httpMethod" - } - }, - "uri": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies Uniform Resource Identifier (URI) of the integration endpoint. For HTTP or\n HTTP_PROXY integrations, the URI must be a fully formed, encoded HTTP(S) URL according to the\n RFC-3986 specification, for either standard integration, where connectionType is not VPC_LINK,\n or private integration, where connectionType is VPC_LINK. For a private HTTP integration, the\n URI is not used for routing. For AWS or AWS_PROXY integrations, the URI is of the form\n arn:aws:apigateway:{region}:{subdomain.service|service}:path|action/{service_api}. Here,\n {Region} is the API Gateway region (e.g., us-east-1); {service} is the name of the integrated\n Amazon Web Services service (e.g., s3); and {subdomain} is a designated subdomain supported by certain Amazon Web Services\n service for fast host-name lookup. action can be used for an Amazon Web Services service action-based API,\n using an Action={name}&{p1}={v1}&p2={v2}... query string. The ensuing {service_api} refers to\n a supported action {name} plus any required input parameters. Alternatively, path can be used\n for an Amazon Web Services service path-based API. The ensuing service_api refers to the path to an Amazon Web Services\n service resource, including the region of the integrated Amazon Web Services service, if applicable. For\n example, for integration with the S3 API of GetObject, the uri can be either\n arn:aws:apigateway:us-west-2:s3:action/GetObject&Bucket={bucket}&Key={key} or\n arn:aws:apigateway:us-west-2:s3:path/{bucket}/{key}.

" - } - }, - "connectionType": { - "target": "com.amazonaws.apigateway#ConnectionType", - "traits": { - "smithy.api#documentation": "

The type of the network connection to the integration endpoint. The valid value is INTERNET for connections through the public routable internet or VPC_LINK for private connections between API Gateway and a network load balancer in a VPC. The default value is INTERNET.

" - } - }, - "connectionId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ID of the VpcLink used for the integration. Specify this value only if you specify VPC_LINK as the connection type.

" - } - }, - "credentials": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies whether credentials are required for a put integration.

" - } - }, - "requestParameters": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map specifying request parameters that are passed from the method request to the back end. The key is an integration request parameter name and the associated value is a method request parameter value or static value that must be enclosed within single quotes and pre-encoded as required by the back end. The method request parameter value must match the pattern of method.request.{location}.{name}, where location is querystring, path, or header and name must be a valid and unique method request parameter name.

" - } - }, - "requestTemplates": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Represents a map of Velocity templates that are applied on the request payload based on the value of the Content-Type header sent by the client. The content type value is the key in this map, and the template (as a String) is the value.

" - } - }, - "passthroughBehavior": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the pass-through behavior for incoming requests based on the Content-Type header in the request, and the available mapping templates specified as the requestTemplates property on the Integration resource. There are three valid values: WHEN_NO_MATCH, WHEN_NO_TEMPLATES, and NEVER.\n

" - } - }, - "cacheNamespace": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a group of related cached parameters. By default, API Gateway uses the resource ID as the cacheNamespace. You can specify the same cacheNamespace across resources to return the same cached data for requests to different resources.

" - } - }, - "cacheKeyParameters": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of request parameters whose values API Gateway caches. To be valid values for cacheKeyParameters, these parameters must also be specified for Method requestParameters.

" - } - }, - "contentHandling": { - "target": "com.amazonaws.apigateway#ContentHandlingStrategy", - "traits": { - "smithy.api#documentation": "

Specifies how to handle request payload content type conversions. Supported values are CONVERT_TO_BINARY and CONVERT_TO_TEXT, with the following behaviors:

\n

If this property is not defined, the request payload will be passed through from the method request to integration request without modification, provided that the passthroughBehavior is configured to support payload pass-through.

" - } - }, - "timeoutInMillis": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

Custom timeout between 50 and 29,000 milliseconds. The default value is 29,000 milliseconds or 29 seconds.

" - } - }, - "tlsConfig": { - "target": "com.amazonaws.apigateway#TlsConfig" - } - }, - "traits": { - "smithy.api#documentation": "

Sets up a method's integration.

" - } - }, - "com.amazonaws.apigateway#PutIntegrationResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#PutIntegrationResponseRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#IntegrationResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Represents a put integration.

", - "smithy.api#http": { - "method": "PUT", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration/responses/{statusCode}", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#PutIntegrationResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a put integration response request's resource identifier.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a put integration response request's HTTP method.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

Specifies the status code that is used to map the integration response to an existing MethodResponse.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "selectionPattern": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the selection pattern of a put integration response.

" - } - }, - "responseParameters": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map specifying response parameters that are passed to the method response from the back end.\n The key is a method response header parameter name and the mapped value is an integration response header value, a static value enclosed within a pair of single quotes, or a JSON expression from the integration response body. The mapping key must match the pattern of method.response.header.{name}, where name is a valid and unique header name. The mapped non-static value must match the pattern of integration.response.header.{name} or integration.response.body.{JSON-expression}, where name must be a valid and unique response header name and JSON-expression a valid JSON expression without the $ prefix.

" - } - }, - "responseTemplates": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Specifies a put integration response's templates.

" - } - }, - "contentHandling": { - "target": "com.amazonaws.apigateway#ContentHandlingStrategy", - "traits": { - "smithy.api#documentation": "

Specifies how to handle response payload content type conversions. Supported values are CONVERT_TO_BINARY and CONVERT_TO_TEXT, with the following behaviors:

\n

If this property is not defined, the response payload will be passed through from the integration response to the method response without modification.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a put integration response request.

" - } - }, - "com.amazonaws.apigateway#PutMethod": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#PutMethodRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Method" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Add a method to an existing Resource resource.

", - "smithy.api#http": { - "method": "PUT", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#PutMethodRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Resource identifier for the new Method resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the method request's HTTP method type.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "authorizationType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The method's authorization type. Valid values are NONE for open access, AWS_IAM for using AWS IAM permissions, CUSTOM for using a custom authorizer, or COGNITO_USER_POOLS for using a Cognito user pool.

", - "smithy.api#required": {} - } - }, - "authorizerId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies the identifier of an Authorizer to use on this Method, if the type is CUSTOM or COGNITO_USER_POOLS. The authorizer identifier is generated by API Gateway when you created the authorizer.

" - } - }, - "apiKeyRequired": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether the method required a valid ApiKey.

" - } - }, - "operationName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A human-friendly operation identifier for the method. For example, you can assign the operationName of ListPets for the GET /pets method in the PetStore example.

" - } - }, - "requestParameters": { - "target": "com.amazonaws.apigateway#MapOfStringToBoolean", - "traits": { - "smithy.api#documentation": "

A key-value map defining required or optional method request parameters that can be accepted by API Gateway. A key defines a method request parameter name matching the pattern of method.request.{location}.{name}, where location is querystring, path, or header and name is a valid and unique parameter name. The value associated with the key is a Boolean flag indicating whether the parameter is required (true) or optional (false). The method request parameter names defined here are available in Integration to be mapped to integration request parameters or body-mapping templates.

" - } - }, - "requestModels": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Specifies the Model resources used for the request's content type. Request models are represented as a key/value map, with a content type as the key and a Model name as the value.

" - } - }, - "requestValidatorId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of a RequestValidator for validating the method request.

" - } - }, - "authorizationScopes": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

A list of authorization scopes configured on the method. The scopes are used with a COGNITO_USER_POOLS authorizer to authorize the method invocation. The authorization works by matching the method scopes against the scopes parsed from the access token in the incoming request. The method invocation is authorized if any method scopes matches a claimed scope in the access token. Otherwise, the invocation is not authorized. When the method scope is configured, the client must provide an access token instead of an identity token for authorization purposes.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to add a method to an existing Resource resource.

" - } - }, - "com.amazonaws.apigateway#PutMethodResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#PutMethodResponseRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#MethodResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Adds a MethodResponse to an existing Method resource.

", - "smithy.api#http": { - "method": "PUT", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/responses/{statusCode}", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#PutMethodResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Resource identifier for the Method resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The HTTP verb of the Method resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

The method response's status code.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "responseParameters": { - "target": "com.amazonaws.apigateway#MapOfStringToBoolean", - "traits": { - "smithy.api#documentation": "

A key-value map specifying required or optional response parameters that API Gateway can send back to the caller. A key defines a method response header name and the associated value is a Boolean flag indicating whether the method response parameter is required or not. The method response header names must match the pattern of method.response.header.{name}, where name is a valid and unique header name. The response parameter names defined here are available in the integration response to be mapped from an integration response header expressed in integration.response.header.{name}, a static value enclosed within a pair of single quotes (e.g., 'application/json'), or a JSON expression from the back-end response payload in the form of integration.response.body.{JSON-expression}, where JSON-expression is a valid JSON expression without the $ prefix.)

" - } - }, - "responseModels": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Specifies the Model resources used for the response's content type. Response models are represented as a key/value map, with a content type as the key and a Model name as the value.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to add a MethodResponse to an existing Method resource.

" - } - }, - "com.amazonaws.apigateway#PutMode": { - "type": "enum", - "members": { - "Merge": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "merge" - } - }, - "Overwrite": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "overwrite" - } - } - } - }, - "com.amazonaws.apigateway#PutRestApi": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#PutRestApiRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#RestApi" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

A feature of the API Gateway control service for updating an existing API with an input of external API definitions.\n The update can take the form of merging the supplied definition into the existing API or overwriting the existing API.

", - "smithy.api#http": { - "method": "PUT", - "uri": "/restapis/{restApiId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#PutRestApiRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "mode": { - "target": "com.amazonaws.apigateway#PutMode", - "traits": { - "smithy.api#documentation": "

The mode query parameter to specify the update mode. Valid values are \"merge\" and \"overwrite\". By default,\n the update mode is \"merge\".

", - "smithy.api#httpQuery": "mode" - } - }, - "failOnWarnings": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A query parameter to indicate whether to rollback the API update (true) or not (false)\n when a warning is encountered. The default value is false.

", - "smithy.api#httpQuery": "failonwarnings" - } - }, - "parameters": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

Custom header parameters as part of the request. For example, to exclude DocumentationParts from an imported API, set ignore=documentation as a parameters value, as in the AWS CLI command of aws apigateway import-rest-api --parameters ignore=documentation --body 'file:///path/to/imported-api-body.json'.

", - "smithy.api#httpQueryParams": {} - } - }, - "body": { - "target": "com.amazonaws.apigateway#Blob", - "traits": { - "smithy.api#documentation": "

The PUT request body containing external API definitions. Currently, only OpenAPI definition JSON/YAML files are supported. The maximum size of the API definition file is 6MB.

", - "smithy.api#httpPayload": {}, - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

A PUT request to update an existing API, with external API definitions specified as the request body.

" - } - }, - "com.amazonaws.apigateway#QuotaPeriodType": { - "type": "enum", - "members": { - "DAY": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "DAY" - } - }, - "WEEK": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "WEEK" - } - }, - "MONTH": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "MONTH" - } - } - } - }, - "com.amazonaws.apigateway#QuotaSettings": { - "type": "structure", - "members": { - "limit": { - "target": "com.amazonaws.apigateway#Integer", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

The target maximum number of requests that can be made in a given time period.

" - } - }, - "offset": { - "target": "com.amazonaws.apigateway#Integer", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

The number of requests subtracted from the given limit in the initial time period.

" - } - }, - "period": { - "target": "com.amazonaws.apigateway#QuotaPeriodType", - "traits": { - "smithy.api#documentation": "

The time period in which the limit applies. Valid values are \"DAY\", \"WEEK\" or \"MONTH\".

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Quotas configured for a usage plan.

" - } - }, - "com.amazonaws.apigateway#RequestValidator": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of this RequestValidator.

" - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of this RequestValidator

" - } - }, - "validateRequestBody": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A Boolean flag to indicate whether to validate a request body according to the configured Model schema.

" - } - }, - "validateRequestParameters": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A Boolean flag to indicate whether to validate request parameters (true) or not (false).

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A set of validation rules for incoming Method requests.

" - } - }, - "com.amazonaws.apigateway#RequestValidators": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfRequestValidator", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

A collection of RequestValidator resources of a given RestApi.

" - } - }, - "com.amazonaws.apigateway#Resource": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The resource's identifier.

" - } - }, - "parentId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The parent resource's identifier.

" - } - }, - "pathPart": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The last path segment for this resource.

" - } - }, - "path": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The full path for this resource.

" - } - }, - "resourceMethods": { - "target": "com.amazonaws.apigateway#MapOfMethod", - "traits": { - "smithy.api#documentation": "

Gets an API resource's method of a given HTTP verb.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents an API resource.

" - } - }, - "com.amazonaws.apigateway#Resources": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfResource", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a collection of Resource resources.

" - } - }, - "com.amazonaws.apigateway#RestApi": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The API's identifier. This identifier is unique across all of your APIs in API Gateway.

" - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The API's name.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The API's description.

" - } - }, - "createdDate": { - "target": "com.amazonaws.apigateway#Timestamp", - "traits": { - "smithy.api#documentation": "

The timestamp when the API was created.

" - } - }, - "version": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A version identifier for the API.

" - } - }, - "warnings": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

The warning messages reported when failonwarnings is turned on during API import.

" - } - }, - "binaryMediaTypes": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

The list of binary media types supported by the RestApi. By default, the RestApi supports only UTF-8-encoded text payloads.

" - } - }, - "minimumCompressionSize": { - "target": "com.amazonaws.apigateway#NullableInteger", - "traits": { - "smithy.api#documentation": "

A nullable integer that is used to enable compression (with non-negative between 0 and 10485760 (10M) bytes, inclusive) or disable compression (with a null value) on an API. When compression is enabled, compression or decompression is not applied on the payload if the payload size is smaller than this value. Setting it to zero allows compression for any payload size.

" - } - }, - "apiKeySource": { - "target": "com.amazonaws.apigateway#ApiKeySourceType", - "traits": { - "smithy.api#documentation": "

The source of the API key for metering requests according to a usage plan. Valid values\n are: >HEADER to read the API key from the X-API-Key header of a\n request. AUTHORIZER to read the API key from the UsageIdentifierKey\n from a custom authorizer.

" - } - }, - "endpointConfiguration": { - "target": "com.amazonaws.apigateway#EndpointConfiguration", - "traits": { - "smithy.api#documentation": "

The endpoint configuration of this RestApi showing the endpoint types of the API.

" - } - }, - "policy": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A stringified JSON policy document that applies to this RestApi regardless of the caller and Method configuration.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" - } - }, - "disableExecuteApiEndpoint": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether clients can invoke your API by using the default execute-api endpoint.\n By default, clients can invoke your API with the default\n https://{api_id}.execute-api.{region}.amazonaws.com endpoint. To require that clients use a\n custom domain name to invoke your API, disable the default endpoint.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a REST API.

" - } - }, - "com.amazonaws.apigateway#RestApis": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfRestApi", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Contains references to your APIs and links that guide you in how to interact with your collection. A collection offers a paginated view of your APIs.

" - } - }, - "com.amazonaws.apigateway#SdkConfigurationProperty": { - "type": "structure", - "members": { - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of a an SdkType configuration property.

" - } - }, - "friendlyName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The user-friendly name of an SdkType configuration property.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of an SdkType configuration property.

" - } - }, - "required": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

A boolean flag of an SdkType configuration property to indicate if the associated SDK configuration property is required (true) or not (false).

" - } - }, - "defaultValue": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The default value of an SdkType configuration property.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A configuration property of an SDK type.

" - } - }, - "com.amazonaws.apigateway#SdkResponse": { - "type": "structure", - "members": { - "contentType": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The content-type header value in the HTTP response.

", - "smithy.api#httpHeader": "Content-Type" - } - }, - "contentDisposition": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The content-disposition header value in the HTTP response.

", - "smithy.api#httpHeader": "Content-Disposition" - } - }, - "body": { - "target": "com.amazonaws.apigateway#Blob", - "traits": { - "smithy.api#documentation": "

The binary blob response to GetSdk, which contains the generated SDK.

", - "smithy.api#httpPayload": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

The binary blob response to GetSdk, which contains the generated SDK.

" - } - }, - "com.amazonaws.apigateway#SdkType": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of an SdkType instance.

" - } - }, - "friendlyName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The user-friendly name of an SdkType instance.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of an SdkType.

" - } - }, - "configurationProperties": { - "target": "com.amazonaws.apigateway#ListOfSdkConfigurationProperty", - "traits": { - "smithy.api#documentation": "

A list of configuration properties of an SdkType.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A type of SDK that API Gateway can generate.

" - } - }, - "com.amazonaws.apigateway#SdkTypes": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfSdkType", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - } - }, - "traits": { - "smithy.api#documentation": "

The collection of SdkType instances.

" - } - }, - "com.amazonaws.apigateway#SecurityPolicy": { - "type": "enum", - "members": { - "TLS_1_0": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "TLS_1_0" - } - }, - "TLS_1_2": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "TLS_1_2" - } - } - } - }, - "com.amazonaws.apigateway#ServiceUnavailableException": { - "type": "structure", - "members": { - "retryAfterSeconds": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#httpHeader": "Retry-After" - } - }, - "message": { - "target": "com.amazonaws.apigateway#String" - } - }, - "traits": { - "smithy.api#documentation": "

The requested service is not available. For details see the accompanying error message. Retry after the specified time period.

", - "smithy.api#error": "server", - "smithy.api#httpError": 503 - } - }, - "com.amazonaws.apigateway#Stage": { - "type": "structure", - "members": { - "deploymentId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the Deployment that the stage points to.

" - } - }, - "clientCertificateId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of a client certificate for an API stage.

" - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the stage is the first path segment in the Uniform Resource Identifier (URI) of a call to API Gateway. Stage names can only contain alphanumeric characters, hyphens, and underscores. Maximum length is 128 characters.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The stage's description.

" - } - }, - "cacheClusterEnabled": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether a cache cluster is enabled for the stage.

" - } - }, - "cacheClusterSize": { - "target": "com.amazonaws.apigateway#CacheClusterSize", - "traits": { - "smithy.api#documentation": "

The stage's cache capacity in GB. For more information about choosing a cache size, see Enabling API caching to enhance responsiveness.

" - } - }, - "cacheClusterStatus": { - "target": "com.amazonaws.apigateway#CacheClusterStatus", - "traits": { - "smithy.api#documentation": "

The status of the cache cluster for the stage, if enabled.

" - } - }, - "methodSettings": { - "target": "com.amazonaws.apigateway#MapOfMethodSettings", - "traits": { - "smithy.api#documentation": "

A map that defines the method settings for a Stage resource. Keys (designated as /{method_setting_key below) are method paths defined as {resource_path}/{http_method} for an individual method override, or /\\*/\\* for overriding all methods in the stage.

" - } - }, - "variables": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A map that defines the stage variables for a Stage resource. Variable names can\n have alphanumeric and underscore characters, and the values must match [A-Za-z0-9-._~:/?#&=,]+.

" - } - }, - "documentationVersion": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The version of the associated API documentation.

" - } - }, - "accessLogSettings": { - "target": "com.amazonaws.apigateway#AccessLogSettings", - "traits": { - "smithy.api#documentation": "

Settings for logging access in this stage.

" - } - }, - "canarySettings": { - "target": "com.amazonaws.apigateway#CanarySettings", - "traits": { - "smithy.api#documentation": "

Settings for the canary deployment in this stage.

" - } - }, - "tracingEnabled": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether active tracing with X-ray is enabled for the Stage.

" - } - }, - "webAclArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ARN of the WebAcl associated with the Stage.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" - } - }, - "createdDate": { - "target": "com.amazonaws.apigateway#Timestamp", - "traits": { - "smithy.api#documentation": "

The timestamp when the stage was created.

" - } - }, - "lastUpdatedDate": { - "target": "com.amazonaws.apigateway#Timestamp", - "traits": { - "smithy.api#documentation": "

The timestamp when the stage last updated.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a unique identifier for a version of a deployed RestApi that is callable by users.

" - } - }, - "com.amazonaws.apigateway#StageKey": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

" - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The stage name associated with the stage key.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A reference to a unique stage identified in the format {restApiId}/{stage}.

" - } - }, - "com.amazonaws.apigateway#Stages": { - "type": "structure", - "members": { - "item": { - "target": "com.amazonaws.apigateway#ListOfStage", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A list of Stage resources that are associated with the ApiKey resource.

" - } - }, - "com.amazonaws.apigateway#StatusCode": { - "type": "string", - "traits": { - "smithy.api#documentation": "

The status code.

", - "smithy.api#pattern": "^[1-5]\\d\\d$" - } - }, - "com.amazonaws.apigateway#String": { - "type": "string" - }, - "com.amazonaws.apigateway#TagResource": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#TagResourceRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Adds or updates a tag on a given resource.

", - "smithy.api#http": { - "method": "PUT", - "uri": "/tags/{resourceArn}", - "code": 204 - } - } - }, - "com.amazonaws.apigateway#TagResourceRequest": { - "type": "structure", - "members": { - "resourceArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ARN of a resource that can be tagged.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The key-value map of strings. The valid character set is [a-zA-Z+-=._:/]. The tag key can be up to 128 characters and must not start with aws:. The tag value can be up to 256 characters.

", - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Adds or updates a tag on a given resource.

" - } - }, - "com.amazonaws.apigateway#Tags": { - "type": "structure", - "members": { - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" - } - }, - "com.amazonaws.apigateway#Template": { - "type": "structure", - "members": { - "value": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Apache Velocity Template Language (VTL) template content used for the template resource.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a mapping template used to transform a payload.

" - } - }, - "com.amazonaws.apigateway#TestInvokeAuthorizer": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#TestInvokeAuthorizerRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#TestInvokeAuthorizerResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Simulate the execution of an Authorizer in your RestApi with headers, parameters, and an incoming request body.

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis/{restApiId}/authorizers/{authorizerId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#TestInvokeAuthorizerRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "authorizerId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a test invoke authorizer request's Authorizer ID.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "headers": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map of headers to simulate an incoming invocation request. This is where the incoming authorization token, or identity source, should be specified.

" - } - }, - "multiValueHeaders": { - "target": "com.amazonaws.apigateway#MapOfStringToList", - "traits": { - "smithy.api#documentation": "

The headers as a map from string to list of values to simulate an incoming invocation request. This is where the incoming authorization token, or identity source, may be specified.

" - } - }, - "pathWithQueryString": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The URI path, including query string, of the simulated invocation request. Use this to specify path parameters and query string parameters.

" - } - }, - "body": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The simulated request body of an incoming invocation request.

" - } - }, - "stageVariables": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map of stage variables to simulate an invocation on a deployed Stage.

" - } - }, - "additionalContext": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map of additional context variables.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Make a request to simulate the invocation of an Authorizer.

" - } - }, - "com.amazonaws.apigateway#TestInvokeAuthorizerResponse": { - "type": "structure", - "members": { - "clientStatus": { - "target": "com.amazonaws.apigateway#Integer", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

The HTTP status code that the client would have received. Value is 0 if the authorizer succeeded.

" - } - }, - "log": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The API Gateway execution log for the test authorizer request.

" - } - }, - "latency": { - "target": "com.amazonaws.apigateway#Long", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

The execution latency of the test authorizer request.

" - } - }, - "principalId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The principal identity returned by the Authorizer

" - } - }, - "policy": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The JSON policy document returned by the Authorizer

" - } - }, - "authorization": { - "target": "com.amazonaws.apigateway#MapOfStringToList", - "traits": { - "smithy.api#documentation": "

The authorization response.

" - } - }, - "claims": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The open identity claims, with any supported custom attributes, returned from the Cognito Your User Pool configured for the API.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents the response of the test invoke request for a custom Authorizer

" - } - }, - "com.amazonaws.apigateway#TestInvokeMethod": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#TestInvokeMethodRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#TestInvokeMethodResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Simulate the invocation of a Method in your RestApi with headers, parameters, and an incoming request body.

", - "smithy.api#http": { - "method": "POST", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#TestInvokeMethodRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a test invoke method request's resource ID.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies a test invoke method request's HTTP method.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "pathWithQueryString": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The URI path, including query string, of the simulated invocation request. Use this to specify path parameters and query string parameters.

" - } - }, - "body": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The simulated request body of an incoming invocation request.

" - } - }, - "headers": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map of headers to simulate an incoming invocation request.

" - } - }, - "multiValueHeaders": { - "target": "com.amazonaws.apigateway#MapOfStringToList", - "traits": { - "smithy.api#documentation": "

The headers as a map from string to list of values to simulate an incoming invocation request.

" - } - }, - "clientCertificateId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A ClientCertificate identifier to use in the test invocation. API Gateway will use the certificate when making the HTTPS request to the defined back-end endpoint.

" - } - }, - "stageVariables": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

A key-value map of stage variables to simulate an invocation on a deployed Stage.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Make a request to simulate the invocation of a Method.

" - } - }, - "com.amazonaws.apigateway#TestInvokeMethodResponse": { - "type": "structure", - "members": { - "status": { - "target": "com.amazonaws.apigateway#Integer", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

The HTTP status code.

" - } - }, - "body": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The body of the HTTP response.

" - } - }, - "headers": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The headers of the HTTP response.

" - } - }, - "multiValueHeaders": { - "target": "com.amazonaws.apigateway#MapOfStringToList", - "traits": { - "smithy.api#documentation": "

The headers of the HTTP response as a map from string to list of values.

" - } - }, - "log": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The API Gateway execution log for the test invoke request.

" - } - }, - "latency": { - "target": "com.amazonaws.apigateway#Long", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

The execution latency of the test invoke request.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents the response of the test invoke request in the HTTP method.

" - } - }, - "com.amazonaws.apigateway#ThrottleSettings": { - "type": "structure", - "members": { - "burstLimit": { - "target": "com.amazonaws.apigateway#Integer", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

The API target request burst rate limit. This allows more requests through for a period of time than the target rate limit.

" - } - }, - "rateLimit": { - "target": "com.amazonaws.apigateway#Double", - "traits": { - "smithy.api#default": 0, - "smithy.api#documentation": "

The API target request rate limit.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

The API request rate limits.

" - } - }, - "com.amazonaws.apigateway#Timestamp": { - "type": "timestamp" - }, - "com.amazonaws.apigateway#TlsConfig": { - "type": "structure", - "members": { - "insecureSkipVerification": { - "target": "com.amazonaws.apigateway#Boolean", - "traits": { - "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether or not API Gateway skips verification that the certificate for an integration endpoint is\n issued by a supported certificate authority. This isn’t recommended, but it enables you to\n use certificates that are signed by private certificate authorities, or certificates\n that are self-signed. If enabled, API Gateway still performs basic certificate\n validation, which includes checking the certificate's expiration date, hostname, and\n presence of a root certificate authority. Supported only for HTTP and\n HTTP_PROXY integrations.

\n \n

Enabling insecureSkipVerification isn't recommended, especially for integrations with public\n HTTPS endpoints. If you enable insecureSkipVerification, you increase the risk of man-in-the-middle attacks.

\n
" - } - } - }, - "traits": { - "smithy.api#documentation": "

Specifies the TLS configuration for an integration.

" - } - }, - "com.amazonaws.apigateway#TooManyRequestsException": { - "type": "structure", - "members": { - "retryAfterSeconds": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#httpHeader": "Retry-After" - } - }, - "message": { - "target": "com.amazonaws.apigateway#String" - } - }, - "traits": { - "smithy.api#documentation": "

The request has reached its throttling limit. Retry after the specified time period.

", - "smithy.api#error": "client", - "smithy.api#httpError": 429 - } - }, - "com.amazonaws.apigateway#UnauthorizedCacheControlHeaderStrategy": { - "type": "enum", - "members": { - "FAIL_WITH_403": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "FAIL_WITH_403" - } - }, - "SUCCEED_WITH_RESPONSE_HEADER": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "SUCCEED_WITH_RESPONSE_HEADER" - } - }, - "SUCCEED_WITHOUT_RESPONSE_HEADER": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "SUCCEED_WITHOUT_RESPONSE_HEADER" - } - } - } - }, - "com.amazonaws.apigateway#UnauthorizedException": { - "type": "structure", - "members": { - "message": { - "target": "com.amazonaws.apigateway#String" - } - }, - "traits": { - "smithy.api#documentation": "

The request is denied because the caller has insufficient permissions.

", - "smithy.api#error": "client", - "smithy.api#httpError": 401 - } - }, - "com.amazonaws.apigateway#UntagResource": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UntagResourceRequest" - }, - "output": { - "target": "smithy.api#Unit" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Removes a tag from a given resource.

", - "smithy.api#http": { - "method": "DELETE", - "uri": "/tags/{resourceArn}", - "code": 204 - } - } - }, - "com.amazonaws.apigateway#UntagResourceRequest": { - "type": "structure", - "members": { - "resourceArn": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ARN of a resource that can be tagged.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "tagKeys": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

The Tag keys to delete.

", - "smithy.api#httpQuery": "tagKeys", - "smithy.api#required": {} - } - } - }, - "traits": { - "smithy.api#documentation": "

Removes a tag from a given resource.

" - } - }, - "com.amazonaws.apigateway#UpdateAccount": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateAccountRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Account" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Changes information about the current Account resource.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/account", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateAccountRequest": { - "type": "structure", - "members": { - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to change information about the current Account resource.

" - } - }, - "com.amazonaws.apigateway#UpdateApiKey": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateApiKeyRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#ApiKey" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Changes information about an ApiKey resource.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/apikeys/{apiKey}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateApiKeyRequest": { - "type": "structure", - "members": { - "apiKey": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the ApiKey resource to be updated.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to change information about an ApiKey resource.

" - } - }, - "com.amazonaws.apigateway#UpdateAuthorizer": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateAuthorizerRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Authorizer" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Updates an existing Authorizer resource.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/authorizers/{authorizerId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateAuthorizerRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "authorizerId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the Authorizer resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to update an existing Authorizer resource.

" - } - }, - "com.amazonaws.apigateway#UpdateBasePathMapping": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateBasePathMappingRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#BasePathMapping" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Changes information about the BasePathMapping resource.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/domainnames/{domainName}/basepathmappings/{basePath}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateBasePathMappingRequest": { - "type": "structure", - "members": { - "domainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The domain name of the BasePathMapping resource to change.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "basePath": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The base path of the BasePathMapping resource to change.

\n

To specify an empty base path, set this parameter to '(none)'.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to change information about the BasePathMapping resource.

" - } - }, - "com.amazonaws.apigateway#UpdateClientCertificate": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateClientCertificateRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#ClientCertificate" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Changes information about an ClientCertificate resource.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/clientcertificates/{clientCertificateId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateClientCertificateRequest": { - "type": "structure", - "members": { - "clientCertificateId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the ClientCertificate resource to be updated.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to change information about an ClientCertificate resource.

" - } - }, - "com.amazonaws.apigateway#UpdateDeployment": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateDeploymentRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Deployment" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#ServiceUnavailableException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Changes information about a Deployment resource.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/deployments/{deploymentId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateDeploymentRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "deploymentId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The replacement identifier for the Deployment resource to change information about.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to change information about a Deployment resource.

" - } - }, - "com.amazonaws.apigateway#UpdateDocumentationPart": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateDocumentationPartRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DocumentationPart" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Updates a documentation part.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/documentation/parts/{documentationPartId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateDocumentationPartRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "documentationPartId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the to-be-updated documentation part.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Updates an existing documentation part of a given API.

" - } - }, - "com.amazonaws.apigateway#UpdateDocumentationVersion": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateDocumentationVersionRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DocumentationVersion" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Updates a documentation version.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/documentation/versions/{documentationVersion}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateDocumentationVersionRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi..

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "documentationVersion": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The version identifier of the to-be-updated documentation version.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Updates an existing documentation version of an API.

" - } - }, - "com.amazonaws.apigateway#UpdateDomainName": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateDomainNameRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#DomainName" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Changes information about the DomainName resource.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/domainnames/{domainName}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateDomainNameRequest": { - "type": "structure", - "members": { - "domainName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the DomainName resource to be changed.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to change information about the DomainName resource.

" - } - }, - "com.amazonaws.apigateway#UpdateGatewayResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateGatewayResponseRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#GatewayResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Updates a GatewayResponse of a specified response type on the given RestApi.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/gatewayresponses/{responseType}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateGatewayResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "responseType": { - "target": "com.amazonaws.apigateway#GatewayResponseType", - "traits": { - "smithy.api#documentation": "

The response type of the associated GatewayResponse.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Updates a GatewayResponse of a specified response type on the given RestApi.

" - } - }, - "com.amazonaws.apigateway#UpdateIntegration": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateIntegrationRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Integration" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Represents an update integration.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateIntegrationRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Represents an update integration request's resource identifier.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Represents an update integration request's HTTP method.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents an update integration request.

" - } - }, - "com.amazonaws.apigateway#UpdateIntegrationResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateIntegrationResponseRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#IntegrationResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Represents an update integration response.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/integration/responses/{statusCode}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateIntegrationResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies an update integration response request's resource identifier.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

Specifies an update integration response request's HTTP method.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

Specifies an update integration response request's status code.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents an update integration response request.

" - } - }, - "com.amazonaws.apigateway#UpdateMethod": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateMethodRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Method" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Updates an existing Method resource.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateMethodRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Resource identifier for the Method resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The HTTP verb of the Method resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to update an existing Method resource.

" - } - }, - "com.amazonaws.apigateway#UpdateMethodResponse": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateMethodResponseRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#MethodResponse" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Updates an existing MethodResponse resource.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/resources/{resourceId}/methods/{httpMethod}/responses/{statusCode}", - "code": 201 - } - } - }, - "com.amazonaws.apigateway#UpdateMethodResponseRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Resource identifier for the MethodResponse resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "httpMethod": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The HTTP verb of the Method resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "statusCode": { - "target": "com.amazonaws.apigateway#StatusCode", - "traits": { - "smithy.api#documentation": "

The status code for the MethodResponse resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

A request to update an existing MethodResponse resource.

" - } - }, - "com.amazonaws.apigateway#UpdateModel": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateModelRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Model" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Changes information about a model.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/models/{modelName}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateModelRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "modelName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the model to update.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to update an existing model in an existing RestApi resource.

" - } - }, - "com.amazonaws.apigateway#UpdateRequestValidator": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateRequestValidatorRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#RequestValidator" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Updates a RequestValidator of a given RestApi.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/requestvalidators/{requestValidatorId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateRequestValidatorRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "requestValidatorId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of RequestValidator to be updated.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Updates a RequestValidator of a given RestApi.

" - } - }, - "com.amazonaws.apigateway#UpdateResource": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateResourceRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Resource" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Changes information about a Resource resource.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/resources/{resourceId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateResourceRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "resourceId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the Resource resource.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to change information about a Resource resource.

" - } - }, - "com.amazonaws.apigateway#UpdateRestApi": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateRestApiRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#RestApi" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Changes information about the specified API.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateRestApiRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Request to update an existing RestApi resource in your collection.

" - } - }, - "com.amazonaws.apigateway#UpdateStage": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateStageRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Stage" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Changes information about a Stage resource.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/restapis/{restApiId}/stages/{stageName}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateStageRequest": { - "type": "structure", - "members": { - "restApiId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The string identifier of the associated RestApi.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "stageName": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of the Stage resource to change information about.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Requests API Gateway to change information about a Stage resource.

" - } - }, - "com.amazonaws.apigateway#UpdateUsage": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateUsageRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#Usage" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Grants a temporary extension to the remaining quota of a usage plan associated with a specified API key.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/usageplans/{usagePlanId}/keys/{keyId}/usage", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateUsagePlan": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateUsagePlanRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#UsagePlan" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Updates a usage plan of a given plan Id.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/usageplans/{usagePlanId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateUsagePlanRequest": { - "type": "structure", - "members": { - "usagePlanId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Id of the to-be-updated usage plan.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

The PATCH request to update a usage plan of a given plan Id.

" - } - }, - "com.amazonaws.apigateway#UpdateUsageRequest": { - "type": "structure", - "members": { - "usagePlanId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Id of the usage plan associated with the usage data.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "keyId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the API key associated with the usage plan in which a temporary extension is granted to the remaining quota.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

The PATCH request to grant a temporary extension to the remaining quota of a usage plan associated with a specified API key.

" - } - }, - "com.amazonaws.apigateway#UpdateVpcLink": { - "type": "operation", - "input": { - "target": "com.amazonaws.apigateway#UpdateVpcLinkRequest" - }, - "output": { - "target": "com.amazonaws.apigateway#VpcLink" - }, - "errors": [ - { - "target": "com.amazonaws.apigateway#BadRequestException" - }, - { - "target": "com.amazonaws.apigateway#ConflictException" - }, - { - "target": "com.amazonaws.apigateway#LimitExceededException" - }, - { - "target": "com.amazonaws.apigateway#NotFoundException" - }, - { - "target": "com.amazonaws.apigateway#TooManyRequestsException" - }, - { - "target": "com.amazonaws.apigateway#UnauthorizedException" - } - ], - "traits": { - "smithy.api#documentation": "

Updates an existing VpcLink of a specified identifier.

", - "smithy.api#http": { - "method": "PATCH", - "uri": "/vpclinks/{vpcLinkId}", - "code": 200 - } - } - }, - "com.amazonaws.apigateway#UpdateVpcLinkRequest": { - "type": "structure", - "members": { - "vpcLinkId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the VpcLink. It is used in an Integration to reference this VpcLink.

", - "smithy.api#httpLabel": {}, - "smithy.api#required": {} - } - }, - "patchOperations": { - "target": "com.amazonaws.apigateway#ListOfPatchOperation", - "traits": { - "smithy.api#documentation": "

For more information about supported patch operations, see Patch Operations.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Updates an existing VpcLink of a specified identifier.

" - } - }, - "com.amazonaws.apigateway#Usage": { - "type": "structure", - "members": { - "usagePlanId": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The plan Id associated with this usage data.

" - } - }, - "startDate": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The starting date of the usage data.

" - } - }, - "endDate": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The ending date of the usage data.

" - } - }, - "items": { - "target": "com.amazonaws.apigateway#MapOfKeyUsages", - "traits": { - "smithy.api#documentation": "

The usage data, as daily logs of used and remaining quotas, over the specified time interval indexed over the API keys in a usage plan. For example, {..., \"values\" : { \"{api_key}\" : [ [0, 100], [10, 90], [100, 10]]}, where {api_key} stands for an API key value and the daily log entry is of the format [used quota, remaining quota].

", - "smithy.api#jsonName": "values" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents the usage data of a usage plan.

" - } - }, - "com.amazonaws.apigateway#UsagePlan": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of a UsagePlan resource.

" - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of a usage plan.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of a usage plan.

" - } - }, - "apiStages": { - "target": "com.amazonaws.apigateway#ListOfApiStage", - "traits": { - "smithy.api#documentation": "

The associated API stages of a usage plan.

" - } - }, - "throttle": { - "target": "com.amazonaws.apigateway#ThrottleSettings", - "traits": { - "smithy.api#documentation": "

A map containing method level throttling information for API stage in a usage plan.

" - } - }, - "quota": { - "target": "com.amazonaws.apigateway#QuotaSettings", - "traits": { - "smithy.api#documentation": "

The target maximum number of permitted requests per a given unit time interval.

" - } - }, - "productCode": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The AWS Markeplace product identifier to associate with the usage plan as a SaaS product on AWS Marketplace.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a usage plan used to specify who can assess associated API stages. Optionally, target request rate and quota limits can be set. \n In some cases clients can exceed the targets that you set. Don’t rely on usage plans to control costs. \n Consider using Amazon Web Services Budgets to monitor costs \n and WAF to manage API requests.

" - } - }, - "com.amazonaws.apigateway#UsagePlanKey": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The Id of a usage plan key.

" - } - }, - "type": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The type of a usage plan key. Currently, the valid key type is API_KEY.

" - } - }, - "value": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The value of a usage plan key.

" - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name of a usage plan key.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a usage plan key to identify a plan customer.

" - } - }, - "com.amazonaws.apigateway#UsagePlanKeys": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfUsagePlanKey", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents the collection of usage plan keys added to usage plans for the associated API keys and, possibly, other types of keys.

" - } - }, - "com.amazonaws.apigateway#UsagePlans": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfUsagePlan", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

Represents a collection of usage plans for an AWS account.

" - } - }, - "com.amazonaws.apigateway#VpcLink": { - "type": "structure", - "members": { - "id": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The identifier of the VpcLink. It is used in an Integration to reference this VpcLink.

" - } - }, - "name": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The name used to label and identify the VPC link.

" - } - }, - "description": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The description of the VPC link.

" - } - }, - "targetArns": { - "target": "com.amazonaws.apigateway#ListOfString", - "traits": { - "smithy.api#documentation": "

The ARN of the network load balancer of the VPC targeted by the VPC link. The network load balancer must be owned by the same AWS account of the API owner.

" - } - }, - "status": { - "target": "com.amazonaws.apigateway#VpcLinkStatus", - "traits": { - "smithy.api#documentation": "

The status of the VPC link. The valid values are AVAILABLE, PENDING, DELETING, or FAILED. Deploying an API will wait if the status is PENDING and will fail if the status is DELETING.

" - } - }, - "statusMessage": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

A description about the VPC link status.

" - } - }, - "tags": { - "target": "com.amazonaws.apigateway#MapOfStringToString", - "traits": { - "smithy.api#documentation": "

The collection of tags. Each tag element is associated with a given resource.

" - } - } - }, - "traits": { - "smithy.api#documentation": "

An API Gateway VPC link for a RestApi to access resources in an Amazon Virtual Private Cloud (VPC).

" - } - }, - "com.amazonaws.apigateway#VpcLinkStatus": { - "type": "enum", - "members": { - "AVAILABLE": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "AVAILABLE" - } - }, - "PENDING": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "PENDING" - } - }, - "DELETING": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "DELETING" - } - }, - "FAILED": { - "target": "smithy.api#Unit", - "traits": { - "smithy.api#enumValue": "FAILED" - } - } - } - }, - "com.amazonaws.apigateway#VpcLinks": { - "type": "structure", - "members": { - "items": { - "target": "com.amazonaws.apigateway#ListOfVpcLink", - "traits": { - "smithy.api#documentation": "

The current page of elements from this collection.

", - "smithy.api#jsonName": "item" - } - }, - "position": { - "target": "com.amazonaws.apigateway#String", - "traits": { - "smithy.api#documentation": "

The current pagination position in the paged result set.

", - "smithy.api#httpQuery": "position" - } - } - }, - "traits": { - "smithy.api#documentation": "

The collection of VPC links under the caller's account in a region.

" - } - } - } -} diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index 875710a598..b228458b20 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -60,6 +60,7 @@ val crateVersioner by lazy { aws.sdk.CrateVersioner.defaultFor(rootProject, prop fun getRustMSRV(): String = properties.get("rust.msrv") ?: throw Exception("Rust MSRV missing") fun getPreviousReleaseVersionManifestPath(): String? = properties.get("aws.sdk.previous.release.versions.manifest") +fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "middleware" fun loadServiceMembership(): Membership { val membershipOverride = properties.get("aws.services")?.let { parseMembership(it) } @@ -102,7 +103,7 @@ fun generateSmithyBuild(services: AwsServices): String { "renameErrors": false, "debugMode": $debugMode, "eventStreamAllowList": [$eventStreamAllowListMembers], - "enableNewSmithyRuntime": "middleware" + "enableNewSmithyRuntime": "${getSmithyRuntimeMode()}" }, "service": "${service.service}", "module": "$moduleName", diff --git a/aws/sdk/integration-tests/Cargo.toml b/aws/sdk/integration-tests/Cargo.toml index fd0ab55dcf..7410b5f824 100644 --- a/aws/sdk/integration-tests/Cargo.toml +++ b/aws/sdk/integration-tests/Cargo.toml @@ -2,7 +2,6 @@ # `./gradlew -Paws.fullsdk=true :aws:sdk:assemble` these tests are copied into their respective Service crates. [workspace] members = [ - "apigateway", "dynamodb", "ec2", "glacier", diff --git a/aws/sdk/integration-tests/apigateway/Cargo.toml b/aws/sdk/integration-tests/apigateway/Cargo.toml deleted file mode 100644 index a920367880..0000000000 --- a/aws/sdk/integration-tests/apigateway/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "apigateway-tests" -version = "0.1.0" -authors = ["AWS Rust SDK Team "] -edition = "2021" -license = "Apache-2.0" -repository = "https://github.com/awslabs/smithy-rs" -publish = false - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } -aws-sdk-apigateway = { path = "../../build/aws-sdk/sdk/apigateway" } -aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } -aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test"} -tokio = { version = "1.23.1", features = ["full", "test-util"]} diff --git a/aws/sdk/integration-tests/apigateway/tests/accept_header.rs b/aws/sdk/integration-tests/apigateway/tests/accept_header.rs deleted file mode 100644 index 9a4a087d38..0000000000 --- a/aws/sdk/integration-tests/apigateway/tests/accept_header.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_sdk_apigateway::config::{Credentials, Region}; -use aws_sdk_apigateway::{Client, Config}; -use aws_smithy_client::test_connection::capture_request; -use aws_smithy_protocol_test::{assert_ok, validate_headers}; - -#[tokio::test] -async fn accept_header_is_application_json() { - let (conn, handler) = capture_request(None); - let conf = Config::builder() - .region(Region::new("us-east-1")) - .credentials_provider(Credentials::for_tests()) - .http_connector(conn) - .build(); - - let client = Client::from_conf(conf); - let _result = client - .delete_resource() - .rest_api_id("some-rest-api-id") - .resource_id("some-resource-id") - .send() - .await; - let request = handler.expect_request(); - assert_ok(validate_headers( - request.headers(), - [("accept", "application/json")], - )); -} diff --git a/buildSrc/src/main/kotlin/CodegenTestCommon.kt b/buildSrc/src/main/kotlin/CodegenTestCommon.kt index e42d7e5ed2..4977f3adc4 100644 --- a/buildSrc/src/main/kotlin/CodegenTestCommon.kt +++ b/buildSrc/src/main/kotlin/CodegenTestCommon.kt @@ -252,7 +252,7 @@ fun Project.registerCargoCommandsTasks( dependsOn(dependentTasks) workingDir(outputDir) environment("RUSTFLAGS", "--cfg aws_sdk_unstable") - commandLine("cargo", "test", "--all-features") + commandLine("cargo", "test", "--all-features", "--no-fail-fast") } this.tasks.register(Cargo.DOCS.toString) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index f5d0334067..157e43128c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -24,7 +24,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceGener import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.error.OperationErrorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.DefaultProtocolTestGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientProtocolLoader import software.amazon.smithy.rust.codegen.client.smithy.transformers.AddErrorMessage import software.amazon.smithy.rust.codegen.client.smithy.transformers.RemoveEventStreamOperations @@ -311,22 +311,24 @@ class ClientCodegenVisitor( ) // render protocol tests into `operation.rs` (note operationWriter vs. inputWriter) - ProtocolTestGenerator( + codegenDecorator.protocolTestGenerator( codegenContext, - protocolGeneratorFactory.support(), - operationShape, - this@operationWriter, - ).render() + DefaultProtocolTestGenerator( + codegenContext, + protocolGeneratorFactory.support(), + operationShape, + ), + ).render(this@operationWriter) } - } - rustCrate.withModule(symbolProvider.moduleForOperationError(operationShape)) { - OperationErrorGenerator( - model, - symbolProvider, - operationShape, - codegenDecorator.errorCustomizations(codegenContext, emptyList()), - ).render(this) + rustCrate.withModule(symbolProvider.moduleForOperationError(operationShape)) { + OperationErrorGenerator( + model, + symbolProvider, + operationShape, + codegenDecorator.errorCustomizations(codegenContext, emptyList()), + ).render(this) + } } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt index 190aac3680..4990539575 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt @@ -15,25 +15,28 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.util.orNull class EndpointPrefixGenerator(private val codegenContext: ClientCodegenContext, private val shape: OperationShape) : OperationCustomization() { - override fun section(section: OperationSection): Writable = when (section) { - is OperationSection.MutateRequest -> writable { + companion object { + fun endpointTraitBindings(codegenContext: ClientCodegenContext, shape: OperationShape): EndpointTraitBindings? = shape.getTrait(EndpointTrait::class.java).map { epTrait -> - val endpointTraitBindings = EndpointTraitBindings( + EndpointTraitBindings( codegenContext.model, codegenContext.symbolProvider, codegenContext.runtimeConfig, shape, epTrait, ) + }.orNull() + } + + override fun section(section: OperationSection): Writable = when (section) { + is OperationSection.MutateRequest -> writable { + endpointTraitBindings(codegenContext, shape)?.also { endpointTraitBindings -> withBlock("let endpoint_prefix = ", "?;") { - endpointTraitBindings.render( - this, - "self", - codegenContext.smithyRuntimeMode, - ) + endpointTraitBindings.render(this, "self", codegenContext.smithyRuntimeMode) } rust("request.properties_mut().insert(endpoint_prefix);") } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt index 262baad0c4..9fb2147c91 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRunti import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.smithy.customize.CombinedCoreCodegenDecorator import software.amazon.smithy.rust.codegen.core.smithy.customize.CoreCodegenDecorator @@ -77,6 +78,14 @@ interface ClientCodegenDecorator : CoreCodegenDecorator { operation: OperationShape, baseCustomizations: List, ): List = baseCustomizations + + /** + * Hook to override the protocol test generator + */ + fun protocolTestGenerator( + codegenContext: ClientCodegenContext, + baseGenerator: ProtocolTestGenerator, + ): ProtocolTestGenerator = baseGenerator } /** @@ -143,6 +152,13 @@ open class CombinedClientCodegenDecorator(decorators: List + decorator.protocolTestGenerator(codegenContext, gen) + } + companion object { fun fromClasspath( context: PluginContext, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt index c4d6efc327..c5b415b2a6 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointTestGenerator.kt @@ -10,10 +10,11 @@ import software.amazon.smithy.rulesengine.language.syntax.Identifier import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters import software.amazon.smithy.rulesengine.traits.EndpointTestCase import software.amazon.smithy.rulesengine.traits.ExpectedEndpoint +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Types import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName -import software.amazon.smithy.rust.codegen.client.smithy.generators.clientInstantiator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientInstantiator import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.escape @@ -22,7 +23,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock 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.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq @@ -34,8 +34,7 @@ internal class EndpointTestGenerator( private val resolverType: RuntimeType, private val params: Parameters, private val endpointCustomizations: List, - codegenContext: CodegenContext, - + codegenContext: ClientCodegenContext, ) { private val runtimeConfig = codegenContext.runtimeConfig private val serviceShape = codegenContext.serviceShape @@ -50,7 +49,7 @@ internal class EndpointTestGenerator( "capture_request" to RuntimeType.captureRequest(runtimeConfig), ) - private val instantiator = clientInstantiator(codegenContext) + private val instantiator = ClientInstantiator(codegenContext) private fun EndpointTestCase.docs(): Writable { val self = this diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt index b74079cc21..a065fe3b96 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiator.kt @@ -6,8 +6,14 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.codegen.core.Symbol +import software.amazon.smithy.model.node.Node +import software.amazon.smithy.model.node.ObjectNode import software.amazon.smithy.model.shapes.MemberShape +import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerator +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable @@ -29,11 +35,27 @@ class ClientBuilderKindBehavior(val codegenContext: CodegenContext) : Instantiat override fun doesSetterTakeInOption(memberShape: MemberShape): Boolean = true } -fun clientInstantiator(codegenContext: CodegenContext) = - Instantiator( - codegenContext.symbolProvider, - codegenContext.model, - codegenContext.runtimeConfig, - ClientBuilderKindBehavior(codegenContext), - ::enumFromStringFn, - ) +class ClientInstantiator(private val codegenContext: ClientCodegenContext) : Instantiator( + codegenContext.symbolProvider, + codegenContext.model, + codegenContext.runtimeConfig, + ClientBuilderKindBehavior(codegenContext), + ::enumFromStringFn, +) { + fun renderFluentCall( + writer: RustWriter, + clientName: String, + operationShape: OperationShape, + inputShape: StructureShape, + data: Node, + headers: Map = mapOf(), + ctx: Ctx = Ctx(), + ) { + val operationBuilderName = + FluentClientGenerator.clientOperationFnName(operationShape, codegenContext.symbolProvider) + + writer.rust("$clientName.$operationBuilderName()") + + renderStructureMembers(writer, inputShape, data as ObjectNode, headers, ctx) + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt index f02cb5cde2..102d16d216 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt @@ -45,7 +45,12 @@ class EndpointTraitBindings( * * The returned expression is a `Result` */ - fun render(writer: RustWriter, input: String, smithyRuntimeMode: SmithyRuntimeMode) { + fun render( + writer: RustWriter, + input: String, + smithyRuntimeMode: SmithyRuntimeMode, + generateValidation: Boolean = true, + ) { // the Rust format pattern to make the endpoint prefix e.g. "{}.foo" val formatLiteral = endpointTrait.prefixFormatString() if (endpointTrait.hostPrefix.labels.isEmpty()) { @@ -68,28 +73,30 @@ class EndpointTraitBindings( // NOTE: this is dead code until we start respecting @required rust("let $field = &$input.$field;") } - val contents = if (smithyRuntimeMode.generateOrchestrator) { - // TODO(enableNewSmithyRuntime): Remove the allow attribute once all places need .into method - """ - if $field.is_empty() { - ##[allow(clippy::useless_conversion)] - return Err(#{invalidFieldError:W}.into()) + if (generateValidation) { + val contents = if (smithyRuntimeMode.generateOrchestrator) { + // TODO(enableNewSmithyRuntime): Remove the allow attribute once all places need .into method + """ + if $field.is_empty() { + ##[allow(clippy::useless_conversion)] + return Err(#{invalidFieldError:W}.into()) + } + """ + } else { + """ + if $field.is_empty() { + return Err(#{invalidFieldError:W}) + } + """ } - """ - } else { - """ - if $field.is_empty() { - return Err(#{invalidFieldError:W}) - } - """ + rustTemplate( + contents, + "invalidFieldError" to OperationBuildError(runtimeConfig).invalidField( + field, + "$field was unset or empty but must be set as part of the endpoint prefix", + ), + ) } - rustTemplate( - contents, - "invalidFieldError" to OperationBuildError(runtimeConfig).invalidField( - field, - "$field was unset or empty but must be set as part of the endpoint prefix", - ), - ) "${label.content} = $field" } rustTemplate( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index cafefc1179..04919a5709 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -20,9 +20,11 @@ import software.amazon.smithy.protocoltests.traits.HttpResponseTestCase import software.amazon.smithy.protocoltests.traits.HttpResponseTestsTrait 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.clientInstantiator +import software.amazon.smithy.rust.codegen.client.smithy.customizations.EndpointPrefixGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientInstantiator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.allow +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -44,15 +46,47 @@ import software.amazon.smithy.rust.codegen.core.util.outputShape import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import java.util.logging.Logger +data class ClientCreationParams( + val codegenContext: ClientCodegenContext, + val connectorName: String, + val configBuilderName: String, + val clientName: String, +) + +interface ProtocolTestGenerator { + val codegenContext: ClientCodegenContext + val protocolSupport: ProtocolSupport + val operationShape: OperationShape + + fun render(writer: RustWriter) +} + /** * Generate protocol tests for an operation */ -class ProtocolTestGenerator( - private val codegenContext: ClientCodegenContext, - private val protocolSupport: ProtocolSupport, - private val operationShape: OperationShape, - private val writer: RustWriter, -) { +class DefaultProtocolTestGenerator( + override val codegenContext: ClientCodegenContext, + override val protocolSupport: ProtocolSupport, + override val operationShape: OperationShape, + + private val renderClientCreation: RustWriter.(ClientCreationParams) -> Unit = { params -> + rustTemplate( + """ + let smithy_client = #{Builder}::new() + .connector(${params.connectorName}) + .middleware(#{MapRequestLayer}::for_mapper(#{SmithyEndpointStage}::new())) + .build(); + let ${params.clientName} = #{Client}::with_config(smithy_client, ${params.configBuilderName}.build()); + """, + "Client" to ClientRustModule.root.toType().resolve("Client"), + "Builder" to ClientRustModule.client.toType().resolve("Builder"), + "SmithyEndpointStage" to RuntimeType.smithyHttp(codegenContext.runtimeConfig) + .resolve("endpoint::middleware::SmithyEndpointStage"), + "MapRequestLayer" to RuntimeType.smithyHttpTower(codegenContext.runtimeConfig) + .resolve("map_request::MapRequestLayer"), + ) + }, +) : ProtocolTestGenerator { private val logger = Logger.getLogger(javaClass.name) private val inputShape = operationShape.inputShape(codegenContext.model) @@ -60,7 +94,7 @@ class ProtocolTestGenerator( private val operationSymbol = codegenContext.symbolProvider.toSymbol(operationShape) private val operationIndex = OperationIndex.of(codegenContext.model) - private val instantiator = clientInstantiator(codegenContext) + private val instantiator = ClientInstantiator(codegenContext) private val codegenScope = arrayOf( "SmithyHttp" to RuntimeType.smithyHttp(codegenContext.runtimeConfig), @@ -75,7 +109,7 @@ class ProtocolTestGenerator( TestCase() } - fun render() { + override fun render(writer: RustWriter) { val requestTests = operationShape.getTrait() ?.getTestCasesFor(AppliesTo.CLIENT).orEmpty().map { TestCase.RequestTest(it) } val responseTests = operationShape.getTrait() @@ -150,6 +184,7 @@ class ProtocolTestGenerator( is Action.Response -> "_response" is Action.Request -> "_request" } + Attribute.AllowUnusedMut.render(testModuleWriter) testModuleWriter.rustBlock("async fn ${testCase.id.toSnakeCase()}$fnName()") { block(this) } @@ -166,33 +201,58 @@ class ProtocolTestGenerator( writable { val customizations = codegenContext.rootDecorator.endpointCustomizations(codegenContext) params.getObjectMember("builtInParams").orNull()?.members?.forEach { (name, value) -> - customizations.firstNotNullOf { it.setBuiltInOnServiceConfig(name.value, value, "builder") }(this) + customizations.firstNotNullOf { + it.setBuiltInOnServiceConfig(name.value, value, "config_builder") + }(this) } } } ?: writable { } rustTemplate( """ - let builder = #{config}::Config::builder().with_test_defaults().endpoint_resolver("https://example.com"); + let (conn, request_receiver) = #{capture_request}(None); + let config_builder = #{config}::Config::builder().with_test_defaults().endpoint_resolver("https://example.com"); #{customParams} - let config = builder.build(); """, + "capture_request" to CargoDependency.smithyClient(codegenContext.runtimeConfig) + .toDevDependency() + .withFeature("test-util") + .toType() + .resolve("test_connection::capture_request"), "config" to ClientRustModule.Config, "customParams" to customParams, ) - writeInline("let input =") - instantiator.render(this, inputShape, httpRequestTestCase.params) + renderClientCreation(this, ClientCreationParams(codegenContext, "conn", "config_builder", "client")) + + writeInline("let result = ") + instantiator.renderFluentCall(this, "client", operationShape, inputShape, httpRequestTestCase.params) + rust(""".send().await;""") + // Response parsing will always fail since we feed it an empty response body, so we don't care + // if it fails, but it is helpful to print what that failure was for debugging + rust("let _ = dbg!(result);") + rust("""let http_request = request_receiver.expect_request();""") - rust(""".make_operation(&config).await.expect("operation failed to build");""") - rust("let (http_request, parts) = input.into_request_response().0.into_parts();") with(httpRequestTestCase) { + // Override the endpoint for tests that set a `host`, for example: + // https://github.com/awslabs/smithy/blob/be68f3bbdfe5bf50a104b387094d40c8069f16b1/smithy-aws-protocol-tests/model/restJson1/endpoint-paths.smithy#L19 host.orNull()?.also { host -> val withScheme = "http://$host" + when (val bindings = EndpointPrefixGenerator.endpointTraitBindings(codegenContext, operationShape)) { + null -> rust("let endpoint_prefix = None;") + else -> { + withBlock("let input = ", ";") { + instantiator.render(this@renderHttpRequestTestCase, inputShape, httpRequestTestCase.params) + } + withBlock("let endpoint_prefix = Some({", "}.unwrap());") { + bindings.render(this, "input", codegenContext.smithyRuntimeMode, generateValidation = false) + } + } + } rustTemplate( """ let mut http_request = http_request; let ep = #{SmithyHttp}::endpoint::Endpoint::mutable(${withScheme.dq()}).expect("valid endpoint"); - ep.set_endpoint(http_request.uri_mut(), parts.acquire().get()).expect("valid endpoint"); + ep.set_endpoint(http_request.uri_mut(), endpoint_prefix.as_ref()).expect("valid endpoint"); """, *codegenScope, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index 83bc4dff58..f3bfe104b5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -141,7 +141,7 @@ class ResponseDeserializerGenerator( } else { #{parse_response}(status, headers, body) }; - #{type_erase_result}(parse_result).into() + #{type_erase_result}(parse_result) """, *codegenScope, "parse_error" to parserGenerator.parseErrorFn(operationShape, customizations), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt index abd497b49b..49905fdeb9 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientInstantiatorTest.kt @@ -47,7 +47,7 @@ internal class ClientInstantiatorTest { @Test fun `generate named enums`() { val shape = model.lookup("com.test#NamedEnum") - val sut = clientInstantiator(codegenContext) + val sut = ClientInstantiator(codegenContext) val data = Node.parse("t2.nano".dq()) val project = TestWorkspace.testProject(symbolProvider) @@ -66,7 +66,7 @@ internal class ClientInstantiatorTest { @Test fun `generate unnamed enums`() { val shape = model.lookup("com.test#UnnamedEnum") - val sut = clientInstantiator(codegenContext) + val sut = ClientInstantiator(codegenContext) val data = Node.parse("t2.nano".dq()) val project = TestWorkspace.testProject(symbolProvider) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt index 3a59bc1bcd..eed196e489 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/Instantiator.kt @@ -333,6 +333,23 @@ open class Instantiator( * ``` */ private fun renderStructure(writer: RustWriter, shape: StructureShape, data: ObjectNode, headers: Map, ctx: Ctx) { + writer.rust("#T::builder()", symbolProvider.toSymbol(shape)) + + renderStructureMembers(writer, shape, data, headers, ctx) + + writer.rust(".build()") + if (builderKindBehavior.hasFallibleBuilder(shape)) { + writer.rust(".unwrap()") + } + } + + protected fun renderStructureMembers( + writer: RustWriter, + shape: StructureShape, + data: ObjectNode, + headers: Map, + ctx: Ctx, + ) { fun renderMemberHelper(memberShape: MemberShape, value: Node) { val setterName = builderKindBehavior.setterName(memberShape) writer.withBlock(".$setterName(", ")") { @@ -340,7 +357,6 @@ open class Instantiator( } } - writer.rust("#T::builder()", symbolProvider.toSymbol(shape)) if (defaultsForRequiredFields) { shape.allMembers.entries .filter { (name, memberShape) -> @@ -374,11 +390,6 @@ open class Instantiator( ?.let { renderMemberHelper(it.value, fillDefaultValue(model.expectShape(it.value.target))) } - - writer.rust(".build()") - if (builderKindBehavior.hasFallibleBuilder(shape)) { - writer.rust(".unwrap()") - } } /** diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 8dfedb5148..ab9ee61db3 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -7,6 +7,7 @@ use crate::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use crate::client::orchestrator::{BoxError, HttpRequest}; use crate::config_bag::ConfigBag; use crate::type_erasure::{TypeErasedBox, TypedBox}; +use aws_smithy_types::Document; use std::borrow::Cow; use std::fmt; use std::sync::Arc; @@ -34,6 +35,12 @@ impl AuthSchemeId { } } +impl From<&'static str> for AuthSchemeId { + fn from(scheme_id: &'static str) -> Self { + Self::new(scheme_id) + } +} + #[derive(Debug)] pub struct AuthOptionResolverParams(TypeErasedBox); @@ -105,10 +112,34 @@ pub trait HttpRequestSigner: Send + Sync + fmt::Debug { &self, request: &mut HttpRequest, identity: &Identity, + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, config_bag: &ConfigBag, ) -> Result<(), BoxError>; } +/// Endpoint configuration for the selected auth scheme. +/// +/// This struct gets added to the request state by the auth orchestrator. +#[non_exhaustive] +#[derive(Clone, Debug)] +pub struct AuthSchemeEndpointConfig<'a>(Option<&'a Document>); + +impl<'a> AuthSchemeEndpointConfig<'a> { + /// Creates a new [`AuthSchemeEndpointConfig`]. + pub fn new(config: Option<&'a Document>) -> Self { + Self(config) + } + + /// Creates an empty AuthSchemeEndpointConfig. + pub fn empty() -> Self { + Self(None) + } + + pub fn config(&self) -> Option<&'a Document> { + self.0 + } +} + pub mod builders { use super::*; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index c2c9e9cd54..314fd57249 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -16,7 +16,7 @@ use crate::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_http::body::SdkBody; -use aws_smithy_http::endpoint::EndpointPrefix; +use aws_smithy_types::endpoint::Endpoint; use std::fmt; use std::future::Future as StdFuture; use std::pin::Pin; @@ -74,12 +74,7 @@ impl EndpointResolverParams { } pub trait EndpointResolver: Send + Sync + fmt::Debug { - fn resolve_and_apply_endpoint( - &self, - params: &EndpointResolverParams, - endpoint_prefix: Option<&EndpointPrefix>, - request: &mut HttpRequest, - ) -> Result<(), BoxError>; + fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Result; } /// Time that the request is being made (so that time can be overridden in the [`ConfigBag`]). diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs index a9ca17f0b3..eaf14dd04a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs @@ -8,7 +8,9 @@ use aws_smithy_runtime_api::client::auth::http::{ HTTP_API_KEY_AUTH_SCHEME_ID, HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID, HTTP_DIGEST_AUTH_SCHEME_ID, }; -use aws_smithy_runtime_api::client::auth::{AuthSchemeId, HttpAuthScheme, HttpRequestSigner}; +use aws_smithy_runtime_api::client::auth::{ + AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, +}; use aws_smithy_runtime_api::client::identity::http::{Login, Token}; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpRequest}; @@ -76,6 +78,7 @@ impl HttpRequestSigner for ApiKeySigner { &self, request: &mut HttpRequest, identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { let api_key = identity @@ -141,6 +144,7 @@ impl HttpRequestSigner for BasicAuthSigner { &self, request: &mut HttpRequest, identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { let login = identity @@ -198,6 +202,7 @@ impl HttpRequestSigner for BearerAuthSigner { &self, request: &mut HttpRequest, identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { let token = identity @@ -253,6 +258,7 @@ impl HttpRequestSigner for DigestAuthSigner { &self, _request: &mut HttpRequest, _identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { unimplemented!( @@ -281,7 +287,12 @@ mod tests { .body(SdkBody::empty()) .unwrap(); signer - .sign_request(&mut request, &identity, &config_bag) + .sign_request( + &mut request, + &identity, + AuthSchemeEndpointConfig::empty(), + &config_bag, + ) .expect("success"); assert_eq!( "SomeSchemeName some-token", @@ -304,7 +315,12 @@ mod tests { .body(SdkBody::empty()) .unwrap(); signer - .sign_request(&mut request, &identity, &config_bag) + .sign_request( + &mut request, + &identity, + AuthSchemeEndpointConfig::empty(), + &config_bag, + ) .expect("success"); assert!(request.headers().get("some-query-name").is_none()); assert_eq!( @@ -321,7 +337,12 @@ mod tests { let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); signer - .sign_request(&mut request, &identity, &config_bag) + .sign_request( + &mut request, + &identity, + AuthSchemeEndpointConfig::empty(), + &config_bag, + ) .expect("success"); assert_eq!( "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", @@ -337,7 +358,12 @@ mod tests { let identity = Identity::new(Token::new("some-token", None), None); let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); signer - .sign_request(&mut request, &identity, &config_bag) + .sign_request( + &mut request, + &identity, + AuthSchemeEndpointConfig::empty(), + &config_bag, + ) .expect("success"); assert_eq!( "Bearer some-token", diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 883e628ada..a336e1436c 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -3,11 +3,62 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_runtime_api::client::auth::{AuthSchemeEndpointConfig, AuthSchemeId}; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors}; use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::endpoint::Endpoint; +use aws_smithy_types::Document; +use std::borrow::Cow; +use std::error::Error as StdError; use std::fmt; +#[derive(Debug)] +enum AuthOrchestrationError { + NoMatchingAuthScheme, + BadAuthSchemeEndpointConfig(Cow<'static, str>), + AuthSchemeEndpointConfigMismatch(String), +} + +impl AuthOrchestrationError { + fn auth_scheme_endpoint_config_mismatch<'a>( + auth_schemes: impl Iterator, + ) -> Self { + Self::AuthSchemeEndpointConfigMismatch( + auth_schemes + .flat_map(|s| match s { + Document::Object(map) => match map.get("name") { + Some(Document::String(name)) => Some(name.as_str()), + _ => None, + }, + _ => None, + }) + .collect::>() + .join(", "), + ) + } +} + +impl fmt::Display for AuthOrchestrationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NoMatchingAuthScheme => f.write_str( + "no auth scheme matched auth options. This is a bug. Please file an issue.", + ), + Self::BadAuthSchemeEndpointConfig(message) => f.write_str(message), + Self::AuthSchemeEndpointConfigMismatch(supported_schemes) => { + write!(f, + "selected auth scheme / endpoint config mismatch. Couldn't find `sigv4` endpoint config for this endpoint. \ + The authentication schemes supported by this endpoint are: {:?}", + supported_schemes + ) + } + } + } +} + +impl StdError for AuthOrchestrationError {} + pub(super) async fn orchestrate_auth( ctx: &mut InterceptorContext, cfg: &ConfigBag, @@ -22,36 +73,62 @@ pub(super) async fn orchestrate_auth( identity_resolvers = ?identity_resolvers, "orchestrating auth", ); + for &scheme_id in auth_options.as_ref() { if let Some(auth_scheme) = cfg.http_auth_schemes().scheme(scheme_id) { if let Some(identity_resolver) = auth_scheme.identity_resolver(identity_resolvers) { let request_signer = auth_scheme.request_signer(); + let endpoint = cfg + .get::() + .expect("endpoint added to config bag by endpoint orchestrator"); + let auth_scheme_endpoint_config = + extract_endpoint_auth_scheme_config(endpoint, scheme_id)?; let identity = identity_resolver.resolve_identity(cfg).await?; let request = ctx.request_mut(); - request_signer.sign_request(request, &identity, cfg)?; + request_signer.sign_request( + request, + &identity, + auth_scheme_endpoint_config, + cfg, + )?; return Ok(()); } } } - Err(NoMatchingAuthScheme.into()) + Err(AuthOrchestrationError::NoMatchingAuthScheme.into()) } -#[derive(Debug)] -struct NoMatchingAuthScheme; - -impl fmt::Display for NoMatchingAuthScheme { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "no auth scheme matched auth options. This is a bug. Please file an issue." - ) - } +fn extract_endpoint_auth_scheme_config( + endpoint: &Endpoint, + scheme_id: AuthSchemeId, +) -> Result, AuthOrchestrationError> { + let auth_schemes = match endpoint.properties().get("authSchemes") { + Some(Document::Array(schemes)) => schemes, + // no auth schemes: + None => return Ok(AuthSchemeEndpointConfig::new(None)), + _other => { + return Err(AuthOrchestrationError::BadAuthSchemeEndpointConfig( + "expected an array for `authSchemes` in endpoint config".into(), + )) + } + }; + let auth_scheme_config = auth_schemes + .iter() + .find(|doc| { + let config_scheme_id = doc + .as_object() + .and_then(|object| object.get("name")) + .and_then(Document::as_string); + config_scheme_id == Some(scheme_id.as_str()) + }) + .ok_or_else(|| { + AuthOrchestrationError::auth_scheme_endpoint_config_mismatch(auth_schemes.iter()) + })?; + Ok(AuthSchemeEndpointConfig::new(Some(auth_scheme_config))) } -impl std::error::Error for NoMatchingAuthScheme {} - #[cfg(test)] mod tests { use super::*; @@ -64,6 +141,7 @@ mod tests { use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; use aws_smithy_runtime_api::type_erasure::TypedBox; + use std::collections::HashMap; #[tokio::test] async fn basic_case() { @@ -83,6 +161,7 @@ mod tests { &self, request: &mut HttpRequest, _identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { request @@ -134,6 +213,7 @@ mod tests { .auth_scheme(TEST_SCHEME_ID, TestAuthScheme { signer: TestSigner }) .build(), ); + cfg.put(Endpoint::builder().url("dontcare").build()); orchestrate_auth(&mut ctx, &cfg).await.expect("success"); @@ -170,6 +250,7 @@ mod tests { .auth_scheme(HTTP_BEARER_AUTH_SCHEME_ID, BearerAuthScheme::new()) .build(), ); + cfg.put(Endpoint::builder().url("dontcare").build()); // First, test the presence of a basic auth login and absence of a bearer token cfg.set_identity_resolvers( @@ -203,4 +284,92 @@ mod tests { ctx.request().headers().get("Authorization").unwrap() ); } + + #[test] + fn extract_endpoint_auth_scheme_config_no_config() { + let endpoint = Endpoint::builder() + .url("dontcare") + .property("something-unrelated", Document::Null) + .build(); + let config = extract_endpoint_auth_scheme_config(&endpoint, "test-scheme-id".into()) + .expect("success"); + assert!(config.config().is_none()); + } + + #[test] + fn extract_endpoint_auth_scheme_config_wrong_type() { + let endpoint = Endpoint::builder() + .url("dontcare") + .property("authSchemes", Document::String("bad".into())) + .build(); + extract_endpoint_auth_scheme_config(&endpoint, "test-scheme-id".into()) + .expect_err("should fail because authSchemes is the wrong type"); + } + + #[test] + fn extract_endpoint_auth_scheme_config_no_matching_scheme() { + let endpoint = Endpoint::builder() + .url("dontcare") + .property( + "authSchemes", + vec![ + Document::Object({ + let mut out = HashMap::new(); + out.insert("name".to_string(), "wrong-scheme-id".to_string().into()); + out + }), + Document::Object({ + let mut out = HashMap::new(); + out.insert( + "name".to_string(), + "another-wrong-scheme-id".to_string().into(), + ); + out + }), + ], + ) + .build(); + extract_endpoint_auth_scheme_config(&endpoint, "test-scheme-id".into()) + .expect_err("should fail because authSchemes doesn't include the desired scheme"); + } + + #[test] + fn extract_endpoint_auth_scheme_config_successfully() { + let endpoint = Endpoint::builder() + .url("dontcare") + .property( + "authSchemes", + vec![ + Document::Object({ + let mut out = HashMap::new(); + out.insert("name".to_string(), "wrong-scheme-id".to_string().into()); + out + }), + Document::Object({ + let mut out = HashMap::new(); + out.insert("name".to_string(), "test-scheme-id".to_string().into()); + out.insert( + "magicString".to_string(), + "magic string value".to_string().into(), + ); + out + }), + ], + ) + .build(); + let config = extract_endpoint_auth_scheme_config(&endpoint, "test-scheme-id".into()) + .expect("should find test-scheme-id"); + assert_eq!( + "magic string value", + config + .config() + .expect("config is set") + .as_object() + .expect("it's an object") + .get("magicString") + .expect("magicString is set") + .as_string() + .expect("gimme the string, dammit!") + ); + } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index cb4e4e9c09..139bef24bb 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -5,13 +5,15 @@ use aws_smithy_http::endpoint::error::ResolveEndpointError; use aws_smithy_http::endpoint::{ - apply_endpoint, EndpointPrefix, ResolveEndpoint, SharedEndpointResolver, + apply_endpoint as apply_endpoint_to_request_uri, EndpointPrefix, ResolveEndpoint, + SharedEndpointResolver, }; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, EndpointResolver, EndpointResolverParams, HttpRequest, }; use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::endpoint::Endpoint; use http::header::HeaderName; use http::{HeaderValue, Uri}; use std::fmt::Debug; @@ -36,14 +38,8 @@ impl StaticUriEndpointResolver { } impl EndpointResolver for StaticUriEndpointResolver { - fn resolve_and_apply_endpoint( - &self, - _params: &EndpointResolverParams, - _endpoint_prefix: Option<&EndpointPrefix>, - request: &mut HttpRequest, - ) -> Result<(), BoxError> { - apply_endpoint(request.uri_mut(), &self.endpoint, None)?; - Ok(()) + fn resolve_endpoint(&self, _params: &EndpointResolverParams) -> Result { + Ok(Endpoint::builder().url(self.endpoint.to_string()).build()) } } @@ -81,63 +77,64 @@ impl EndpointResolver for DefaultEndpointResolver where Params: Debug + Send + Sync + 'static, { - fn resolve_and_apply_endpoint( - &self, - params: &EndpointResolverParams, - endpoint_prefix: Option<&EndpointPrefix>, - request: &mut HttpRequest, - ) -> Result<(), BoxError> { - let endpoint = match params.get::() { - Some(params) => self.inner.resolve_endpoint(params)?, - None => { - return Err(Box::new(ResolveEndpointError::message( - "params of expected type was not present", - ))); - } - }; - - let uri: Uri = endpoint.url().parse().map_err(|err| { - ResolveEndpointError::from_source("endpoint did not have a valid uri", err) - })?; - - apply_endpoint(request.uri_mut(), &uri, endpoint_prefix).map_err(|err| { - ResolveEndpointError::message(format!( - "failed to apply endpoint `{:?}` to request `{:?}`", - uri, request, - )) - .with_source(Some(err.into())) - })?; - - for (header_name, header_values) in endpoint.headers() { - request.headers_mut().remove(header_name); - for value in header_values { - request.headers_mut().insert( - HeaderName::from_str(header_name).map_err(|err| { - ResolveEndpointError::message("invalid header name") - .with_source(Some(err.into())) - })?, - HeaderValue::from_str(value).map_err(|err| { - ResolveEndpointError::message("invalid header value") - .with_source(Some(err.into())) - })?, - ); - } + fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Result { + match params.get::() { + Some(params) => Ok(self.inner.resolve_endpoint(params)?), + None => Err(Box::new(ResolveEndpointError::message( + "params of expected type was not present", + ))), } - - Ok(()) } } pub(super) fn orchestrate_endpoint( ctx: &mut InterceptorContext, - cfg: &ConfigBag, + cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let params = cfg.endpoint_resolver_params(); let endpoint_prefix = cfg.get::(); let request = ctx.request_mut(); let endpoint_resolver = cfg.endpoint_resolver(); - endpoint_resolver.resolve_and_apply_endpoint(params, endpoint_prefix, request)?; + let endpoint = endpoint_resolver.resolve_endpoint(params)?; + apply_endpoint(request, &endpoint, endpoint_prefix)?; + + // Make the endpoint config available to interceptors + cfg.put(endpoint); + Ok(()) +} +fn apply_endpoint( + request: &mut HttpRequest, + endpoint: &Endpoint, + endpoint_prefix: Option<&EndpointPrefix>, +) -> Result<(), BoxError> { + let uri: Uri = endpoint.url().parse().map_err(|err| { + ResolveEndpointError::from_source("endpoint did not have a valid uri", err) + })?; + + apply_endpoint_to_request_uri(request.uri_mut(), &uri, endpoint_prefix).map_err(|err| { + ResolveEndpointError::message(format!( + "failed to apply endpoint `{:?}` to request `{:?}`", + uri, request, + )) + .with_source(Some(err.into())) + })?; + + for (header_name, header_values) in endpoint.headers() { + request.headers_mut().remove(header_name); + for value in header_values { + request.headers_mut().insert( + HeaderName::from_str(header_name).map_err(|err| { + ResolveEndpointError::message("invalid header name") + .with_source(Some(err.into())) + })?, + HeaderValue::from_str(value).map_err(|err| { + ResolveEndpointError::message("invalid header value") + .with_source(Some(err.into())) + })?, + ); + } + } Ok(()) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs index 1fbc44b1fd..f641e48379 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs @@ -10,7 +10,7 @@ use aws_smithy_runtime_api::client::auth::option_resolver::{ StaticAuthOptionResolver, StaticAuthOptionResolverParams, }; use aws_smithy_runtime_api::client::auth::{ - AuthSchemeId, HttpAuthScheme, HttpAuthSchemes, HttpRequestSigner, + AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpAuthSchemes, HttpRequestSigner, }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; @@ -83,6 +83,7 @@ impl HttpRequestSigner for AnonymousSigner { &self, _request: &mut HttpRequest, _identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { Ok(()) diff --git a/rust-runtime/aws-smithy-types/src/document.rs b/rust-runtime/aws-smithy-types/src/document.rs index 4bfb641fe3..a7c46d04db 100644 --- a/rust-runtime/aws-smithy-types/src/document.rs +++ b/rust-runtime/aws-smithy-types/src/document.rs @@ -4,6 +4,7 @@ */ use crate::Number; +use std::borrow::Cow; use std::collections::HashMap; /* ANCHOR: document */ @@ -14,7 +15,7 @@ use std::collections::HashMap; /// Open content is useful for modeling unstructured data that has no schema, data that can't be /// modeled using rigid types, or data that has a schema that evolves outside of the purview of a model. /// The serialization format of a document is an implementation detail of a protocol. -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum Document { /// JSON object Object(HashMap), @@ -30,12 +31,135 @@ pub enum Document { Null, } +impl Document { + /// Returns the inner map value if this `Document` is an object. + pub fn as_object(&self) -> Option<&HashMap> { + if let Self::Object(object) = self { + Some(object) + } else { + None + } + } + + /// Returns the mutable inner map value if this `Document` is an object. + pub fn as_object_mut(&mut self) -> Option<&mut HashMap> { + if let Self::Object(object) = self { + Some(object) + } else { + None + } + } + + /// Returns the inner array value if this `Document` is an array. + pub fn as_array(&self) -> Option<&Vec> { + if let Self::Array(array) = self { + Some(array) + } else { + None + } + } + + /// Returns the mutable inner array value if this `Document` is an array. + pub fn as_array_mut(&mut self) -> Option<&mut Vec> { + if let Self::Array(array) = self { + Some(array) + } else { + None + } + } + + /// Returns the inner number value if this `Document` is a number. + pub fn as_number(&self) -> Option<&Number> { + if let Self::Number(number) = self { + Some(number) + } else { + None + } + } + + /// Returns the inner string value if this `Document` is a string. + pub fn as_string(&self) -> Option<&str> { + if let Self::String(string) = self { + Some(string) + } else { + None + } + } + + /// Returns the inner boolean value if this `Document` is a boolean. + pub fn as_bool(&self) -> Option { + if let Self::Bool(boolean) = self { + Some(*boolean) + } else { + None + } + } + + /// Returns `Some(())` if this `Document` is a null. + pub fn as_null(&self) -> Option<()> { + if let Self::Null = self { + Some(()) + } else { + None + } + } + + /// Returns `true` if this `Document` is an object. + pub fn is_object(&self) -> bool { + matches!(self, Self::Object(_)) + } + + /// Returns `true` if this `Document` is an array. + pub fn is_array(&self) -> bool { + matches!(self, Self::Array(_)) + } + + /// Returns `true` if this `Document` is a number. + pub fn is_number(&self) -> bool { + matches!(self, Self::Number(_)) + } + + /// Returns `true` if this `Document` is a string. + pub fn is_string(&self) -> bool { + matches!(self, Self::String(_)) + } + + /// Returns `true` if this `Document` is a bool. + pub fn is_bool(&self) -> bool { + matches!(self, Self::Bool(_)) + } + + /// Returns `true` if this `Document` is a boolean. + pub fn is_null(&self) -> bool { + matches!(self, Self::Null) + } +} + +/// The default value is `Document::Null`. +impl Default for Document { + fn default() -> Self { + Self::Null + } +} + impl From for Document { fn from(value: bool) -> Self { Document::Bool(value) } } +impl<'a> From<&'a str> for Document { + fn from(value: &'a str) -> Self { + Document::String(value.to_string()) + } +} + +impl<'a> From> for Document { + fn from(value: Cow<'a, str>) -> Self { + Document::String(value.into_owned()) + } +} + impl From for Document { fn from(value: String) -> Self { Document::String(value) @@ -71,3 +195,15 @@ impl From for Document { Document::Number(Number::NegInt(value as i64)) } } + +impl From for Document { + fn from(value: f64) -> Self { + Document::Number(Number::Float(value)) + } +} + +impl From for Document { + fn from(value: Number) -> Self { + Document::Number(value) + } +} diff --git a/tools/ci-scripts/check-aws-sdk-adhoc-tests b/tools/ci-scripts/check-aws-sdk-adhoc-tests index 0ab0ba71c2..71e11c66ba 100755 --- a/tools/ci-scripts/check-aws-sdk-adhoc-tests +++ b/tools/ci-scripts/check-aws-sdk-adhoc-tests @@ -4,6 +4,17 @@ # SPDX-License-Identifier: Apache-2.0 # -set -eux +C_YELLOW='\033[1;33m' +C_RESET='\033[0m' + +set -eu cd smithy-rs -./gradlew aws:sdk-adhoc-test:test + +# TODO(enableNewSmithyRuntime): Remove the middleware test run when cleaning up middleware +echo -e "## ${C_YELLOW}Running SDK adhoc tests against the middleware implementation...${C_RESET}" +./gradlew aws:sdk-adhoc-test:clean +./gradlew aws:sdk-adhoc-test:check -Psmithy.runtime.mode=middleware + +echo -e "## ${C_YELLOW}Running SDK adhoc tests against the orchestrator implementation...${C_RESET}" +./gradlew aws:sdk-adhoc-test:clean +./gradlew aws:sdk-adhoc-test:check -Psmithy.runtime.mode=orchestrator From 3a9e64e5041f6518b412576f0819602daca5e23a Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 19 May 2023 10:28:37 -0700 Subject: [PATCH 103/253] Test the orchestrator against the SDK in CI (#2715) ## Motivation and Context This PR adds the SDK smoke test with the orchestrator implementation to CI, and excludes services that don't yet pass tests. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .github/workflows/ci.yml | 3 + aws/sdk/README.md | 2 +- aws/sdk/build.gradle.kts | 50 ++++++++-------- .../check-aws-sdk-orchestrator-impl | 59 +++++++++++++++++++ 4 files changed, 90 insertions(+), 24 deletions(-) create mode 100755 tools/ci-scripts/check-aws-sdk-orchestrator-impl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 54ccda52ff..cb0d9e5207 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,6 +84,9 @@ jobs: test: - action: check-aws-sdk-adhoc-tests runner: ubuntu-latest + # TODO(enableNewSmithyRuntime): Remove `check-aws-sdk-orchestrator-impl` when cleaning up middleware + - action: check-aws-sdk-orchestrator-impl + runner: smithy_ubuntu-latest_8-core - action: check-client-codegen-integration-tests runner: smithy_ubuntu-latest_8-core - action: check-client-codegen-unit-tests diff --git a/aws/sdk/README.md b/aws/sdk/README.md index 160bea4da9..f7a48ebdaa 100644 --- a/aws/sdk/README.md +++ b/aws/sdk/README.md @@ -12,7 +12,7 @@ Generate an SDK: `./gradlew :aws:sdk:assemble` Generate, compile, and test an SDK: -`./gradlew :aws:sdk:build` +`./gradlew :aws:sdk:check` Run an SDK example: `./gradlew :aws:sdk:runExample --example dynamo-helloworld` diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index b228458b20..e90752cce3 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -151,6 +151,8 @@ tasks.register("generateSmithyBuild") { } tasks.register("generateIndexMd") { + dependsOn("smithyBuildJar") + inputs.property("servicelist", awsServices.services.toString()) val indexMd = outputDir.resolve("index.md") outputs.file(indexMd) @@ -161,6 +163,8 @@ tasks.register("generateIndexMd") { tasks.register("relocateServices") { description = "relocate AWS services to their final destination" + dependsOn("smithyBuildJar") + doLast { awsServices.services.forEach { logger.info("Relocating ${it.module}...") @@ -186,6 +190,8 @@ tasks.register("relocateServices") { tasks.register("relocateExamples") { description = "relocate the examples folder & rewrite path dependencies" + dependsOn("smithyBuildJar") + doLast { if (awsServices.examples.isNotEmpty()) { copy { @@ -207,6 +213,8 @@ tasks.register("relocateExamples") { tasks.register("relocateTests") { description = "relocate the root integration tests and rewrite path dependencies" + dependsOn("smithyBuildJar") + doLast { if (awsServices.rootTests.isNotEmpty()) { copy { @@ -230,6 +238,7 @@ tasks.register("relocateTests") { tasks.register("fixExampleManifests") { description = "Adds dependency path and corrects version number of examples after relocation" enabled = awsServices.examples.isNotEmpty() + dependsOn("relocateExamples") toolPath = sdkVersionerToolPath binaryName = "sdk-versioner" @@ -257,6 +266,7 @@ fun rewritePathDependency(line: String): String { } tasks.register("copyAllRuntimes") { + dependsOn("smithyBuildJar") from("$rootDir/aws/rust-runtime") { CrateSet.AWS_SDK_RUNTIME.forEach { include("$it/**") } } @@ -293,6 +303,7 @@ tasks.register("relocateRuntime") { } tasks.register("relocateChangelog") { + dependsOn("smithyBuildJar") from("$rootDir/aws") include("SDK_CHANGELOG.md") into(outputDir) @@ -328,6 +339,11 @@ tasks.register("generateCargoWorkspace") { tasks.register("fixManifests") { description = "Run the publisher tool's `fix-manifests` sub-command on the generated services" + dependsOn("relocateServices") + dependsOn("relocateRuntime") + dependsOn("relocateAwsRuntime") + dependsOn("relocateExamples") + dependsOn("relocateTests") inputs.dir(publisherToolPath) outputs.dir(outputDir) @@ -339,18 +355,10 @@ tasks.register("fixManifests") { add("--disable-version-number-validation") } } - - dependsOn("assemble") - dependsOn("relocateServices") - dependsOn("relocateRuntime") - dependsOn("relocateAwsRuntime") - dependsOn("relocateExamples") - dependsOn("relocateTests") } tasks.register("hydrateReadme") { description = "Run the publisher tool's `hydrate-readme` sub-command to create the final AWS Rust SDK README file" - dependsOn("generateVersionManifest") inputs.dir(publisherToolPath) @@ -401,10 +409,17 @@ tasks.register("generateVersionManifest") { } } -tasks.register("finalizeSdk") { - dependsOn("assemble") +tasks["smithyBuildJar"].apply { + inputs.file(buildDir.resolve("smithy-build.json")) + inputs.dir(projectDir.resolve("aws-models")) + dependsOn("generateSmithyBuild") + dependsOn("generateCargoWorkspace") outputs.upToDateWhen { false } - finalizedBy( +} +tasks["assemble"].apply { + dependsOn( + "deleteSdk", + "smithyBuildJar", "relocateServices", "relocateRuntime", "relocateAwsRuntime", @@ -417,24 +432,13 @@ tasks.register("finalizeSdk") { "hydrateReadme", "relocateChangelog", ) -} - -tasks["smithyBuildJar"].apply { - inputs.file(buildDir.resolve("smithy-build.json")) - inputs.dir(projectDir.resolve("aws-models")) - dependsOn("generateSmithyBuild") - dependsOn("generateCargoWorkspace") outputs.upToDateWhen { false } } -tasks["assemble"].apply { - dependsOn("deleteSdk") - dependsOn("smithyBuildJar") - finalizedBy("finalizeSdk") -} project.registerCargoCommandsTasks(outputDir, defaultRustDocFlags) project.registerGenerateCargoConfigTomlTask(outputDir) +tasks["test"].dependsOn("assemble") tasks["test"].finalizedBy(Cargo.CLIPPY.toString, Cargo.TEST.toString, Cargo.DOCS.toString) tasks.register("deleteSdk") { diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl new file mode 100755 index 0000000000..146db0d073 --- /dev/null +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -0,0 +1,59 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +# This script tests the SDK smoke test services against the orchestrator implementation + +C_YELLOW='\033[1;33m' +C_RESET='\033[0m' + +set -eu +cd smithy-rs + +# TODO(enableNewSmithyRuntime): Move these into `services_that_compile` as more progress is made +services_that_fail_compile=(\ + "s3"\ + "s3control"\ + "transcribestreaming"\ +) + +# TODO(enableNewSmithyRuntime): Move these into `services_that_pass_tests` as more progress is made +services_that_compile=(\ + "dynamodb"\ + "glacier"\ + "route53"\ + "sts"\ +) + +services_that_pass_tests=(\ + "config"\ + "ec2"\ + "ecs"\ + "iam"\ + "kms"\ + "lambda"\ + "polly"\ + "qldbsession"\ + "sso"\ +) + +./gradlew aws:sdk:assemble -Psmithy.runtime.mode=orchestrator + +cd aws/sdk/build/aws-sdk/sdk +for service in "${services_that_compile[@]}"; do + pushd "${service}" + echo -e "${C_YELLOW}# Running 'cargo check --all-features' on '${service}'${C_RESET}" + cargo check --all-features + popd +done + +for service in "${services_that_pass_tests[@]}"; do + pushd "${service}" + echo -e "${C_YELLOW}# Running 'cargo test --all-features' on '${service}'${C_RESET}" + cargo test --all-features --no-fail-fast + popd +done + +echo "SUCCESS" From 61b7a774b425cbfe24555ff5447fec32237ab122 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 19 May 2023 13:29:39 -0500 Subject: [PATCH 104/253] Allow integration tests to run in both runtime modes (#2709) ## Description This will allow integration tests under `aws/sdk/integration-tests` to run in both smithy runtime modes. Prior to the PR, the integration tests use `map_operation` to customize operations, which is not supported in the orchestrator, preventing us from running them in the orchestrator mode. Fortunately, all the usages of `map_operation` in the integration tests involve setting a test request time and a test user agent in a property bag. This PR stops using `map_operation` in those tests and instead introduces separate test helper methods in both runtime modes: one for setting a test request and the other for setting a test user agent. They allow the integration tests to compile and run in both modes. Note that the integration tests in the orchestrator do not necessarily pass at this time. We'll address the failures subsequently. ## Testing Confirmed integration tests continue passing with the changes when run in the middleware . Confirmed those complied (but not necessarily passed) in the orchestrator. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- .../AwsCustomizableOperationDecorator.kt | 103 ++++++++++++++++++ .../rustsdk/AwsFluentClientDecorator.kt | 2 +- .../integration-tests/s3/tests/checksums.rs | 26 +---- .../s3/tests/customizable-operation.rs | 12 +- .../integration-tests/s3/tests/endpoints.rs | 8 +- .../s3/tests/ignore-invalid-xml-body-root.rs | 16 +-- .../s3/tests/naughty-string-metadata.rs | 16 +-- .../s3/tests/normalize-uri-path.rs | 12 +- .../query-strings-are-correctly-encoded.rs | 12 +- .../integration-tests/s3/tests/signing-it.rs | 12 +- .../s3control/tests/signing-it.rs | 12 +- .../aws-sdk-s3/tests/interceptors.rs | 24 ++-- .../aws-sdk-s3/tests/util.rs | 1 - .../client/CustomizableOperationDecorator.kt | 19 ++++ .../client/CustomizableOperationGenerator.kt | 13 +++ .../client/FluentClientGenerator.kt | 4 +- .../src/client/test_util.rs | 1 + .../src/client/test_util/interceptor.rs | 78 +++++++++++++ 18 files changed, 252 insertions(+), 119 deletions(-) create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationDecorator.kt create mode 100644 rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt new file mode 100644 index 0000000000..70cc99e52c --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -0,0 +1,103 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import software.amazon.smithy.rust.codegen.client.smithy.generators.client.CustomizableOperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.client.CustomizableOperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +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.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType + +class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : + CustomizableOperationCustomization() { + private val codegenScope = arrayOf( + *RuntimeType.preludeScope, + "AwsUserAgent" to AwsRuntimeType.awsHttp(runtimeConfig) + .resolve("user_agent::AwsUserAgent"), + "BeforeTransmitInterceptorContextMut" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::interceptors::BeforeTransmitInterceptorContextMut"), + "ConfigBag" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("config_bag::ConfigBag"), + "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::ConfigBagAccessors"), + "http" to CargoDependency.Http.toType(), + "InterceptorContext" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::interceptors::InterceptorContext"), + "RequestTime" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::RequestTime"), + "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::interceptors::SharedInterceptor"), + "TestParamsSetterInterceptor" to CargoDependency.smithyRuntime(runtimeConfig).withFeature("test-util") + .toType().resolve("client::test_util::interceptor::TestParamsSetterInterceptor"), + ) + + override fun section(section: CustomizableOperationSection): Writable = + writable { + if (section is CustomizableOperationSection.CustomizableOperationImpl) { + if (section.operationShape == null) { + // TODO(enableNewSmithyRuntime): Delete this branch when middleware is no longer used + // This branch customizes CustomizableOperation in the middleware. section.operationShape being + // null means that this customization is rendered in a place where we don't need to figure out + // the module for an operation (which is the case for CustomizableOperation in the middleware + // that is rendered in the customize module). + rustTemplate( + """ + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self { + self.operation.properties_mut().insert(request_time); + self + } + + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn user_agent_for_tests(mut self) -> Self { + self.operation.properties_mut().insert(#{AwsUserAgent}::for_tests()); + self + } + """, + *codegenScope, + ) + } else { + // The else branch is for rendering customization for the orchestrator. + rustTemplate( + """ + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self { + use #{ConfigBagAccessors}; + let interceptor = #{TestParamsSetterInterceptor}::new(move |_: &mut #{BeforeTransmitInterceptorContextMut}<'_>, cfg: &mut #{ConfigBag}| { + cfg.set_request_time(#{RequestTime}::new(request_time)); + }); + self.interceptors.push(#{SharedInterceptor}::new(interceptor)); + self + } + + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn user_agent_for_tests(mut self) -> Self { + let interceptor = #{TestParamsSetterInterceptor}::new(|context: &mut #{BeforeTransmitInterceptorContextMut}<'_>, _: &mut #{ConfigBag}| { + let headers = context.request_mut().headers_mut(); + let user_agent = #{AwsUserAgent}::for_tests(); + headers.insert( + #{http}::header::USER_AGENT, + #{http}::HeaderValue::try_from(user_agent.ua_header()).unwrap(), + ); + headers.insert( + #{http}::HeaderName::from_static("x-amz-user-agent"), + #{http}::HeaderValue::try_from(user_agent.aws_ua_header()).unwrap(), + ); + }); + self.interceptors.push(#{SharedInterceptor}::new(interceptor)); + self + } + """, + *codegenScope, + ) + } + } + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index b0963eaea9..050ba65be0 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -71,7 +71,7 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { AwsFluentClientDocs(codegenContext), ), retryClassifier = AwsRuntimeType.awsHttp(runtimeConfig).resolve("retry::AwsResponseRetryClassifier"), - ).render(rustCrate) + ).render(rustCrate, listOf(CustomizableOperationTestHelpers(runtimeConfig))) rustCrate.withModule(ClientRustModule.Client.customize) { renderCustomizableOperationSendMethod(runtimeConfig, generics, this) } diff --git a/aws/sdk/integration-tests/s3/tests/checksums.rs b/aws/sdk/integration-tests/s3/tests/checksums.rs index a82aac5e0a..ae533964a1 100644 --- a/aws/sdk/integration-tests/s3/tests/checksums.rs +++ b/aws/sdk/integration-tests/s3/tests/checksums.rs @@ -5,7 +5,6 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::types::ChecksumMode; use aws_sdk_s3::Client; @@ -14,10 +13,7 @@ use aws_smithy_client::test_connection::{capture_request, TestConnection}; use aws_smithy_http::body::SdkBody; use http::header::AUTHORIZATION; use http::{HeaderValue, Uri}; -use std::{ - convert::Infallible, - time::{Duration, UNIX_EPOCH}, -}; +use std::time::{Duration, UNIX_EPOCH}; use tracing_test::traced_test; /// Test connection for the movies IT @@ -77,14 +73,8 @@ async fn test_checksum_on_streaming_response( .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests() .send() .await .unwrap(); @@ -191,14 +181,8 @@ async fn test_checksum_on_streaming_request<'a>( .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests() .send() .await .unwrap(); diff --git a/aws/sdk/integration-tests/s3/tests/customizable-operation.rs b/aws/sdk/integration-tests/s3/tests/customizable-operation.rs index 24b53523a9..09dd42fb8f 100644 --- a/aws/sdk/integration-tests/s3/tests/customizable-operation.rs +++ b/aws/sdk/integration-tests/s3/tests/customizable-operation.rs @@ -5,12 +5,10 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::Client; use aws_smithy_client::test_connection::capture_request; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] @@ -29,14 +27,8 @@ async fn test_s3_ops_are_customizable() { .customize() .await .expect("list_buckets is customizable") - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::<_, Infallible>::Ok(op) - }) - .expect("inserting into the property bag is infallible"); + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests(); // The response from the fake connection won't return the expected XML but we don't care about // that error in this test diff --git a/aws/sdk/integration-tests/s3/tests/endpoints.rs b/aws/sdk/integration-tests/s3/tests/endpoints.rs index e74d1a0a83..e6428ccc2b 100644 --- a/aws/sdk/integration-tests/s3/tests/endpoints.rs +++ b/aws/sdk/integration-tests/s3/tests/endpoints.rs @@ -9,7 +9,6 @@ use aws_sdk_s3::config::Builder; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::Client; use aws_smithy_client::test_connection::{capture_request, CaptureRequestReceiver}; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; fn test_client(update_builder: fn(Builder) -> Builder) -> (CaptureRequestReceiver, Client) { @@ -90,12 +89,7 @@ async fn s3_object_lambda() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1234567890)); - Result::<_, Infallible>::Ok(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1234567890)) .send() .await .unwrap(); diff --git a/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs b/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs index f4e8704360..3e55ffe9c3 100644 --- a/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs +++ b/aws/sdk/integration-tests/s3/tests/ignore-invalid-xml-body-root.rs @@ -4,16 +4,12 @@ */ use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::{config::Credentials, config::Region, types::ObjectAttributes, Client}; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_http::body::SdkBody; use aws_types::SdkConfig; use http::header::AUTHORIZATION; -use std::{ - convert::Infallible, - time::{Duration, UNIX_EPOCH}, -}; +use std::time::{Duration, UNIX_EPOCH}; const RESPONSE_BODY_XML: &[u8] = b"\ne1AsOh9IyGCa4hLN+2Od7jlnP14="; @@ -60,14 +56,8 @@ async fn ignore_invalid_xml_body_root() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests() .send() .await .unwrap(); diff --git a/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs b/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs index eef5b0b14b..96142f2b43 100644 --- a/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs +++ b/aws/sdk/integration-tests/s3/tests/naughty-string-metadata.rs @@ -4,15 +4,11 @@ */ use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::{config::Credentials, config::Region, primitives::ByteStream, Client}; use aws_smithy_client::test_connection::capture_request; use aws_types::SdkConfig; use http::HeaderValue; -use std::{ - convert::Infallible, - time::{Duration, UNIX_EPOCH}, -}; +use std::time::{Duration, UNIX_EPOCH}; const NAUGHTY_STRINGS: &str = include_str!("blns/blns.txt"); @@ -83,14 +79,8 @@ async fn test_s3_signer_with_naughty_string_metadata() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests() .send() .await .unwrap(); diff --git a/aws/sdk/integration-tests/s3/tests/normalize-uri-path.rs b/aws/sdk/integration-tests/s3/tests/normalize-uri-path.rs index 1f903abb7a..a50867ccd0 100644 --- a/aws/sdk/integration-tests/s3/tests/normalize-uri-path.rs +++ b/aws/sdk/integration-tests/s3/tests/normalize-uri-path.rs @@ -5,11 +5,9 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::primitives::ByteStream; use aws_sdk_s3::{config::Credentials, config::Region, Client}; use aws_smithy_client::test_connection::capture_request; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] @@ -33,14 +31,8 @@ async fn test_operation_should_not_normalize_uri_path() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1669257290)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1669257290)) + .user_agent_for_tests() .send() .await .unwrap(); diff --git a/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs b/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs index 03393a5a74..7d7880fa81 100644 --- a/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs +++ b/aws/sdk/integration-tests/s3/tests/query-strings-are-correctly-encoded.rs @@ -5,11 +5,9 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::Client; use aws_smithy_client::test_connection::capture_request; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] @@ -35,14 +33,8 @@ async fn test_s3_signer_query_string_with_all_valid_chars() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests() .send() .await; diff --git a/aws/sdk/integration-tests/s3/tests/signing-it.rs b/aws/sdk/integration-tests/s3/tests/signing-it.rs index 6e20e187e2..a02b6b670b 100644 --- a/aws/sdk/integration-tests/s3/tests/signing-it.rs +++ b/aws/sdk/integration-tests/s3/tests/signing-it.rs @@ -5,12 +5,10 @@ use aws_config::SdkConfig; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::Client; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_http::body::SdkBody; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] @@ -36,14 +34,8 @@ async fn test_signer() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1624036048)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) + .user_agent_for_tests() .send() .await; diff --git a/aws/sdk/integration-tests/s3control/tests/signing-it.rs b/aws/sdk/integration-tests/s3control/tests/signing-it.rs index f5c583bbcd..88dc2debfb 100644 --- a/aws/sdk/integration-tests/s3control/tests/signing-it.rs +++ b/aws/sdk/integration-tests/s3control/tests/signing-it.rs @@ -4,13 +4,11 @@ */ use aws_credential_types::provider::SharedCredentialsProvider; -use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3control::config::{Credentials, Region}; use aws_sdk_s3control::Client; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_http::body::SdkBody; use aws_types::SdkConfig; -use std::convert::Infallible; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] @@ -39,14 +37,8 @@ async fn test_signer() { .customize() .await .unwrap() - .map_operation(|mut op| { - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1636751225)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - Result::Ok::<_, Infallible>(op) - }) - .unwrap() + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1636751225)) + .user_agent_for_tests() .send() .await .expect_err("empty response"); diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs index 1f061f7f9d..b1e708aad1 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs @@ -5,18 +5,20 @@ mod util; +use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::Client; use aws_smithy_client::dvr; use aws_smithy_client::dvr::MediaType; use aws_smithy_client::erase::DynConnector; -use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output}; use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, Interceptor, }; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::orchestrator::RequestTime; use aws_smithy_runtime_api::config_bag::ConfigBag; +use http::header::USER_AGENT; +use http::HeaderValue; use std::time::{Duration, SystemTime, UNIX_EPOCH}; const LIST_BUCKETS_PATH: &str = "test-data/list-objects-v2.json"; @@ -148,16 +150,16 @@ async fn set_test_user_agent_through_request_mutation() { .await .unwrap() .mutate_request(|request| { - request.headers_mut() - .insert( - http::HeaderName::from_static("user-agent"), - http::HeaderValue::from_str("aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0").unwrap(), - ); - request.headers_mut() - .insert( - http::HeaderName::from_static("x-amz-user-agent"), - http::HeaderValue::from_str("aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0").unwrap(), - ); + let headers = request.headers_mut(); + let user_agent = AwsUserAgent::for_tests(); + headers.insert( + USER_AGENT, + HeaderValue::try_from(user_agent.ua_header()).unwrap(), + ); + headers.insert( + util::X_AMZ_USER_AGENT, + HeaderValue::try_from(user_agent.aws_ua_header()).unwrap(), + ); }) .send_orchestrator_with_plugin(Some(fixup)) .await diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs index 4f2997f5c6..7ce99e3af3 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs @@ -5,7 +5,6 @@ use aws_http::user_agent::AwsUserAgent; use aws_runtime::invocation_id::InvocationId; -use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output}; use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, Interceptor, InterceptorRegistrar, }; diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationDecorator.kt new file mode 100644 index 0000000000..93a01e41bb --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationDecorator.kt @@ -0,0 +1,19 @@ +/* + * 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.client + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.Section + +sealed class CustomizableOperationSection(name: String) : Section(name) { + /** Write custom code into a customizable operation's impl block */ + data class CustomizableOperationImpl( + val operationShape: OperationShape?, + ) : CustomizableOperationSection("CustomizableOperationImpl") +} + +abstract class CustomizableOperationCustomization : NamedCustomization() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index 3bfd660a75..c3108a2f57 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -13,10 +13,12 @@ import software.amazon.smithy.rust.codegen.core.rustlang.GenericTypeArg import software.amazon.smithy.rust.codegen.core.rustlang.RustGenerics import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.RuntimeConfig 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.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.util.outputShape /** @@ -26,6 +28,7 @@ import software.amazon.smithy.rust.codegen.core.util.outputShape class CustomizableOperationGenerator( private val codegenContext: ClientCodegenContext, private val generics: FluentClientGenerics, + private val customizations: List, ) { private val runtimeConfig = codegenContext.runtimeConfig private val smithyHttp = CargoDependency.smithyHttp(runtimeConfig).toType() @@ -123,9 +126,14 @@ class CustomizableOperationGenerator( pub fn request_mut(&mut self) -> &mut #{HttpRequest}<#{SdkBody}> { self.operation.request_mut() } + + #{additional_methods} } """, *codegenScope, + "additional_methods" to writable { + writeCustomizations(customizations, CustomizableOperationSection.CustomizableOperationImpl(null)) + }, ) } @@ -262,9 +270,14 @@ class CustomizableOperationGenerator( .send_orchestrator_with_plugin(final_plugin) .await } + + #{additional_methods} } """, *codegenScope, + "additional_methods" to writable { + writeCustomizations(customizations, CustomizableOperationSection.CustomizableOperationImpl(operation)) + }, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 53c17b8d61..d81cfa32d1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -81,10 +81,10 @@ class FluentClientGenerator( private val core = FluentClientCore(model) private val smithyRuntimeMode = codegenContext.smithyRuntimeMode - fun render(crate: RustCrate) { + fun render(crate: RustCrate, customizableOperationCustomizations: List = emptyList()) { renderFluentClient(crate) - val customizableOperationGenerator = CustomizableOperationGenerator(codegenContext, generics) + val customizableOperationGenerator = CustomizableOperationGenerator(codegenContext, generics, customizableOperationCustomizations) operations.forEach { operation -> crate.withModule(symbolProvider.moduleForBuilder(operation)) { renderFluentBuilder(operation) diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs index c85ab8f245..71afb382e4 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs @@ -5,4 +5,5 @@ pub mod connector; pub mod deserializer; +pub mod interceptor; pub mod serializer; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs new file mode 100644 index 0000000000..74fa60bd70 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs @@ -0,0 +1,78 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// TODO(enableNewSmithyRuntime): Delete this file once test helpers on `CustomizableOperation` have been removed + +use aws_smithy_runtime_api::client::interceptors::{ + BeforeTransmitInterceptorContextMut, BoxError, Interceptor, +}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use std::fmt; + +pub struct TestParamsSetterInterceptor { + f: F, +} + +impl fmt::Debug for TestParamsSetterInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TestParamsSetterInterceptor") + } +} + +impl TestParamsSetterInterceptor { + pub fn new(f: F) -> Self { + Self { f } + } +} + +impl Interceptor for TestParamsSetterInterceptor +where + F: Fn(&mut BeforeTransmitInterceptorContextMut<'_>, &mut ConfigBag) + Send + Sync + 'static, +{ + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + (self.f)(context, cfg); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; + use aws_smithy_runtime_api::type_erasure::TypedBox; + use std::time::{Duration, UNIX_EPOCH}; + + #[test] + fn set_test_request_time() { + let mut cfg = ConfigBag::base(); + let mut ctx = InterceptorContext::new(TypedBox::new("anything").erase()); + ctx.enter_serialization_phase(); + ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = ctx.take_input(); + ctx.enter_before_transmit_phase(); + let mut ctx = Into::into(&mut ctx); + let request_time = UNIX_EPOCH + Duration::from_secs(1624036048); + let interceptor = TestParamsSetterInterceptor::new({ + let request_time = request_time.clone(); + move |_: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag| { + cfg.set_request_time(RequestTime::new(request_time)); + } + }); + interceptor + .modify_before_signing(&mut ctx, &mut cfg) + .unwrap(); + assert_eq!( + request_time, + cfg.get::().unwrap().system_time() + ); + } +} From 64fb3dd76878fee6d062b850ad5416f4f5d138e3 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Mon, 22 May 2023 04:26:37 -0500 Subject: [PATCH 105/253] Restore `--location` to `publisher` (#2716) ## Motivation and Context This PR adds back `--location` to the subcommand `generate-version-manifest` for the `publisher`. Without this, CI would fail due to the PR bot trying to use the already deleted command option ([link](https://github.com/awslabs/smithy-rs/actions/runs/5023168618/jobs/9007489752?pr=2712)). It's important to note that all changes to `tools` need to be backwards compatible across releases. ## Testing Against the old and the new versions of [`build.gradle.kts`](https://github.com/awslabs/smithy-rs/pull/2663/files), manually ran ``` ./gradlew :aws:sdk:assemble ``` and confirmed that `versions.toml` got created under `smithy-rs/aws/sdk/build/aws-sdk`. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ Co-authored-by: Yuki Saito --- .../subcommand/generate_version_manifest.rs | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs b/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs index 35d1bef9a4..ffa5eabca1 100644 --- a/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs +++ b/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs @@ -26,12 +26,15 @@ pub struct GenerateVersionManifestArgs { /// Revision of `aws-doc-sdk-examples` repository used to retrieve examples #[clap(long)] examples_revision: String, + /// Same as `input_location` but kept for backwards compatibility + #[clap(long, required_unless_present = "input-location")] + location: Option, /// Path containing the generated SDK to generate a version manifest for - #[clap(long)] - input_location: PathBuf, + #[clap(long, required_unless_present = "location")] + input_location: Option, /// Path to a directory in which a version manifest is generated - #[clap(long)] - output_location: PathBuf, + #[clap(long, required_unless_present = "location")] + output_location: Option, /// Optional path to the `versions.toml` manifest from the previous SDK release #[clap(long)] previous_release_versions: Option, @@ -41,6 +44,7 @@ pub async fn subcommand_generate_version_manifest( GenerateVersionManifestArgs { smithy_build, examples_revision, + location, input_location, output_location, previous_release_versions, @@ -56,6 +60,16 @@ pub async fn subcommand_generate_version_manifest( info!("Resolved smithy-rs revision to {}", smithy_rs_revision); let smithy_build_root = SmithyBuildRoot::from_file(smithy_build)?; + let input_location = match (location, input_location) { + (Some(location), None) => location, + (None, Some(input_location)) => input_location, + _ => bail!("Only one of `--location` or `--input-location` should be provided"), + }; + let output_location = match (location, output_location) { + (Some(location), None) => location, + (None, Some(output_location)) => output_location, + _ => bail!("Only one of `--location` or `--output-location` should be provided"), + }; let manifests = discover_package_manifests(input_location.into()) .await .context("discover package manifests")?; From c0345a5b5f0f048592bf1a7fd51a293e716d792c Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 23 May 2023 14:16:28 -0400 Subject: [PATCH 106/253] Add timesource abstration to aws-smithy-async (#2721) ## Motivation and Context - Controlling time is required for several testing use cases - #2087 - #2262 ## Description Introduce `TimeSource` trait, a real implementation, and a test implementation. ## Testing These changes are used in the timestream PR ## Checklist No changelog, these changes have no impact since the code is not yet utilized ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- rust-runtime/aws-smithy-async/Cargo.toml | 1 + rust-runtime/aws-smithy-async/src/lib.rs | 3 + rust-runtime/aws-smithy-async/src/rt/sleep.rs | 1 + .../aws-smithy-async/src/test_util.rs | 241 ++++++++++++++++++ rust-runtime/aws-smithy-async/src/time.rs | 32 +++ .../ci-scripts/codegen-diff/semver-checks.py | 2 +- 6 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 rust-runtime/aws-smithy-async/src/test_util.rs create mode 100644 rust-runtime/aws-smithy-async/src/time.rs diff --git a/rust-runtime/aws-smithy-async/Cargo.toml b/rust-runtime/aws-smithy-async/Cargo.toml index d148e229e4..c95862d9ff 100644 --- a/rust-runtime/aws-smithy-async/Cargo.toml +++ b/rust-runtime/aws-smithy-async/Cargo.toml @@ -9,6 +9,7 @@ repository = "https://github.com/awslabs/smithy-rs" [features] rt-tokio = ["tokio/time"] +test-util = [] [dependencies] pin-project-lite = "0.2" diff --git a/rust-runtime/aws-smithy-async/src/lib.rs b/rust-runtime/aws-smithy-async/src/lib.rs index f9c4f9d9d9..59fdc54e0f 100644 --- a/rust-runtime/aws-smithy-async/src/lib.rs +++ b/rust-runtime/aws-smithy-async/src/lib.rs @@ -18,6 +18,9 @@ pub mod future; pub mod rt; +#[cfg(feature = "test-util")] +pub mod test_util; +pub mod time; /// Given an `Instant` and a `Duration`, assert time elapsed since `Instant` is equal to `Duration`. /// This macro allows for a 5ms margin of error. diff --git a/rust-runtime/aws-smithy-async/src/rt/sleep.rs b/rust-runtime/aws-smithy-async/src/rt/sleep.rs index 9921fd9d0b..4ea9f70ed5 100644 --- a/rust-runtime/aws-smithy-async/src/rt/sleep.rs +++ b/rust-runtime/aws-smithy-async/src/rt/sleep.rs @@ -53,6 +53,7 @@ pub fn default_async_sleep() -> Option> { /// Future returned by [`AsyncSleep`]. #[non_exhaustive] +#[must_use] pub struct Sleep(Pin + Send + 'static>>); impl Debug for Sleep { diff --git a/rust-runtime/aws-smithy-async/src/test_util.rs b/rust-runtime/aws-smithy-async/src/test_util.rs new file mode 100644 index 0000000000..5a8e864ada --- /dev/null +++ b/rust-runtime/aws-smithy-async/src/test_util.rs @@ -0,0 +1,241 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Test utilities for time and sleep + +use std::sync::{Arc, Mutex}; +use std::time::{Duration, SystemTime}; + +use tokio::sync::oneshot; +use tokio::sync::Barrier; +use tokio::time::timeout; + +use crate::rt::sleep::{AsyncSleep, Sleep}; +use crate::time::TimeSource; + +/// Manually controlled time source +#[derive(Debug, Clone)] +pub struct ManualTimeSource { + start_time: SystemTime, + log: Arc>>, +} + +impl TimeSource for ManualTimeSource { + fn now(&self) -> SystemTime { + self.start_time + dbg!(self.log.lock().unwrap()).iter().sum() + } +} + +/// A sleep implementation where calls to [`AsyncSleep::sleep`] block until [`SleepGate::expect_sleep`] is called +/// +/// Create a [`ControlledSleep`] with [`controlled_time_and_sleep`] +#[derive(Debug, Clone)] +pub struct ControlledSleep { + barrier: Arc, + log: Arc>>, + duration: Arc>>, + advance_guard: Arc>>>, +} + +/// Gate that allows [`ControlledSleep`] to advance. +/// +/// See [`controlled_time_and_sleep`] for more details +pub struct SleepGate { + gate: Arc, + pending: Arc>>, + advance_guard: Arc>>>, +} + +impl ControlledSleep { + fn new(log: Arc>>) -> (ControlledSleep, SleepGate) { + let gate = Arc::new(Barrier::new(2)); + let pending = Arc::new(Mutex::new(None)); + let advance_guard: Arc>>> = Default::default(); + ( + ControlledSleep { + barrier: gate.clone(), + log, + duration: pending.clone(), + advance_guard: advance_guard.clone(), + }, + SleepGate { + gate, + pending, + advance_guard, + }, + ) + } +} + +/// Guard returned from [`SleepGate::expect_sleep`] +/// +/// # Examples +/// ```rust +/// # use std::sync::Arc; +/// use std::sync::atomic::{AtomicUsize, Ordering}; +/// # async { +/// use std::time::{Duration, UNIX_EPOCH}; +/// use aws_smithy_async::rt::sleep::AsyncSleep; +/// use aws_smithy_async::test_util::controlled_time_and_sleep; +/// let (time, sleep, mut gate) = controlled_time_and_sleep(UNIX_EPOCH); +/// let progress = Arc::new(AtomicUsize::new(0)); +/// let task_progress = progress.clone(); +/// let task = tokio::spawn(async move { +/// let progress = task_progress; +/// progress.store(1, Ordering::Release); +/// sleep.sleep(Duration::from_secs(1)).await; +/// progress.store(2, Ordering::Release); +/// sleep.sleep(Duration::from_secs(2)).await; +/// }); +/// while progress.load(Ordering::Acquire) != 1 {} +/// let guard = gate.expect_sleep().await; +/// assert_eq!(guard.duration(), Duration::from_secs(1)); +/// assert_eq!(progress.load(Ordering::Acquire), 1); +/// guard.allow_progress(); +/// +/// let guard = gate.expect_sleep().await; +/// assert_eq!(progress.load(Ordering::Acquire), 2); +/// assert_eq!(task.is_finished(), false); +/// guard.allow_progress(); +/// task.await.expect("successful completion"); +/// # }; +/// ``` +pub struct CapturedSleep<'a>(oneshot::Sender<()>, &'a SleepGate, Duration); +impl CapturedSleep<'_> { + /// Allow the calling code to advance past the call to [`AsyncSleep::sleep`] + /// + /// In order to facilitate testing with no flakiness, the future returned by the call to `sleep` + /// will not resolve until [`CapturedSleep`] is dropped or this method is called. + /// + /// ```rust + /// use std::time::Duration; + /// use aws_smithy_async::rt::sleep::AsyncSleep; + /// fn do_something(sleep: &dyn AsyncSleep) { + /// println!("before sleep"); + /// sleep.sleep(Duration::from_secs(1)); + /// println!("after sleep"); + /// } + /// ``` + /// + /// To be specific, when `do_something` is called, the code will advance to `sleep.sleep`. + /// When [`SleepGate::expect_sleep`] is called, the 1 second sleep will be captured, but `after sleep` + /// WILL NOT be printed, until `allow_progress` is called. + pub fn allow_progress(self) { + drop(self) + } + + /// Duration in the call to [`AsyncSleep::sleep`] + pub fn duration(&self) -> Duration { + self.2 + } +} + +impl AsRef for CapturedSleep<'_> { + fn as_ref(&self) -> &Duration { + &self.2 + } +} + +impl SleepGate { + /// Expect the time source to sleep + /// + /// This returns the duration that was slept and a [`CapturedSleep`]. The drop guard is used + /// to precisely control + pub async fn expect_sleep(&mut self) -> CapturedSleep<'_> { + timeout(Duration::from_secs(1), self.gate.wait()) + .await + .expect("timeout"); + let dur = self + .pending + .lock() + .unwrap() + .take() + .unwrap_or(Duration::from_secs(123456)); + let guard = CapturedSleep( + self.advance_guard.lock().unwrap().take().unwrap(), + self, + dur, + ); + guard + } +} + +impl AsyncSleep for ControlledSleep { + fn sleep(&self, duration: Duration) -> Sleep { + let barrier = self.barrier.clone(); + let log = self.log.clone(); + let pending = self.duration.clone(); + let drop_guard = self.advance_guard.clone(); + Sleep::new(async move { + // 1. write the duration into the shared mutex + assert!(pending.lock().unwrap().is_none()); + *pending.lock().unwrap() = Some(duration); + let (tx, rx) = oneshot::channel(); + *drop_guard.lock().unwrap() = Some(tx); + // 2. first wait on the barrier—this is how we wait for an invocation of `expect_sleep` + barrier.wait().await; + log.lock().unwrap().push(duration); + let _ = dbg!(rx.await); + }) + } +} + +/// Returns a trio of tools to test interactions with time +/// +/// 1. [`ManualTimeSource`] which starts at a specific time and only advances when `sleep` is called. +/// It MUST be paired with [`ControlledSleep`] in order to function. +pub fn controlled_time_and_sleep( + start_time: SystemTime, +) -> (ManualTimeSource, ControlledSleep, SleepGate) { + let log = Arc::new(Mutex::new(vec![])); + let (sleep, gate) = ControlledSleep::new(log.clone()); + (ManualTimeSource { start_time, log }, sleep, gate) +} + +#[cfg(test)] +mod test { + use crate::rt::sleep::AsyncSleep; + use crate::test_util::controlled_time_and_sleep; + use crate::time::TimeSource; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; + use tokio::task::yield_now; + use tokio::time::timeout; + + #[tokio::test] + async fn test_sleep_gate() { + use std::time::{Duration, UNIX_EPOCH}; + let start = UNIX_EPOCH; + let (time, sleep, mut gate) = controlled_time_and_sleep(UNIX_EPOCH); + let progress = Arc::new(AtomicUsize::new(0)); + let task_progress = progress.clone(); + let task = tokio::spawn(async move { + assert_eq!(time.now(), start); + let progress = task_progress; + progress.store(1, Ordering::Release); + sleep.sleep(Duration::from_secs(1)).await; + assert_eq!(time.now(), start + Duration::from_secs(1)); + progress.store(2, Ordering::Release); + sleep.sleep(Duration::from_secs(2)).await; + assert_eq!(time.now(), start + Duration::from_secs(3)); + }); + while progress.load(Ordering::Acquire) != 1 { + yield_now().await + } + let guard = gate.expect_sleep().await; + assert_eq!(guard.duration(), Duration::from_secs(1)); + assert_eq!(progress.load(Ordering::Acquire), 1); + guard.allow_progress(); + + let guard = gate.expect_sleep().await; + assert_eq!(progress.load(Ordering::Acquire), 2); + assert_eq!(task.is_finished(), false); + guard.allow_progress(); + timeout(Duration::from_secs(1), task) + .await + .expect("no timeout") + .expect("successful completion"); + } +} diff --git a/rust-runtime/aws-smithy-async/src/time.rs b/rust-runtime/aws-smithy-async/src/time.rs new file mode 100644 index 0000000000..85c2ff0519 --- /dev/null +++ b/rust-runtime/aws-smithy-async/src/time.rs @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Time source abstraction to support WASM and testing +use std::fmt::Debug; +use std::time::SystemTime; + +/// Trait with a `now()` function returning the current time +pub trait TimeSource: Debug + Send + Sync { + /// Returns the current time + fn now(&self) -> SystemTime; +} + +/// Time source that delegates to [`SystemTime::now`] +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct SystemTimeSource; + +impl SystemTimeSource { + /// Creates a new SystemTimeSource + pub fn new() -> Self { + SystemTimeSource + } +} + +impl TimeSource for SystemTimeSource { + fn now(&self) -> SystemTime { + SystemTime::now() + } +} diff --git a/tools/ci-scripts/codegen-diff/semver-checks.py b/tools/ci-scripts/codegen-diff/semver-checks.py index 69ba9726d1..054315fb56 100755 --- a/tools/ci-scripts/codegen-diff/semver-checks.py +++ b/tools/ci-scripts/codegen-diff/semver-checks.py @@ -50,7 +50,7 @@ def main(skip_generation=False): # package and manifest path explicitly f'--manifest-path {path}/Cargo.toml ' f'-p {path} ' - f'--release-type patch', check=False, quiet=True) + f'--release-type minor', check=False, quiet=True) if status == 0: eprint('ok!') else: From bbe9d52a76b0e61ce53c518b95795f3dc37e359f Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 23 May 2023 15:15:47 -0400 Subject: [PATCH 107/253] Fix the error message for SSO credential providers (#2722) ## Motivation and Context The current error if the SSO error message is not enabled is inscrutable and doesn't point the customer towards the removed feature. ## Description Fix the error message, add tests. ## Testing tests ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --- CHANGELOG.next.toml | 6 +++++ .../src/default_provider/credentials.rs | 25 ++++++++++++------- .../aws-config/src/profile/credentials.rs | 13 ++++++++++ .../src/profile/credentials/exec.rs | 4 +-- aws/rust-runtime/aws-config/src/test_case.rs | 2 +- 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 06b3ab3d54..3a8a4c1110 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -35,3 +35,9 @@ message = "Fix compiler errors in generated code when naming shapes after types references = ["smithy-rs#2696"] meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client"} author = "jdisanti" + +[[aws-sdk-rust]] +message = "Fix error message when `credentials-sso` feature is not enabled on `aws-config`. NOTE: if you use `no-default-features`, you will need to manually able `credentials-sso` after 0.55.*" +references = ["smithy-rs#2722", "aws-sdk-rust#703"] +meta = { "breaking" = false, "tada" = false, "bug" = true } +author = "rcoh" diff --git a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs index e1cb203ebc..c1d48c584f 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs @@ -224,8 +224,8 @@ mod test { /// make_test!(live: test_name) /// ``` macro_rules! make_test { - ($name: ident) => { - make_test!($name, execute); + ($name: ident $(#[$m:meta])*) => { + make_test!($name, execute, $(#[$m])*); }; (update: $name:ident) => { make_test!($name, execute_and_update); @@ -233,13 +233,14 @@ mod test { (live: $name:ident) => { make_test!($name, execute_from_live_traffic); }; - ($name: ident, $func: ident) => { - make_test!($name, $func, std::convert::identity); + ($name: ident, $func: ident, $(#[$m:meta])*) => { + make_test!($name, $func, std::convert::identity $(, #[$m])*); }; - ($name: ident, $provider_config_builder: expr) => { + ($name: ident, builder: $provider_config_builder: expr) => { make_test!($name, execute, $provider_config_builder); }; - ($name: ident, $func: ident, $provider_config_builder: expr) => { + ($name: ident, $func: ident, $provider_config_builder: expr $(, #[$m:meta])*) => { + $(#[$m])* #[tokio::test] async fn $name() { crate::test_case::TestEnvironment::from_dir(concat!( @@ -274,19 +275,19 @@ mod test { make_test!(imds_no_iam_role); make_test!(imds_default_chain_error); - make_test!(imds_default_chain_success, |config| { + make_test!(imds_default_chain_success, builder: |config| { config.with_time_source(aws_credential_types::time_source::TimeSource::testing( &aws_credential_types::time_source::TestingTimeSource::new(std::time::UNIX_EPOCH), )) }); make_test!(imds_assume_role); - make_test!(imds_config_with_no_creds, |config| { + make_test!(imds_config_with_no_creds, builder: |config| { config.with_time_source(aws_credential_types::time_source::TimeSource::testing( &aws_credential_types::time_source::TestingTimeSource::new(std::time::UNIX_EPOCH), )) }); make_test!(imds_disabled); - make_test!(imds_default_chain_retries, |config| { + make_test!(imds_default_chain_retries, builder: |config| { config.with_time_source(aws_credential_types::time_source::TimeSource::testing( &aws_credential_types::time_source::TestingTimeSource::new(std::time::UNIX_EPOCH), )) @@ -295,8 +296,14 @@ mod test { make_test!(ecs_credentials); make_test!(ecs_credentials_invalid_profile); + #[cfg(not(feature = "credentials-sso"))] + make_test!(sso_assume_role #[should_panic(expected = "This behavior requires following cargo feature(s) enabled: credentials-sso")]); + #[cfg(not(feature = "credentials-sso"))] + make_test!(sso_no_token_file #[should_panic(expected = "This behavior requires following cargo feature(s) enabled: credentials-sso")]); + #[cfg(feature = "credentials-sso")] make_test!(sso_assume_role); + #[cfg(feature = "credentials-sso")] make_test!(sso_no_token_file); diff --git a/aws/rust-runtime/aws-config/src/profile/credentials.rs b/aws/rust-runtime/aws-config/src/profile/credentials.rs index 1f043c0959..d5bf61d9c6 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials.rs @@ -257,6 +257,13 @@ pub enum ProfileFileError { /// The name of the provider name: String, }, + + /// Feature not enabled + #[non_exhaustive] + FeatureNotEnabled { + /// The feature or comma delimited list of features that must be enabled + feature: Cow<'static, str>, + }, } impl ProfileFileError { @@ -309,6 +316,12 @@ impl Display for ProfileFileError { "profile `{}` did not contain credential information", profile ), + ProfileFileError::FeatureNotEnabled { feature: message } => { + write!( + f, + "This behavior requires following cargo feature(s) enabled: {message}", + ) + } } } } diff --git a/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs b/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs index 351e7df8d8..b6b0ba3e23 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs @@ -137,8 +137,8 @@ impl ProviderChain { } #[cfg(not(feature = "credentials-sso"))] { - Err(ProfileFileError::UnknownProvider { - name: "sso".to_string(), + Err(ProfileFileError::FeatureNotEnabled { + feature: "credentials-sso".into(), })? } } diff --git a/aws/rust-runtime/aws-config/src/test_case.rs b/aws/rust-runtime/aws-config/src/test_case.rs index 28c10afd7d..e51ac4504b 100644 --- a/aws/rust-runtime/aws-config/src/test_case.rs +++ b/aws/rust-runtime/aws-config/src/test_case.rs @@ -114,7 +114,7 @@ where ); } (Err(actual_error), GenericTestResult::Ok(expected_creds)) => panic!( - "expected credentials ({:?}) but an error was returned: {}", + "expected credentials ({:?}) but an error was returned: {:?}", expected_creds, actual_error ), (Ok(creds), GenericTestResult::ErrorContains(substr)) => panic!( From 7ccac0607c0fbadcc6a4dc7f1c661c714e7bc193 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Wed, 24 May 2023 16:53:30 -0500 Subject: [PATCH 108/253] Add `NoCredentialsCache` that offers no caching ability (#2720) ## Motivation and Context Related to https://github.com/awslabs/aws-sdk-rust/issues/809 ## Description It has been discovered that when `AssumeRoleProvider` is used, the Rust SDK emits `credentials cache miss occurred` twice per request. The reason why that log is shown twice is illustrated in the following diagram: ![Screenshot 2023-05-19 at 4 10 20 PM](https://github.com/awslabs/smithy-rs/assets/15333866/c6cce018-c821-4b46-8d47-b414af7b4d1e) One of the cache miss messages is due to the fact `AssumeRoleProvider` internally uses an STS client, which, in turn, is wrapped by a `LazyCredentialsCache` by default. However, that use of `LazyCredentialsCache` is pointless because caching is already in effect with the outermost `LazyCredentialsCache`. This PR adds a new kind of `CredentialsCache`, `NoCredentialsCache`. As its name suggests, it simplify delegates `provide_cached_credentials` to the underlying provider's `provide_credentials` with no caching functionality. We then update `SsoCredentialsProvider`, `AssumeRoleProvider`, and `WebIdentityTokenCredentialsProvider` to use `NoCredentialsCache` for their STS clients so the logs won't show `credentials cache miss occurred` twice per request. ## Testing - Added unit tests for `NoCredentialsCache` - Updated unit test for `AssumeRoleProvider` to verify `NoCredentialsCache` is used by default ## Checklist - [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._ --------- Co-authored-by: Yuki Saito --- CHANGELOG.next.toml | 6 ++ aws/rust-runtime/aws-config/src/sso.rs | 2 + .../aws-config/src/sts/assume_role.rs | 70 ++++++++++++---- .../aws-config/src/web_identity_token.rs | 2 + .../aws-credential-types/src/cache.rs | 11 +++ .../src/cache/no_caching.rs | 83 +++++++++++++++++++ 6 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 aws/rust-runtime/aws-credential-types/src/cache/no_caching.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 3a8a4c1110..49fe59767a 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -41,3 +41,9 @@ message = "Fix error message when `credentials-sso` feature is not enabled on `a references = ["smithy-rs#2722", "aws-sdk-rust#703"] meta = { "breaking" = false, "tada" = false, "bug" = true } author = "rcoh" + +[[aws-sdk-rust]] +message = "`SsoCredentialsProvider`, `AssumeRoleProvider`, and `WebIdentityTokenCredentialsProvider` now use `NoCredentialsCache` internally when fetching credentials using an STS client. This avoids double-caching when these providers are wrapped by `LazyCredentialsCache` when a service client is created." +references = ["smithy-rs#2720"] +meta = { "breaking" = false, "tada" = false, "bug" = true } +author = "ysaito1001" diff --git a/aws/rust-runtime/aws-config/src/sso.rs b/aws/rust-runtime/aws-config/src/sso.rs index 7c693bdf2e..6adc586ab4 100644 --- a/aws/rust-runtime/aws-config/src/sso.rs +++ b/aws/rust-runtime/aws-config/src/sso.rs @@ -14,6 +14,7 @@ use crate::fs_util::{home_dir, Os}; use crate::json_credentials::{json_parse_loop, InvalidJsonCredentials}; use crate::provider_config::ProviderConfig; +use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_credential_types::Credentials; use aws_sdk_sso::middleware::DefaultMiddleware as SsoMiddleware; @@ -211,6 +212,7 @@ async fn load_sso_credentials( .map_err(CredentialsError::provider_error)?; let config = aws_sdk_sso::Config::builder() .region(sso_config.region.clone()) + .credentials_cache(CredentialsCache::no_caching()) .build(); let operation = GetRoleCredentialsInput::builder() .role_name(&sso_config.role_name) diff --git a/aws/rust-runtime/aws-config/src/sts/assume_role.rs b/aws/rust-runtime/aws-config/src/sts/assume_role.rs index 35f2b3fa27..66ad8cd89d 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -179,7 +179,13 @@ impl AssumeRoleProviderBuilder { self } + #[deprecated( + note = "This should not be necessary as the default, no caching, is usually what you want." + )] /// Set the [`CredentialsCache`] for credentials retrieved from STS. + /// + /// By default, an [`AssumeRoleProvider`] internally uses `NoCredentialsCache` because the + /// provider itself will be wrapped by `LazyCredentialsCache` when a service client is created. pub fn credentials_cache(mut self, cache: CredentialsCache) -> Self { self.credentials_cache = Some(cache); self @@ -198,11 +204,9 @@ impl AssumeRoleProviderBuilder { pub fn build(self, provider: impl ProvideCredentials + 'static) -> AssumeRoleProvider { let conf = self.conf.unwrap_or_default(); - let credentials_cache = self.credentials_cache.unwrap_or_else(|| { - let mut builder = CredentialsCache::lazy_builder().time_source(conf.time_source()); - builder.set_sleep(conf.sleep()); - builder.into_credentials_cache() - }); + let credentials_cache = self + .credentials_cache + .unwrap_or_else(CredentialsCache::no_caching); let config = aws_sdk_sts::Config::builder() .credentials_cache(credentials_cache) @@ -333,39 +337,71 @@ mod test { } #[tokio::test] - async fn provider_caches_credentials() { + async fn provider_does_not_cache_credentials_by_default() { let conn = TestConnection::new(vec![ (http::Request::new(SdkBody::from("request body")), http::Response::builder().status(200).body(SdkBody::from( - "\n \n \n AROAR42TAWARILN3MNKUT:assume-role-from-profile-1632246085998\n arn:aws:sts::130633740322:assumed-role/imds-chained-role-test/assume-role-from-profile-1632246085998\n \n \n ASIARCORRECT\n secretkeycorrect\n tokencorrect\n 2009-02-13T23:31:30Z\n \n \n \n d9d47248-fd55-4686-ad7c-0fb7cd1cddd7\n \n\n" + "\n \n \n AROAR42TAWARILN3MNKUT:assume-role-from-profile-1632246085998\n arn:aws:sts::130633740322:assumed-role/assume-provider-test/assume-role-from-profile-1632246085998\n \n \n ASIARCORRECT\n secretkeycorrect\n tokencorrect\n 2009-02-13T23:31:30Z\n \n \n \n d9d47248-fd55-4686-ad7c-0fb7cd1cddd7\n \n\n" )).unwrap()), (http::Request::new(SdkBody::from("request body")), http::Response::builder().status(200).body(SdkBody::from( - "\n \n \n AROAR42TAWARILN3MNKUT:assume-role-from-profile-1632246085998\n arn:aws:sts::130633740322:assumed-role/imds-chained-role-test/assume-role-from-profile-1632246085998\n \n \n ASIARCORRECT\n secretkeycorrect\n tokencorrect\n 2009-02-13T23:31:30Z\n \n \n \n d9d47248-fd55-4686-ad7c-0fb7cd1cddd7\n \n\n" + "\n \n \n AROAR42TAWARILN3MNKUT:assume-role-from-profile-1632246085998\n arn:aws:sts::130633740322:assumed-role/assume-provider-test/assume-role-from-profile-1632246085998\n \n \n ASIARCORRECT\n TESTSECRET\n tokencorrect\n 2009-02-13T23:33:30Z\n \n \n \n c2e971c2-702d-4124-9b1f-1670febbea18\n \n\n" )).unwrap()), ]); + + let mut testing_time_source = TestingTimeSource::new( + UNIX_EPOCH + Duration::from_secs(1234567890 - 120), // 1234567890 since UNIX_EPOCH is 2009-02-13T23:31:30Z + ); + let provider_conf = ProviderConfig::empty() .with_sleep(TokioSleep::new()) - .with_time_source(TimeSource::testing(&TestingTimeSource::new( - UNIX_EPOCH + Duration::from_secs(1234567890 - 120), - ))) + .with_time_source(TimeSource::testing(&testing_time_source)) .with_http_connector(DynConnector::new(conn)); + let credentials_list = std::sync::Arc::new(std::sync::Mutex::new(vec![ + Credentials::new( + "test", + "test", + None, + Some(UNIX_EPOCH + Duration::from_secs(1234567890 + 1)), + "test", + ), + Credentials::new( + "test", + "test", + None, + Some(UNIX_EPOCH + Duration::from_secs(1234567890 + 120)), + "test", + ), + ])); + let credentials_list_cloned = credentials_list.clone(); let provider = AssumeRoleProvider::builder("myrole") .configure(&provider_conf) .region(Region::new("us-east-1")) - .build(provide_credentials_fn(|| async { - Ok(Credentials::for_tests()) + .build(provide_credentials_fn(move || { + let list = credentials_list.clone(); + async move { + let next = list.lock().unwrap().remove(0); + Ok(next) + } })); + + tokio::time::pause(); + let creds_first = provider .provide_credentials() .await .expect("should return valid credentials"); - // The effect of caching is implicitly enabled by a `LazyCredentialsCache` - // baked in the configuration for STS stored in `provider.inner.conf`. + + // After time has been advanced by 120 seconds, the first credentials _could_ still be valid + // if `LazyCredentialsCache` were used, but the provider uses `NoCredentialsCache` by default + // so the first credentials will not be used. + testing_time_source.advance(Duration::from_secs(120)); + let creds_second = provider .provide_credentials() .await - .expect("cached credentials should be returned"); - assert_eq!(creds_first, creds_second); + .expect("should return the second credentials"); + assert_ne!(creds_first, creds_second); + assert!(credentials_list_cloned.lock().unwrap().is_empty()); } } diff --git a/aws/rust-runtime/aws-config/src/web_identity_token.rs b/aws/rust-runtime/aws-config/src/web_identity_token.rs index dd13524b06..31819a1952 100644 --- a/aws/rust-runtime/aws-config/src/web_identity_token.rs +++ b/aws/rust-runtime/aws-config/src/web_identity_token.rs @@ -63,6 +63,7 @@ use crate::provider_config::ProviderConfig; use crate::sts; +use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_sdk_sts::config::Region; use aws_sdk_sts::middleware::DefaultMiddleware; @@ -235,6 +236,7 @@ async fn load_credentials( })?; let conf = aws_sdk_sts::Config::builder() .region(region.clone()) + .credentials_cache(CredentialsCache::no_caching()) .build(); let operation = AssumeRoleWithWebIdentityInput::builder() diff --git a/aws/rust-runtime/aws-credential-types/src/cache.rs b/aws/rust-runtime/aws-credential-types/src/cache.rs index 23e90c48fa..4d4810efab 100644 --- a/aws/rust-runtime/aws-credential-types/src/cache.rs +++ b/aws/rust-runtime/aws-credential-types/src/cache.rs @@ -7,9 +7,11 @@ mod expiring_cache; mod lazy_caching; +mod no_caching; pub use expiring_cache::ExpiringCache; pub use lazy_caching::Builder as LazyBuilder; +use no_caching::NoCredentialsCache; use crate::provider::{future, SharedCredentialsProvider}; use std::sync::Arc; @@ -63,6 +65,7 @@ impl ProvideCachedCredentials for SharedCredentialsCache { #[derive(Clone, Debug)] pub(crate) enum Inner { Lazy(lazy_caching::Builder), + NoCaching, } /// `CredentialsCache` allows for configuring and creating a credentials cache. @@ -104,10 +107,18 @@ impl CredentialsCache { lazy_caching::Builder::new() } + /// Creates a [`CredentialsCache`] that offers no caching ability. + pub fn no_caching() -> Self { + Self { + inner: Inner::NoCaching, + } + } + /// Creates a [`SharedCredentialsCache`] wrapping a concrete caching implementation. pub fn create_cache(self, provider: SharedCredentialsProvider) -> SharedCredentialsCache { match self.inner { Inner::Lazy(builder) => SharedCredentialsCache::new(builder.build(provider)), + Inner::NoCaching => SharedCredentialsCache::new(NoCredentialsCache::new(provider)), } } } diff --git a/aws/rust-runtime/aws-credential-types/src/cache/no_caching.rs b/aws/rust-runtime/aws-credential-types/src/cache/no_caching.rs new file mode 100644 index 0000000000..5827f5cea1 --- /dev/null +++ b/aws/rust-runtime/aws-credential-types/src/cache/no_caching.rs @@ -0,0 +1,83 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Credentials cache that offers no caching ability + +use crate::cache::ProvideCachedCredentials; +use crate::provider::SharedCredentialsProvider; +use crate::provider::{future, ProvideCredentials}; +use tracing::debug; + +#[derive(Debug)] +pub(crate) struct NoCredentialsCache { + provider: SharedCredentialsProvider, +} + +impl NoCredentialsCache { + pub(crate) fn new(provider: SharedCredentialsProvider) -> Self { + Self { provider } + } +} + +impl ProvideCachedCredentials for NoCredentialsCache { + fn provide_cached_credentials<'a>(&'a self) -> future::ProvideCredentials<'_> + where + Self: 'a, + { + debug!("Delegating `provide_cached_credentials` to `provide_credentials` on the provider"); + self.provider.provide_credentials() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::credential_fn::provide_credentials_fn; + use crate::Credentials; + use std::sync::{Arc, Mutex}; + use std::time::{Duration, SystemTime}; + + fn test_provider(load_list: Vec) -> NoCredentialsCache { + let load_list = Arc::new(Mutex::new(load_list)); + NoCredentialsCache::new(SharedCredentialsProvider::new(provide_credentials_fn( + move || { + let list = load_list.clone(); + async move { + let next = list.lock().unwrap().remove(0); + next + } + }, + ))) + } + + fn epoch_secs(secs: u64) -> SystemTime { + SystemTime::UNIX_EPOCH + Duration::from_secs(secs) + } + + fn credentials(expired_secs: u64) -> Credentials { + Credentials::new("test", "test", None, Some(epoch_secs(expired_secs)), "test") + } + + async fn expect_creds(expired_secs: u64, provider: &NoCredentialsCache) { + let creds = provider + .provide_cached_credentials() + .await + .expect("expected credentials"); + assert_eq!(Some(epoch_secs(expired_secs)), creds.expiry()); + } + + #[tokio::test] + async fn no_caching() { + let credentials_cache = test_provider(vec![ + Ok(credentials(1000)), + Ok(credentials(2000)), + Ok(credentials(3000)), + ]); + + expect_creds(1000, &credentials_cache).await; + expect_creds(2000, &credentials_cache).await; + expect_creds(3000, &credentials_cache).await; + } +} From 3c68521cd917645d56ba4b575bdb97b691667705 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 25 May 2023 10:12:51 -0700 Subject: [PATCH 109/253] Remove middleware code when generating the orchestrator exclusively (#2723) ## Motivation and Context This PR removes all the middleware code from generated clients when the `smithy.runtime.mode` flag is set to `orchestrator`. It also changes the `aws-config` crate to use the fluent client instead of the Smithy client for STS and SSO based credential providers. The `polly` test no longer compiles in orchestrator mode with these changes since presigning directly references middleware code. This will get fixed in a later PR. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-config/src/profile/credentials.rs | 13 +- .../src/profile/credentials/exec.rs | 37 +- aws/rust-runtime/aws-config/src/sso.rs | 80 +++-- aws/rust-runtime/aws-config/src/sts.rs | 22 +- .../aws-config/src/sts/assume_role.rs | 46 +-- aws/rust-runtime/aws-config/src/test_case.rs | 5 +- .../aws-config/src/web_identity_token.rs | 42 +-- .../rustsdk/AwsFluentClientDecorator.kt | 174 ++++----- .../smithy/rustsdk/AwsPresigningDecorator.kt | 8 +- .../amazon/smithy/rustsdk/AwsRuntimeType.kt | 1 + .../dynamodb/benches/deserialization_bench.rs | 18 +- .../dynamodb/benches/serialization_bench.rs | 24 +- aws/sdk/integration-tests/kms/Cargo.toml | 2 + .../kms/tests/integration.rs | 76 ++-- .../kms/tests/retryable_errors.rs | 139 ++++++++ .../kms/tests/sensitive-it.rs | 102 +----- aws/sdk/integration-tests/kms/tests/traits.rs | 50 +++ aws/sdk/integration-tests/lambda/Cargo.toml | 1 + .../lambda/tests/request_id.rs | 82 +++-- .../qldbsession/tests/integration.rs | 35 +- .../smithy/generators/PaginatorGenerator.kt | 56 ++- .../client/CustomizableOperationGenerator.kt | 41 ++- .../client/FluentClientDecorator.kt | 2 +- .../client/FluentClientGenerator.kt | 331 ++++++++++-------- .../protocol/ClientProtocolGenerator.kt | 81 ++++- rust-runtime/aws-smithy-http/src/operation.rs | 3 + .../src/client/runtime_plugin.rs | 6 + .../src/client/interceptor.rs | 14 +- .../check-aws-sdk-orchestrator-impl | 6 +- 29 files changed, 854 insertions(+), 643 deletions(-) create mode 100644 aws/sdk/integration-tests/kms/tests/retryable_errors.rs create mode 100644 aws/sdk/integration-tests/kms/tests/traits.rs diff --git a/aws/rust-runtime/aws-config/src/profile/credentials.rs b/aws/rust-runtime/aws-config/src/profile/credentials.rs index d5bf61d9c6..928f164d79 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials.rs @@ -23,12 +23,13 @@ //! through a series of providers. use crate::profile::credentials::exec::named::NamedProviderFactory; -use crate::profile::credentials::exec::{ClientConfiguration, ProviderChain}; +use crate::profile::credentials::exec::ProviderChain; use crate::profile::parser::ProfileFileLoadError; use crate::profile::profile_file::ProfileFiles; use crate::profile::Profile; use crate::provider_config::ProviderConfig; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; +use aws_sdk_sts::config::Builder as StsConfigBuilder; use aws_smithy_types::error::display::DisplayErrorContext; use std::borrow::Cow; use std::collections::HashMap; @@ -141,7 +142,7 @@ impl ProvideCredentials for ProfileFileCredentialsProvider { #[derive(Debug)] pub struct ProfileFileCredentialsProvider { factory: NamedProviderFactory, - client_config: ClientConfiguration, + sts_config: StsConfigBuilder, provider_config: ProviderConfig, } @@ -181,7 +182,7 @@ impl ProfileFileCredentialsProvider { }; for provider in inner_provider.chain().iter() { let next_creds = provider - .credentials(creds, &self.client_config) + .credentials(creds, &self.sts_config) .instrument(tracing::debug_span!("load_assume_role", provider = ?provider)) .await; match next_creds { @@ -440,14 +441,10 @@ impl Builder { ) }); let factory = exec::named::NamedProviderFactory::new(named_providers); - let core_client = conf.sts_client(); ProfileFileCredentialsProvider { factory, - client_config: ClientConfiguration { - sts_client: core_client, - region: conf.region(), - }, + sts_config: conf.sts_client_config(), provider_config: conf, } } diff --git a/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs b/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs index b6b0ba3e23..86622d0c17 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs @@ -8,15 +8,12 @@ use crate::credential_process::CredentialProcessProvider; use crate::profile::credentials::ProfileFileError; use crate::provider_config::ProviderConfig; #[cfg(feature = "credentials-sso")] -use crate::sso::{SsoConfig, SsoCredentialsProvider}; +use crate::sso::{SsoCredentialsProvider, SsoProviderConfig}; use crate::sts; use crate::web_identity_token::{StaticConfiguration, WebIdentityTokenCredentialsProvider}; use aws_credential_types::provider::{self, error::CredentialsError, ProvideCredentials}; -use aws_sdk_sts::middleware::DefaultMiddleware; -use aws_sdk_sts::operation::assume_role::AssumeRoleInput; -use aws_sdk_sts::{config::Credentials, Config}; -use aws_smithy_client::erase::DynConnector; -use aws_types::region::Region; +use aws_sdk_sts::config::{Builder as StsConfigBuilder, Credentials}; +use aws_sdk_sts::Client as StsClient; use std::fmt::Debug; use std::sync::Arc; @@ -27,39 +24,28 @@ pub(super) struct AssumeRoleProvider { session_name: Option, } -#[derive(Debug)] -pub(super) struct ClientConfiguration { - pub(super) sts_client: aws_smithy_client::Client, - pub(super) region: Option, -} - impl AssumeRoleProvider { pub(super) async fn credentials( &self, input_credentials: Credentials, - client_config: &ClientConfiguration, + sts_config: &StsConfigBuilder, ) -> provider::Result { - let config = Config::builder() + let config = sts_config + .clone() .credentials_provider(input_credentials) - .region(client_config.region.clone()) .build(); + let client = StsClient::from_conf(config); let session_name = &self .session_name .as_ref() .cloned() .unwrap_or_else(|| sts::util::default_session_name("assume-role-from-profile")); - let operation = AssumeRoleInput::builder() + let assume_role_creds = client + .assume_role() .role_arn(&self.role_arn) .set_external_id(self.external_id.clone()) .role_session_name(session_name) - .build() - .expect("operation is valid") - .make_operation(&config) - .await - .expect("valid operation"); - let assume_role_creds = client_config - .sts_client - .call(operation) + .send() .await .map_err(CredentialsError::provider_error)? .credentials; @@ -127,7 +113,8 @@ impl ProviderChain { } => { #[cfg(feature = "credentials-sso")] { - let sso_config = SsoConfig { + use aws_types::region::Region; + let sso_config = SsoProviderConfig { account_id: sso_account_id.to_string(), role_name: sso_role_name.to_string(), start_url: sso_start_url.to_string(), diff --git a/aws/rust-runtime/aws-config/src/sso.rs b/aws/rust-runtime/aws-config/src/sso.rs index 6adc586ab4..cd0b2df812 100644 --- a/aws/rust-runtime/aws-config/src/sso.rs +++ b/aws/rust-runtime/aws-config/src/sso.rs @@ -17,10 +17,8 @@ use crate::provider_config::ProviderConfig; use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_credential_types::Credentials; -use aws_sdk_sso::middleware::DefaultMiddleware as SsoMiddleware; -use aws_sdk_sso::operation::get_role_credentials::GetRoleCredentialsInput; use aws_sdk_sso::types::RoleCredentials; -use aws_smithy_client::erase::DynConnector; +use aws_sdk_sso::{config::Builder as SsoConfigBuilder, Client as SsoClient, Config as SsoConfig}; use aws_smithy_json::deserialize::Token; use aws_smithy_types::date_time::Format; use aws_smithy_types::DateTime; @@ -33,23 +31,11 @@ use std::fmt::{Display, Formatter}; use std::io; use std::path::PathBuf; +use crate::connector::expect_connector; +use aws_smithy_types::retry::RetryConfig; use ring::digest; use zeroize::Zeroizing; -impl crate::provider_config::ProviderConfig { - pub(crate) fn sso_client( - &self, - ) -> aws_smithy_client::Client { - use crate::connector::expect_connector; - - let mut client_builder = aws_smithy_client::Client::builder() - .connector(expect_connector(self.connector(&Default::default()))) - .middleware(SsoMiddleware::default()); - client_builder.set_sleep_impl(self.sleep()); - client_builder.build() - } -} - /// SSO Credentials Provider /// /// _Note: This provider is part of the default credentials chain and is integrated with the profile-file provider._ @@ -60,8 +46,8 @@ impl crate::provider_config::ProviderConfig { pub struct SsoCredentialsProvider { fs: Fs, env: Env, - sso_config: SsoConfig, - client: aws_smithy_client::Client, + sso_provider_config: SsoProviderConfig, + sso_config: SsoConfigBuilder, } impl SsoCredentialsProvider { @@ -70,20 +56,36 @@ impl SsoCredentialsProvider { Builder::new() } - pub(crate) fn new(provider_config: &ProviderConfig, sso_config: SsoConfig) -> Self { + pub(crate) fn new( + provider_config: &ProviderConfig, + sso_provider_config: SsoProviderConfig, + ) -> Self { let fs = provider_config.fs(); let env = provider_config.env(); + let mut sso_config = SsoConfig::builder() + .http_connector(expect_connector( + provider_config.connector(&Default::default()), + )) + .retry_config(RetryConfig::standard()); + sso_config.set_sleep_impl(provider_config.sleep()); + SsoCredentialsProvider { fs, env, - client: provider_config.sso_client(), + sso_provider_config, sso_config, } } async fn credentials(&self) -> provider::Result { - load_sso_credentials(&self.sso_config, &self.client, &self.env, &self.fs).await + load_sso_credentials( + &self.sso_provider_config, + &self.sso_config, + &self.env, + &self.fs, + ) + .await } } @@ -152,7 +154,7 @@ impl Builder { /// - [`region`](Self::region) pub fn build(self) -> SsoCredentialsProvider { let provider_config = self.provider_config.unwrap_or_default(); - let sso_config = SsoConfig { + let sso_config = SsoProviderConfig { account_id: self.account_id.expect("account_id must be set"), role_name: self.role_name.expect("role_name must be set"), start_url: self.start_url.expect("start_url must be set"), @@ -194,7 +196,7 @@ impl Error for LoadTokenError { } #[derive(Debug)] -pub(crate) struct SsoConfig { +pub(crate) struct SsoProviderConfig { pub(crate) account_id: String, pub(crate) role_name: String, pub(crate) start_url: String, @@ -202,31 +204,27 @@ pub(crate) struct SsoConfig { } async fn load_sso_credentials( - sso_config: &SsoConfig, - sso: &aws_smithy_client::Client, + sso_provider_config: &SsoProviderConfig, + sso_config: &SsoConfigBuilder, env: &Env, fs: &Fs, ) -> provider::Result { - let token = load_token(&sso_config.start_url, env, fs) + let token = load_token(&sso_provider_config.start_url, env, fs) .await .map_err(CredentialsError::provider_error)?; - let config = aws_sdk_sso::Config::builder() - .region(sso_config.region.clone()) + let config = sso_config + .clone() + .region(sso_provider_config.region.clone()) .credentials_cache(CredentialsCache::no_caching()) .build(); - let operation = GetRoleCredentialsInput::builder() - .role_name(&sso_config.role_name) + // TODO(enableNewSmithyRuntime): Use `customize().config_override()` to set the region instead of creating a new client once middleware is removed + let client = SsoClient::from_conf(config); + let resp = client + .get_role_credentials() + .role_name(&sso_provider_config.role_name) .access_token(&*token.access_token) - .account_id(&sso_config.account_id) - .build() - .map_err(|err| { - CredentialsError::unhandled(format!("could not construct SSO token input: {}", err)) - })? - .make_operation(&config) - .await - .map_err(CredentialsError::unhandled)?; - let resp = sso - .call(operation) + .account_id(&sso_provider_config.account_id) + .send() .await .map_err(CredentialsError::provider_error)?; let credentials: RoleCredentials = resp diff --git a/aws/rust-runtime/aws-config/src/sts.rs b/aws/rust-runtime/aws-config/src/sts.rs index e6e02a43cb..11a6ec591d 100644 --- a/aws/rust-runtime/aws-config/src/sts.rs +++ b/aws/rust-runtime/aws-config/src/sts.rs @@ -5,23 +5,25 @@ //! Credential provider augmentation through the AWS Security Token Service (STS). -use crate::connector::expect_connector; -use aws_sdk_sts::middleware::DefaultMiddleware; -use aws_smithy_client::erase::DynConnector; -use aws_smithy_client::Client; - pub(crate) mod util; pub use assume_role::{AssumeRoleProvider, AssumeRoleProviderBuilder}; mod assume_role; +use crate::connector::expect_connector; +use aws_credential_types::cache::CredentialsCache; +use aws_sdk_sts::config::Builder as StsConfigBuilder; +use aws_smithy_types::retry::RetryConfig; + impl crate::provider_config::ProviderConfig { - pub(crate) fn sts_client(&self) -> Client { - let mut builder = Client::builder() - .connector(expect_connector(self.connector(&Default::default()))) - .middleware(DefaultMiddleware::default()); + pub(crate) fn sts_client_config(&self) -> StsConfigBuilder { + let mut builder = aws_sdk_sts::Config::builder() + .http_connector(expect_connector(self.connector(&Default::default()))) + .retry_config(RetryConfig::standard()) + .region(self.region()) + .credentials_cache(CredentialsCache::no_caching()); builder.set_sleep_impl(self.sleep()); - builder.build() + builder } } diff --git a/aws/rust-runtime/aws-config/src/sts/assume_role.rs b/aws/rust-runtime/aws-config/src/sts/assume_role.rs index 66ad8cd89d..84343059f9 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -5,12 +5,14 @@ //! Assume credentials for a role through the AWS Security Token Service (STS). +use crate::connector::expect_connector; use crate::provider_config::ProviderConfig; use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; -use aws_sdk_sts::middleware::DefaultMiddleware; -use aws_sdk_sts::operation::assume_role::{AssumeRoleError, AssumeRoleInput}; +use aws_sdk_sts::operation::assume_role::builders::AssumeRoleFluentBuilder; +use aws_sdk_sts::operation::assume_role::AssumeRoleError; use aws_sdk_sts::types::PolicyDescriptorType; +use aws_sdk_sts::Client as StsClient; use aws_smithy_client::erase::DynConnector; use aws_smithy_http::result::SdkError; use aws_smithy_types::error::display::DisplayErrorContext; @@ -46,9 +48,7 @@ pub struct AssumeRoleProvider { #[derive(Debug)] struct Inner { - sts: aws_smithy_client::Client, - conf: aws_sdk_sts::Config, - op: AssumeRoleInput, + fluent_builder: AssumeRoleFluentBuilder, } impl AssumeRoleProvider { @@ -208,41 +208,29 @@ impl AssumeRoleProviderBuilder { .credentials_cache .unwrap_or_else(CredentialsCache::no_caching); - let config = aws_sdk_sts::Config::builder() + let mut config = aws_sdk_sts::Config::builder() .credentials_cache(credentials_cache) .credentials_provider(provider) .region(self.region.clone()) - .build(); - - let conn = conf - .connector(&Default::default()) - .expect("A connector must be provided"); - let mut client_builder = aws_smithy_client::Client::builder() - .connector(conn) - .middleware(DefaultMiddleware::new()); - client_builder.set_sleep_impl(conf.sleep()); - let client = client_builder.build(); + .http_connector(expect_connector(conf.connector(&Default::default()))); + config.set_sleep_impl(conf.sleep()); let session_name = self .session_name .unwrap_or_else(|| super::util::default_session_name("assume-role-provider")); - let operation = AssumeRoleInput::builder() + let sts_client = StsClient::from_conf(config.build()); + let fluent_builder = sts_client + .assume_role() .set_role_arn(Some(self.role_arn)) .set_external_id(self.external_id) .set_role_session_name(Some(session_name)) .set_policy(self.policy) .set_policy_arns(self.policy_arns) - .set_duration_seconds(self.session_length.map(|dur| dur.as_secs() as i32)) - .build() - .expect("operation is valid"); + .set_duration_seconds(self.session_length.map(|dur| dur.as_secs() as i32)); AssumeRoleProvider { - inner: Inner { - sts: client, - conf: config, - op: operation, - }, + inner: Inner { fluent_builder }, } } } @@ -250,14 +238,8 @@ impl AssumeRoleProviderBuilder { impl Inner { async fn credentials(&self) -> provider::Result { tracing::debug!("retrieving assumed credentials"); - let op = self - .op - .clone() - .make_operation(&self.conf) - .await - .expect("valid operation"); - let assumed = self.sts.call(op).in_current_span().await; + let assumed = self.fluent_builder.clone().send().in_current_span().await; match assumed { Ok(assumed) => { tracing::debug!( diff --git a/aws/rust-runtime/aws-config/src/test_case.rs b/aws/rust-runtime/aws-config/src/test_case.rs index e51ac4504b..8392a92a16 100644 --- a/aws/rust-runtime/aws-config/src/test_case.rs +++ b/aws/rust-runtime/aws-config/src/test_case.rs @@ -114,8 +114,9 @@ where ); } (Err(actual_error), GenericTestResult::Ok(expected_creds)) => panic!( - "expected credentials ({:?}) but an error was returned: {:?}", - expected_creds, actual_error + "expected credentials ({:?}) but an error was returned: {}", + expected_creds, + DisplayErrorContext(&actual_error) ), (Ok(creds), GenericTestResult::ErrorContains(substr)) => panic!( "expected an error containing: `{}`, but a result was returned: {:?}", diff --git a/aws/rust-runtime/aws-config/src/web_identity_token.rs b/aws/rust-runtime/aws-config/src/web_identity_token.rs index 31819a1952..15da88d6c5 100644 --- a/aws/rust-runtime/aws-config/src/web_identity_token.rs +++ b/aws/rust-runtime/aws-config/src/web_identity_token.rs @@ -63,12 +63,8 @@ use crate::provider_config::ProviderConfig; use crate::sts; -use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; -use aws_sdk_sts::config::Region; -use aws_sdk_sts::middleware::DefaultMiddleware; -use aws_sdk_sts::operation::assume_role_with_web_identity::AssumeRoleWithWebIdentityInput; -use aws_smithy_client::erase::DynConnector; +use aws_sdk_sts::Client as StsClient; use aws_smithy_types::error::display::DisplayErrorContext; use aws_types::os_shim_internal::{Env, Fs}; use std::borrow::Cow; @@ -85,8 +81,7 @@ const ENV_VAR_SESSION_NAME: &str = "AWS_ROLE_SESSION_NAME"; pub struct WebIdentityTokenCredentialsProvider { source: Source, fs: Fs, - client: aws_smithy_client::Client, - region: Option, + sts_client: StsClient, } impl WebIdentityTokenCredentialsProvider { @@ -152,12 +147,7 @@ impl WebIdentityTokenCredentialsProvider { let conf = self.source()?; load_credentials( &self.fs, - &self.client, - &self.region.as_ref().cloned().ok_or_else(|| { - CredentialsError::invalid_configuration( - "region is required for WebIdentityTokenProvider", - ) - })?, + &self.sts_client, &conf.web_identity_token_file, &conf.role_arn, &conf.session_name, @@ -208,21 +198,18 @@ impl Builder { /// builder, this function will panic. pub fn build(self) -> WebIdentityTokenCredentialsProvider { let conf = self.config.unwrap_or_default(); - let client = conf.sts_client(); let source = self.source.unwrap_or_else(|| Source::Env(conf.env())); WebIdentityTokenCredentialsProvider { source, fs: conf.fs(), - client, - region: conf.region(), + sts_client: StsClient::from_conf(conf.sts_client_config().build()), } } } async fn load_credentials( fs: &Fs, - client: &aws_smithy_client::Client, - region: &Region, + sts_client: &StsClient, token_file: impl AsRef, role_arn: &str, session_name: &str, @@ -234,24 +221,17 @@ async fn load_credentials( let token = String::from_utf8(token).map_err(|_utf_8_error| { CredentialsError::unhandled("WebIdentityToken was not valid UTF-8") })?; - let conf = aws_sdk_sts::Config::builder() - .region(region.clone()) - .credentials_cache(CredentialsCache::no_caching()) - .build(); - let operation = AssumeRoleWithWebIdentityInput::builder() + let resp = sts_client.assume_role_with_web_identity() .role_arn(role_arn) .role_session_name(session_name) .web_identity_token(token) - .build() - .expect("valid operation") - .make_operation(&conf) + .send() .await - .expect("valid operation"); - let resp = client.call(operation).await.map_err(|sdk_error| { - tracing::warn!(error = %DisplayErrorContext(&sdk_error), "STS returned an error assuming web identity role"); - CredentialsError::provider_error(sdk_error) - })?; + .map_err(|sdk_error| { + tracing::warn!(error = %DisplayErrorContext(&sdk_error), "STS returned an error assuming web identity role"); + CredentialsError::provider_error(sdk_error) + })?; sts::util::into_credentials(resp.credentials, "WebIdentityToken") } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index 050ba65be0..20c3264b38 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -11,15 +11,13 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientDocs import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientGenerics import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientSection import software.amazon.smithy.rust.codegen.client.smithy.generators.client.NoClientGenerics +import software.amazon.smithy.rust.codegen.client.smithy.generators.client.renderCustomizableOperationSend import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.DefaultProtocolTestGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Feature -import software.amazon.smithy.rust.codegen.core.rustlang.GenericTypeArg -import software.amazon.smithy.rust.codegen.core.rustlang.RustGenerics import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -42,7 +40,6 @@ private class Types(runtimeConfig: RuntimeConfig) { val awsTypes = AwsRuntimeType.awsTypes(runtimeConfig) val connectorError = smithyHttp.resolve("result::ConnectorError") val connectorSettings = smithyClient.resolve("http_connector::ConnectorSettings") - val defaultMiddleware = runtimeConfig.defaultMiddleware() val dynConnector = smithyClient.resolve("erase::DynConnector") val dynMiddleware = smithyClient.resolve("erase::DynMiddleware") val retryConfig = smithyTypes.resolve("retry::RetryConfig") @@ -73,10 +70,10 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { retryClassifier = AwsRuntimeType.awsHttp(runtimeConfig).resolve("retry::AwsResponseRetryClassifier"), ).render(rustCrate, listOf(CustomizableOperationTestHelpers(runtimeConfig))) rustCrate.withModule(ClientRustModule.Client.customize) { - renderCustomizableOperationSendMethod(runtimeConfig, generics, this) + renderCustomizableOperationSend(codegenContext, generics, this) } rustCrate.withModule(ClientRustModule.client) { - AwsFluentClientExtensions(types).render(this) + AwsFluentClientExtensions(codegenContext, types).render(this) } val awsSmithyClient = "aws-smithy-client" rustCrate.mergeFeature(Feature("rustls", default = true, listOf("$awsSmithyClient/rustls"))) @@ -123,13 +120,13 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { ) } -private class AwsFluentClientExtensions(types: Types) { +private class AwsFluentClientExtensions(private val codegenContext: ClientCodegenContext, private val types: Types) { private val codegenScope = arrayOf( + "Arc" to RuntimeType.Arc, "ConnectorError" to types.connectorError, + "ConnectorSettings" to types.connectorSettings, "DynConnector" to types.dynConnector, "DynMiddleware" to types.dynMiddleware, - "ConnectorSettings" to types.connectorSettings, - "Middleware" to types.defaultMiddleware, "RetryConfig" to types.retryConfig, "SmithyConnector" to types.smithyConnector, "TimeoutConfig" to types.timeoutConfig, @@ -153,65 +150,72 @@ private class AwsFluentClientExtensions(types: Types) { pub fn new(sdk_config: &#{aws_types}::sdk_config::SdkConfig) -> Self { Self::from_conf(sdk_config.into()) } - - /// Creates a new client from the service [`Config`](crate::Config). - /// - /// ## Panics - /// - /// - This method will panic if the `conf` is missing an async sleep implementation. If you experience this panic, set - /// the `sleep_impl` on the Config passed into this function to fix it. - /// - This method will panic if the `conf` is missing an HTTP connector. If you experience this panic, set the - /// `http_connector` on the Config passed into this function to fix it. - pub fn from_conf(conf: crate::Config) -> Self { - let retry_config = conf.retry_config().cloned().unwrap_or_else(#{RetryConfig}::disabled); - let timeout_config = conf.timeout_config().cloned().unwrap_or_else(#{TimeoutConfig}::disabled); - let sleep_impl = conf.sleep_impl(); - if (retry_config.has_retry() || timeout_config.has_timeouts()) && sleep_impl.is_none() { - panic!("An async sleep implementation is required for retries or timeouts to work. \ - Set the `sleep_impl` on the Config passed into this function to fix this panic."); - } - - let connector = conf.http_connector().and_then(|c| { - let timeout_config = conf - .timeout_config() - .cloned() - .unwrap_or_else(#{TimeoutConfig}::disabled); - let connector_settings = #{ConnectorSettings}::from_timeout_config( - &timeout_config, - ); - c.connector(&connector_settings, conf.sleep_impl()) - }); - - let builder = #{SmithyClientBuilder}::new(); - - let builder = match connector { - // Use provided connector - Some(c) => builder.connector(c), - None =>{ - ##[cfg(any(feature = "rustls", feature = "native-tls"))] - { - // Use default connector based on enabled features - builder.dyn_https_connector(#{ConnectorSettings}::from_timeout_config(&timeout_config)) - } - ##[cfg(not(any(feature = "rustls", feature = "native-tls")))] - { - panic!("No HTTP connector was available. Enable the `rustls` or `native-tls` crate feature or set a connector to fix this."); - } - } - }; - let mut builder = builder - .middleware(#{DynMiddleware}::new(#{Middleware}::new())) - .reconnect_mode(retry_config.reconnect_mode()) - .retry_config(retry_config.into()) - .operation_timeout_config(timeout_config.into()); - builder.set_sleep_impl(sleep_impl); - let client = builder.build(); - - Self { handle: std::sync::Arc::new(Handle { client, conf }) } - } """, *codegenScope, ) + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rustTemplate( + """ + /// Creates a new client from the service [`Config`](crate::Config). + /// + /// ## Panics + /// + /// - This method will panic if the `conf` is missing an async sleep implementation. If you experience this panic, set + /// the `sleep_impl` on the Config passed into this function to fix it. + /// - This method will panic if the `conf` is missing an HTTP connector. If you experience this panic, set the + /// `http_connector` on the Config passed into this function to fix it. + pub fn from_conf(conf: crate::Config) -> Self { + let retry_config = conf.retry_config().cloned().unwrap_or_else(#{RetryConfig}::disabled); + let timeout_config = conf.timeout_config().cloned().unwrap_or_else(#{TimeoutConfig}::disabled); + let sleep_impl = conf.sleep_impl(); + if (retry_config.has_retry() || timeout_config.has_timeouts()) && sleep_impl.is_none() { + panic!("An async sleep implementation is required for retries or timeouts to work. \ + Set the `sleep_impl` on the Config passed into this function to fix this panic."); + } + + let connector = conf.http_connector().and_then(|c| { + let timeout_config = conf + .timeout_config() + .cloned() + .unwrap_or_else(#{TimeoutConfig}::disabled); + let connector_settings = #{ConnectorSettings}::from_timeout_config( + &timeout_config, + ); + c.connector(&connector_settings, conf.sleep_impl()) + }); + + let builder = #{SmithyClientBuilder}::new(); + + let builder = match connector { + // Use provided connector + Some(c) => builder.connector(c), + None =>{ + ##[cfg(any(feature = "rustls", feature = "native-tls"))] + { + // Use default connector based on enabled features + builder.dyn_https_connector(#{ConnectorSettings}::from_timeout_config(&timeout_config)) + } + ##[cfg(not(any(feature = "rustls", feature = "native-tls")))] + { + panic!("No HTTP connector was available. Enable the `rustls` or `native-tls` crate feature or set a connector to fix this."); + } + } + }; + let mut builder = builder + .middleware(#{DynMiddleware}::new(#{Middleware}::new())) + .reconnect_mode(retry_config.reconnect_mode()) + .retry_config(retry_config.into()) + .operation_timeout_config(timeout_config.into()); + builder.set_sleep_impl(sleep_impl); + let client = builder.build(); + + Self { handle: #{Arc}::new(Handle { client, conf }) } + } + """, + *codegenScope, + "Middleware" to codegenContext.runtimeConfig.defaultMiddleware(), + ) + } } } } @@ -237,43 +241,3 @@ private class AwsFluentClientDocs(private val codegenContext: ClientCodegenConte } } } - -private fun renderCustomizableOperationSendMethod( - runtimeConfig: RuntimeConfig, - generics: FluentClientGenerics, - writer: RustWriter, -) { - val operationGenerics = RustGenerics(GenericTypeArg("O"), GenericTypeArg("Retry")) - val handleGenerics = generics.toRustGenerics() - val combinedGenerics = operationGenerics + handleGenerics - - val codegenScope = arrayOf( - *RuntimeType.preludeScope, - "combined_generics_decl" to combinedGenerics.declaration(), - "handle_generics_bounds" to handleGenerics.bounds(), - "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), - "SdkError" to RuntimeType.sdkError(runtimeConfig), - "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), - "ParseHttpResponse" to RuntimeType.parseHttpResponse(runtimeConfig), - ) - - writer.rustTemplate( - """ - impl#{combined_generics_decl:W} CustomizableOperation#{combined_generics_decl:W} - where - #{handle_generics_bounds:W} - { - /// Sends this operation's request - pub async fn send(self) -> #{Result}> - where - E: std::error::Error + #{Send} + #{Sync} + 'static, - O: #{ParseHttpResponse}> + #{Send} + #{Sync} + #{Clone} + 'static, - Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + #{Send} + #{Sync} + #{Clone}, - { - self.handle.client.call(self.operation).await - } - } - """, - *codegenScope, - ) -} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index f6a3cd889c..ee5f82aff6 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -38,6 +38,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtoc import software.amazon.smithy.rust.codegen.core.util.cloneOperation import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rustsdk.AwsRuntimeType.defaultMiddleware import software.amazon.smithy.rustsdk.traits.PresignableTrait import kotlin.streams.toList @@ -100,7 +101,12 @@ class AwsPresigningDecorator internal constructor( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = baseCustomizations + listOf(AwsInputPresignedMethod(codegenContext, operation)) + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateMiddleware) { + it + listOf( + AwsInputPresignedMethod(codegenContext, operation), + ) + } /** * Adds presignable trait to known presignable operations and creates synthetic presignable shapes for codegen diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt index e11f696d58..dc2afd1a35 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt @@ -44,6 +44,7 @@ object AwsRuntimeType { fun presigning(): RuntimeType = RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning", visibility = Visibility.PUBLIC)) + // TODO(enableNewSmithyRuntime): Delete defaultMiddleware and middleware.rs, and remove tower dependency from inlinables, when cleaning up middleware fun RuntimeConfig.defaultMiddleware() = RuntimeType.forInlineDependency( InlineAwsDependency.forRustFile( "middleware", visibility = Visibility.PUBLIC, diff --git a/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs b/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs index faf7eba2ec..a29f74d72f 100644 --- a/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs +++ b/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs @@ -3,13 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_dynamodb::operation::query::Query; -use aws_smithy_http::response::ParseHttpResponse; -use bytes::Bytes; use criterion::{criterion_group, criterion_main, Criterion}; fn do_bench() { - let response = http::Response::builder() + #[cfg(not(aws_sdk_orchestrator_mode))] + { + use aws_sdk_dynamodb::operation::query::Query; + use aws_smithy_http::response::ParseHttpResponse; + use bytes::Bytes; + + let response = http::Response::builder() .header("server", "Server") .header("date", "Mon, 08 Mar 2021 15:51:23 GMT") .header("content-type", "application/x-amz-json-1.0") @@ -21,9 +24,10 @@ fn do_bench() { .body(Bytes::copy_from_slice(br#"{"Count":2,"Items":[{"year":{"N":"2013"},"info":{"M":{"actors":{"L":[{"S":"Daniel Bruhl"},{"S":"Chris Hemsworth"},{"S":"Olivia Wilde"}]},"plot":{"S":"A re-creation of the merciless 1970s rivalry between Formula One rivals James Hunt and Niki Lauda."},"release_date":{"S":"2013-09-02T00:00:00Z"},"image_url":{"S":"http://ia.media-imdb.com/images/M/MV5BMTQyMDE0MTY0OV5BMl5BanBnXkFtZTcwMjI2OTI0OQ@@._V1_SX400_.jpg"},"genres":{"L":[{"S":"Action"},{"S":"Biography"},{"S":"Drama"},{"S":"Sport"}]},"directors":{"L":[{"S":"Ron Howard"}]},"rating":{"N":"8.3"},"rank":{"N":"2"},"running_time_secs":{"N":"7380"}}},"title":{"S":"Rush"}},{"year":{"N":"2013"},"info":{"M":{"actors":{"L":[{"S":"David Matthewman"},{"S":"Ann Thomas"},{"S":"Jonathan G. Neff"}]},"release_date":{"S":"2013-01-18T00:00:00Z"},"plot":{"S":"A rock band plays their music at high volumes, annoying the neighbors."},"genres":{"L":[{"S":"Comedy"},{"S":"Drama"}]},"image_url":{"S":"http://ia.media-imdb.com/images/N/O9ERWAU7FS797AJ7LU8HN09AMUP908RLlo5JF90EWR7LJKQ7@@._V1_SX400_.jpg"},"directors":{"L":[{"S":"Alice Smith"},{"S":"Bob Jones"}]},"rating":{"N":"6.2"},"rank":{"N":"11"},"running_time_secs":{"N":"5215"}}},"title":{"S":"Turn It Down, Or Else!"}}],"ScannedCount":2}"#)) .unwrap(); - let parser = Query::new(); - let output = ::parse_loaded(&parser, &response).unwrap(); - assert_eq!(2, output.count); + let parser = Query::new(); + let output = ::parse_loaded(&parser, &response).unwrap(); + assert_eq!(2, output.count); + } } fn bench_group(c: &mut Criterion) { diff --git a/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs b/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs index 82e37cfe8b..909ed85a51 100644 --- a/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs +++ b/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs @@ -7,7 +7,6 @@ use aws_sdk_dynamodb::operation::put_item::PutItemInput; use aws_sdk_dynamodb::types::AttributeValue; use aws_sdk_dynamodb::Config; use criterion::{criterion_group, criterion_main, Criterion}; -use futures_util::FutureExt; macro_rules! attr_s { ($str_val:expr) => { @@ -34,15 +33,20 @@ macro_rules! attr_obj { }; } -fn do_bench(config: &Config, input: &PutItemInput) { - let operation = input - .make_operation(&config) - .now_or_never() - .unwrap() - .expect("operation failed to build"); - let (http_request, _parts) = operation.into_request_response().0.into_parts(); - let body = http_request.body().bytes().unwrap(); - assert_eq!(body[0], b'{'); +fn do_bench(_config: &Config, _input: &PutItemInput) { + #[cfg(not(aws_sdk_orchestrator_mode))] + { + use futures_util::FutureExt; + + let operation = _input + .make_operation(&_config) + .now_or_never() + .unwrap() + .expect("operation failed to build"); + let (http_request, _parts) = operation.into_request_response().0.into_parts(); + let body = http_request.body().bytes().unwrap(); + assert_eq!(body[0], b'{'); + } } fn bench_group(c: &mut Criterion) { diff --git a/aws/sdk/integration-tests/kms/Cargo.toml b/aws/sdk/integration-tests/kms/Cargo.toml index 8820956d27..fbed1109f7 100644 --- a/aws/sdk/integration-tests/kms/Cargo.toml +++ b/aws/sdk/integration-tests/kms/Cargo.toml @@ -13,10 +13,12 @@ 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-runtime = { path = "../../build/aws-sdk/sdk/aws-runtime" } aws-sdk-kms = { path = "../../build/aws-sdk/sdk/kms" } aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } +aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api" } bytes = "1.0.0" http = "0.2.0" tokio = { version = "1.23.1", features = ["full", "test-util"]} diff --git a/aws/sdk/integration-tests/kms/tests/integration.rs b/aws/sdk/integration-tests/kms/tests/integration.rs index 8d17e13d48..ba2744f1f3 100644 --- a/aws/sdk/integration-tests/kms/tests/integration.rs +++ b/aws/sdk/integration-tests/kms/tests/integration.rs @@ -3,21 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_http::user_agent::AwsUserAgent; use aws_sdk_kms as kms; -use aws_sdk_kms::middleware::DefaultMiddleware; use aws_sdk_kms::operation::RequestId; use aws_smithy_client::test_connection::TestConnection; -use aws_smithy_client::{Client as CoreClient, SdkError}; +use aws_smithy_client::SdkError; use aws_smithy_http::body::SdkBody; use http::header::AUTHORIZATION; use http::Uri; use kms::config::{Config, Credentials, Region}; -use kms::operation::generate_random::GenerateRandomInput; use std::time::{Duration, UNIX_EPOCH}; -type Client = CoreClient; - // TODO(DVR): 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 @@ -34,9 +29,9 @@ async fn generate_random_cn() { .body(r#"{"Plaintext":"6CG0fbzzhg5G2VcFCPmJMJ8Njv3voYCgrGlp3+BZe7eDweCXgiyDH9BnkKvLmS7gQhnYDUlyES3fZVGwv5+CxA=="}"#).unwrap()) ]); let conf = Config::builder() + .http_connector(conn.clone()) .region(Region::new("cn-north-1")) .credentials_provider(Credentials::for_tests()) - .http_connector(conn.clone()) .build(); let client = kms::Client::from_conf(conf); let _ = client @@ -68,22 +63,27 @@ async fn generate_random() { .status(http::StatusCode::from_u16(200).unwrap()) .body(r#"{"Plaintext":"6CG0fbzzhg5G2VcFCPmJMJ8Njv3voYCgrGlp3+BZe7eDweCXgiyDH9BnkKvLmS7gQhnYDUlyES3fZVGwv5+CxA=="}"#).unwrap()) ]); - let client = Client::new(conn.clone()); let conf = Config::builder() + .http_connector(conn.clone()) .region(Region::new("us-east-1")) .credentials_provider(Credentials::for_tests()) .build(); - let mut op = GenerateRandomInput::builder() + let client = kms::Client::from_conf(conf); + let resp = client + .generate_random() .number_of_bytes(64) - .build() - .unwrap() - .make_operation(&conf) + .customize() .await - .expect("valid operation"); - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1614952162)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - let resp = client.call(op).await.expect("request should succeed"); + .expect("customizable") + .mutate_request(|req| { + // Remove the invocation ID since the signed request above doesn't have it + req.headers_mut().remove("amz-sdk-invocation-id"); + }) + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1614952162)) + .user_agent_for_tests() + .send() + .await + .expect("request should succeed"); // primitive checksum assert_eq!( resp.plaintext @@ -106,27 +106,22 @@ async fn generate_random_malformed_response() { // last `}` replaced with a space, invalid JSON .body(r#"{"Plaintext":"6CG0fbzzhg5G2VcFCPmJMJ8Njv3voYCgrGlp3+BZe7eDweCXgiyDH9BnkKvLmS7gQhnYDUlyES3fZVGwv5+CxA==" "#).unwrap()) ]); - let client = Client::new(conn.clone()); let conf = Config::builder() + .http_connector(conn.clone()) .region(Region::new("us-east-1")) .credentials_provider(Credentials::for_tests()) .build(); - let op = GenerateRandomInput::builder() + let client = kms::Client::from_conf(conf); + client + .generate_random() .number_of_bytes(64) - .build() - .unwrap() - .make_operation(&conf) + .send() .await - .expect("valid operation"); - client.call(op).await.expect_err("response was malformed"); + .expect_err("response was malformed"); } #[tokio::test] async fn generate_random_keystore_not_found() { - let conf = Config::builder() - .region(Region::new("us-east-1")) - .credentials_provider(Credentials::for_tests()) - .build(); let conn = TestConnection::new(vec![( http::Request::builder() .header("content-type", "application/x-amz-json-1.1") @@ -150,21 +145,26 @@ async fn generate_random_keystore_not_found() { .header("content-length", "44") .body(r#"{"__type":"CustomKeyStoreNotFoundException"}"#).unwrap()) ]); + let conf = Config::builder() + .http_connector(conn.clone()) + .region(Region::new("us-east-1")) + .credentials_provider(Credentials::for_tests()) + .build(); + let client = kms::Client::from_conf(conf); - let mut op = GenerateRandomInput::builder() + let err = client + .generate_random() .number_of_bytes(64) .custom_key_store_id("does not exist") - .build() - .unwrap() - .make_operation(&conf) + .customize() + .await + .expect("customizable") + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1614955644)) + .user_agent_for_tests() + .send() .await - .expect("valid operation"); + .expect_err("key store doesn't exist"); - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1614955644)); - op.properties_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 { SdkError::ServiceError(context) => context.into_err(), other => panic!("Incorrect error received: {:}", other), diff --git a/aws/sdk/integration-tests/kms/tests/retryable_errors.rs b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs new file mode 100644 index 0000000000..5c859da51b --- /dev/null +++ b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs @@ -0,0 +1,139 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(not(aws_sdk_orchestrator_mode))] +mod middleware_mode_tests { + use aws_http::retry::AwsResponseRetryClassifier; + use aws_sdk_kms as kms; + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::operation::{self, Parts}; + use aws_smithy_http::response::ParseStrictResponse; + use aws_smithy_http::result::SdkError; + use aws_smithy_http::retry::ClassifyRetry; + use aws_smithy_types::retry::{ErrorKind, RetryKind}; + use bytes::Bytes; + use kms::operation::create_alias::{CreateAlias, CreateAliasInput}; + + async fn create_alias_op() -> Parts { + let conf = kms::Config::builder().build(); + let (_, parts) = CreateAliasInput::builder() + .build() + .unwrap() + .make_operation(&conf) + .await + .expect("valid request") + .into_request_response(); + parts + } + + /// Parse a semi-real response body and assert that the correct retry status is returned + #[tokio::test] + async fn errors_are_retryable() { + let op = create_alias_op().await; + let http_response = http::Response::builder() + .status(400) + .body(Bytes::from_static( + br#"{ "code": "LimitExceededException" }"#, + )) + .unwrap(); + let err = op.response_handler.parse(&http_response).map_err(|e| { + SdkError::service_error( + e, + operation::Response::new(http_response.map(SdkBody::from)), + ) + }); + let retry_kind = op.retry_classifier.classify_retry(err.as_ref()); + assert_eq!(retry_kind, RetryKind::Error(ErrorKind::ThrottlingError)); + } + + #[tokio::test] + async fn unmodeled_errors_are_retryable() { + let op = create_alias_op().await; + let http_response = http::Response::builder() + .status(400) + .body(Bytes::from_static(br#"{ "code": "ThrottlingException" }"#)) + .unwrap(); + let err = op.response_handler.parse(&http_response).map_err(|e| { + SdkError::service_error( + e, + operation::Response::new(http_response.map(SdkBody::from)), + ) + }); + let retry_kind = op.retry_classifier.classify_retry(err.as_ref()); + assert_eq!(retry_kind, RetryKind::Error(ErrorKind::ThrottlingError)); + } +} + +#[cfg(aws_sdk_orchestrator_mode)] +mod orchestrator_mode_tests { + use aws_credential_types::Credentials; + use aws_runtime::retries::classifier::AwsErrorCodeClassifier; + use aws_sdk_kms as kms; + use aws_smithy_client::test_connection::infallible_connection_fn; + use aws_smithy_http::result::SdkError; + use aws_smithy_runtime_api::client::orchestrator::HttpResponse; + use aws_smithy_runtime_api::client::retries::RetryReason; + use aws_smithy_types::retry::ErrorKind; + use bytes::Bytes; + use kms::operation::create_alias::CreateAliasError; + + async fn make_err( + response: impl Fn() -> http::Response + Send + Sync + 'static, + ) -> SdkError { + let conn = infallible_connection_fn(move |_| response()); + let conf = kms::Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(kms::config::Region::from_static("us-east-1")) + .build(); + let client = kms::Client::from_conf(conf); + client + .create_alias() + .send() + .await + .expect_err("response was a failure") + } + + /// Parse a semi-real response body and assert that the correct retry status is returned + #[tokio::test] + async fn errors_are_retryable() { + let err = make_err(|| { + http::Response::builder() + .status(400) + .body(Bytes::from_static( + br#"{ "code": "LimitExceededException" }"#, + )) + .unwrap() + }) + .await; + + dbg!(&err); + let classifier = AwsErrorCodeClassifier; + let retry_kind = classifier.classify_error(&err); + assert_eq!( + Some(RetryReason::Error(ErrorKind::ThrottlingError)), + retry_kind + ); + } + + #[tokio::test] + async fn unmodeled_errors_are_retryable() { + let err = make_err(|| { + http::Response::builder() + .status(400) + .body(Bytes::from_static(br#"{ "code": "ThrottlingException" }"#)) + .unwrap() + }) + .await; + + dbg!(&err); + let classifier = AwsErrorCodeClassifier; + let retry_kind = classifier.classify_error(&err); + assert_eq!( + Some(RetryReason::Error(ErrorKind::ThrottlingError)), + retry_kind + ); + } +} diff --git a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs index 77c1940ae1..6d60b0e33e 100644 --- a/aws/sdk/integration-tests/kms/tests/sensitive-it.rs +++ b/aws/sdk/integration-tests/kms/tests/sensitive-it.rs @@ -3,17 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_http::retry::AwsResponseRetryClassifier; use aws_sdk_kms as kms; -use aws_smithy_http::body::SdkBody; -use aws_smithy_http::operation::{self, Parts}; -use aws_smithy_http::response::ParseStrictResponse; -use aws_smithy_http::result::SdkError; -use aws_smithy_http::retry::ClassifyRetry; -use aws_smithy_types::retry::{ErrorKind, RetryKind}; -use bytes::Bytes; -use kms::operation::create_alias::{CreateAlias, CreateAliasError, CreateAliasInput}; -use kms::operation::generate_random::{GenerateRandom, GenerateRandomOutput}; +use kms::operation::generate_random::GenerateRandomOutput; use kms::primitives::Blob; #[test] @@ -31,94 +22,3 @@ fn validate_sensitive_trait() { "GenerateRandomOutput { plaintext: \"*** Sensitive Data Redacted ***\", ciphertext_for_recipient: None, _request_id: None }" ); } - -fn assert_send_sync() {} -fn assert_send_fut(_: T) {} -fn assert_debug() {} - -#[tokio::test] -async fn types_are_send_sync() { - assert_send_sync::(); - assert_send_sync::>(); - assert_send_sync::(); - assert_send_sync::(); - assert_send_sync::(); - assert_send_sync::(); - let conf = kms::Config::builder().build(); - assert_send_fut(kms::Client::from_conf(conf).list_keys().send()); -} - -#[tokio::test] -async fn client_is_debug() { - let conf = kms::Config::builder().build(); - let client = kms::Client::from_conf(conf); - assert_ne!(format!("{:?}", client), ""); -} - -#[tokio::test] -async fn client_is_clone() { - let conf = kms::Config::builder().build(); - let client = kms::Client::from_conf(conf); - - fn is_clone(it: impl Clone) { - drop(it) - } - - is_clone(client); -} - -#[test] -fn types_are_debug() { - assert_debug::(); - assert_debug::(); - assert_debug::(); -} - -async fn create_alias_op() -> Parts { - let conf = kms::Config::builder().build(); - let (_, parts) = CreateAliasInput::builder() - .build() - .unwrap() - .make_operation(&conf) - .await - .expect("valid request") - .into_request_response(); - parts -} - -/// Parse a semi-real response body and assert that the correct retry status is returned -#[tokio::test] -async fn errors_are_retryable() { - let op = create_alias_op().await; - let http_response = http::Response::builder() - .status(400) - .body(Bytes::from_static( - br#"{ "code": "LimitExceededException" }"#, - )) - .unwrap(); - let err = op.response_handler.parse(&http_response).map_err(|e| { - SdkError::service_error( - e, - operation::Response::new(http_response.map(SdkBody::from)), - ) - }); - let retry_kind = op.retry_classifier.classify_retry(err.as_ref()); - assert_eq!(retry_kind, RetryKind::Error(ErrorKind::ThrottlingError)); -} - -#[tokio::test] -async fn unmodeled_errors_are_retryable() { - let op = create_alias_op().await; - let http_response = http::Response::builder() - .status(400) - .body(Bytes::from_static(br#"{ "code": "ThrottlingException" }"#)) - .unwrap(); - let err = op.response_handler.parse(&http_response).map_err(|e| { - SdkError::service_error( - e, - operation::Response::new(http_response.map(SdkBody::from)), - ) - }); - let retry_kind = op.retry_classifier.classify_retry(err.as_ref()); - assert_eq!(retry_kind, RetryKind::Error(ErrorKind::ThrottlingError)); -} diff --git a/aws/sdk/integration-tests/kms/tests/traits.rs b/aws/sdk/integration-tests/kms/tests/traits.rs new file mode 100644 index 0000000000..bfc1cf900b --- /dev/null +++ b/aws/sdk/integration-tests/kms/tests/traits.rs @@ -0,0 +1,50 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_sdk_kms as kms; +use kms::operation::create_alias::CreateAliasError; +use kms::operation::generate_random::GenerateRandom; + +fn assert_send_sync() {} +fn assert_send_fut(_: T) {} +fn assert_debug() {} + +#[tokio::test] +async fn types_are_send_sync() { + assert_send_sync::(); + assert_send_sync::>(); + assert_send_sync::(); + assert_send_sync::(); + assert_send_sync::(); + assert_send_sync::(); + let conf = kms::Config::builder().build(); + assert_send_fut(kms::Client::from_conf(conf).list_keys().send()); +} + +#[tokio::test] +async fn client_is_debug() { + let conf = kms::Config::builder().build(); + let client = kms::Client::from_conf(conf); + assert_ne!(format!("{:?}", client), ""); +} + +#[tokio::test] +async fn client_is_clone() { + let conf = kms::Config::builder().build(); + let client = kms::Client::from_conf(conf); + + fn is_clone(it: impl Clone) { + drop(it) + } + + is_clone(client); +} + +#[test] +fn types_are_debug() { + assert_debug::(); + assert_debug::(); + assert_debug::(); +} diff --git a/aws/sdk/integration-tests/lambda/Cargo.toml b/aws/sdk/integration-tests/lambda/Cargo.toml index 169327eb75..6e51d089f5 100644 --- a/aws/sdk/integration-tests/lambda/Cargo.toml +++ b/aws/sdk/integration-tests/lambda/Cargo.toml @@ -10,6 +10,7 @@ publish = false [dev-dependencies] async-stream = "0.3.0" aws-http = { path = "../../build/aws-sdk/sdk/aws-http" } +aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } aws-sdk-lambda = { path = "../../build/aws-sdk/sdk/lambda" } aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-eventstream = { path = "../../build/aws-sdk/sdk/aws-smithy-eventstream" } diff --git a/aws/sdk/integration-tests/lambda/tests/request_id.rs b/aws/sdk/integration-tests/lambda/tests/request_id.rs index 2bc465835e..0e071945aa 100644 --- a/aws/sdk/integration-tests/lambda/tests/request_id.rs +++ b/aws/sdk/integration-tests/lambda/tests/request_id.rs @@ -3,36 +3,62 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_lambda::operation::list_functions::{ListFunctions, ListFunctionsError}; +use aws_sdk_lambda::config::{Credentials, Region}; +use aws_sdk_lambda::operation::list_functions::ListFunctionsError; use aws_sdk_lambda::operation::RequestId; -use aws_smithy_http::response::ParseHttpResponse; -use bytes::Bytes; +use aws_sdk_lambda::{Client, Config}; +use aws_smithy_client::test_connection::infallible_connection_fn; -#[test] -fn get_request_id_from_unmodeled_error() { - let resp = http::Response::builder() - .header("x-amzn-RequestId", "correct-request-id") - .header("X-Amzn-Errortype", "ListFunctions") - .status(500) - .body("{}") - .unwrap(); - let err = ListFunctions::new() - .parse_loaded(&resp.map(Bytes::from)) - .expect_err("status was 500, this is an error"); - assert!(matches!(err, ListFunctionsError::Unhandled(_))); - assert_eq!(Some("correct-request-id"), err.request_id()); - assert_eq!(Some("correct-request-id"), err.meta().request_id()); +async fn run_test( + response: impl Fn() -> http::Response<&'static str> + Send + Sync + 'static, + expect_error: bool, +) { + let conn = infallible_connection_fn(move |_| response()); + let conf = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::from_static("us-east-1")) + .build(); + let client = Client::from_conf(conf); + let resp = client.list_functions().send().await; + if expect_error { + let err = resp.err().expect("should be an error").into_service_error(); + assert!(matches!(err, ListFunctionsError::Unhandled(_))); + assert_eq!(Some("correct-request-id"), err.request_id()); + assert_eq!(Some("correct-request-id"), err.meta().request_id()); + } else { + let output = resp.expect("should be successful"); + assert_eq!(Some("correct-request-id"), output.request_id()); + } } -#[test] -fn get_request_id_from_successful_response() { - let resp = http::Response::builder() - .header("x-amzn-RequestId", "correct-request-id") - .status(200) - .body(r#"{"Functions":[],"NextMarker":null}"#) - .unwrap(); - let output = ListFunctions::new() - .parse_loaded(&resp.map(Bytes::from)) - .expect("valid successful response"); - assert_eq!(Some("correct-request-id"), output.request_id()); +#[tokio::test] +async fn get_request_id_from_unmodeled_error() { + run_test( + || { + http::Response::builder() + .header("x-amzn-RequestId", "correct-request-id") + .header("X-Amzn-Errortype", "ListFunctions") + .status(500) + .body("{}") + .unwrap() + }, + true, + ) + .await; +} + +#[tokio::test] +async fn get_request_id_from_successful_response() { + run_test( + || { + http::Response::builder() + .header("x-amzn-RequestId", "correct-request-id") + .status(200) + .body(r#"{"Functions":[],"NextMarker":null}"#) + .unwrap() + }, + false, + ) + .await; } diff --git a/aws/sdk/integration-tests/qldbsession/tests/integration.rs b/aws/sdk/integration-tests/qldbsession/tests/integration.rs index 680e1647d5..cd1f77cd86 100644 --- a/aws/sdk/integration-tests/qldbsession/tests/integration.rs +++ b/aws/sdk/integration-tests/qldbsession/tests/integration.rs @@ -3,18 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_http::user_agent::AwsUserAgent; use aws_sdk_qldbsession as qldbsession; use aws_smithy_client::test_connection::TestConnection; -use aws_smithy_client::Client as CoreClient; use aws_smithy_http::body::SdkBody; use http::Uri; use qldbsession::config::{Config, Credentials, Region}; -use qldbsession::middleware::DefaultMiddleware; -use qldbsession::operation::send_command::SendCommandInput; use qldbsession::types::StartSessionRequest; +use qldbsession::Client; use std::time::{Duration, UNIX_EPOCH}; -pub type Client = CoreClient; // TODO(DVR): 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 @@ -38,30 +34,33 @@ async fn signv4_use_correct_service_name() { .status(http::StatusCode::from_u16(200).unwrap()) .body(r#"{}"#).unwrap()), ]); - - let client = Client::new(conn.clone()); let conf = Config::builder() + .http_connector(conn.clone()) .region(Region::new("us-east-1")) .credentials_provider(Credentials::for_tests()) .build(); + let client = Client::from_conf(conf); - let mut op = SendCommandInput::builder() + let _ = client + .send_command() .start_session( StartSessionRequest::builder() .ledger_name("not-real-ledger") .build(), ) - .build() - .unwrap() - .make_operation(&conf) + .customize() .await - .expect("valid operation"); - // Fix the request time and user agent so the headers are stable - op.properties_mut() - .insert(UNIX_EPOCH + Duration::from_secs(1614952162)); - op.properties_mut().insert(AwsUserAgent::for_tests()); - - let _ = client.call(op).await.expect("request should succeed"); + .expect("should be customizable") + // Fix the request time and user agent so the headers are stable + .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1614952162)) + .user_agent_for_tests() + .mutate_request(|req| { + // Remove the invocation ID since the signed request above doesn't have it + req.headers_mut().remove("amz-sdk-invocation-id"); + }) + .send() + .await + .expect("request should succeed"); conn.assert_requests_match(&[]); } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt index d9709f5ff0..3ee8af0de5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt @@ -105,6 +105,7 @@ class PaginatorGenerator private constructor( "Builder" to symbolProvider.symbolForBuilder(operation.inputShape(model)), // SDK Types + "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse"), "SdkError" to RuntimeType.sdkError(runtimeConfig), "client" to RuntimeType.smithyClient(runtimeConfig), "fn_stream" to RuntimeType.smithyAsync(runtimeConfig).resolve("future::fn_stream"), @@ -159,7 +160,7 @@ class PaginatorGenerator private constructor( /// Create the pagination stream /// /// _Note:_ No requests will be dispatched until the stream is used (eg. with [`.next().await`](tokio_stream::StreamExt::next)). - pub fn send(self) -> impl #{Stream}>> + #{Unpin} + pub fn send(self) -> impl #{Stream} + #{Unpin} #{send_bounds:W} { // Move individual fields out of self for the borrow checker let builder = self.builder; @@ -171,16 +172,7 @@ class PaginatorGenerator private constructor( #{Err}(e) => { let _ = tx.send(#{Err}(e)).await; return; } }; loop { - let op = match input.make_operation(&handle.conf) - .await - .map_err(#{SdkError}::construction_failure) { - #{Ok}(op) => op, - #{Err}(e) => { - let _ = tx.send(#{Err}(e)).await; - return; - } - }; - let resp = handle.client.call(op).await; + let resp = #{orchestrate}; // If the input member is None or it was an error let done = match resp { #{Ok}(ref resp) => { @@ -210,6 +202,39 @@ class PaginatorGenerator private constructor( *codegenScope, "items_fn" to itemsFn(), "output_token" to outputTokenLens, + "item_type" to writable { + if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + rustTemplate("#{Result}<#{Output}, #{SdkError}<#{Error}>>", *codegenScope) + } else { + rustTemplate("#{Result}<#{Output}, #{SdkError}<#{Error}, #{HttpResponse}>>", *codegenScope) + } + }, + "orchestrate" to writable { + if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + rustTemplate( + """ + { + let op = match input.make_operation(&handle.conf) + .await + .map_err(#{SdkError}::construction_failure) { + #{Ok}(op) => op, + #{Err}(e) => { + let _ = tx.send(#{Err}(e)).await; + return; + } + }; + handle.client.call(op).await + } + """, + *codegenScope, + ) + } else { + rustTemplate( + "#{operation}::orchestrate(input.clone(), handle.clone(), None).await", + *codegenScope, + ) + } + }, ) } @@ -264,7 +289,7 @@ class PaginatorGenerator private constructor( /// _Note: No requests will be dispatched until the stream is used (eg. with [`.next().await`](tokio_stream::StreamExt::next))._ /// /// To read the entirety of the paginator, use [`.collect::, _>()`](tokio_stream::StreamExt::collect). - pub fn send(self) -> impl #{Stream}>> + #{Unpin} + pub fn send(self) -> impl #{Stream} + #{Unpin} #{send_bounds:W} { #{fn_stream}::TryFlatMap::new(self.0.send()).flat_map(|page| #{extract_items}(page).unwrap_or_default().into_iter()) } @@ -275,6 +300,13 @@ class PaginatorGenerator private constructor( outputShape, paginationInfo.itemsMemberPath, ), + "item_type" to writable { + if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + rustTemplate("#{Result}<${itemType()}, #{SdkError}<#{Error}>>", *codegenScope) + } else { + rustTemplate("#{Result}<${itemType()}, #{SdkError}<#{Error}, #{HttpResponse}>>", *codegenScope) + } + }, *codegenScope, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index c3108a2f57..ee4b0f3c79 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -14,7 +14,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustGenerics import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.RuntimeConfig 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.smithy.RustCrate @@ -37,6 +36,8 @@ class CustomizableOperationGenerator( fun render(crate: RustCrate) { crate.withModule(ClientRustModule.Client.customize) { rustTemplate( + // TODO(enableNewSmithyRuntime): Stop exporting `Operation` when removing middleware + // TODO(enableNewSmithyRuntime): Re-export orchestrator equivalents for retry types when removing middleware """ pub use #{Operation}; pub use #{Request}; @@ -50,7 +51,9 @@ class CustomizableOperationGenerator( "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), "RetryKind" to smithyTypes.resolve("retry::RetryKind"), ) - renderCustomizableOperationModule(this) + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + renderCustomizableOperationModule(this) + } } } @@ -147,6 +150,8 @@ class CustomizableOperationGenerator( val codegenScope = arrayOf( *preludeScope, + "HttpRequest" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::HttpRequest"), "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::orchestrator::HttpResponse"), "Interceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) @@ -164,6 +169,7 @@ class CustomizableOperationGenerator( .resolve("client::interceptors::SharedInterceptor"), ) + // TODO(enableNewSmithyRuntime): Centralize this struct rather than generating it once per operation writer.rustTemplate( """ /// A wrapper type for [`$builderName`]($builderName) that allows for configuring a single @@ -189,7 +195,7 @@ class CustomizableOperationGenerator( /// Allows for customizing the operation's request. pub fn map_request(mut self, f: F) -> Self where - F: #{Fn}(&mut http::Request<#{SdkBody}>) -> #{Result}<(), E> + F: #{Fn}(#{HttpRequest}) -> #{Result}<#{HttpRequest}, E> + #{Send} + #{Sync} + 'static, @@ -244,17 +250,6 @@ class CustomizableOperationGenerator( #{HttpResponse} > > { - self.send_orchestrator_with_plugin(#{Option}::<#{Box}>::None) - .await - } - - ##[doc(hidden)] - // TODO(enableNewSmithyRuntime): Delete when unused - /// Equivalent to [`Self::send`] but adds a final runtime plugin to shim missing behavior - pub async fn send_orchestrator_with_plugin( - self, - final_plugin: #{Option} - ) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { let mut config_override = if let Some(config_override) = self.config_override { config_override } else { @@ -267,7 +262,7 @@ class CustomizableOperationGenerator( self.fluent_builder .config_override(config_override) - .send_orchestrator_with_plugin(final_plugin) + .send_orchestrator() .await } @@ -282,7 +277,8 @@ class CustomizableOperationGenerator( } } -fun renderCustomizableOperationSend(runtimeConfig: RuntimeConfig, generics: FluentClientGenerics, writer: RustWriter) { +fun renderCustomizableOperationSend(codegenContext: ClientCodegenContext, generics: FluentClientGenerics, writer: RustWriter) { + val runtimeConfig = codegenContext.runtimeConfig val smithyHttp = CargoDependency.smithyHttp(runtimeConfig).toType() val smithyClient = CargoDependency.smithyClient(runtimeConfig).toType() @@ -292,14 +288,16 @@ fun renderCustomizableOperationSend(runtimeConfig: RuntimeConfig, generics: Flue val codegenScope = arrayOf( *preludeScope, - "combined_generics_decl" to combinedGenerics.declaration(), - "handle_generics_bounds" to handleGenerics.bounds(), + "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + // TODO(enableNewSmithyRuntime): Delete the trait bounds when cleaning up middleware "ParseHttpResponse" to smithyHttp.resolve("response::ParseHttpResponse"), "NewRequestPolicy" to smithyClient.resolve("retry::NewRequestPolicy"), "SmithyRetryPolicy" to smithyClient.resolve("bounds::SmithyRetryPolicy"), "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), - "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), - "SdkError" to RuntimeType.sdkError(runtimeConfig), + // TODO(enableNewSmithyRuntime): Delete the generics when cleaning up middleware + "combined_generics_decl" to combinedGenerics.declaration(), + "handle_generics_bounds" to handleGenerics.bounds(), ) if (generics is FlexibleClientGenerics) { @@ -324,7 +322,7 @@ fun renderCustomizableOperationSend(runtimeConfig: RuntimeConfig, generics: Flue """, *codegenScope, ) - } else { + } else if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { writer.rustTemplate( """ impl#{combined_generics_decl:W} CustomizableOperation#{combined_generics_decl:W} @@ -336,6 +334,7 @@ fun renderCustomizableOperationSend(runtimeConfig: RuntimeConfig, generics: Flue where E: std::error::Error + #{Send} + #{Sync} + 'static, O: #{ParseHttpResponse}> + #{Send} + #{Sync} + #{Clone} + 'static, + Retry: #{Send} + #{Sync} + #{Clone}, Retry: #{ClassifyRetry}<#{SdkSuccess}, #{SdkError}> + #{Send} + #{Sync} + #{Clone}, { self.handle.client.call(self.operation).await diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt index 7fb225fa48..267b5bbb46 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt @@ -53,7 +53,7 @@ class FluentClientDecorator : ClientCodegenDecorator { customizations = listOf(GenericFluentClient(codegenContext)), ).render(rustCrate) rustCrate.withModule(ClientRustModule.Client.customize) { - renderCustomizableOperationSend(codegenContext.runtimeConfig, generics, this) + renderCustomizableOperationSend(codegenContext, generics, this) } rustCrate.mergeFeature(Feature("rustls", default = true, listOf("aws-smithy-client/rustls"))) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index d81cfa32d1..40e43425c4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -108,53 +108,9 @@ class FluentClientGenerator( "client" to RuntimeType.smithyClient(runtimeConfig), ) } - rustTemplate( - """ - ##[derive(Debug)] - pub(crate) struct Handle#{generics_decl:W} { - pub(crate) client: #{client}::Client#{smithy_inst:W}, - pub(crate) conf: crate::Config, - } - - #{client_docs:W} - ##[derive(::std::fmt::Debug)] - pub struct Client#{generics_decl:W} { - handle: #{Arc} - } - - impl${generics.inst} #{Clone} for Client${generics.inst} { - fn clone(&self) -> Self { - Self { handle: self.handle.clone() } - } - } - - impl${generics.inst} From<#{client}::Client#{smithy_inst:W}> for Client${generics.inst} { - fn from(client: #{client}::Client#{smithy_inst:W}) -> Self { - Self::with_config(client, crate::Config::builder().build()) - } - } - - impl${generics.inst} Client${generics.inst} { - /// Creates a client with the given service configuration. - pub fn with_config(client: #{client}::Client#{smithy_inst:W}, conf: crate::Config) -> Self { - Self { - handle: #{Arc}::new(Handle { - client, - conf, - }) - } - } - - /// Returns the client's configuration. - pub fn conf(&self) -> &crate::Config { - &self.handle.conf - } - } - """, + val clientScope = arrayOf( *preludeScope, "Arc" to RuntimeType.Arc, - "generics_decl" to generics.decl, - "smithy_inst" to generics.smithyInst, "client" to RuntimeType.smithyClient(runtimeConfig), "client_docs" to writable { @@ -166,7 +122,128 @@ class FluentClientGenerator( )(this) } }, + "RetryConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::RetryConfig"), + "TimeoutConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout::TimeoutConfig"), + // TODO(enableNewSmithyRuntime): Delete the generics when cleaning up middleware + "generics_decl" to generics.decl, + "smithy_inst" to generics.smithyInst, ) + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rustTemplate( + """ + ##[derive(Debug)] + pub(crate) struct Handle#{generics_decl:W} { + pub(crate) client: #{client}::Client#{smithy_inst:W}, + pub(crate) conf: crate::Config, + } + + #{client_docs:W} + ##[derive(::std::fmt::Debug)] + pub struct Client#{generics_decl:W} { + handle: #{Arc} + } + + impl${generics.inst} #{Clone} for Client${generics.inst} { + fn clone(&self) -> Self { + Self { handle: self.handle.clone() } + } + } + + impl${generics.inst} From<#{client}::Client#{smithy_inst:W}> for Client${generics.inst} { + fn from(client: #{client}::Client#{smithy_inst:W}) -> Self { + Self::with_config(client, crate::Config::builder().build()) + } + } + + impl${generics.inst} Client${generics.inst} { + /// Creates a client with the given service configuration. + pub fn with_config(client: #{client}::Client#{smithy_inst:W}, conf: crate::Config) -> Self { + Self { + handle: #{Arc}::new(Handle { + client, + conf, + }) + } + } + + /// Returns the client's configuration. + pub fn conf(&self) -> &crate::Config { + &self.handle.conf + } + } + """, + *clientScope, + ) + } else { + rustTemplate( + """ + ##[derive(Debug)] + pub(crate) struct Handle { + pub(crate) conf: crate::Config, + } + + #{client_docs:W} + ##[derive(::std::fmt::Debug)] + pub struct Client { + handle: #{Arc} + } + + impl #{Clone} for Client { + fn clone(&self) -> Self { + Self { handle: self.handle.clone() } + } + } + + impl Client { + /// Creates a new client from the service [`Config`](crate::Config). + /// + /// ## Panics + /// + /// - This method will panic if the `conf` is missing an async sleep implementation. If you experience this panic, set + /// the `sleep_impl` on the Config passed into this function to fix it. + /// - This method will panic if the `conf` is missing an HTTP connector. If you experience this panic, set the + /// `http_connector` on the Config passed into this function to fix it. + pub fn from_conf(conf: crate::Config) -> Self { + let retry_config = conf.retry_config().cloned().unwrap_or_else(#{RetryConfig}::disabled); + let timeout_config = conf.timeout_config().cloned().unwrap_or_else(#{TimeoutConfig}::disabled); + let sleep_impl = conf.sleep_impl(); + if (retry_config.has_retry() || timeout_config.has_timeouts()) && sleep_impl.is_none() { + panic!("An async sleep implementation is required for retries or timeouts to work. \ + Set the `sleep_impl` on the Config passed into this function to fix this panic."); + } + + Self { + handle: #{Arc}::new(Handle { conf }) + } + } + + /// Returns the client's configuration. + pub fn config(&self) -> &crate::Config { + &self.handle.conf + } + + ##[doc(hidden)] + // TODO(enableNewSmithyRuntime): Delete this function when cleaning up middleware + // This is currently kept around so the tests still compile in both modes + /// Creates a client with the given service configuration. + pub fn with_config(_client: #{client}::Client, conf: crate::Config) -> Self { + Self { + handle: #{Arc}::new(Handle { conf }) + } + } + + ##[doc(hidden)] + // TODO(enableNewSmithyRuntime): Delete this function when cleaning up middleware + // This is currently kept around so the tests still compile in both modes + /// Returns the client's configuration. + pub fn conf(&self) -> &crate::Config { + &self.handle.conf + } + } + """, + *clientScope, + ) + } } operations.forEach { operation -> @@ -293,78 +370,81 @@ class FluentClientGenerator( } } } - val middlewareScope = arrayOf( - *preludeScope, - "CustomizableOperation" to ClientRustModule.Client.customize.toType() - .resolve("CustomizableOperation"), - "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), - "OperationError" to errorType, - "OperationOutput" to outputType, - "SdkError" to RuntimeType.sdkError(runtimeConfig), - "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), - "send_bounds" to generics.sendBounds(operationSymbol, outputType, errorType, retryClassifier), - "customizable_op_type_params" to rustTypeParameters( - symbolProvider.toSymbol(operation), - retryClassifier, - generics.toRustGenerics(), - ), - ) - rustTemplate( - """ - // This function will go away in the near future. Do not rely on it. - ##[doc(hidden)] - pub async fn customize_middleware(self) -> #{Result}< - #{CustomizableOperation}#{customizable_op_type_params:W}, - #{SdkError}<#{OperationError}> - > #{send_bounds:W} { - let handle = self.handle.clone(); - let operation = self.inner.build().map_err(#{SdkError}::construction_failure)? - .make_operation(&handle.conf) - .await - .map_err(#{SdkError}::construction_failure)?; - #{Ok}(#{CustomizableOperation} { handle, operation }) - } - - // This function will go away in the near future. Do not rely on it. - ##[doc(hidden)] - pub async fn send_middleware(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}>> - #{send_bounds:W} { - let op = self.inner.build().map_err(#{SdkError}::construction_failure)? - .make_operation(&self.handle.conf) - .await - .map_err(#{SdkError}::construction_failure)?; - self.handle.client.call(op).await - } - """, - *middlewareScope, - ) - if (smithyRuntimeMode.defaultToMiddleware) { + if (smithyRuntimeMode.generateMiddleware) { + val middlewareScope = arrayOf( + *preludeScope, + "CustomizableOperation" to ClientRustModule.Client.customize.toType() + .resolve("CustomizableOperation"), + "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), + "Operation" to operationSymbol, + "OperationError" to errorType, + "OperationOutput" to outputType, + "SdkError" to RuntimeType.sdkError(runtimeConfig), + "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), + "send_bounds" to generics.sendBounds(operationSymbol, outputType, errorType, retryClassifier), + "customizable_op_type_params" to rustTypeParameters( + symbolProvider.toSymbol(operation), + retryClassifier, + generics.toRustGenerics(), + ), + ) rustTemplate( """ - /// Sends the request and returns the response. - /// - /// If an error occurs, an `SdkError` will be returned with additional details that - /// can be matched against. - /// - /// By default, any retryable failures will be retried twice. Retry behavior - /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be - /// set when configuring the client. - pub async fn send(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}>> - #{send_bounds:W} { - self.send_middleware().await - } - - /// Consumes this builder, creating a customizable operation that can be modified before being - /// sent. The operation's inner [http::Request] can be modified as well. - pub async fn customize(self) -> #{Result}< + // This function will go away in the near future. Do not rely on it. + ##[doc(hidden)] + pub async fn customize_middleware(self) -> #{Result}< #{CustomizableOperation}#{customizable_op_type_params:W}, #{SdkError}<#{OperationError}> > #{send_bounds:W} { - self.customize_middleware().await + let handle = self.handle.clone(); + let operation = self.inner.build().map_err(#{SdkError}::construction_failure)? + .make_operation(&handle.conf) + .await + .map_err(#{SdkError}::construction_failure)?; + #{Ok}(#{CustomizableOperation} { handle, operation }) + } + + // This function will go away in the near future. Do not rely on it. + ##[doc(hidden)] + pub async fn send_middleware(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}>> + #{send_bounds:W} { + let op = self.inner.build().map_err(#{SdkError}::construction_failure)? + .make_operation(&self.handle.conf) + .await + .map_err(#{SdkError}::construction_failure)?; + self.handle.client.call(op).await } """, *middlewareScope, ) + if (smithyRuntimeMode.defaultToMiddleware) { + rustTemplate( + """ + /// Sends the request and returns the response. + /// + /// If an error occurs, an `SdkError` will be returned with additional details that + /// can be matched against. + /// + /// By default, any retryable failures will be retried twice. Retry behavior + /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be + /// set when configuring the client. + pub async fn send(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}>> + #{send_bounds:W} { + self.send_middleware().await + } + + /// Consumes this builder, creating a customizable operation that can be modified before being + /// sent. The operation's inner [http::Request] can be modified as well. + pub async fn customize(self) -> #{Result}< + #{CustomizableOperation}#{customizable_op_type_params:W}, + #{SdkError}<#{OperationError}> + > #{send_bounds:W} { + self.customize_middleware().await + } + """, + *middlewareScope, + ) + } } if (smithyRuntimeMode.generateOrchestrator) { @@ -374,48 +454,17 @@ class FluentClientGenerator( .resolve("CustomizableOperation"), "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::orchestrator::HttpResponse"), + "Operation" to operationSymbol, "OperationError" to errorType, - "Operation" to symbolProvider.toSymbol(operation), "OperationOutput" to outputType, - "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), - "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::runtime_plugin::RuntimePlugins"), "SdkError" to RuntimeType.sdkError(runtimeConfig), - "TypedBox" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypedBox"), - "invoke" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke"), ) rustTemplate( """ ##[doc(hidden)] pub async fn send_orchestrator(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { - self.send_orchestrator_with_plugin(#{Option}::>::None).await - } - - ##[doc(hidden)] - // TODO(enableNewSmithyRuntime): Delete when unused - /// Equivalent to [`Self::send_orchestrator`] but adds a final runtime plugin to shim missing behavior - pub async fn send_orchestrator_with_plugin(self, final_plugin: #{Option}) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { - let mut runtime_plugins = #{RuntimePlugins}::new() - .with_client_plugin(crate::config::ServiceRuntimePlugin::new(self.handle.clone())); - runtime_plugins = runtime_plugins.with_operation_plugin(#{Operation}::new()); - if let Some(config_override) = self.config_override { - runtime_plugins = runtime_plugins.with_operation_plugin(config_override); - } - if let Some(final_plugin) = final_plugin { - runtime_plugins = runtime_plugins.with_client_plugin(final_plugin); - } let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; - let input = #{TypedBox}::new(input).erase(); - let output = #{invoke}(input, &runtime_plugins) - .await - .map_err(|err| { - err.map_service_error(|err| { - #{TypedBox}::<#{OperationError}>::assume_from(err.into()) - .expect("correct error type") - .unwrap() - }) - })?; - #{Ok}(#{TypedBox}::<#{OperationOutput}>::assume_from(output).expect("correct output type").unwrap()) + #{Operation}::orchestrate(input, self.handle, self.config_override).await } ##[doc(hidden)] diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt index 85dcb206db..02f281dfd4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol import software.amazon.smithy.model.shapes.OperationShape 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.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsInterceptorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginGenerator @@ -17,7 +18,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +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.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations @@ -25,6 +28,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.Proto import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.util.inputShape +import software.amazon.smithy.rust.codegen.core.util.outputShape open class ClientProtocolGenerator( private val codegenContext: ClientCodegenContext, @@ -39,6 +43,8 @@ open class ClientProtocolGenerator( // TODO(enableNewSmithyRuntime): Remove the `traitGenerator` private val traitGenerator: HttpBoundProtocolTraitImplGenerator, ) : ProtocolGenerator(codegenContext, protocol) { + private val runtimeConfig = codegenContext.runtimeConfig + /** * Render all code required for serializing requests and deserializing responses for the operation * @@ -63,7 +69,9 @@ open class ClientProtocolGenerator( operationCustomizations, OperationSection.InputImpl(operationCustomizations, operationShape, inputShape, protocol), ) - makeOperationGenerator.generateMakeOperation(this, operationShape, operationCustomizations) + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + makeOperationGenerator.generateMakeOperation(this, operationShape, operationCustomizations) + } } renderOperationStruct(operationWriter, operationShape, operationCustomizations, codegenDecorator) @@ -80,7 +88,7 @@ open class ClientProtocolGenerator( // pub struct Operation { ... } operationWriter.rust( """ - /// `ParseStrictResponse` impl for `$operationName`. + /// Orchestration and serialization glue logic for `$operationName`. """, ) Attribute(derive(RuntimeType.Clone, RuntimeType.Default, RuntimeType.Debug)).render(operationWriter) @@ -93,9 +101,76 @@ open class ClientProtocolGenerator( rust("Self") } + val outputType = symbolProvider.toSymbol(operationShape.outputShape(model)) + val errorType = symbolProvider.symbolForOperationError(operationShape) + val codegenScope = arrayOf( + *preludeScope, + "Arc" to RuntimeType.Arc, + "Input" to symbolProvider.toSymbol(operationShape.inputShape(model)), + "Operation" to symbolProvider.toSymbol(operationShape), + "OperationError" to errorType, + "OperationOutput" to outputType, + "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse"), + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::RuntimePlugins"), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + ) + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + val setupRuntimePluginsFn = + RuntimeType.forInlineFun("setup_runtime_plugins", ClientRustModule.Operation) { + rustTemplate( + """ + pub(crate) fn setup_runtime_plugins( + operation: #{Box}, + handle: #{Arc}, + config_override: #{Option}, + ) -> #{RuntimePlugins} { + let mut runtime_plugins = #{RuntimePlugins}::for_operation(operation) + .with_client_plugin(crate::config::ServiceRuntimePlugin::new(handle)); + if let Some(config_override) = config_override { + runtime_plugins = runtime_plugins.with_operation_plugin(config_override); + } + runtime_plugins + } + """, + *codegenScope, + ) + } + rustTemplate( + """ + pub(crate) async fn orchestrate( + input: #{Input}, + handle: #{Arc}, + config_override: #{Option}, + ) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { + let runtime_plugins = #{setup_runtime_plugins}(#{Box}::new(#{Operation}::new()) as _, handle, config_override); + let input = #{TypedBox}::new(input).erase(); + let output = #{invoke}(input, &runtime_plugins) + .await + .map_err(|err| { + err.map_service_error(|err| { + #{TypedBox}::<#{OperationError}>::assume_from(err.into()) + .expect("correct error type") + .unwrap() + }) + })?; + #{Ok}(#{TypedBox}::<#{OperationOutput}>::assume_from(output).expect("correct output type").unwrap()) + } + """, + *codegenScope, + "TypedBox" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypedBox"), + "invoke" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke"), + "setup_runtime_plugins" to setupRuntimePluginsFn, + ) + } + writeCustomizations(operationCustomizations, OperationSection.OperationImplBlock(operationCustomizations)) } - traitGenerator.generateTraitImpls(operationWriter, operationShape, operationCustomizations) + + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + traitGenerator.generateTraitImpls(operationWriter, operationShape, operationCustomizations) + } if (codegenContext.smithyRuntimeMode.generateOrchestrator) { OperationRuntimePluginGenerator(codegenContext).render( diff --git a/rust-runtime/aws-smithy-http/src/operation.rs b/rust-runtime/aws-smithy-http/src/operation.rs index a4d9657cfb..d5b62898ae 100644 --- a/rust-runtime/aws-smithy-http/src/operation.rs +++ b/rust-runtime/aws-smithy-http/src/operation.rs @@ -60,6 +60,7 @@ pub struct Parts { pub metadata: Option, } +// TODO(enableNewSmithyRuntime): Delete `operation::Operation` when cleaning up middleware /// An [`Operation`] is a request paired with a response handler, retry classifier, /// and metadata that identifies the API being called. /// @@ -159,6 +160,7 @@ impl Operation { } } +// TODO(enableNewSmithyRuntime): Delete `operation::Request` when cleaning up middleware /// Operation request type that associates a property bag with an underlying HTTP request. /// This type represents the request in the Tower `Service` in middleware so that middleware /// can share information with each other via the properties. @@ -250,6 +252,7 @@ impl Request { } } +// TODO(enableNewSmithyRuntime): Delete `operation::Response` when cleaning up middleware /// Operation response type that associates a property bag with an underlying HTTP response. /// This type represents the response in the Tower `Service` in middleware so that middleware /// can share information with each other via the properties. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index ea3754cd06..2e913ac524 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -39,6 +39,12 @@ impl RuntimePlugins { Default::default() } + pub fn for_operation(operation: Box) -> Self { + let mut plugins = Self::new(); + plugins.operation_plugins.push(operation); + plugins + } + pub fn with_client_plugin( mut self, plugin: impl RuntimePlugin + Send + Sync + 'static, diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs index 39ea3b35b3..bbd2263ddf 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs @@ -7,7 +7,9 @@ use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, BoxError, Interceptor, }; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_runtime_api::config_bag::ConfigBag; +use std::error::Error as StdError; use std::fmt; use std::marker::PhantomData; @@ -33,16 +35,18 @@ impl MapRequestInterceptor { impl Interceptor for MapRequestInterceptor where - F: Fn(&mut http::Request) -> Result<(), E> + Send + Sync + 'static, - E: std::error::Error + Send + Sync + 'static, + F: Fn(HttpRequest) -> Result + Send + Sync + 'static, + E: StdError + Send + Sync + 'static, { fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let request = context.request_mut(); - (self.f)(request)?; + let mut request = HttpRequest::new(SdkBody::taken()); + std::mem::swap(&mut request, context.request_mut()); + let mut mapped = (self.f)(request)?; + std::mem::swap(&mut mapped, context.request_mut()); Ok(()) } @@ -66,7 +70,7 @@ impl MutateRequestInterceptor { impl Interceptor for MutateRequestInterceptor where - F: Fn(&mut http::Request) + Send + Sync + 'static, + F: Fn(&mut HttpRequest) + Send + Sync + 'static, { fn modify_before_signing( &self, diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index 146db0d073..8cd7d32e45 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -17,6 +17,7 @@ services_that_fail_compile=(\ "s3"\ "s3control"\ "transcribestreaming"\ + "polly"\ ) # TODO(enableNewSmithyRuntime): Move these into `services_that_pass_tests` as more progress is made @@ -34,7 +35,6 @@ services_that_pass_tests=(\ "iam"\ "kms"\ "lambda"\ - "polly"\ "qldbsession"\ "sso"\ ) @@ -45,14 +45,14 @@ cd aws/sdk/build/aws-sdk/sdk for service in "${services_that_compile[@]}"; do pushd "${service}" echo -e "${C_YELLOW}# Running 'cargo check --all-features' on '${service}'${C_RESET}" - cargo check --all-features + RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo check --all-features popd done for service in "${services_that_pass_tests[@]}"; do pushd "${service}" echo -e "${C_YELLOW}# Running 'cargo test --all-features' on '${service}'${C_RESET}" - cargo test --all-features --no-fail-fast + RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo test --all-features --no-fail-fast popd done From f89b14337cf6c4f13d47a3275201278a5041d4fd Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 25 May 2023 12:50:37 -0700 Subject: [PATCH 110/253] Port Glacier customizations to the orchestrator (#2704) ## Motivation and Context This PR gets the Glacier integration tests to pass against the orchestrator implementation by porting the Glacier customizations from `make_operation` changes to interceptors. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-inlineable/Cargo.toml | 3 + .../aws-inlineable/src/glacier_checksums.rs | 2 + .../src/glacier_interceptors.rs | 388 ++++++++++++++++++ aws/rust-runtime/aws-inlineable/src/lib.rs | 3 + .../customize/ServiceSpecificDecorator.kt | 9 + .../customize/glacier/AccountIdAutofill.kt | 2 + .../customize/glacier/ApiVersionHeader.kt | 2 + .../customize/glacier/GlacierDecorator.kt | 163 +++++++- .../customize/glacier/TreeHashHeader.kt | 2 + .../glacier/tests/custom-headers.rs | 46 +++ .../protocol/ClientProtocolGenerator.kt | 9 +- .../protocol/RequestSerializerGenerator.kt | 3 +- .../protocol/ResponseDeserializerGenerator.kt | 4 +- .../aws-smithy-runtime-api/Cargo.toml | 1 + .../external-types.toml | 2 + .../src/client/orchestrator.rs | 34 ++ .../src/client/orchestrator/error.rs | 9 + .../src/client/orchestrator.rs | 87 ++-- .../check-aws-sdk-orchestrator-impl | 2 +- 19 files changed, 724 insertions(+), 47 deletions(-) create mode 100644 aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml index f471975d82..8836e8c558 100644 --- a/aws/rust-runtime/aws-inlineable/Cargo.toml +++ b/aws/rust-runtime/aws-inlineable/Cargo.toml @@ -15,6 +15,8 @@ repository = "https://github.com/awslabs/smithy-rs" aws-credential-types = { path = "../aws-credential-types" } aws-endpoint = { path = "../aws-endpoint" } aws-http = { path = "../aws-http" } +aws-runtime = { path = "../aws-runtime" } +aws-sigv4 = { path = "../aws-sigv4" } aws-sig-auth = { path = "../aws-sig-auth" } aws-smithy-checksums = { path = "../../../rust-runtime/aws-smithy-checksums" } aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client" } @@ -39,6 +41,7 @@ tracing = "0.1" aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client", features = ["test-util"] } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http", features = ["rt-tokio"] } +aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } tempfile = "3.2.0" tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs b/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs index c60e4d02fe..d22d28ed92 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +// TODO(enableNewSmithyRuntime): Delete this file when cleaning up middleware + use aws_sig_auth::signer::SignableBody; use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream::{self, ByteStream}; diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs new file mode 100644 index 0000000000..3dddba4381 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -0,0 +1,388 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// This code is referenced in generated code, so the compiler doesn't realize it is used. +#![allow(dead_code)] + +use aws_runtime::auth::sigv4::SigV4OperationSigningConfig; +use aws_sigv4::http_request::SignableBody; +use aws_smithy_http::body::SdkBody; +use aws_smithy_http::byte_stream; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, BoxError, + Interceptor, +}; +use aws_smithy_runtime_api::client::orchestrator::LoadedRequestBody; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use bytes::Bytes; +use http::header::{HeaderName, HeaderValue}; +use http::Request; +use ring::digest::{Context, Digest, SHA256}; +use std::fmt; +use std::marker::PhantomData; + +/// The default account ID when none is set on an input +const DEFAULT_ACCOUNT_ID: &str = "-"; + +const TREE_HASH_HEADER: &str = "x-amz-sha256-tree-hash"; +const X_AMZ_CONTENT_SHA256: &str = "x-amz-content-sha256"; +const API_VERSION_HEADER: &str = "x-amz-glacier-version"; + +/// Adds an account ID autofill method to generated input structs +/// +/// Some Glacier operations have an account ID field that needs to get defaulted to `-` if not set. +/// This trait is implemented via codegen customization for those operation inputs so that +/// the [`GlacierAccountIdAutofillInterceptor`] can do this defaulting. +pub(crate) trait GlacierAccountId: fmt::Debug { + /// Returns a mutable reference to the account ID field + fn account_id_mut(&mut self) -> &mut Option; + + /// Autofills the account ID with the default if not set + fn autofill_account_id(&mut self) { + let account_id = self.account_id_mut(); + if account_id.as_deref().unwrap_or_default().is_empty() { + *account_id = Some(DEFAULT_ACCOUNT_ID.into()); + } + } +} + +/// Autofills account ID input fields with a default if no value is set +#[derive(Debug)] +pub(crate) struct GlacierAccountIdAutofillInterceptor { + _phantom: PhantomData, +} + +impl GlacierAccountIdAutofillInterceptor { + /// Constructs a new [`GlacierAccountIdAutofillInterceptor`] + pub(crate) fn new() -> Self { + Self { + _phantom: Default::default(), + } + } +} + +impl Interceptor + for GlacierAccountIdAutofillInterceptor +{ + fn modify_before_serialization( + &self, + context: &mut BeforeSerializationInterceptorContextMut<'_>, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let erased_input = context.input_mut(); + let input: &mut I = erased_input + .downcast_mut() + .expect("typechecked at registration"); + input.autofill_account_id(); + Ok(()) + } +} + +/// Attaches the `x-amz-glacier-version` header to the request +#[derive(Debug)] +pub(crate) struct GlacierApiVersionInterceptor { + api_version: &'static str, +} + +impl GlacierApiVersionInterceptor { + /// Constructs a new [`GlacierApiVersionInterceptor`] with the given API version. + pub(crate) fn new(api_version: &'static str) -> Self { + Self { api_version } + } +} + +impl Interceptor for GlacierApiVersionInterceptor { + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + context.request_mut().headers_mut().insert( + API_VERSION_HEADER, + HeaderValue::from_static(self.api_version), + ); + Ok(()) + } +} + +/// Adds a glacier tree hash checksum to the HTTP Request +#[derive(Debug, Default)] +pub(crate) struct GlacierTreeHashHeaderInterceptor; + +impl Interceptor for GlacierTreeHashHeaderInterceptor { + fn modify_before_serialization( + &self, + _context: &mut BeforeSerializationInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + // Request the request body to be loaded into memory immediately after serialization + // so that it can be checksummed before signing and transmit + cfg.put(LoadedRequestBody::Requested); + Ok(()) + } + + fn modify_before_retry_loop( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let maybe_loaded_body = cfg.get::(); + if let Some(LoadedRequestBody::Loaded(body)) = maybe_loaded_body { + let content_sha256 = add_checksum_treehash(context.request_mut(), body)?; + + // Override the signing payload with this precomputed hash + let mut signing_config = cfg + .get::() + .ok_or("SigV4OperationSigningConfig not found")? + .clone(); + signing_config.signing_options.payload_override = + Some(SignableBody::Precomputed(content_sha256)); + cfg.put(signing_config); + } else { + return Err( + "the request body wasn't loaded into memory before the retry loop, \ + so the Glacier tree hash header can't be computed" + .into(), + ); + } + Ok(()) + } +} + +/// Adds a glacier tree hash checksum to the HTTP Request +/// +/// This handles two cases: +/// 1. A body which is retryable: the body will be streamed through a digest calculator, limiting memory usage. +/// 2. A body which is not retryable: the body will be converted into `Bytes`, then streamed through a digest calculator. +/// +/// The actual checksum algorithm will first compute a SHA256 checksum for each 1MB chunk. Then, a tree +/// will be assembled, recursively pairing neighboring chunks and computing their combined checksum. The 1 leftover +/// chunk (if it exists) is paired at the end. +/// +/// See for more information. +fn add_checksum_treehash( + request: &mut Request, + body: &Bytes, +) -> Result { + let (full_body, hashes) = compute_hashes(body, MEGABYTE)?; + let tree_hash = hex::encode(compute_hash_tree(hashes)); + let complete_hash = hex::encode(full_body); + if !request.headers().contains_key(TREE_HASH_HEADER) { + request.headers_mut().insert( + HeaderName::from_static(TREE_HASH_HEADER), + tree_hash.parse().expect("hash must be valid header"), + ); + } + if !request.headers().contains_key(X_AMZ_CONTENT_SHA256) { + request.headers_mut().insert( + HeaderName::from_static(X_AMZ_CONTENT_SHA256), + complete_hash.parse().expect("hash must be valid header"), + ); + } + Ok(complete_hash) +} + +const MEGABYTE: usize = 1024 * 1024; +fn compute_hashes( + body: &Bytes, + chunk_size: usize, +) -> Result<(Digest, Vec), byte_stream::error::Error> { + let mut hashes = Vec::new(); + let mut full_body = Context::new(&SHA256); + for chunk in body.chunks(chunk_size) { + let mut local = Context::new(&SHA256); + local.update(chunk); + hashes.push(local.finish()); + + full_body.update(chunk); + } + if hashes.is_empty() { + hashes.push(Context::new(&SHA256).finish()) + } + Ok((full_body.finish(), hashes)) +} + +/// Compute the glacier tree hash for a vector of hashes. +/// +/// Adjacent hashes are combined into a single hash. This process occurs recursively until only 1 hash remains. +/// +/// See for more information. +fn compute_hash_tree(mut hashes: Vec) -> Digest { + assert!( + !hashes.is_empty(), + "even an empty file will produce a digest. this function assumes that hashes is non-empty" + ); + while hashes.len() > 1 { + let next = hashes.chunks(2).into_iter().map(|chunk| match *chunk { + [left, right] => { + let mut ctx = Context::new(&SHA256); + ctx.update(left.as_ref()); + ctx.update(right.as_ref()); + ctx.finish() + } + [last] => last, + _ => unreachable!(), + }); + hashes = next.collect(); + } + hashes[0] +} + +#[cfg(test)] +mod account_id_autofill_tests { + use super::*; + use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::type_erasure::TypedBox; + + #[test] + fn autofill_account_id() { + #[derive(Debug)] + struct SomeInput { + account_id: Option, + } + impl GlacierAccountId for SomeInput { + fn account_id_mut(&mut self) -> &mut Option { + &mut self.account_id + } + } + + let mut cfg = ConfigBag::base(); + let mut context = + InterceptorContext::new(TypedBox::new(SomeInput { account_id: None }).erase()); + let mut context = BeforeSerializationInterceptorContextMut::from(&mut context); + let interceptor = GlacierAccountIdAutofillInterceptor::::new(); + interceptor + .modify_before_serialization(&mut context, &mut cfg) + .expect("success"); + assert_eq!( + DEFAULT_ACCOUNT_ID, + context + .input() + .downcast_ref::() + .unwrap() + .account_id + .as_ref() + .expect("it is set now") + ); + } +} + +#[cfg(test)] +mod api_version_tests { + use super::*; + use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::type_erasure::TypedBox; + + #[test] + fn api_version_interceptor() { + let mut cfg = ConfigBag::base(); + let mut context = InterceptorContext::new(TypedBox::new("dontcare").erase()); + context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let mut context = BeforeTransmitInterceptorContextMut::from(&mut context); + + let interceptor = GlacierApiVersionInterceptor::new("some-version"); + interceptor + .modify_before_signing(&mut context, &mut cfg) + .expect("success"); + + assert_eq!( + "some-version", + context + .request() + .headers() + .get(API_VERSION_HEADER) + .expect("header set") + ); + } +} + +#[cfg(test)] +mod treehash_checksum_tests { + use super::*; + + #[test] + fn compute_digests() { + { + let body = Bytes::from_static(b"1234"); + let hashes = compute_hashes(&body, 1).expect("succeeds").1; + assert_eq!(hashes.len(), 4); + } + { + let body = Bytes::from_static(b"1234"); + let hashes = compute_hashes(&body, 2).expect("succeeds").1; + assert_eq!(hashes.len(), 2); + } + { + let body = Bytes::from_static(b"12345"); + let hashes = compute_hashes(&body, 3).expect("succeeds").1; + assert_eq!(hashes.len(), 2); + } + { + let body = Bytes::from_static(b"11221122"); + let hashes = compute_hashes(&body, 2).expect("succeeds").1; + assert_eq!(hashes[0].as_ref(), hashes[2].as_ref()); + } + } + + #[test] + fn empty_body_computes_digest() { + let body = Bytes::from_static(b""); + let (_, hashes) = compute_hashes(&body, 2).expect("succeeds"); + assert_eq!(hashes.len(), 1); + } + + #[test] + fn compute_tree_digest() { + macro_rules! hash { + ($($inp:expr),*) => { + { + let mut ctx = ring::digest::Context::new(&ring::digest::SHA256); + $( + ctx.update($inp.as_ref()); + )* + ctx.finish() + } + } + } + let body = Bytes::from_static(b"1234567891011"); + let (complete, hashes) = compute_hashes(&body, 3).expect("succeeds"); + assert_eq!(hashes.len(), 5); + assert_eq!(complete.as_ref(), hash!("1234567891011").as_ref()); + let final_digest = compute_hash_tree(hashes); + let expected_digest = hash!( + hash!( + hash!(hash!("123"), hash!("456")), + hash!(hash!("789"), hash!("101")) + ), + hash!("1") + ); + assert_eq!(expected_digest.as_ref(), final_digest.as_ref()); + } + + #[test] + fn hash_value_test() { + // the test data consists of an 11 byte sequence, repeated. Since the sequence length is + // relatively prime with 1 megabyte, we can ensure that chunks will all have different hashes. + let base_seq = b"01245678912"; + let total_size = MEGABYTE * 101 + 500; + let mut test_data = vec![]; + while test_data.len() < total_size { + test_data.extend_from_slice(base_seq) + } + let test_data = Bytes::from(test_data); + + let mut http_req = http::Request::builder() + .uri("http://example.com/hello") + .body(SdkBody::taken()) // the body isn't used by add_checksum_treehash + .unwrap(); + + add_checksum_treehash(&mut http_req, &test_data).expect("should succeed"); + // hash value verified with AWS CLI + assert_eq!( + http_req.headers().get(TREE_HASH_HEADER).unwrap(), + "3d417484359fc9f5a3bafd576dc47b8b2de2bf2d4fdac5aa2aff768f2210d386" + ); + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index c2414b6b5f..c4b7d23684 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -34,6 +34,9 @@ pub mod s3_request_id; /// Glacier-specific checksumming behavior pub mod glacier_checksums; +/// Glacier-specific behavior +pub mod glacier_interceptors; + /// Default middleware stack for AWS services pub mod middleware; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index f540152a25..cc9c34797d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientProtocolMap import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization @@ -146,4 +147,12 @@ class ServiceSpecificDecorator( ): ProtocolTestGenerator = baseGenerator.maybeApply(codegenContext.serviceShape) { delegateTo.protocolTestGenerator(codegenContext, baseGenerator) } + + override fun operationRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { + delegateTo.operationRuntimePluginCustomizations(codegenContext, operation, baseCustomizations) + } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt index 63ef01d1b1..19484b07b7 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt @@ -14,6 +14,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustom import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.inputShape +// TODO(enableNewSmithyRuntime): Delete this file when cleaning up middleware. + class AccountIdAutofill : OperationCustomization() { override fun mutSelf(): Boolean = true override fun consumesSelf(): Boolean = false diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt index 8772de0ce9..91dece494e 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt @@ -13,6 +13,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustom import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.dq +// TODO(enableNewSmithyRuntime): Delete this file when cleaning up middleware. + class ApiVersionHeader( /** * ApiVersion diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt index 5ba71e2f20..0110331b48 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt @@ -6,21 +6,174 @@ package software.amazon.smithy.rustsdk.customize.glacier import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +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.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection +import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.letIf +import software.amazon.smithy.rustsdk.AwsCargoDependency +import software.amazon.smithy.rustsdk.InlineAwsDependency +/** + * Glacier has three required customizations: + * + * 1. Add the `x-amz-glacier-version` header to all requests + * 2. For operations with an `account_id` field, autofill that field with `-` if it is not set by the customer. + * This is required because the account ID is sent across as part of the URL, and `-` has been pre-established + * "no account ID" in the Glacier service. + * 3. The `UploadArchive` and `UploadMultipartPart` operations require tree hash headers to be + * calculated and added to the request. + * + * This decorator wires up these three customizations. + */ class GlacierDecorator : ClientCodegenDecorator { override val name: String = "Glacier" override val order: Byte = 0 + // TODO(enableNewSmithyRuntime): Delete the operation customizations when cleaning up middleware override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = baseCustomizations + listOfNotNull( - ApiVersionHeader(codegenContext.serviceShape.version), - TreeHashHeader.forOperation(operation, codegenContext.runtimeConfig), - AccountIdAutofill.forOperation(operation, codegenContext.model), - ) + ): List = baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateMiddleware) { + it + listOfNotNull( + ApiVersionHeader(codegenContext.serviceShape.version), + TreeHashHeader.forOperation(operation, codegenContext.runtimeConfig), + AccountIdAutofill.forOperation(operation, codegenContext.model), + ) + } + + override fun structureCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(GlacierAccountIdCustomization(codegenContext)) + } + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(GlacierApiVersionCustomization(codegenContext)) + } + + override fun operationRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(GlacierOperationInterceptorsCustomization(codegenContext)) + } +} + +/** Implements the `GlacierAccountId` trait for inputs that have an `account_id` field */ +private class GlacierAccountIdCustomization(private val codegenContext: ClientCodegenContext) : + StructureCustomization() { + override fun section(section: StructureSection): Writable = writable { + if (section is StructureSection.AdditionalTraitImpls && section.shape.inputWithAccountId()) { + val inlineModule = inlineModule(codegenContext.runtimeConfig) + rustTemplate( + """ + impl #{GlacierAccountId} for ${section.structName} { + fn account_id_mut(&mut self) -> &mut Option { + &mut self.account_id + } + } + """, + "GlacierAccountId" to inlineModule.resolve("GlacierAccountId"), + ) + } + } } + +/** Adds the `x-amz-glacier-version` header to all requests */ +private class GlacierApiVersionCustomization(private val codegenContext: ClientCodegenContext) : + ServiceRuntimePluginCustomization() { + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.AdditionalConfig) { + val apiVersion = codegenContext.serviceShape.version + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rustTemplate( + "#{Interceptor}::new(${apiVersion.dq()})", + "Interceptor" to inlineModule(codegenContext.runtimeConfig).resolve("GlacierApiVersionInterceptor"), + ) + } + } + } +} + +/** + * Adds two interceptors: + * 1. `GlacierAccountIdAutofillInterceptor`: Uses the `GlacierAccountId` trait to correctly autofill the account ID field + * 2. `GlacierSetPrecomputedSignableBodyInterceptor`: Reuses the tree hash computation during signing rather than allowing + * the `aws-sigv4` module to recalculate the payload hash. + */ +private class GlacierOperationInterceptorsCustomization(private val codegenContext: ClientCodegenContext) : + OperationRuntimePluginCustomization() { + override fun section(section: OperationRuntimePluginSection): Writable = writable { + if (section is OperationRuntimePluginSection.AdditionalConfig) { + val inputShape = codegenContext.model.expectShape(section.operationShape.inputShape) as StructureShape + val inlineModule = inlineModule(codegenContext.runtimeConfig) + if (inputShape.inputWithAccountId()) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rustTemplate( + "#{Interceptor}::<#{Input}>::new()", + "Interceptor" to inlineModule.resolve("GlacierAccountIdAutofillInterceptor"), + "Input" to codegenContext.symbolProvider.toSymbol(inputShape), + ) + } + } + if (section.operationShape.requiresTreeHashHeader()) { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rustTemplate( + "#{Interceptor}::default()", + "Interceptor" to inlineModule.resolve("GlacierTreeHashHeaderInterceptor"), + ) + } + } + } + } +} + +/** True when the operation requires tree hash headers */ +private fun OperationShape.requiresTreeHashHeader(): Boolean = + id == ShapeId.from("com.amazonaws.glacier#UploadArchive") || + id == ShapeId.from("com.amazonaws.glacier#UploadMultipartPart") + +private fun StructureShape.inputWithAccountId(): Boolean = + hasTrait() && members().any { it.memberName.lowercase() == "accountid" } + +private fun inlineModule(runtimeConfig: RuntimeConfig) = RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "glacier_interceptors", + additionalDependency = glacierInterceptorDependencies(runtimeConfig).toTypedArray(), + ), +) + +private fun glacierInterceptorDependencies(runtimeConfig: RuntimeConfig) = listOf( + AwsCargoDependency.awsRuntime(runtimeConfig), + AwsCargoDependency.awsSigv4(runtimeConfig), + CargoDependency.Bytes, + CargoDependency.Hex, + CargoDependency.Ring, + CargoDependency.smithyHttp(runtimeConfig), + CargoDependency.smithyRuntimeApi(runtimeConfig), +) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt index c97357a2fd..7a972b93aa 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt @@ -18,6 +18,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSectio import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rustsdk.InlineAwsDependency +// TODO(enableNewSmithyRuntime): Delete this file when cleaning up middleware. + val TreeHashDependencies = listOf( CargoDependency.Ring, CargoDependency.TokioStream, diff --git a/aws/sdk/integration-tests/glacier/tests/custom-headers.rs b/aws/sdk/integration-tests/glacier/tests/custom-headers.rs index 00163b0a81..941ed0a999 100644 --- a/aws/sdk/integration-tests/glacier/tests/custom-headers.rs +++ b/aws/sdk/integration-tests/glacier/tests/custom-headers.rs @@ -39,3 +39,49 @@ async fn set_correct_headers() { ], )); } + +#[tokio::test] +async fn autofill_account_id() { + let (conn, handler) = capture_request(None); + let conf = aws_sdk_glacier::Config::builder() + .region(Region::new("us-east-1")) + .credentials_provider(Credentials::for_tests()) + .http_connector(conn) + .build(); + + let client = aws_sdk_glacier::Client::from_conf(conf); + let _resp = client + .abort_multipart_upload() + .vault_name("vault") + .upload_id("some/upload/id") + .send() + .await; + let req = handler.expect_request(); + assert_eq!( + "/-/vaults/vault/multipart-uploads/some%2Fupload%2Fid", + req.uri().path() + ); +} + +#[tokio::test] +async fn api_version_set() { + let (conn, handler) = capture_request(None); + let conf = aws_sdk_glacier::Config::builder() + .region(Region::new("us-east-1")) + .credentials_provider(Credentials::for_tests()) + .http_connector(conn) + .build(); + + let client = aws_sdk_glacier::Client::from_conf(conf); + let _resp = client + .abort_multipart_upload() + .vault_name("vault") + .upload_id("some/upload/id") + .send() + .await; + let req = handler.expect_request(); + assert_ok(validate_headers( + req.headers(), + [("x-amz-glacier-version", "2012-06-01")], + )); +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt index 02f281dfd4..1c8a69bece 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt @@ -74,7 +74,12 @@ open class ClientProtocolGenerator( } } - renderOperationStruct(operationWriter, operationShape, operationCustomizations, codegenDecorator) + renderOperationStruct( + operationWriter, + operationShape, + operationCustomizations, + codegenDecorator, + ) } private fun renderOperationStruct( @@ -183,7 +188,7 @@ open class ClientProtocolGenerator( ResponseDeserializerGenerator(codegenContext, protocol) .render(operationWriter, operationShape, operationCustomizations) RequestSerializerGenerator(codegenContext, protocol, bodyGenerator) - .render(operationWriter, operationShape, operationCustomizations) + .render(operationWriter, operationShape) EndpointParamsInterceptorGenerator(codegenContext) .render(operationWriter, operationShape) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt index 74cac8c60e..22870147b5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt @@ -17,7 +17,6 @@ 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.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol @@ -52,7 +51,7 @@ class RequestSerializerGenerator( } } - fun render(writer: RustWriter, operationShape: OperationShape, customizations: List) { + fun render(writer: RustWriter, operationShape: OperationShape) { val inputShape = operationShape.inputShape(codegenContext.model) val operationName = symbolProvider.toSymbol(operationShape).name val inputSymbol = symbolProvider.toSymbol(inputShape) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index f3bfe104b5..d66f02c3da 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -99,7 +99,7 @@ class ResponseDeserializerGenerator( if !response.status().is_success() && response.status().as_u16() != $successCode { return None; } - Some(#{type_erase_result}(#{parse_streaming_response}(response)).into()) + Some(#{type_erase_result}(#{parse_streaming_response}(response))) } """, *codegenScope, @@ -118,7 +118,7 @@ class ResponseDeserializerGenerator( """ // For streaming operations, we only hit this case if its an error let body = response.body().bytes().expect("body loaded"); - #{type_erase_result}(#{parse_error}(response.status().as_u16(), response.headers(), body)).into() + #{type_erase_result}(#{parse_error}(response.status().as_u16(), response.headers(), body)) """, *codegenScope, "parse_error" to parserGenerator.parseErrorFn(operationShape, customizations), diff --git a/rust-runtime/aws-smithy-runtime-api/Cargo.toml b/rust-runtime/aws-smithy-runtime-api/Cargo.toml index 8fe513faf1..726b923482 100644 --- a/rust-runtime/aws-smithy-runtime-api/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime-api/Cargo.toml @@ -17,6 +17,7 @@ http-auth = ["dep:zeroize"] aws-smithy-async = { path = "../aws-smithy-async" } aws-smithy-http = { path = "../aws-smithy-http" } aws-smithy-types = { path = "../aws-smithy-types" } +bytes = "1" http = "0.2.3" tokio = { version = "1.25", features = ["sync"] } tracing = "0.1" diff --git a/rust-runtime/aws-smithy-runtime-api/external-types.toml b/rust-runtime/aws-smithy-runtime-api/external-types.toml index ab041b9f7e..91e6e35f0c 100644 --- a/rust-runtime/aws-smithy-runtime-api/external-types.toml +++ b/rust-runtime/aws-smithy-runtime-api/external-types.toml @@ -3,6 +3,8 @@ allowed_external_types = [ "aws_smithy_types::*", "aws_smithy_http::*", + "bytes::bytes::Bytes", + "http::request::Request", "http::response::Response", "http::uri::Uri", diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 314fd57249..3b9aaff2f3 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -17,6 +17,7 @@ use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_http::body::SdkBody; use aws_smithy_types::endpoint::Endpoint; +use bytes::Bytes; use std::fmt; use std::future::Future as StdFuture; use std::pin::Pin; @@ -100,6 +101,26 @@ impl RequestTime { } } +/// Informs the orchestrator on whether or not the request body needs to be loaded into memory before transmit. +/// +/// This enum gets placed into the `ConfigBag` to change the orchestrator behavior. +/// Immediately after serialization (before the `read_after_serialization` interceptor hook), +/// if it was set to `Requested` in the config bag, it will be replaced back into the config bag as +/// `Loaded` with the request body contents for use in later interceptors. +/// +/// This all happens before the attempt loop, so the loaded request body will remain available +/// for interceptors that run in any subsequent retry attempts. +#[non_exhaustive] +#[derive(Clone, Debug)] +pub enum LoadedRequestBody { + /// Don't attempt to load the request body into memory. + NotNeeded, + /// Attempt to load the request body into memory. + Requested, + /// The request body is already loaded. + Loaded(Bytes), +} + pub trait ConfigBagAccessors { fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams; fn set_auth_option_resolver_params( @@ -145,8 +166,13 @@ pub trait ConfigBagAccessors { fn sleep_impl(&self) -> Option>; fn set_sleep_impl(&mut self, async_sleep: Option>); + + fn loaded_request_body(&self) -> &LoadedRequestBody; + fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody); } +const NOT_NEEDED: LoadedRequestBody = LoadedRequestBody::NotNeeded; + impl ConfigBagAccessors for ConfigBag { fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams { self.get::() @@ -281,4 +307,12 @@ impl ConfigBagAccessors for ConfigBag { self.unset::>(); } } + + fn loaded_request_body(&self) -> &LoadedRequestBody { + self.get::().unwrap_or(&NOT_NEEDED) + } + + fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody) { + self.put::(loaded_request_body); + } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs index 36b12d9d29..2cc44a2647 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs @@ -115,3 +115,12 @@ impl From for OrchestratorError { Self::operation(err) } } + +impl From for OrchestratorError +where + E: Debug + std::error::Error + 'static, +{ + fn from(err: aws_smithy_http::byte_stream::error::Error) -> Self { + Self::other(err.into()) + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 72cd3cbef5..51cd3ee710 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -7,13 +7,18 @@ use self::auth::orchestrate_auth; use crate::client::orchestrator::endpoints::orchestrate_endpoint; use crate::client::orchestrator::http::read_body; use crate::client::timeout::{MaybeTimeout, ProvideMaybeTimeoutConfig, TimeoutKind}; +use aws_smithy_http::body::SdkBody; +use aws_smithy_http::byte_stream::ByteStream; use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output}; use aws_smithy_runtime_api::client::interceptors::{InterceptorContext, Interceptors}; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpResponse}; +use aws_smithy_runtime_api::client::orchestrator::{ + BoxError, ConfigBagAccessors, HttpResponse, LoadedRequestBody, +}; use aws_smithy_runtime_api::client::retries::ShouldAttempt; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; use aws_smithy_runtime_api::config_bag::ConfigBag; +use std::mem; use tracing::{debug_span, Instrument}; mod auth; @@ -22,14 +27,18 @@ pub mod endpoints; mod http; pub mod interceptors; +macro_rules! halt { + ([$ctx:ident] => $err:expr) => {{ + $ctx.fail($err.into()); + return; + }}; +} + macro_rules! halt_on_err { ([$ctx:ident] => $expr:expr) => { match $expr { Ok(ok) => ok, - Err(err) => { - $ctx.fail(err); - return; - } + Err(err) => halt!([$ctx] => err), } }; } @@ -37,7 +46,7 @@ macro_rules! halt_on_err { macro_rules! continue_on_err { ([$ctx:ident] => $expr:expr) => { if let Err(err) = $expr { - $ctx.fail(err); + $ctx.fail(err.into()); } }; } @@ -80,33 +89,41 @@ fn apply_configuration( runtime_plugins: &RuntimePlugins, ) -> Result<(), BoxError> { runtime_plugins.apply_client_configuration(cfg, interceptors.client_interceptors_mut())?; - continue_on_err!([ctx] =>interceptors.client_read_before_execution(ctx, cfg).map_err(Into::into)); + continue_on_err!([ctx] =>interceptors.client_read_before_execution(ctx, cfg)); runtime_plugins .apply_operation_configuration(cfg, interceptors.operation_interceptors_mut())?; - continue_on_err!([ctx] => interceptors.operation_read_before_execution(ctx, cfg).map_err(Into::into)); + continue_on_err!([ctx] => interceptors.operation_read_before_execution(ctx, cfg)); Ok(()) } async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: &Interceptors) { // Before serialization - halt_on_err!([ctx] => interceptors.read_before_serialization(ctx, cfg).map_err(Into::into)); - halt_on_err!([ctx] => interceptors.modify_before_serialization(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.read_before_serialization(ctx, cfg)); + halt_on_err!([ctx] => interceptors.modify_before_serialization(ctx, cfg)); // Serialization ctx.enter_serialization_phase(); { let request_serializer = cfg.request_serializer(); let input = ctx.take_input().expect("input set at this point"); - let request = - halt_on_err!([ctx] => request_serializer.serialize_input(input).map_err(Into::into)); + let request = halt_on_err!([ctx] => request_serializer.serialize_input(input)); ctx.set_request(request); } + // Load the request body into memory if configured to do so + if let LoadedRequestBody::Requested = cfg.loaded_request_body() { + let mut body = SdkBody::taken(); + mem::swap(&mut body, ctx.request_mut().body_mut()); + let loaded_body = halt_on_err!([ctx] => ByteStream::new(body).collect().await).into_bytes(); + *ctx.request_mut().body_mut() = SdkBody::from(loaded_body.clone()); + cfg.set_loaded_request_body(LoadedRequestBody::Loaded(loaded_body)); + } + // Before transmit ctx.enter_before_transmit_phase(); - halt_on_err!([ctx] => interceptors.read_after_serialization(ctx, cfg).map_err(Into::into)); - halt_on_err!([ctx] => interceptors.modify_before_retry_loop(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.read_after_serialization(ctx, cfg)); + halt_on_err!([ctx] => interceptors.modify_before_retry_loop(ctx, cfg)); let retry_strategy = cfg.retry_strategy(); match retry_strategy.should_attempt_initial_request(cfg) { @@ -114,11 +131,11 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: Ok(ShouldAttempt::Yes) => { /* Keep going */ } // No, this request shouldn't be sent Ok(ShouldAttempt::No) => { - let err: Box = "The retry strategy indicates that an initial request shouldn't be made, but it did specify why.".into(); - halt_on_err!([ctx] => Err(err.into())); + let err: BoxError = "The retry strategy indicates that an initial request shouldn't be made, but it did specify why.".into(); + halt!([ctx] => err); } // No, we shouldn't make a request because... - Err(err) => halt_on_err!([ctx] => Err(err.into())), + Err(err) => halt!([ctx] => err), Ok(ShouldAttempt::YesAfterDelay(_)) => { unreachable!("Delaying the initial request is currently unsupported. If this feature is important to you, please file an issue in GitHub.") } @@ -135,7 +152,7 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: .await .expect("These are infallible; The retry strategy will decide whether to stop or not."); let retry_strategy = cfg.retry_strategy(); - let should_attempt = halt_on_err!([ctx] => retry_strategy.should_attempt_retry(ctx, cfg).map_err(Into::into)); + let should_attempt = halt_on_err!([ctx] => retry_strategy.should_attempt_retry(ctx, cfg)); match should_attempt { // Yes, let's retry the request ShouldAttempt::Yes => continue, @@ -156,30 +173,30 @@ async fn try_attempt( cfg: &mut ConfigBag, interceptors: &Interceptors, ) { - halt_on_err!([ctx] => interceptors.read_before_attempt(ctx, cfg).map_err(Into::into)); - halt_on_err!([ctx] => orchestrate_endpoint(ctx, cfg).map_err(Into::into)); - halt_on_err!([ctx] => interceptors.modify_before_signing(ctx, cfg).map_err(Into::into)); - halt_on_err!([ctx] => interceptors.read_before_signing(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.read_before_attempt(ctx, cfg)); + halt_on_err!([ctx] => orchestrate_endpoint(ctx, cfg)); + halt_on_err!([ctx] => interceptors.modify_before_signing(ctx, cfg)); + halt_on_err!([ctx] => interceptors.read_before_signing(ctx, cfg)); - halt_on_err!([ctx] => orchestrate_auth(ctx, cfg).await.map_err(Into::into)); + halt_on_err!([ctx] => orchestrate_auth(ctx, cfg).await); - halt_on_err!([ctx] => interceptors.read_after_signing(ctx, cfg).map_err(Into::into)); - halt_on_err!([ctx] => interceptors.modify_before_transmit(ctx, cfg).map_err(Into::into)); - halt_on_err!([ctx] => interceptors.read_before_transmit(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.read_after_signing(ctx, cfg)); + halt_on_err!([ctx] => interceptors.modify_before_transmit(ctx, cfg)); + halt_on_err!([ctx] => interceptors.read_before_transmit(ctx, cfg)); // The connection consumes the request but we need to keep a copy of it // within the interceptor context, so we clone it here. ctx.enter_transmit_phase(); let call_result = halt_on_err!([ctx] => { let request = ctx.take_request(); - cfg.connection().call(request).await.map_err(Into::into) + cfg.connection().call(request).await }); ctx.set_response(call_result); ctx.enter_before_deserialization_phase(); - halt_on_err!([ctx] => interceptors.read_after_transmit(ctx, cfg).map_err(Into::into)); - halt_on_err!([ctx] => interceptors.modify_before_deserialization(ctx, cfg).map_err(Into::into)); - halt_on_err!([ctx] => interceptors.read_before_deserialization(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.read_after_transmit(ctx, cfg)); + halt_on_err!([ctx] => interceptors.modify_before_deserialization(ctx, cfg)); + halt_on_err!([ctx] => interceptors.read_before_deserialization(ctx, cfg)); ctx.enter_deserialization_phase(); let output_or_error = async { @@ -198,7 +215,7 @@ async fn try_attempt( ctx.set_output_or_error(output_or_error); ctx.enter_after_deserialization_phase(); - halt_on_err!([ctx] => interceptors.read_after_deserialization(ctx, cfg).map_err(Into::into)); + halt_on_err!([ctx] => interceptors.read_after_deserialization(ctx, cfg)); } async fn finally_attempt( @@ -206,8 +223,8 @@ async fn finally_attempt( cfg: &mut ConfigBag, interceptors: &Interceptors, ) { - continue_on_err!([ctx] => interceptors.modify_before_attempt_completion(ctx, cfg).map_err(Into::into)); - continue_on_err!([ctx] => interceptors.read_after_attempt(ctx, cfg).map_err(Into::into)); + continue_on_err!([ctx] => interceptors.modify_before_attempt_completion(ctx, cfg)); + continue_on_err!([ctx] => interceptors.read_after_attempt(ctx, cfg)); } async fn finally_op( @@ -215,8 +232,8 @@ async fn finally_op( cfg: &mut ConfigBag, interceptors: &Interceptors, ) { - continue_on_err!([ctx] => interceptors.modify_before_completion(ctx, cfg).map_err(Into::into)); - continue_on_err!([ctx] => interceptors.read_after_execution(ctx, cfg).map_err(Into::into)); + continue_on_err!([ctx] => interceptors.modify_before_completion(ctx, cfg)); + continue_on_err!([ctx] => interceptors.read_after_execution(ctx, cfg)); } #[cfg(all(test, feature = "test-util", feature = "anonymous-auth"))] diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index 8cd7d32e45..483e3075bf 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -23,7 +23,6 @@ services_that_fail_compile=(\ # TODO(enableNewSmithyRuntime): Move these into `services_that_pass_tests` as more progress is made services_that_compile=(\ "dynamodb"\ - "glacier"\ "route53"\ "sts"\ ) @@ -32,6 +31,7 @@ services_that_pass_tests=(\ "config"\ "ec2"\ "ecs"\ + "glacier"\ "iam"\ "kms"\ "lambda"\ From 529024116a39974759faf0d699c736c0c0dbe8a8 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 25 May 2023 17:39:17 -0700 Subject: [PATCH 111/253] Merge changelog from the `smithy-rs-release-0.55.x` branch (#2731) ## Motivation and Context This PR merges the changelog changes made by the release script from the `smithy-rs-release-0.55.x` branch. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ Co-authored-by: AWS SDK Rust Bot --- CHANGELOG.md | 42 +++++++ CHANGELOG.next.toml | 25 ---- aws/SDK_CHANGELOG.next.json | 222 +++++++++++++++++++++++++++++++++--- 3 files changed, 245 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4eaa57e2f..495ef2143f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,46 @@ +May 23rd, 2023 +============== +**New this release:** +- (all, [smithy-rs#2612](https://github.com/awslabs/smithy-rs/issues/2612)) The `Debug` implementation for `PropertyBag` now prints a list of the types it contains. This significantly improves debuggability. +- (all, [smithy-rs#2653](https://github.com/awslabs/smithy-rs/issues/2653), [smithy-rs#2656](https://github.com/awslabs/smithy-rs/issues/2656), @henriiik) Implement `Ord` and `PartialOrd` for `DateTime`. +- 🐛 (client, [smithy-rs#2696](https://github.com/awslabs/smithy-rs/issues/2696)) Fix compiler errors in generated code when naming shapes after types in the Rust standard library prelude. + +**Contributors** +Thank you for your contributions! ❤ +- @henriiik ([smithy-rs#2653](https://github.com/awslabs/smithy-rs/issues/2653), [smithy-rs#2656](https://github.com/awslabs/smithy-rs/issues/2656)) + + +April 26th, 2023 +================ +**Breaking Changes:** +- ⚠ (all, [smithy-rs#2611](https://github.com/awslabs/smithy-rs/issues/2611)) Update MSRV to Rust 1.67.1 + +**New this release:** +- 🎉 (server, [smithy-rs#2540](https://github.com/awslabs/smithy-rs/issues/2540)) Implement layer for servers to handle [ALB health checks](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/target-group-health-checks.html). + Take a look at `aws_smithy_http_server::plugin::alb_health_check` to learn about it. +- 🎉 (client, [smithy-rs#2254](https://github.com/awslabs/smithy-rs/issues/2254), @eduardomourar) Clients now compile for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it! +- (server, [smithy-rs#2540](https://github.com/awslabs/smithy-rs/issues/2540)) Implement `PluginPipeline::http_layer` which allows you to apply a `tower::Layer` to all operations. +- (client, [aws-sdk-rust#784](https://github.com/awslabs/aws-sdk-rust/issues/784), @abusch) Implement std::error::Error#source() properly for the service meta Error enum. +- 🐛 (all, [smithy-rs#2496](https://github.com/awslabs/smithy-rs/issues/2496)) The outputs for event stream operations now implement the `Sync` auto-trait. +- 🐛 (all, [smithy-rs#2495](https://github.com/awslabs/smithy-rs/issues/2495)) Streaming operations now emit the request ID at the `debug` log level like their non-streaming counterparts. +- 🐛 (client, [smithy-rs#2495](https://github.com/awslabs/smithy-rs/issues/2495)) Streaming operations now emit the request ID at the `debug` log level like their non-streaming counterparts. +- (client, [smithy-rs#2507](https://github.com/awslabs/smithy-rs/issues/2507)) The `enableNewCrateOrganizationScheme` codegen flag has been removed. If you opted out of the new crate organization scheme, it must be adopted now in order to upgrade (see [the upgrade guidance](https://github.com/awslabs/smithy-rs/discussions/2449) from March 23rd's release). +- (client, [smithy-rs#2534](https://github.com/awslabs/smithy-rs/issues/2534)) `aws_smithy_types::date_time::Format` has been re-exported in service client crates. +- 🐛 (server, [smithy-rs#2582](https://github.com/awslabs/smithy-rs/issues/2582), [smithy-rs#2585](https://github.com/awslabs/smithy-rs/issues/2585)) Fix generation of constrained shapes reaching `@sensitive` shapes +- 🐛 (server, [smithy-rs#2583](https://github.com/awslabs/smithy-rs/issues/2583), [smithy-rs#2584](https://github.com/awslabs/smithy-rs/issues/2584)) Fix server code generation bug affecting constrained shapes bound with `@httpPayload` +- (client, [smithy-rs#2603](https://github.com/awslabs/smithy-rs/issues/2603)) Add a sensitive method to `ParseHttpResponse`. When this returns true, logging of the HTTP response body will be suppressed. + +**Contributors** +Thank you for your contributions! ❤ +- @abusch ([aws-sdk-rust#784](https://github.com/awslabs/aws-sdk-rust/issues/784)) +- @eduardomourar ([smithy-rs#2254](https://github.com/awslabs/smithy-rs/issues/2254)) + + +April 11th, 2023 +================ + + March 23rd, 2023 ================ **Breaking Changes:** diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 49fe59767a..9ec3266e3d 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -11,31 +11,6 @@ # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} # author = "rcoh" -[[smithy-rs]] -message = "The `Debug` implementation for `PropertyBag` now prints a list of the types it contains. This significantly improves debuggability." -author = "rcoh" -references = ["smithy-rs#2612"] -meta = { "breaking" = false, "tada" = false, "bug" = false } - - -[[smithy-rs]] -message = "Implement `Ord` and `PartialOrd` for `DateTime`." -author = "henriiik" -references = ["smithy-rs#2653", "smithy-rs#2656"] -meta = { "breaking" = false, "tada" = false, "bug" = false } - -[[aws-sdk-rust]] -message = "Avoid extending IMDS credentials' expiry unconditionally, which may incorrectly extend it beyond what is originally defined; If returned credentials are not stale, use them as they are." -references = ["smithy-rs#2687", "smithy-rs#2694"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "ysaito1001" - -[[smithy-rs]] -message = "Fix compiler errors in generated code when naming shapes after types in the Rust standard library prelude." -references = ["smithy-rs#2696"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client"} -author = "jdisanti" - [[aws-sdk-rust]] message = "Fix error message when `credentials-sso` feature is not enabled on `aws-config`. NOTE: if you use `no-default-features`, you will need to manually able `credentials-sso` after 0.55.*" references = ["smithy-rs#2722", "aws-sdk-rust#703"] diff --git a/aws/SDK_CHANGELOG.next.json b/aws/SDK_CHANGELOG.next.json index 6d3e509f8e..1beee31b1e 100644 --- a/aws/SDK_CHANGELOG.next.json +++ b/aws/SDK_CHANGELOG.next.json @@ -104,7 +104,7 @@ "smithy-rs#2227" ], "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 3 + "age": 5 }, { "message": "The introduction of `CredentialsCache` comes with an accompanying type `SharedCredentialsCache`, which we will store in the property bag instead of a `SharedCredentialsProvider`. As a result, `aws_http::auth:set_provider` has been updated to `aws_http::auth::set_credentials_cache`.\n\nBefore:\n```rust\nuse aws_credential_types::Credentials;\nuse aws_credential_types::provider::SharedCredentialsProvider;\nuse aws_http::auth::set_provider;\nuse aws_smithy_http::body::SdkBody;\nuse aws_smithy_http::operation;\n\nlet mut req = operation::Request::new(http::Request::new(SdkBody::from(\"some body\")));\nlet credentials = Credentials::new(\"example\", \"example\", None, None, \"my_provider_name\");\nset_provider(\n &mut req.properties_mut(),\n SharedCredentialsProvider::new(credentials),\n);\n```\n\nAfter:\n```rust\nuse aws_credential_types::Credentials;\nuse aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache};\nuse aws_credential_types::provider::SharedCredentialsProvider;\nuse aws_http::auth::set_credentials_cache;\nuse aws_smithy_http::body::SdkBody;\nuse aws_smithy_http::operation;\n\nlet mut req = operation::Request::new(http::Request::new(SdkBody::from(\"some body\")));\nlet credentials = Credentials::new(\"example\", \"example\", None, None, \"my_provider_name\");\nlet credentials_cache = CredentialsCache::lazy_builder()\n .into_credentials_cache()\n .create_cache(SharedCredentialsProvider::new(credentials));\nset_credentials_cache(\n &mut req.properties_mut(),\n SharedCredentialsCache::new(credentials_cache),\n);\n```\n", @@ -119,7 +119,7 @@ "smithy-rs#2227" ], "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 3 + "age": 5 }, { "message": "Fix endpoint for s3.write_get_object_response(). This bug was introduced in 0.53.", @@ -133,7 +133,7 @@ "smithy-rs#2204" ], "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 3 + "age": 5 }, { "message": "Add `with_test_defaults()` and `set_test_defaults()` to `::Config`. These methods fill in defaults for configuration that is mandatory to successfully send a request.", @@ -147,7 +147,7 @@ "smithy-rs#2204" ], "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 3 + "age": 5 }, { "message": "Request IDs can now be easily retrieved on successful responses. For example, with S3:\n```rust\n// Import the trait to get the `request_id` method on outputs\nuse aws_sdk_s3::types::RequestId;\nlet output = client.list_buckets().send().await?;\nprintln!(\"Request ID: {:?}\", output.request_id());\n```\n", @@ -162,7 +162,7 @@ "smithy-rs#2129" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "Retrieving a request ID from errors now requires importing the `RequestId` trait. For example, with S3:\n```rust\nuse aws_sdk_s3::types::RequestId;\nprintln!(\"Request ID: {:?}\", error.request_id());\n```\n", @@ -177,7 +177,7 @@ "smithy-rs#2129" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "The `message()` and `code()` methods on errors have been moved into `ProvideErrorMetadata` trait. This trait will need to be imported to continue calling these.", @@ -192,7 +192,7 @@ "smithy-rs#2129" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "The `*Error` and `*ErrorKind` types have been combined to make error matching simpler.\n
\nExample with S3\n**Before:**\n```rust\nlet result = client\n .get_object()\n .bucket(BUCKET_NAME)\n .key(\"some-key\")\n .send()\n .await;\nmatch result {\n Ok(_output) => { /* Do something with the output */ }\n Err(err) => match err.into_service_error() {\n GetObjectError { kind, .. } => match kind {\n GetObjectErrorKind::InvalidObjectState(value) => println!(\"invalid object state: {:?}\", value),\n GetObjectErrorKind::NoSuchKey(_) => println!(\"object didn't exist\"),\n }\n err @ GetObjectError { .. } if err.code() == Some(\"SomeUnmodeledError\") => {}\n err @ _ => return Err(err.into()),\n },\n}\n```\n**After:**\n```rust\n// Needed to access the `.code()` function on the error type:\nuse aws_sdk_s3::types::ProvideErrorMetadata;\nlet result = client\n .get_object()\n .bucket(BUCKET_NAME)\n .key(\"some-key\")\n .send()\n .await;\nmatch result {\n Ok(_output) => { /* Do something with the output */ }\n Err(err) => match err.into_service_error() {\n GetObjectError::InvalidObjectState(value) => {\n println!(\"invalid object state: {:?}\", value);\n }\n GetObjectError::NoSuchKey(_) => {\n println!(\"object didn't exist\");\n }\n err if err.code() == Some(\"SomeUnmodeledError\") => {}\n err @ _ => return Err(err.into()),\n },\n}\n```\n
\n", @@ -208,7 +208,7 @@ "smithy-rs#2075" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "`aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`.", @@ -223,7 +223,7 @@ "smithy-rs#2129" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "Fluent builder methods on the client are now marked as deprecated when the related operation is deprecated.", @@ -237,7 +237,7 @@ "aws-sdk-rust#740" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "`SdkError` variants can now be constructed for easier unit testing.", @@ -252,7 +252,7 @@ "smithy-rs#2208" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "Add more client re-exports. Specifically, it re-exports `aws_smithy_http::body::SdkBody`, `aws_smithy_http::byte_stream::error::Error`, and `aws_smithy_http::operation::{Request, Response}`.", @@ -267,7 +267,7 @@ "aws-sdk-rust#600" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "Enable presigning for S3's `HeadObject` operation.", @@ -282,7 +282,7 @@ "smithy-rs#2451" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "The modules in the SDK crates have been reorganized. See the [SDK Crate Reorganization Upgrade Guidance](https://github.com/awslabs/aws-sdk-rust/discussions/752) to see how to fix your code after this change.", @@ -296,7 +296,7 @@ "smithy-rs#2433" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "Reconnect on transient errors.\n\nIf a transient error (timeout, 500, 503, 503) is encountered, the connection will be evicted from the pool and will not\nbe reused. This is enabled by default for all AWS services. It can be disabled by setting `RetryConfig::with_reconnect_mode`\n\nAlthough there is no API breakage from this change, it alters the client behavior in a way that may cause breakage for customers.\n", @@ -311,7 +311,7 @@ "smithy-rs#2445" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "Update MSRV to 1.66.1", @@ -325,7 +325,7 @@ "smithy-rs#2467" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "Default connector provided by `aws-config` now respects `ConnectorSettings`.\n\nPreviously, it used the timeout settings provided by aws-config. A test from @Oliboy50 has been incorporated to verify this behavior.\n\n**Behavior Change**: Prior to this change, the Hyper client would be shared between all service clients. After this change, each service client will use its own Hyper Client.\nTo revert to the previous behavior, set `HttpConnector::Prebuilt` on `SdkConfig::http_connector`.\n", @@ -341,7 +341,7 @@ "smithy-rs#2151" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "Remove deprecated `ResolveAwsEndpoint` interfaces.\n[For details see the longform changelog entry](https://github.com/awslabs/aws-sdk-rust/discussions/755).\n", @@ -356,7 +356,7 @@ "smithy-rs#1784" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 1 + "age": 3 }, { "message": "Increase Tokio version to 1.23.1 for all crates. This is to address [RUSTSEC-2023-0001](https://rustsec.org/advisories/RUSTSEC-2023-0001)", @@ -370,8 +370,192 @@ "smithy-rs#2474" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", + "age": 3 + }, + { + "message": "Implement std::error::Error#source() properly for the service meta Error enum.", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "abusch", + "references": [ + "aws-sdk-rust#784" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "The outputs for event stream operations (for example, S3's SelectObjectContent) now implement the `Sync` auto-trait.", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "jdisanti", + "references": [ + "smithy-rs#2496" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "The AWS SDK now compiles for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it!", + "meta": { + "bug": false, + "breaking": false, + "tada": true + }, + "author": "eduardomourar", + "references": [ + "smithy-rs#2254" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "S3's `GetObject` will no longer panic when checksum validation is enabled and the target object was uploaded as a multi-part upload.\nHowever, these objects cannot be checksum validated by the SDK due to the way checksums are calculated for multipart uploads.\nFor more information, see [this page](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#large-object-checksums).\n", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "Velfi", + "references": [ + "aws-sdk-rust#764" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "`AppName` is now configurable from within `ConfigLoader`.", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "ysaito1001", + "references": [ + "smithy-rs#2513" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "Add support for omitting session token in canonical requests for SigV4 signing.", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "martinjlowm", + "references": [ + "smithy-rs#2473" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "Add `into_segments` method to `AggregatedBytes`, for zero-copy conversions.", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "parker-timmerman", + "references": [ + "smithy-rs#2525" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "Fix bug where an incorrect endpoint was produced for `WriteGetObjectResponse`", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "rcoh", + "references": [ + "smithy-rs#781", + "aws-sdk-rust#781" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "Update the `std::fmt::Debug` implementation for `aws-sigv4::SigningParams` so that it will no longer print sensitive information.", + "meta": { + "bug": true, + "breaking": false, + "tada": true + }, + "author": "Velfi", + "references": [ + "smithy-rs#2562" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "`aws_smithy_types::date_time::Format` has been re-exported in SDK crates.", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "ysaito1001", + "references": [ + "smithy-rs#2534" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "Reduce several instances of credential exposure in the SDK logs:\n- IMDS now suppresses the body of the response from logs\n- `aws-sigv4` marks the `x-amz-session-token` header as sensitive\n- STS & SSO credentials have been manually marked as sensitive which suppresses logging of response bodies for relevant operations\n", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "rcoh", + "references": [ + "smithy-rs#2603" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "Update MSRV to Rust 1.67.1", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "jdisanti", + "references": [ + "smithy-rs#2611" + ], + "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", + "age": 2 + }, + { + "message": "Avoid extending IMDS credentials' expiry unconditionally, which may incorrectly extend it beyond what is originally defined; If returned credentials are not stale, use them as they are.", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "ysaito1001", + "references": [ + "smithy-rs#2687", + "smithy-rs#2694" + ], + "since-commit": "3b5fc51a41700c88270145e38fa708eca72dc414", "age": 1 } ], "aws-sdk-model": [] -} \ No newline at end of file +} From d083c6f271f4e8c2cf1fb35e5fd57945ece361cd Mon Sep 17 00:00:00 2001 From: 82marbag <69267416+82marbag@users.noreply.github.com> Date: Fri, 26 May 2023 11:18:29 +0100 Subject: [PATCH 112/253] remove native-tls (#2675) * Show how to plug a connector * For TLS: keep rustls, only ## Checklist - [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._ --------- Signed-off-by: Daniele Ahmed --- .github/workflows/ci.yml | 10 +- CHANGELOG.next.toml | 12 +++ aws/rust-runtime/aws-config/Cargo.toml | 3 +- aws/rust-runtime/aws-config/src/connector.rs | 19 ++-- .../src/default_provider/credentials.rs | 6 +- .../aws-config/src/imds/client.rs | 2 +- .../aws-config/src/imds/credentials.rs | 6 +- .../aws-config/src/meta/credentials/chain.rs | 4 +- .../aws-config/src/profile/credentials.rs | 4 +- .../aws-config/src/provider_config.rs | 2 +- .../tests/middleware_e2e_test.rs | 2 +- aws/rust-runtime/aws-types/Cargo.toml | 4 +- .../rustsdk/AwsFluentClientDecorator.kt | 7 +- aws/sdk/integration-tests/Cargo.toml | 1 - .../tests/client-construction.rs | 4 +- .../Cargo.toml | 20 ---- .../tests/no-rustls-in-dependency.rs | 52 --------- .../client/FluentClientDecorator.kt | 1 - design/src/transport/connector.md | 39 +++++++ rust-runtime/aws-smithy-client/Cargo.toml | 9 +- rust-runtime/aws-smithy-client/additional-ci | 2 +- .../aws-smithy-client/external-types.toml | 2 +- rust-runtime/aws-smithy-client/src/builder.rs | 43 ++------ rust-runtime/aws-smithy-client/src/conns.rs | 100 +++++++++--------- .../aws-smithy-client/src/hyper_ext.rs | 17 +-- rust-runtime/aws-smithy-client/src/lib.rs | 6 +- tools/ci-scripts/check-aws-config | 2 +- 27 files changed, 160 insertions(+), 219 deletions(-) delete mode 100644 aws/sdk/integration-tests/using-native-tls-instead-of-rustls/Cargo.toml delete mode 100644 aws/sdk/integration-tests/using-native-tls-instead-of-rustls/tests/no-rustls-in-dependency.rs create mode 100644 design/src/transport/connector.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb0d9e5207..a3ce1bd51c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -211,8 +211,6 @@ jobs: matrix: include: # We always exclude aws-smithy-http-server-python since the Python framework is experimental. - # We only build the `native-tls` feature here because `rustls` depends on `ring` which in turn - # does not support powerpc as a target platform (see https://github.com/briansmith/ring/issues/389) - target: i686-unknown-linux-gnu build_smithy_rs_features: --all-features build_aws_exclude: '' @@ -221,17 +219,17 @@ jobs: test_aws_exclude: '' test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript - target: powerpc-unknown-linux-gnu - build_smithy_rs_features: --features native-tls + build_smithy_rs_features: '' build_aws_exclude: --exclude aws-inlineable build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript - test_smithy_rs_features: --features native-tls + test_smithy_rs_features: '' test_aws_exclude: --exclude aws-inlineable test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript - target: powerpc64-unknown-linux-gnu - build_smithy_rs_features: --features native-tls + build_smithy_rs_features: '' build_aws_exclude: --exclude aws-inlineable build_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript - test_smithy_rs_features: --features native-tls + test_smithy_rs_features: '' test_aws_exclude: --exclude aws-inlineable test_smithy_rs_exclude: --exclude aws-smithy-http-server-python --exclude aws-smithy-http-server-typescript env: diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 9ec3266e3d..f6dec54418 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -11,6 +11,18 @@ # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} # author = "rcoh" +[[aws-sdk-rust]] +message = "Remove native-tls and add a migration guide." +author = "82marbag" +references = ["smithy-rs#2675"] +meta = { "breaking" = true, "tada" = false, "bug" = false } + +[[smithy-rs]] +message = "Remove native-tls and add a migration guide." +author = "82marbag" +references = ["smithy-rs#2675"] +meta = { "breaking" = true, "tada" = false, "bug" = false } + [[aws-sdk-rust]] message = "Fix error message when `credentials-sso` feature is not enabled on `aws-config`. NOTE: if you use `no-default-features`, you will need to manually able `credentials-sso` after 0.55.*" references = ["smithy-rs#2722", "aws-sdk-rust#703"] diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index 9fb7ff39d4..2a350f3ef2 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -11,7 +11,8 @@ repository = "https://github.com/awslabs/smithy-rs" [features] client-hyper = ["aws-smithy-client/client-hyper"] rustls = ["aws-smithy-client/rustls"] -native-tls = ["aws-smithy-client/native-tls"] +native-tls = [] +allow-compilation = [] # our tests use `cargo test --all-features` and native-tls breaks CI rt-tokio = ["aws-smithy-async/rt-tokio", "tokio/rt"] credentials-sso = ["dep:aws-sdk-sso", "dep:ring", "dep:hex", "dep:zeroize"] diff --git a/aws/rust-runtime/aws-config/src/connector.rs b/aws/rust-runtime/aws-config/src/connector.rs index f03d44ab6e..82bda6e02e 100644 --- a/aws/rust-runtime/aws-config/src/connector.rs +++ b/aws/rust-runtime/aws-config/src/connector.rs @@ -13,10 +13,13 @@ use std::sync::Arc; // unused when all crate features are disabled /// Unwrap an [`Option`](aws_smithy_client::erase::DynConnector), and panic with a helpful error message if it's `None` pub(crate) fn expect_connector(connector: Option) -> DynConnector { - connector.expect("No HTTP connector was available. Enable the `rustls` or `native-tls` crate feature or set a connector to fix this.") + connector.expect("No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this.") } -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(all(feature = "native-tls", not(feature = "allow-compilation")))] +compile_error!("Feature native-tls has been removed. For upgrade instructions, see: https://awslabs.github.io/smithy-rs/design/transport/connector.html"); + +#[cfg(feature = "rustls")] fn base( settings: &ConnectorSettings, sleep: Option>, @@ -41,17 +44,7 @@ pub fn default_connector( } /// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. -#[cfg(all(not(feature = "rustls"), feature = "native-tls"))] -pub fn default_connector( - settings: &ConnectorSettings, - sleep: Option>, -) -> Option { - let hyper = base(settings, sleep).build(aws_smithy_client::conns::native_tls()); - Some(DynConnector::new(hyper)) -} - -/// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. -#[cfg(not(any(feature = "rustls", feature = "native-tls")))] +#[cfg(not(feature = "rustls"))] pub fn default_connector( _settings: &ConnectorSettings, _sleep: Option>, diff --git a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs index c1d48c584f..db749d28e9 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs @@ -14,7 +14,7 @@ use crate::meta::credentials::CredentialsProviderChain; use crate::meta::region::ProvideRegion; use crate::provider_config::ProviderConfig; -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] /// Default Credentials Provider chain /// /// The region from the default region provider will be used @@ -170,8 +170,8 @@ impl Builder { /// Creates a `DefaultCredentialsChain` /// /// ## Panics - /// This function will panic if no connector has been set and neither `rustls` and `native-tls` - /// features have both been disabled. + /// This function will panic if no connector has been set or the `rustls` + /// feature has been disabled. pub async fn build(self) -> DefaultCredentialsChain { let region = match self.region_override { Some(provider) => provider.region().await, diff --git a/aws/rust-runtime/aws-config/src/imds/client.rs b/aws/rust-runtime/aws-config/src/imds/client.rs index 05e7236cbd..e73c6454ac 100644 --- a/aws/rust-runtime/aws-config/src/imds/client.rs +++ b/aws/rust-runtime/aws-config/src/imds/client.rs @@ -933,7 +933,7 @@ pub(crate) mod test { /// Verify that the end-to-end real client has a 1-second connect timeout #[tokio::test] - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] async fn one_second_connect_timeout() { use crate::imds::client::ImdsError; use aws_smithy_types::error::display::DisplayErrorContext; diff --git a/aws/rust-runtime/aws-config/src/imds/credentials.rs b/aws/rust-runtime/aws-config/src/imds/credentials.rs index fd7fd02bab..83265c37a5 100644 --- a/aws/rust-runtime/aws-config/src/imds/credentials.rs +++ b/aws/rust-runtime/aws-config/src/imds/credentials.rs @@ -444,7 +444,7 @@ mod test { } #[tokio::test] - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] async fn read_timeout_during_credentials_refresh_should_yield_last_retrieved_credentials() { let client = crate::imds::Client::builder() // 240.* can never be resolved @@ -463,7 +463,7 @@ mod test { } #[tokio::test] - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] async fn read_timeout_during_credentials_refresh_should_error_without_last_retrieved_credentials( ) { let client = crate::imds::Client::builder() @@ -484,7 +484,7 @@ mod test { } #[tokio::test] - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] async fn external_timeout_during_credentials_refresh_should_yield_last_retrieved_credentials() { use aws_smithy_async::rt::sleep::AsyncSleep; let client = crate::imds::Client::builder() diff --git a/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs b/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs index 193d9fb1ec..732d2b08fd 100644 --- a/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs +++ b/aws/rust-runtime/aws-config/src/meta/credentials/chain.rs @@ -60,7 +60,7 @@ impl CredentialsProviderChain { } /// Add a fallback to the default provider chain - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] pub async fn or_default_provider(self) -> Self { self.or_else( "DefaultProviderChain", @@ -69,7 +69,7 @@ impl CredentialsProviderChain { } /// Creates a credential provider chain that starts with the default provider - #[cfg(any(feature = "rustls", feature = "native-tls"))] + #[cfg(feature = "rustls")] pub async fn default_provider() -> Self { Self::first_try( "DefaultProviderChain", diff --git a/aws/rust-runtime/aws-config/src/profile/credentials.rs b/aws/rust-runtime/aws-config/src/profile/credentials.rs index 928f164d79..774d5785a6 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials.rs @@ -98,7 +98,7 @@ impl ProvideCredentials for ProfileFileCredentialsProvider { /// future::ProvideCredentials::new(self.load_credentials()) /// } /// } -/// # if cfg!(any(feature = "rustls", feature = "native-tls")) { +/// # if cfg!(feature = "rustls") { /// let provider = ProfileFileCredentialsProvider::builder() /// .with_custom_provider("Custom", MyCustomProvider) /// .build(); @@ -376,7 +376,7 @@ impl Builder { /// } /// } /// - /// # if cfg!(any(feature = "rustls", feature = "native-tls")) { + /// # if cfg!(feature = "rustls") { /// let provider = ProfileFileCredentialsProvider::builder() /// .with_custom_provider("Custom", MyCustomProvider) /// .build(); diff --git a/aws/rust-runtime/aws-config/src/provider_config.rs b/aws/rust-runtime/aws-config/src/provider_config.rs index cb4bd5aa17..57fa11a621 100644 --- a/aws/rust-runtime/aws-config/src/provider_config.rs +++ b/aws/rust-runtime/aws-config/src/provider_config.rs @@ -121,7 +121,7 @@ impl ProviderConfig { /// /// # Examples /// ```no_run - /// # #[cfg(any(feature = "rustls", feature = "native-tls"))] + /// # #[cfg(feature = "rustls")] /// # fn example() { /// use aws_config::provider_config::ProviderConfig; /// use aws_sdk_sts::config::Region; diff --git a/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs b/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs index 29db9af0fa..3aefdf5093 100644 --- a/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs +++ b/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs @@ -104,7 +104,7 @@ fn test_operation() -> Operation builder.connector(c), None =>{ - ##[cfg(any(feature = "rustls", feature = "native-tls"))] + ##[cfg(feature = "rustls")] { // Use default connector based on enabled features builder.dyn_https_connector(#{ConnectorSettings}::from_timeout_config(&timeout_config)) } - ##[cfg(not(any(feature = "rustls", feature = "native-tls")))] + ##[cfg(not(feature = "rustls"))] { - panic!("No HTTP connector was available. Enable the `rustls` or `native-tls` crate feature or set a connector to fix this."); + panic!("No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this."); } } }; diff --git a/aws/sdk/integration-tests/Cargo.toml b/aws/sdk/integration-tests/Cargo.toml index 7410b5f824..74a6f261dc 100644 --- a/aws/sdk/integration-tests/Cargo.toml +++ b/aws/sdk/integration-tests/Cargo.toml @@ -15,6 +15,5 @@ members = [ "s3control", "sts", "transcribestreaming", - "using-native-tls-instead-of-rustls", "webassembly", ] diff --git a/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs b/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs index 8f250f9322..64f0137b24 100644 --- a/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs +++ b/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs @@ -6,7 +6,7 @@ // This will fail due to lack of a connector when constructing the SDK Config #[tokio::test] #[should_panic( - expected = "No HTTP connector was available. Enable the `rustls` or `native-tls` crate feature or set a connector to fix this." + expected = "No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this." )] async fn test_clients_from_sdk_config() { aws_config::load_from_env().await; @@ -15,7 +15,7 @@ async fn test_clients_from_sdk_config() { // This will fail due to lack of a connector when constructing the service client #[tokio::test] #[should_panic( - expected = "No HTTP connector was available. Enable the `rustls` or `native-tls` crate feature or set a connector to fix this." + expected = "No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this." )] async fn test_clients_from_service_config() { #[derive(Clone, Debug)] diff --git a/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/Cargo.toml b/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/Cargo.toml deleted file mode 100644 index 3642d7ba24..0000000000 --- a/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "using-native-tls-instead-of-rustls" -version = "0.1.0" -authors = ["AWS Rust SDK Team "] -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dev-dependencies] -# aws-config pulls in rustls and several other things by default. We have to disable defaults in order to use native-tls -# and then manually bring the other defaults back -aws-config = { path = "../../build/aws-sdk/sdk/aws-config", default-features = false, features = [ - "native-tls", - "rt-tokio", -] } -# aws-sdk-s3 brings in rustls by default so we disable that in order to use native-tls only -aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3", default-features = false, features = [ - "native-tls", -] } -tokio = { version = "1.20.1", features = ["rt", "macros"] } diff --git a/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/tests/no-rustls-in-dependency.rs b/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/tests/no-rustls-in-dependency.rs deleted file mode 100644 index 1df97865db..0000000000 --- a/aws/sdk/integration-tests/using-native-tls-instead-of-rustls/tests/no-rustls-in-dependency.rs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -/// The SDK defaults to using RusTLS by default but you can also use [`native_tls`](https://github.com/sfackler/rust-native-tls) -/// which will choose a TLS implementation appropriate for your platform. This test looks much like -/// any other. Activating and deactivating `features` in your app's `Cargo.toml` is all that's needed. - -async fn list_buckets() -> Result<(), aws_sdk_s3::Error> { - let sdk_config = aws_config::load_from_env().await; - let client = aws_sdk_s3::Client::new(&sdk_config); - - let _resp = client.list_buckets().send().await?; - - Ok(()) -} - -/// You can run this test to ensure that it is only using `native-tls` and -/// that nothing is pulling in `rustls` as a dependency -#[test] -#[should_panic = "error: package ID specification `rustls` did not match any packages"] -fn test_rustls_is_not_in_dependency_tree() { - let cargo_location = std::env::var("CARGO").unwrap(); - let cargo_command = std::process::Command::new(cargo_location) - .arg("tree") - .arg("--invert") - .arg("rustls") - .output() - .expect("failed to run 'cargo tree'"); - - let stderr = String::from_utf8_lossy(&cargo_command.stderr); - - // We expect the call to `cargo tree` to error out. If it did, we panic with the resulting - // message here. In the case that no error message is set, that's bad. - if !stderr.is_empty() { - panic!("{}", stderr); - } - - // Uh oh. We expected an error message but got none, likely because `cargo tree` found - // `rustls` in our dependencies. We'll print out the message we got to see what went wrong. - let stdout = String::from_utf8_lossy(&cargo_command.stdout); - - println!("{}", stdout) -} - -// NOTE: not currently run in CI, separate PR will set up a with-creds CI runner -#[tokio::test] -#[ignore] -async fn needs_creds_native_tls_works() { - list_buckets().await.expect("should succeed") -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt index 267b5bbb46..9775a342fb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientDecorator.kt @@ -57,7 +57,6 @@ class FluentClientDecorator : ClientCodegenDecorator { } rustCrate.mergeFeature(Feature("rustls", default = true, listOf("aws-smithy-client/rustls"))) - rustCrate.mergeFeature(Feature("native-tls", default = false, listOf("aws-smithy-client/native-tls"))) } override fun libRsCustomizations( diff --git a/design/src/transport/connector.md b/design/src/transport/connector.md new file mode 100644 index 0000000000..cadbcb6dd5 --- /dev/null +++ b/design/src/transport/connector.md @@ -0,0 +1,39 @@ +The Smithy client provides a default TLS connector, but a custom one can be plugged in. +`rustls` is enabled with the feature flag `rustls`. + +The client had previously supported `native-tls`. You can use your custom connector like this. + +Create your connector: + +```rust +/// A `hyper` connector that uses the `native-tls` crate for TLS. To use this in a smithy client, +/// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter). +pub type NativeTls = hyper_tls::HttpsConnector; + +pub fn native_tls() -> NativeTls { + let mut tls = hyper_tls::native_tls::TlsConnector::builder(); + let tls = tls + .min_protocol_version(Some(hyper_tls::native_tls::Protocol::Tlsv12)) + .build() + .unwrap_or_else(|e| panic!("Error while creating TLS connector: {}", e)); + let mut http = hyper::client::HttpConnector::new(); + http.enforce_http(false); + hyper_tls::HttpsConnector::from((http, tls.into())) +} +``` + +Plug the connector in the client: +```rust +let mut builder = hyper::client::Builder::default(); +builder.pool_max_idle_per_host(70); +let connector = aws_smithy_client::erase::DynConnector::new( + aws_smithy_client::hyper_ext::Adapter::builder() + .hyper_builder(builder) + .connector_settings(std::default::Default::default()) + .build(native_tls()), +); +let raw_client = aws_smithy_client::builder::Builder::new() + .connector(connector) + .middleware_fn(...) + .build_dyn(); +``` diff --git a/rust-runtime/aws-smithy-client/Cargo.toml b/rust-runtime/aws-smithy-client/Cargo.toml index e68320d7b9..95d8e0c005 100644 --- a/rust-runtime/aws-smithy-client/Cargo.toml +++ b/rust-runtime/aws-smithy-client/Cargo.toml @@ -10,12 +10,12 @@ repository = "https://github.com/awslabs/smithy-rs" [features] rt-tokio = ["aws-smithy-async/rt-tokio"] test-util = ["dep:aws-smithy-protocol-test", "dep:hyper", "hyper?/server", "hyper?/h2", "dep:serde", "dep:serde_json", "serde?/derive", "rustls", "tokio/full"] -native-tls = ["dep:hyper-tls", "client-hyper", "rt-tokio"] +native-tls = [] +allow-compilation = [] # our tests use `cargo test --all-features` and native-tls breaks CI rustls = ["dep:hyper-rustls", "dep:lazy_static", "dep:rustls", "client-hyper", "rt-tokio"] client-hyper = ["dep:hyper"] hyper-webpki-doctest-only = ["dep:hyper-rustls", "hyper-rustls?/webpki-roots"] - [dependencies] aws-smithy-async = { path = "../aws-smithy-async" } aws-smithy-http = { path = "../aws-smithy-http" } @@ -27,8 +27,8 @@ fastrand = "1.4.0" http = "0.2.3" http-body = "0.4.4" hyper = { version = "0.14.25", features = ["client", "http2", "http1", "tcp"], optional = true } -# cargo does not support optional test dependencies, so to completely disable rustls when -# the native-tls feature is enabled, we need to add the webpki-roots feature here. +# cargo does not support optional test dependencies, so to completely disable rustls +# we need to add the webpki-roots feature here. # https://github.com/rust-lang/cargo/issues/1596 hyper-rustls = { version = "0.23.0", optional = true, features = ["rustls-native-certs", "http2"] } hyper-tls = { version = "0.5.0", optional = true } @@ -43,6 +43,7 @@ tracing = "0.1" [dev-dependencies] aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio"] } +hyper-tls = { version = "0.5.0" } serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1.23.1", features = ["full", "test-util"] } diff --git a/rust-runtime/aws-smithy-client/additional-ci b/rust-runtime/aws-smithy-client/additional-ci index fde542e9fc..bb21b8b01f 100755 --- a/rust-runtime/aws-smithy-client/additional-ci +++ b/rust-runtime/aws-smithy-client/additional-ci @@ -12,4 +12,4 @@ echo "### Checking for duplicate dependency versions in the normal dependency gr cargo tree -d --edges normal --all-features echo "### Testing every combination of features (excluding --all-features)" -cargo hack test --feature-powerset --exclude-all-features +cargo hack test --feature-powerset --exclude-all-features --exclude-features native-tls diff --git a/rust-runtime/aws-smithy-client/external-types.toml b/rust-runtime/aws-smithy-client/external-types.toml index 5e4b43b8a0..0ca5028416 100644 --- a/rust-runtime/aws-smithy-client/external-types.toml +++ b/rust-runtime/aws-smithy-client/external-types.toml @@ -11,7 +11,7 @@ allowed_external_types = [ "tower::retry::policy::Policy", "tower_service::Service", - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Move `rustls`/`native-tls` features into separate crates + # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Move `rustls` feature into separate crates "hyper::client::connect::http::HttpConnector", "hyper_rustls::connector::HttpsConnector", "hyper_tls::client::HttpsConnector", diff --git a/rust-runtime/aws-smithy-client/src/builder.rs b/rust-runtime/aws-smithy-client/src/builder.rs index d226dcb1cf..063c9a6a47 100644 --- a/rust-runtime/aws-smithy-client/src/builder.rs +++ b/rust-runtime/aws-smithy-client/src/builder.rs @@ -88,21 +88,24 @@ where } } -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] use crate::erase::DynConnector; -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] use crate::http_connector::ConnectorSettings; -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] use crate::hyper_ext::Adapter as HyperAdapter; +#[cfg(all(feature = "native-tls", not(feature = "allow-compilation")))] +compile_error!("Feature native-tls has been removed. For upgrade instructions, see: https://awslabs.github.io/smithy-rs/design/transport/connector.html"); + /// Max idle connections is not standardized across SDKs. Java V1 and V2 use 50, and Go V2 uses 100. /// The number below was chosen arbitrarily between those two reference points, and should allow /// for 14 separate SDK clients in a Lambda where the max file handles is 1024. -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] const DEFAULT_MAX_IDLE_CONNECTIONS: usize = 70; /// Returns default HTTP client settings for hyper. -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] fn default_hyper_builder() -> hyper::client::Builder { let mut builder = hyper::client::Builder::default(); builder.pool_max_idle_per_host(DEFAULT_MAX_IDLE_CONNECTIONS); @@ -125,24 +128,7 @@ impl Builder<(), M, R> { } } -#[cfg(feature = "native-tls")] -impl Builder<(), M, R> { - /// Connect to the service over HTTPS using the native TLS library on your - /// platform using dynamic dispatch. - pub fn native_tls_connector( - self, - connector_settings: ConnectorSettings, - ) -> Builder { - self.connector(DynConnector::new( - HyperAdapter::builder() - .hyper_builder(default_hyper_builder()) - .connector_settings(connector_settings) - .build(crate::conns::native_tls()), - )) - } -} - -#[cfg(any(feature = "rustls", feature = "native-tls"))] +#[cfg(feature = "rustls")] impl Builder<(), M, R> { /// Create a Smithy client builder with an HTTPS connector and the [standard retry /// policy](crate::retry::Standard) over the default middleware implementation. @@ -150,17 +136,13 @@ impl Builder<(), M, R> { /// For convenience, this constructor type-erases the concrete TLS connector backend used using /// dynamic dispatch. This comes at a slight runtime performance cost. See /// [`DynConnector`](crate::erase::DynConnector) for details. To avoid that overhead, use - /// [`Builder::rustls_connector`] or [`Builder::native_tls_connector`] instead. + /// [`Builder::rustls_connector`] instead. + #[cfg(feature = "rustls")] pub fn dyn_https_connector( self, connector_settings: ConnectorSettings, ) -> Builder { - #[cfg(feature = "rustls")] let with_https = |b: Builder<_, M, R>| b.rustls_connector(connector_settings); - // If we are compiling this function & rustls is not enabled, then native-tls MUST be enabled - #[cfg(not(feature = "rustls"))] - let with_https = |b: Builder<_, M, R>| b.native_tls_connector(connector_settings); - with_https(self) } } @@ -582,8 +564,5 @@ mod tests { #[cfg(feature = "rustls")] let _builder: Builder = Builder::new().rustls_connector(Default::default()); - #[cfg(feature = "native-tls")] - let _builder: Builder = - Builder::new().native_tls_connector(Default::default()); } } diff --git a/rust-runtime/aws-smithy-client/src/conns.rs b/rust-runtime/aws-smithy-client/src/conns.rs index 6ccf07f462..041ba85089 100644 --- a/rust-runtime/aws-smithy-client/src/conns.rs +++ b/rust-runtime/aws-smithy-client/src/conns.rs @@ -10,11 +10,6 @@ /// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter). pub type Https = hyper_rustls::HttpsConnector; -#[cfg(feature = "native-tls")] -/// A `hyper` connector that uses the `native-tls` crate for TLS. To use this in a smithy client, -/// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter). -pub type NativeTls = hyper_tls::HttpsConnector; - #[cfg(feature = "rustls")] /// A smithy connector that uses the `rustls` crate for TLS. pub type Rustls = crate::hyper_ext::Adapter; @@ -63,25 +58,7 @@ pub fn https() -> Https { HTTPS_NATIVE_ROOTS.clone() } -#[cfg(feature = "native-tls")] -/// Return a default HTTPS connector backed by the `hyper_tls` crate. -/// -/// It requires a minimum TLS version of 1.2. -/// It allows you to connect to both `http` and `https` URLs. -pub fn native_tls() -> NativeTls { - // `TlsConnector` actually comes for here: https://docs.rs/native-tls/latest/native_tls/ - // hyper_tls just re-exports the crate for convenience. - let mut tls = hyper_tls::native_tls::TlsConnector::builder(); - let tls = tls - .min_protocol_version(Some(hyper_tls::native_tls::Protocol::Tlsv12)) - .build() - .unwrap_or_else(|e| panic!("Error while creating TLS connector: {}", e)); - let mut http = hyper::client::HttpConnector::new(); - http.enforce_http(false); - hyper_tls::HttpsConnector::from((http, tls.into())) -} - -#[cfg(all(test, any(feature = "native-tls", feature = "rustls")))] +#[cfg(all(test, feature = "rustls"))] mod tests { use crate::erase::DynConnector; use crate::hyper_ext::Adapter; @@ -100,30 +77,6 @@ mod tests { assert!(res.status().is_success()); } - #[cfg(feature = "native-tls")] - mod native_tls_tests { - use super::super::native_tls; - use super::*; - - #[tokio::test] - async fn test_native_tls_connector_can_make_http_requests() { - let conn = Adapter::builder().build(native_tls()); - let conn = DynConnector::new(conn); - let http_uri: Uri = "http://example.com/".parse().unwrap(); - - send_request_and_assert_success(conn, &http_uri).await; - } - - #[tokio::test] - async fn test_native_tls_connector_can_make_https_requests() { - let conn = Adapter::builder().build(native_tls()); - let conn = DynConnector::new(conn); - let https_uri: Uri = "https://example.com/".parse().unwrap(); - - send_request_and_assert_success(conn, &https_uri).await; - } - } - #[cfg(feature = "rustls")] mod rustls_tests { use super::super::https; @@ -148,3 +101,54 @@ mod tests { } } } + +#[cfg(test)] +mod custom_tls_tests { + use crate::erase::DynConnector; + use crate::hyper_ext::Adapter; + use aws_smithy_http::body::SdkBody; + use http::{Method, Request, Uri}; + use tower::{Service, ServiceBuilder}; + + type NativeTls = hyper_tls::HttpsConnector; + + fn native_tls() -> NativeTls { + let mut tls = hyper_tls::native_tls::TlsConnector::builder(); + let tls = tls + .min_protocol_version(Some(hyper_tls::native_tls::Protocol::Tlsv12)) + .build() + .unwrap_or_else(|e| panic!("Error while creating TLS connector: {}", e)); + let mut http = hyper::client::HttpConnector::new(); + http.enforce_http(false); + hyper_tls::HttpsConnector::from((http, tls.into())) + } + + #[tokio::test] + async fn test_native_tls_connector_can_make_http_requests() { + let conn = Adapter::builder().build(native_tls()); + let conn = DynConnector::new(conn); + let http_uri: Uri = "http://example.com/".parse().unwrap(); + + send_request_and_assert_success(conn, &http_uri).await; + } + + #[tokio::test] + async fn test_native_tls_connector_can_make_https_requests() { + let conn = Adapter::builder().build(native_tls()); + let conn = DynConnector::new(conn); + let https_uri: Uri = "https://example.com/".parse().unwrap(); + + send_request_and_assert_success(conn, &https_uri).await; + } + + async fn send_request_and_assert_success(conn: DynConnector, uri: &Uri) { + let mut svc = ServiceBuilder::new().service(conn); + let req = Request::builder() + .uri(uri) + .method(Method::GET) + .body(SdkBody::empty()) + .unwrap(); + let res = svc.call(req).await.unwrap(); + assert!(res.status().is_success()); + } +} diff --git a/rust-runtime/aws-smithy-client/src/hyper_ext.rs b/rust-runtime/aws-smithy-client/src/hyper_ext.rs index 7032604a78..8f0ca40609 100644 --- a/rust-runtime/aws-smithy-client/src/hyper_ext.rs +++ b/rust-runtime/aws-smithy-client/src/hyper_ext.rs @@ -18,19 +18,10 @@ //! [`Client`](crate::Client), directly, use the `dyn_https_https()` method to match that default behavior: //! #![cfg_attr( - not(all( - any(feature = "rustls", feature = "native-tls"), - feature = "client-hyper" - )), + not(all(feature = "rustls", feature = "client-hyper")), doc = "```no_run,ignore" )] -#![cfg_attr( - all( - any(feature = "rustls", feature = "native-tls"), - feature = "client-hyper" - ), - doc = "```no_run" -)] +#![cfg_attr(all(feature = "rustls", feature = "client-hyper"), doc = "```no_run")] //! use aws_smithy_client::Client; //! //! let client = Client::builder() @@ -237,8 +228,8 @@ fn find_source<'a, E: Error + 'static>(err: &'a (dyn Error + 'static)) -> Option /// Builder for [`hyper_ext::Adapter`](Adapter) /// /// Unlike a Smithy client, the [`Service`] inside a [`hyper_ext::Adapter`](Adapter) is actually a service that -/// accepts a `Uri` and returns a TCP stream. Two default implementations of this are provided, one -/// that encrypts the stream with `rustls`, the other that encrypts the stream with `native-tls`. +/// accepts a `Uri` and returns a TCP stream. One default implementation of this is provided, +/// that encrypts the stream with `rustls`. /// /// # Examples /// Construct a HyperAdapter with the default HTTP implementation (rustls). This can be useful when you want to share a Hyper connector diff --git a/rust-runtime/aws-smithy-client/src/lib.rs b/rust-runtime/aws-smithy-client/src/lib.rs index 35065c84b4..7d3a6f256e 100644 --- a/rust-runtime/aws-smithy-client/src/lib.rs +++ b/rust-runtime/aws-smithy-client/src/lib.rs @@ -10,7 +10,6 @@ //! | `event-stream` | Provides Sender/Receiver implementations for Event Stream codegen. | //! | `rt-tokio` | Run async code with the `tokio` runtime | //! | `test-util` | Include various testing utils | -//! | `native-tls` | Use `native-tls` as the HTTP client's TLS implementation | //! | `rustls` | Use `rustls` as the HTTP client's TLS implementation | //! | `client-hyper` | Use `hyper` to handle HTTP requests | @@ -85,9 +84,8 @@ use tracing::{debug_span, field, Instrument}; /// to the inner service, and then ultimately returning the inner service's response. /// /// With the `hyper` feature enabled, you can construct a `Client` directly from a -/// `hyper::Client` using `hyper_ext::Adapter::builder`. You can also enable the `rustls` or `native-tls` -/// features to construct a Client against a standard HTTPS endpoint using `Builder::rustls_connector` and -/// `Builder::native_tls_connector` respectively. +/// `hyper::Client` using `hyper_ext::Adapter::builder`. You can also enable the `rustls` +/// feature to construct a Client against a standard HTTPS endpoint using `Builder::rustls_connector`. #[derive(Debug)] pub struct Client< Connector = erase::DynConnector, diff --git a/tools/ci-scripts/check-aws-config b/tools/ci-scripts/check-aws-config index 60af5d28d2..5c6d910fe2 100755 --- a/tools/ci-scripts/check-aws-config +++ b/tools/ci-scripts/check-aws-config @@ -36,7 +36,7 @@ echo "${C_YELLOW}## Checking for duplicate dependency versions in the normal dep cargo tree -d --edges normal --all-features echo "${C_YELLOW}## Testing every combination of features${C_RESET}" -cargo hack test --feature-powerset --exclude-all-features +cargo hack test --feature-powerset --exclude-all-features --exclude-features native-tls echo "${C_YELLOW}## Checking the wasm32-unknown-unknown and wasm32-wasi targets${C_RESET}" cargo check --target wasm32-unknown-unknown --no-default-features From 8773a70428f193d5b65d02121509e51abfad6d80 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 26 May 2023 08:49:56 -0700 Subject: [PATCH 113/253] Simplify event stream message signer configuration (#2671) ## Motivation and Context This PR creates a `DeferredSigner` implementation that allows for the event stream message signer to be wired up by the signing implementation later in the request lifecycle rather than by adding an event stream signer method to the config. Refactoring this brings the middleware client implementation closer to how the orchestrator implementation will work, which unblocks the work required to make event streams work in the orchestrator. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- CHANGELOG.next.toml | 16 ++ .../aws-sig-auth/src/event_stream.rs | 115 +++++++++++++- .../aws-sig-auth/src/middleware.rs | 66 ++++++++ aws/rust-runtime/aws-sig-auth/src/signer.rs | 2 +- .../smithy/rustsdk/AwsPresigningDecorator.kt | 4 +- .../smithy/rustsdk/SigV4SigningDecorator.kt | 39 ++--- ...onTest.kt => SigV4SigningDecoratorTest.kt} | 2 +- .../client/smithy/RustClientCodegenPlugin.kt | 2 - .../NoOpEventStreamSigningDecorator.kt | 66 -------- .../config/EventStreamSigningConfig.kt | 46 ------ .../protocols/HttpBoundProtocolGenerator.kt | 35 ++++- .../client/smithy/ClientCodegenVisitorTest.kt | 2 - .../HttpVersionListGeneratorTest.kt | 85 ---------- .../protocol/ProtocolTestGeneratorTest.kt | 2 +- .../generators/protocol/ProtocolGenerator.kt | 4 +- .../HttpBoundProtocolPayloadGenerator.kt | 86 ++++------ .../ServerHttpBoundProtocolGenerator.kt | 32 +++- .../aws-smithy-eventstream/src/frame.rs | 147 ++++++++++++++++++ 18 files changed, 456 insertions(+), 295 deletions(-) rename aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/{SigV4SigningCustomizationTest.kt => SigV4SigningDecoratorTest.kt} (96%) delete mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/NoOpEventStreamSigningDecorator.kt delete mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/EventStreamSigningConfig.kt diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index f6dec54418..86fe2d7fe1 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -34,3 +34,19 @@ message = "`SsoCredentialsProvider`, `AssumeRoleProvider`, and `WebIdentityToken references = ["smithy-rs#2720"] meta = { "breaking" = false, "tada" = false, "bug" = true } author = "ysaito1001" + +[[smithy-rs]] +message = """ +
+Breaking change in how event stream signing works (click to expand more details) + +This change will only impact you if you are wiring up their own event stream signing/authentication scheme. If you're using `aws-sig-auth` to use AWS SigV4 event stream signing, then this change will **not** impact you. + +Previously, event stream signing was configured at codegen time by placing a `new_event_stream_signer` method on the `Config`. This function was called at serialization time to connect the signer to the streaming body. Now, instead, a special `DeferredSigner` is wired up at serialization time that relies on a signing implementation to be sent on a channel by the HTTP request signer. To do this, a `DeferredSignerSender` must be pulled out of the property bag, and its `send()` method called with the desired event stream signing implementation. + +See the changes in https://github.com/awslabs/smithy-rs/pull/2671 for an example of how this was done for SigV4. +
+""" +references = ["smithy-rs#2671"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } +author = "jdisanti" diff --git a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs index 74b8c65dd4..e6e06dac75 100644 --- a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs +++ b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +// TODO(enableNewSmithyRuntime): Remove this blanket allow once the old implementations are deleted +#![allow(deprecated)] + use crate::middleware::Signature; use aws_credential_types::Credentials; use aws_sigv4::event_stream::{sign_empty_message, sign_message}; @@ -15,6 +18,115 @@ use std::time::SystemTime; /// Event Stream SigV4 signing implementation. #[derive(Debug)] +pub struct SigV4MessageSigner { + last_signature: String, + credentials: Credentials, + signing_region: SigningRegion, + signing_service: SigningService, + time: Option, +} + +impl SigV4MessageSigner { + pub fn new( + last_signature: String, + credentials: Credentials, + signing_region: SigningRegion, + signing_service: SigningService, + time: Option, + ) -> Self { + Self { + last_signature, + credentials, + signing_region, + signing_service, + time, + } + } + + fn signing_params(&self) -> SigningParams<()> { + let mut builder = SigningParams::builder() + .access_key(self.credentials.access_key_id()) + .secret_key(self.credentials.secret_access_key()) + .region(self.signing_region.as_ref()) + .service_name(self.signing_service.as_ref()) + .time(self.time.unwrap_or_else(SystemTime::now)) + .settings(()); + builder.set_security_token(self.credentials.session_token()); + builder.build().unwrap() + } +} + +impl SignMessage for SigV4MessageSigner { + fn sign(&mut self, message: Message) -> Result { + let (signed_message, signature) = { + let params = self.signing_params(); + sign_message(&message, &self.last_signature, ¶ms).into_parts() + }; + self.last_signature = signature; + Ok(signed_message) + } + + fn sign_empty(&mut self) -> Option> { + let (signed_message, signature) = { + let params = self.signing_params(); + sign_empty_message(&self.last_signature, ¶ms).into_parts() + }; + self.last_signature = signature; + Some(Ok(signed_message)) + } +} + +#[cfg(test)] +mod tests { + use crate::event_stream::SigV4MessageSigner; + use aws_credential_types::Credentials; + use aws_smithy_eventstream::frame::{HeaderValue, Message, SignMessage}; + use aws_types::region::Region; + use aws_types::region::SigningRegion; + use aws_types::SigningService; + use std::time::{Duration, UNIX_EPOCH}; + + fn check_send_sync(value: T) -> T { + value + } + + #[test] + fn sign_message() { + let region = Region::new("us-east-1"); + let mut signer = check_send_sync(SigV4MessageSigner::new( + "initial-signature".into(), + Credentials::for_tests(), + SigningRegion::from(region), + SigningService::from_static("transcribe"), + Some(UNIX_EPOCH + Duration::new(1611160427, 0)), + )); + let mut signatures = Vec::new(); + for _ in 0..5 { + let signed = signer + .sign(Message::new(&b"identical message"[..])) + .unwrap(); + if let HeaderValue::ByteArray(signature) = signed + .headers() + .iter() + .find(|h| h.name().as_str() == ":chunk-signature") + .unwrap() + .value() + { + signatures.push(signature.clone()); + } else { + panic!("failed to get the :chunk-signature") + } + } + for i in 1..signatures.len() { + assert_ne!(signatures[i - 1], signatures[i]); + } + } +} + +// TODO(enableNewSmithyRuntime): Delete this old implementation that was kept around to support patch releases. +#[deprecated = "use aws_sig_auth::event_stream::SigV4MessageSigner instead (this may require upgrading the smithy-rs code generator)"] +#[derive(Debug)] +/// Event Stream SigV4 signing implementation. pub struct SigV4Signer { properties: SharedPropertyBag, last_signature: Option, @@ -87,8 +199,9 @@ impl SignMessage for SigV4Signer { } } +// TODO(enableNewSmithyRuntime): Delete this old implementation that was kept around to support patch releases. #[cfg(test)] -mod tests { +mod old_tests { use crate::event_stream::SigV4Signer; use crate::middleware::Signature; use aws_credential_types::Credentials; diff --git a/aws/rust-runtime/aws-sig-auth/src/middleware.rs b/aws/rust-runtime/aws-sig-auth/src/middleware.rs index d7ec53454c..c8c4794f0b 100644 --- a/aws/rust-runtime/aws-sig-auth/src/middleware.rs +++ b/aws/rust-runtime/aws-sig-auth/src/middleware.rs @@ -20,8 +20,15 @@ use crate::signer::{ OperationSigningConfig, RequestConfig, SigV4Signer, SigningError, SigningRequirements, }; +#[cfg(feature = "sign-eventstream")] +use crate::event_stream::SigV4MessageSigner as EventStreamSigV4Signer; +#[cfg(feature = "sign-eventstream")] +use aws_smithy_eventstream::frame::DeferredSignerSender; + +// TODO(enableNewSmithyRuntime): Delete `Signature` when switching to the orchestrator /// Container for the request signature for use in the property bag. #[non_exhaustive] +#[derive(Debug, Clone)] pub struct Signature(String); impl Signature { @@ -181,6 +188,22 @@ impl MapRequest for SigV4SigningStage { .signer .sign(operation_config, &request_config, &creds, &mut req) .map_err(SigningStageErrorKind::SigningFailure)?; + + // If this is an event stream operation, set up the event stream signer + #[cfg(feature = "sign-eventstream")] + if let Some(signer_sender) = config.get::() { + let time_override = config.get::().copied(); + signer_sender + .send(Box::new(EventStreamSigV4Signer::new( + signature.as_ref().into(), + creds, + request_config.region.clone(), + request_config.service.clone(), + time_override, + )) as _) + .expect("failed to send deferred signer"); + } + config.insert(signature); Ok(req) }) @@ -234,6 +257,49 @@ mod test { assert!(signature.is_some()); } + #[cfg(feature = "sign-eventstream")] + #[test] + fn sends_event_stream_signer_for_event_stream_operations() { + use crate::event_stream::SigV4MessageSigner as EventStreamSigV4Signer; + use aws_smithy_eventstream::frame::{DeferredSigner, SignMessage}; + use std::time::SystemTime; + + let (mut deferred_signer, deferred_signer_sender) = DeferredSigner::new(); + let req = http::Request::builder() + .uri("https://test-service.test-region.amazonaws.com/") + .body(SdkBody::from("")) + .unwrap(); + let region = Region::new("us-east-1"); + let req = operation::Request::new(req) + .augment(|req, properties| { + properties.insert(region.clone()); + properties.insert::(UNIX_EPOCH + Duration::new(1611160427, 0)); + properties.insert(SigningService::from_static("kinesis")); + properties.insert(OperationSigningConfig::default_config()); + properties.insert(Credentials::for_tests()); + properties.insert(SigningRegion::from(region.clone())); + properties.insert(deferred_signer_sender); + Result::<_, Infallible>::Ok(req) + }) + .expect("succeeds"); + + let signer = SigV4SigningStage::new(SigV4Signer::new()); + let _ = signer.apply(req).unwrap(); + + let mut signer_for_comparison = EventStreamSigV4Signer::new( + // This is the expected SigV4 signature for the HTTP request above + "abac477b4afabf5651079e7b9a0aa6a1a3e356a7418a81d974cdae9d4c8e5441".into(), + Credentials::for_tests(), + SigningRegion::from(region), + SigningService::from_static("kinesis"), + Some(UNIX_EPOCH + Duration::new(1611160427, 0)), + ); + + let expected_signed_empty = signer_for_comparison.sign_empty().unwrap().unwrap(); + let actual_signed_empty = deferred_signer.sign_empty().unwrap().unwrap(); + assert_eq!(expected_signed_empty, actual_signed_empty); + } + // check that the endpoint middleware followed by signing middleware produce the expected result #[test] fn endpoint_plus_signer() { diff --git a/aws/rust-runtime/aws-sig-auth/src/signer.rs b/aws/rust-runtime/aws-sig-auth/src/signer.rs index a1d36c97ca..d71c6ecf42 100644 --- a/aws/rust-runtime/aws-sig-auth/src/signer.rs +++ b/aws/rust-runtime/aws-sig-auth/src/signer.rs @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::middleware::Signature; use aws_credential_types::Credentials; use aws_sigv4::http_request::{ sign, PayloadChecksumKind, PercentEncodingMode, SessionTokenMode, SignableRequest, @@ -15,6 +14,7 @@ use aws_types::SigningService; use std::fmt; use std::time::{Duration, SystemTime}; +use crate::middleware::Signature; pub use aws_sigv4::http_request::SignableBody; pub type SigningError = aws_sigv4::http_request::SigningError; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index ee5f82aff6..4c9f6feffc 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -21,6 +21,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientSection import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator +import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientHttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs @@ -34,7 +35,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection -import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.util.cloneOperation import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -179,7 +179,7 @@ class AwsInputPresignedMethod( MakeOperationGenerator( codegenContext, protocol, - HttpBoundProtocolPayloadGenerator(codegenContext, protocol), + ClientHttpBoundProtocolPayloadGenerator(codegenContext, protocol), // Prefixed with underscore to avoid colliding with modeled functions functionName = makeOperationFn, public = false, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt index 81400d6597..3c40b518eb 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt @@ -16,7 +16,7 @@ import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.EventStreamSigningConfig +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig 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 @@ -77,17 +77,17 @@ class SigV4SigningDecorator : ClientCodegenDecorator { } class SigV4SigningConfig( - runtimeConfig: RuntimeConfig, + private val runtimeConfig: RuntimeConfig, private val serviceHasEventStream: Boolean, private val sigV4Trait: SigV4Trait, -) : EventStreamSigningConfig(runtimeConfig) { - private val codegenScope = arrayOf( - "SigV4Signer" to AwsRuntimeType.awsSigAuthEventStream(runtimeConfig).resolve("event_stream::SigV4Signer"), - ) - - override fun configImplSection(): Writable { - return writable { - rustTemplate( +) : ConfigCustomization() { + override fun section(section: ServiceConfig): Writable = writable { + if (section is ServiceConfig.ConfigImpl) { + if (serviceHasEventStream) { + // enable the aws-sig-auth `sign-eventstream` feature + addDependency(AwsRuntimeType.awsSigAuthEventStream(runtimeConfig).toSymbol()) + } + rust( """ /// The signature version 4 service signing name to use in the credential scope when signing requests. /// @@ -97,24 +97,7 @@ class SigV4SigningConfig( ${sigV4Trait.name.dq()} } """, - *codegenScope, ) - if (serviceHasEventStream) { - rustTemplate( - "#{signerFn:W}", - "signerFn" to - renderEventStreamSignerFn { propertiesName -> - writable { - rustTemplate( - """ - #{SigV4Signer}::new($propertiesName) - """, - *codegenScope, - ) - } - }, - ) - } } } } @@ -209,5 +192,3 @@ class SigV4SigningFeature( } } } - -fun RuntimeConfig.sigAuth() = awsRuntimeCrate("aws-sig-auth") diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningCustomizationTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt similarity index 96% rename from aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningCustomizationTest.kt rename to aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt index 9de7c91f65..71bd5eaf6c 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningCustomizationTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt @@ -12,7 +12,7 @@ import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest -internal class SigV4SigningCustomizationTest { +internal class SigV4SigningDecoratorTest { @Test fun `generates a valid config`() { val project = stubConfigProject( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt index faa3a01c5d..08ba90d140 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt @@ -15,7 +15,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpAuth import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpConnectorConfigDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.customize.NoOpEventStreamSigningDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.RequiredCustomizations import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointParamsDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointsDecorator @@ -62,7 +61,6 @@ class RustClientCodegenPlugin : ClientDecoratableBuildPlugin() { FluentClientDecorator(), EndpointsDecorator(), EndpointParamsDecorator(), - NoOpEventStreamSigningDecorator(), ApiKeyAuthDecorator(), HttpAuthDecorator(), HttpConnectorConfigDecorator(), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/NoOpEventStreamSigningDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/NoOpEventStreamSigningDecorator.kt deleted file mode 100644 index 7d924ee75a..0000000000 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/NoOpEventStreamSigningDecorator.kt +++ /dev/null @@ -1,66 +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.customize - -import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.EventStreamSigningConfig -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.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.util.hasEventStreamOperations - -/** - * The NoOpEventStreamSigningDecorator: - * - adds a `new_event_stream_signer()` method to `config` to create an Event Stream NoOp signer - */ -open class NoOpEventStreamSigningDecorator : ClientCodegenDecorator { - override val name: String = "NoOpEventStreamSigning" - override val order: Byte = Byte.MIN_VALUE - - private fun applies(codegenContext: CodegenContext, baseCustomizations: List): Boolean = - codegenContext.serviceShape.hasEventStreamOperations(codegenContext.model) && - // and if there is no other `EventStreamSigningConfig`, apply this one - !baseCustomizations.any { it is EventStreamSigningConfig } - - override fun configCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List { - if (!applies(codegenContext, baseCustomizations)) { - return baseCustomizations - } - return baseCustomizations + NoOpEventStreamSigningConfig( - codegenContext.serviceShape.hasEventStreamOperations(codegenContext.model), - codegenContext.runtimeConfig, - ) - } -} - -class NoOpEventStreamSigningConfig( - private val serviceHasEventStream: Boolean, - runtimeConfig: RuntimeConfig, -) : EventStreamSigningConfig(runtimeConfig) { - - private val codegenScope = arrayOf( - "NoOpSigner" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::NoOpSigner"), - ) - - override fun configImplSection() = renderEventStreamSignerFn { - writable { - if (serviceHasEventStream) { - rustTemplate( - """ - #{NoOpSigner}{} - """, - *codegenScope, - ) - } - } - } -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/EventStreamSigningConfig.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/EventStreamSigningConfig.kt deleted file mode 100644 index 35da7d63b0..0000000000 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/EventStreamSigningConfig.kt +++ /dev/null @@ -1,46 +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 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.RuntimeConfig -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType - -open class EventStreamSigningConfig( - runtimeConfig: RuntimeConfig, -) : ConfigCustomization() { - private val codegenScope = arrayOf( - "SharedPropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::SharedPropertyBag"), - "SignMessage" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::SignMessage"), - ) - - override fun section(section: ServiceConfig): Writable { - return when (section) { - is ServiceConfig.ConfigImpl -> configImplSection() - else -> emptySection - } - } - - open fun configImplSection(): Writable = emptySection - - fun renderEventStreamSignerFn(signerInstantiator: (String) -> Writable): Writable = writable { - rustTemplate( - """ - /// Creates a new Event Stream `SignMessage` implementor. - pub fn new_event_stream_signer( - &self, - _properties: #{SharedPropertyBag} - ) -> impl #{SignMessage} { - #{signer:W} - } - """, - *codegenScope, - "signer" to signerInstantiator("_properties"), - ) - } -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index ae83e78cfe..69e0bdd480 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.Cli import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolParserGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate @@ -23,6 +24,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.pre import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations +import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol @@ -30,11 +32,11 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctio import software.amazon.smithy.rust.codegen.core.util.hasStreamingMember import software.amazon.smithy.rust.codegen.core.util.outputShape -// TODO(enableNewSmithyRuntime): Delete this class when cleaning up `enableNewSmithyRuntime` +// TODO(enableNewSmithyRuntime): Delete this class when cleaning up `enableNewSmithyRuntime` (replace with ClientProtocolGenerator) class HttpBoundProtocolGenerator( codegenContext: ClientCodegenContext, protocol: Protocol, - bodyGenerator: ProtocolPayloadGenerator = HttpBoundProtocolPayloadGenerator(codegenContext, protocol), + bodyGenerator: ProtocolPayloadGenerator = ClientHttpBoundProtocolPayloadGenerator(codegenContext, protocol), ) : ClientProtocolGenerator( codegenContext, protocol, @@ -49,6 +51,35 @@ class HttpBoundProtocolGenerator( HttpBoundProtocolTraitImplGenerator(codegenContext, protocol), ) +class ClientHttpBoundProtocolPayloadGenerator( + codegenContext: ClientCodegenContext, + protocol: Protocol, +) : ProtocolPayloadGenerator by HttpBoundProtocolPayloadGenerator( + codegenContext, protocol, HttpMessageType.REQUEST, + renderEventStreamBody = { writer, params -> + writer.rustTemplate( + """ + { + let error_marshaller = #{errorMarshallerConstructorFn}(); + let marshaller = #{marshallerConstructorFn}(); + let (signer, signer_sender) = #{DeferredSigner}::new(); + properties.acquire_mut().insert(signer_sender); + let adapter: #{aws_smithy_http}::event_stream::MessageStreamAdapter<_, _> = + ${params.outerName}.${params.memberName}.into_body_stream(marshaller, error_marshaller, signer); + let body: #{SdkBody} = #{hyper}::Body::wrap_stream(adapter).into(); + body + } + """, + "hyper" to CargoDependency.HyperWithStream.toType(), + "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), + "aws_smithy_http" to RuntimeType.smithyHttp(codegenContext.runtimeConfig), + "DeferredSigner" to RuntimeType.smithyEventStream(codegenContext.runtimeConfig).resolve("frame::DeferredSigner"), + "marshallerConstructorFn" to params.marshallerConstructorFn, + "errorMarshallerConstructorFn" to params.errorMarshallerConstructorFn, + ) + }, +) + // TODO(enableNewSmithyRuntime): Delete this class when cleaning up `enableNewSmithyRuntime` open class HttpBoundProtocolTraitImplGenerator( codegenContext: ClientCodegenContext, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitorTest.kt index 2cb21423fd..f9cf52375b 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitorTest.kt @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.customizations.ClientCustomizations import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.customize.NoOpEventStreamSigningDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.RequiredCustomizations import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientDecorator import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel @@ -52,7 +51,6 @@ class ClientCodegenVisitorTest { ClientCustomizations(), RequiredCustomizations(), FluentClientDecorator(), - NoOpEventStreamSigningDecorator(), ) val visitor = ClientCodegenVisitor(ctx, codegenDecorator) val baselineModel = visitor.baselineTransform(model) diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt index 3b5fc70a6f..73633a603e 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt @@ -6,19 +6,9 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import org.junit.jupiter.api.Test -import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.EventStreamSigningConfig -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute -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.RuntimeConfig -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest @@ -172,7 +162,6 @@ internal class HttpVersionListGeneratorTest { clientIntegrationTest( model, IntegrationTestParams(addModuleToEventStreamAllowList = true), - additionalDecorators = listOf(FakeSigningDecorator()), ) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("validate_eventstream_http") { @@ -196,77 +185,3 @@ internal class HttpVersionListGeneratorTest { } } } - -class FakeSigningDecorator : ClientCodegenDecorator { - override val name: String = "fakesigning" - override val order: Byte = 0 - override fun classpathDiscoverable(): Boolean = false - override fun configCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List { - return baseCustomizations.filterNot { - it is EventStreamSigningConfig - } + FakeSigningConfig(codegenContext.runtimeConfig) - } -} - -class FakeSigningConfig( - runtimeConfig: RuntimeConfig, -) : EventStreamSigningConfig(runtimeConfig) { - private val codegenScope = arrayOf( - "SharedPropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::SharedPropertyBag"), - "SignMessageError" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::SignMessageError"), - "SignMessage" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::SignMessage"), - "Message" to RuntimeType.smithyEventStream(runtimeConfig).resolve("frame::Message"), - ) - - override fun section(section: ServiceConfig): Writable { - return when (section) { - is ServiceConfig.ConfigImpl -> writable { - rustTemplate( - """ - /// Creates a new Event Stream `SignMessage` implementor. - pub fn new_event_stream_signer( - &self, - properties: #{SharedPropertyBag} - ) -> FakeSigner { - FakeSigner::new(properties) - } - """, - *codegenScope, - ) - } - - is ServiceConfig.Extras -> writable { - rustTemplate( - """ - /// Fake signing implementation. - ##[derive(Debug)] - pub struct FakeSigner; - - impl FakeSigner { - /// Create a real `FakeSigner` - pub fn new(_properties: #{SharedPropertyBag}) -> Self { - Self {} - } - } - - impl #{SignMessage} for FakeSigner { - fn sign(&mut self, message: #{Message}) -> Result<#{Message}, #{SignMessageError}> { - Ok(message) - } - - fn sign_empty(&mut self) -> Option> { - Some(Ok(#{Message}::new(Vec::new()))) - } - } - """, - *codegenScope, - ) - } - - else -> emptySection - } - } -} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt index d0d271231a..b03f374403 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt @@ -38,7 +38,7 @@ private class TestProtocolPayloadGenerator(private val body: String) : ProtocolP override fun payloadMetadata(operationShape: OperationShape) = ProtocolPayloadGenerator.PayloadMetadata(takesOwnership = false) - override fun generatePayload(writer: RustWriter, self: String, operationShape: OperationShape) { + override fun generatePayload(writer: RustWriter, shapeName: String, operationShape: OperationShape) { writer.writeWithNoFormatting(body) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt index 4f410bf03a..ceb385c6d1 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt @@ -36,13 +36,13 @@ interface ProtocolPayloadGenerator { /** * Write the payload into [writer]. * - * [self] is the name of the variable binding for the Rust struct that is to be serialized into the payload. + * [shapeName] is the name of the variable binding for the Rust struct that is to be serialized into the payload. * * This should be an expression that returns bytes: * - a `Vec` for non-streaming operations; or * - a `ByteStream` for streaming operations. */ - fun generatePayload(writer: RustWriter, self: String, operationShape: OperationShape) + fun generatePayload(writer: RustWriter, shapeName: String, operationShape: OperationShape) } /** diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt index 6d4e7bf850..1e17beca4e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt @@ -18,7 +18,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate -import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext @@ -42,10 +41,18 @@ import software.amazon.smithy.rust.codegen.core.util.isOutputEventStream import software.amazon.smithy.rust.codegen.core.util.isStreaming import software.amazon.smithy.rust.codegen.core.util.outputShape +data class EventStreamBodyParams( + val outerName: String, + val memberName: String, + val marshallerConstructorFn: RuntimeType, + val errorMarshallerConstructorFn: RuntimeType, +) + class HttpBoundProtocolPayloadGenerator( codegenContext: CodegenContext, private val protocol: Protocol, private val httpMessageType: HttpMessageType = HttpMessageType.REQUEST, + private val renderEventStreamBody: (RustWriter, EventStreamBodyParams) -> Unit, ) : ProtocolPayloadGenerator { private val symbolProvider = codegenContext.symbolProvider private val model = codegenContext.model @@ -91,38 +98,38 @@ class HttpBoundProtocolPayloadGenerator( } } - override fun generatePayload(writer: RustWriter, self: String, operationShape: OperationShape) { + override fun generatePayload(writer: RustWriter, shapeName: String, operationShape: OperationShape) { when (httpMessageType) { - HttpMessageType.RESPONSE -> generateResponsePayload(writer, self, operationShape) - HttpMessageType.REQUEST -> generateRequestPayload(writer, self, operationShape) + HttpMessageType.RESPONSE -> generateResponsePayload(writer, shapeName, operationShape) + HttpMessageType.REQUEST -> generateRequestPayload(writer, shapeName, operationShape) } } - private fun generateRequestPayload(writer: RustWriter, self: String, operationShape: OperationShape) { + private fun generateRequestPayload(writer: RustWriter, shapeName: String, operationShape: OperationShape) { val payloadMemberName = httpBindingResolver.requestMembers(operationShape, HttpLocation.PAYLOAD).firstOrNull()?.memberName if (payloadMemberName == null) { val serializerGenerator = protocol.structuredDataSerializer() - generateStructureSerializer(writer, self, serializerGenerator.operationInputSerializer(operationShape)) + generateStructureSerializer(writer, shapeName, serializerGenerator.operationInputSerializer(operationShape)) } else { - generatePayloadMemberSerializer(writer, self, operationShape, payloadMemberName) + generatePayloadMemberSerializer(writer, shapeName, operationShape, payloadMemberName) } } - private fun generateResponsePayload(writer: RustWriter, self: String, operationShape: OperationShape) { + private fun generateResponsePayload(writer: RustWriter, shapeName: String, operationShape: OperationShape) { val payloadMemberName = httpBindingResolver.responseMembers(operationShape, HttpLocation.PAYLOAD).firstOrNull()?.memberName if (payloadMemberName == null) { val serializerGenerator = protocol.structuredDataSerializer() - generateStructureSerializer(writer, self, serializerGenerator.operationOutputSerializer(operationShape)) + generateStructureSerializer(writer, shapeName, serializerGenerator.operationOutputSerializer(operationShape)) } else { - generatePayloadMemberSerializer(writer, self, operationShape, payloadMemberName) + generatePayloadMemberSerializer(writer, shapeName, operationShape, payloadMemberName) } } private fun generatePayloadMemberSerializer( writer: RustWriter, - self: String, + shapeName: String, operationShape: OperationShape, payloadMemberName: String, ) { @@ -131,7 +138,7 @@ class HttpBoundProtocolPayloadGenerator( if (operationShape.isEventStream(model)) { if (operationShape.isInputEventStream(model) && target == CodegenTarget.CLIENT) { val payloadMember = operationShape.inputShape(model).expectMember(payloadMemberName) - writer.serializeViaEventStream(operationShape, payloadMember, serializerGenerator, "self") + writer.serializeViaEventStream(operationShape, payloadMember, serializerGenerator, shapeName) } else if (operationShape.isOutputEventStream(model) && target == CodegenTarget.SERVER) { val payloadMember = operationShape.outputShape(model).expectMember(payloadMemberName) writer.serializeViaEventStream(operationShape, payloadMember, serializerGenerator, "output") @@ -144,16 +151,16 @@ class HttpBoundProtocolPayloadGenerator( HttpMessageType.RESPONSE -> operationShape.outputShape(model).expectMember(payloadMemberName) HttpMessageType.REQUEST -> operationShape.inputShape(model).expectMember(payloadMemberName) } - writer.serializeViaPayload(bodyMetadata, self, payloadMember, serializerGenerator) + writer.serializeViaPayload(bodyMetadata, shapeName, payloadMember, serializerGenerator) } } - private fun generateStructureSerializer(writer: RustWriter, self: String, serializer: RuntimeType?) { + private fun generateStructureSerializer(writer: RustWriter, shapeName: String, serializer: RuntimeType?) { if (serializer == null) { writer.rust("\"\"") } else { writer.rust( - "#T(&$self)?", + "#T(&$shapeName)?", serializer, ) } @@ -193,47 +200,20 @@ class HttpBoundProtocolPayloadGenerator( // TODO(EventStream): [RPC] RPC protocols need to send an initial message with the // parameters that are not `@eventHeader` or `@eventPayload`. - when (target) { - CodegenTarget.CLIENT -> - rustTemplate( - """ - { - let error_marshaller = #{errorMarshallerConstructorFn}(); - let marshaller = #{marshallerConstructorFn}(); - let signer = _config.new_event_stream_signer(properties.clone()); - let adapter: #{SmithyHttp}::event_stream::MessageStreamAdapter<_, _> = - $outerName.$memberName.into_body_stream(marshaller, error_marshaller, signer); - let body: #{SdkBody} = #{hyper}::Body::wrap_stream(adapter).into(); - body - } - """, - *codegenScope, - "marshallerConstructorFn" to marshallerConstructorFn, - "errorMarshallerConstructorFn" to errorMarshallerConstructorFn, - ) - CodegenTarget.SERVER -> { - rustTemplate( - """ - { - let error_marshaller = #{errorMarshallerConstructorFn}(); - let marshaller = #{marshallerConstructorFn}(); - let signer = #{NoOpSigner}{}; - let adapter: #{SmithyHttp}::event_stream::MessageStreamAdapter<_, _> = - $outerName.$memberName.into_body_stream(marshaller, error_marshaller, signer); - adapter - } - """, - *codegenScope, - "marshallerConstructorFn" to marshallerConstructorFn, - "errorMarshallerConstructorFn" to errorMarshallerConstructorFn, - ) - } - } + renderEventStreamBody( + this, + EventStreamBodyParams( + outerName, + memberName, + marshallerConstructorFn, + errorMarshallerConstructorFn, + ), + ) } private fun RustWriter.serializeViaPayload( payloadMetadata: ProtocolPayloadGenerator.PayloadMetadata, - self: String, + shapeName: String, member: MemberShape, serializerGenerator: StructuredDataSerializerGenerator, ) { @@ -281,7 +261,7 @@ class HttpBoundProtocolPayloadGenerator( } } } - rust("#T($ref $self.${symbolProvider.toMemberName(member)})?", serializer) + rust("#T($ref $shapeName.${symbolProvider.toMemberName(member)})?", serializer) } private fun RustWriter.renderPayload( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 2530f04404..89accafd74 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -41,6 +41,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization @@ -48,12 +49,14 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustom import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.mapRustType import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBindingDescriptor import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation +import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctions import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.parse.StructuredDataParserGenerator @@ -114,6 +117,31 @@ class ServerHttpBoundProtocolGenerator( } } +class ServerHttpBoundProtocolPayloadGenerator( + codegenContext: CodegenContext, + protocol: Protocol, +) : ProtocolPayloadGenerator by HttpBoundProtocolPayloadGenerator( + codegenContext, protocol, HttpMessageType.RESPONSE, + renderEventStreamBody = { writer, params -> + writer.rustTemplate( + """ + { + let error_marshaller = #{errorMarshallerConstructorFn}(); + let marshaller = #{marshallerConstructorFn}(); + let signer = #{NoOpSigner}{}; + let adapter: #{aws_smithy_http}::event_stream::MessageStreamAdapter<_, _> = + ${params.outerName}.${params.memberName}.into_body_stream(marshaller, error_marshaller, signer); + adapter + } + """, + "aws_smithy_http" to RuntimeType.smithyHttp(codegenContext.runtimeConfig), + "NoOpSigner" to RuntimeType.smithyEventStream(codegenContext.runtimeConfig).resolve("frame::NoOpSigner"), + "marshallerConstructorFn" to params.marshallerConstructorFn, + "errorMarshallerConstructorFn" to params.errorMarshallerConstructorFn, + ) + }, +) + /* * Generate all operation input parsers and output serializers for streaming and * non-streaming types. @@ -504,12 +532,12 @@ class ServerHttpBoundProtocolTraitImplGenerator( ?: serverRenderHttpResponseCode(httpTraitStatusCode)(this) operationShape.outputShape(model).findStreamingMember(model)?.let { - val payloadGenerator = HttpBoundProtocolPayloadGenerator(codegenContext, protocol, httpMessageType = HttpMessageType.RESPONSE) + val payloadGenerator = ServerHttpBoundProtocolPayloadGenerator(codegenContext, protocol) withBlockTemplate("let body = #{SmithyHttpServer}::body::boxed(#{SmithyHttpServer}::body::Body::wrap_stream(", "));", *codegenScope) { payloadGenerator.generatePayload(this, "output", operationShape) } } ?: run { - val payloadGenerator = HttpBoundProtocolPayloadGenerator(codegenContext, protocol, httpMessageType = HttpMessageType.RESPONSE) + val payloadGenerator = ServerHttpBoundProtocolPayloadGenerator(codegenContext, protocol) withBlockTemplate("let payload = ", ";") { payloadGenerator.generatePayload(this, "output", operationShape) } diff --git a/rust-runtime/aws-smithy-eventstream/src/frame.rs b/rust-runtime/aws-smithy-eventstream/src/frame.rs index a3d9c29a00..edf48e60de 100644 --- a/rust-runtime/aws-smithy-eventstream/src/frame.rs +++ b/rust-runtime/aws-smithy-eventstream/src/frame.rs @@ -14,6 +14,7 @@ use std::convert::{TryFrom, TryInto}; use std::error::Error as StdError; use std::fmt; use std::mem::size_of; +use std::sync::{mpsc, Mutex}; const PRELUDE_LENGTH_BYTES: u32 = 3 * size_of::() as u32; const PRELUDE_LENGTH_BYTES_USIZE: usize = PRELUDE_LENGTH_BYTES as usize; @@ -34,6 +35,95 @@ pub trait SignMessage: fmt::Debug { fn sign_empty(&mut self) -> Option>; } +/// A sender that gets placed in the request config to wire up an event stream signer after signing. +#[derive(Debug)] +#[non_exhaustive] +pub struct DeferredSignerSender(Mutex>>); + +impl DeferredSignerSender { + /// Creates a new `DeferredSignerSender` + fn new(tx: mpsc::Sender>) -> Self { + Self(Mutex::new(tx)) + } + + /// Sends a signer on the channel + pub fn send( + &self, + signer: Box, + ) -> Result<(), mpsc::SendError>> { + self.0.lock().unwrap().send(signer) + } +} + +/// Deferred event stream signer to allow a signer to be wired up later. +/// +/// HTTP request signing takes place after serialization, and the event stream +/// message stream body is established during serialization. Since event stream +/// signing may need context from the initial HTTP signing operation, this +/// [`DeferredSigner`] is needed to wire up the signer later in the request lifecycle. +/// +/// This signer basically just establishes a MPSC channel so that the sender can +/// be placed in the request's config. Then the HTTP signer implementation can +/// retrieve the sender from that config and send an actual signing implementation +/// with all the context needed. +/// +/// When an event stream implementation needs to sign a message, the first call to +/// sign will acquire a signing implementation off of the channel and cache it +/// for the remainder of the operation. +#[derive(Debug)] +pub struct DeferredSigner { + rx: Option>>>, + signer: Option>, +} + +impl DeferredSigner { + pub fn new() -> (Self, DeferredSignerSender) { + let (tx, rx) = mpsc::channel(); + ( + Self { + rx: Some(Mutex::new(rx)), + signer: None, + }, + DeferredSignerSender::new(tx), + ) + } + + fn acquire(&mut self) -> &mut (dyn SignMessage + Send + Sync) { + // Can't use `if let Some(signer) = &mut self.signer` because the borrow checker isn't smart enough + if self.signer.is_some() { + return self.signer.as_mut().unwrap().as_mut(); + } else { + self.signer = Some( + self.rx + .take() + .expect("only taken once") + .lock() + .unwrap() + .try_recv() + .ok() + // TODO(enableNewSmithyRuntime): When the middleware implementation is removed, + // this should panic rather than default to the `NoOpSigner`. The reason it defaults + // is because middleware-based generic clients don't have any default middleware, + // so there is no way to send a `NoOpSigner` by default when there is no other + // auth scheme. The orchestrator auth setup is a lot more robust and will make + // this problem trivial. + .unwrap_or_else(|| Box::new(NoOpSigner {}) as _), + ); + self.acquire() + } + } +} + +impl SignMessage for DeferredSigner { + fn sign(&mut self, message: Message) -> Result { + self.acquire().sign(message) + } + + fn sign_empty(&mut self) -> Option> { + self.acquire().sign_empty() + } +} + #[derive(Debug)] pub struct NoOpSigner {} impl SignMessage for NoOpSigner { @@ -848,3 +938,60 @@ mod message_frame_decoder_tests { } } } + +#[cfg(test)] +mod deferred_signer_tests { + use crate::frame::{DeferredSigner, Header, HeaderValue, Message, SignMessage}; + use bytes::Bytes; + + fn check_send_sync(value: T) -> T { + value + } + + #[test] + fn deferred_signer() { + #[derive(Default, Debug)] + struct TestSigner { + call_num: i32, + } + impl SignMessage for TestSigner { + fn sign( + &mut self, + message: crate::frame::Message, + ) -> Result { + self.call_num += 1; + Ok(message.add_header(Header::new("call_num", HeaderValue::Int32(self.call_num)))) + } + + fn sign_empty( + &mut self, + ) -> Option> { + None + } + } + + let (mut signer, sender) = check_send_sync(DeferredSigner::new()); + + sender + .send(Box::new(TestSigner::default())) + .expect("success"); + + let message = signer.sign(Message::new(Bytes::new())).expect("success"); + assert_eq!(1, message.headers()[0].value().as_int32().unwrap()); + + let message = signer.sign(Message::new(Bytes::new())).expect("success"); + assert_eq!(2, message.headers()[0].value().as_int32().unwrap()); + + assert!(signer.sign_empty().is_none()); + } + + #[test] + fn deferred_signer_defaults_to_noop_signer() { + let (mut signer, _sender) = DeferredSigner::new(); + assert_eq!( + Message::new(Bytes::new()), + signer.sign(Message::new(Bytes::new())).unwrap() + ); + assert!(signer.sign_empty().is_none()); + } +} From 0beb8905013248d447b30ebf6cea607d09592c21 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 26 May 2023 11:17:56 -0500 Subject: [PATCH 114/253] Render `CustomizableOperation` per crate in orchestrator (#2726) ## Motivation and Context Render `CustomizableOperation` once in the `customizable` module instead of rendering it in every operation's `builders` module. ## Description This PR is a follow-up on #2706. The previous PR ported `CustomizableOperation` in the middleware to the orchestrator but it had a flaw in that the type was rendered per every operation. This was clearly a codegen regression because the code for `CustomizableOperation` is repeated many times and is rendered even if no customization is requested for an operation. This PR attempts to fix that problem. Just like `CustomizableOperation` in the middleware, we render the new `CustomizableOperation` once in the `customize` module per crate. To accomplish that, the new `CustomizableOperation` uses a closure, called `customizable_send`, to encapsulate a call to a fluent builder's `send` method and invokes it when its own `send` method is called. ~~As a side note, this PR will not take care of the following. We have [a separate PR](https://github.com/awslabs/smithy-rs/pull/2723) to fix them and once we've merged it to this PR, they should be addressed automatically (there will be merge conflicts, though):~~ - ~~Removing `send_orchestrator_with_plugin` (which is why a type parameter `R` is present in the code changes, but it'll soon go away)~~ - ~~Incorrect signature of a closure in orchestrator's `CustomizableOperaton::map_request`~~ ## Testing No new tests added as it's purely refactoring. Confirmed `sra-test` passed (except for the one that's known to fail). ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- .../AwsCustomizableOperationDecorator.kt | 47 ++-- .../client/CustomizableOperationDecorator.kt | 3 +- .../client/CustomizableOperationGenerator.kt | 264 ++++++++++-------- .../client/FluentClientGenerator.kt | 46 ++- 4 files changed, 210 insertions(+), 150 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index 70cc99e52c..884fefc829 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -37,32 +37,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : override fun section(section: CustomizableOperationSection): Writable = writable { if (section is CustomizableOperationSection.CustomizableOperationImpl) { - if (section.operationShape == null) { - // TODO(enableNewSmithyRuntime): Delete this branch when middleware is no longer used - // This branch customizes CustomizableOperation in the middleware. section.operationShape being - // null means that this customization is rendered in a place where we don't need to figure out - // the module for an operation (which is the case for CustomizableOperation in the middleware - // that is rendered in the customize module). - rustTemplate( - """ - ##[doc(hidden)] - // This is a temporary method for testing. NEVER use it in production - pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self { - self.operation.properties_mut().insert(request_time); - self - } - - ##[doc(hidden)] - // This is a temporary method for testing. NEVER use it in production - pub fn user_agent_for_tests(mut self) -> Self { - self.operation.properties_mut().insert(#{AwsUserAgent}::for_tests()); - self - } - """, - *codegenScope, - ) - } else { - // The else branch is for rendering customization for the orchestrator. + if (section.isRuntimeModeOrchestrator) { rustTemplate( """ ##[doc(hidden)] @@ -97,6 +72,26 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : """, *codegenScope, ) + } else { + // TODO(enableNewSmithyRuntime): Delete this branch when middleware is no longer used + rustTemplate( + """ + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self { + self.operation.properties_mut().insert(request_time); + self + } + + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn user_agent_for_tests(mut self) -> Self { + self.operation.properties_mut().insert(#{AwsUserAgent}::for_tests()); + self + } + """, + *codegenScope, + ) } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationDecorator.kt index 93a01e41bb..8859cc598f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationDecorator.kt @@ -5,14 +5,13 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.client -import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section sealed class CustomizableOperationSection(name: String) : Section(name) { /** Write custom code into a customizable operation's impl block */ data class CustomizableOperationImpl( - val operationShape: OperationShape?, + val isRuntimeModeOrchestrator: Boolean, ) : CustomizableOperationSection("CustomizableOperationImpl") } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index ee4b0f3c79..eab249a9c0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -5,20 +5,20 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.client -import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.GenericTypeArg import software.amazon.smithy.rust.codegen.core.rustlang.RustGenerics +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations -import software.amazon.smithy.rust.codegen.core.util.outputShape /** * Generates the code required to add the `.customize()` function to the @@ -135,21 +135,18 @@ class CustomizableOperationGenerator( """, *codegenScope, "additional_methods" to writable { - writeCustomizations(customizations, CustomizableOperationSection.CustomizableOperationImpl(null)) + writeCustomizations(customizations, CustomizableOperationSection.CustomizableOperationImpl(false)) }, ) } - fun renderForOrchestrator(writer: RustWriter, operation: OperationShape) { - val symbolProvider = codegenContext.symbolProvider - val model = codegenContext.model - - val builderName = operation.fluentBuilderType(symbolProvider).name - val outputType = symbolProvider.toSymbol(operation.outputShape(model)) - val errorType = symbolProvider.symbolForOperationError(operation) - + fun renderForOrchestrator(crate: RustCrate) { val codegenScope = arrayOf( *preludeScope, + "CustomizableOperation" to ClientRustModule.Client.customize.toType() + .resolve("orchestrator::CustomizableOperation"), + "CustomizableSend" to ClientRustModule.Client.customize.toType() + .resolve("internal::CustomizableSend"), "HttpRequest" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::orchestrator::HttpRequest"), "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) @@ -160,120 +157,165 @@ class CustomizableOperationGenerator( .resolve("client::interceptor::MapRequestInterceptor"), "MutateRequestInterceptor" to RuntimeType.smithyRuntime(runtimeConfig) .resolve("client::interceptor::MutateRequestInterceptor"), - "OperationError" to errorType, - "OperationOutput" to outputType, "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "SendResult" to ClientRustModule.Client.customize.toType() + .resolve("internal::SendResult"), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), "SdkError" to RuntimeType.sdkError(runtimeConfig), "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::interceptors::SharedInterceptor"), ) - // TODO(enableNewSmithyRuntime): Centralize this struct rather than generating it once per operation - writer.rustTemplate( - """ - /// A wrapper type for [`$builderName`]($builderName) that allows for configuring a single - /// operation invocation. - pub struct CustomizableOperation { - pub(crate) fluent_builder: $builderName, - pub(crate) config_override: #{Option}, - pub(crate) interceptors: Vec<#{SharedInterceptor}>, - } + val customizeModule = ClientRustModule.Client.customize - impl CustomizableOperation { - /// Adds an [`Interceptor`](#{Interceptor}) that runs at specific stages of the request execution pipeline. - /// - /// Note that interceptors can also be added to `CustomizableOperation` by `config_override`, - /// `map_request`, and `mutate_request` (the last two are implemented via interceptors under the hood). - /// The order in which those user-specified operation interceptors are invoked should not be relied upon - /// as it is an implementation detail. - pub fn interceptor(mut self, interceptor: impl #{Interceptor} + #{Send} + #{Sync} + 'static) -> Self { - self.interceptors.push(#{SharedInterceptor}::new(interceptor)); - self - } + crate.withModule(customizeModule) { + renderConvenienceAliases(customizeModule, this) - /// Allows for customizing the operation's request. - pub fn map_request(mut self, f: F) -> Self - where - F: #{Fn}(#{HttpRequest}) -> #{Result}<#{HttpRequest}, E> - + #{Send} - + #{Sync} - + 'static, - E: ::std::error::Error + #{Send} + #{Sync} + 'static, - { - self.interceptors.push( - #{SharedInterceptor}::new( - #{MapRequestInterceptor}::new(f), - ), - ); - self - } + // TODO(enableNewSmithyRuntime): Render it directly under the customize module when CustomizableOperation + // in the middleware has been removed. + withInlineModule( + RustModule.new( + "orchestrator", + Visibility.PUBLIC, + true, + customizeModule, + documentationOverride = "Module for defining types for `CustomizableOperation` in the orchestrator", + ), + null, + ) { + rustTemplate( + """ + /// `CustomizableOperation` allows for configuring a single operation invocation before it is sent. + pub struct CustomizableOperation { + pub(crate) customizable_send: #{Box}>, + pub(crate) config_override: #{Option}, + pub(crate) interceptors: Vec<#{SharedInterceptor}>, + } - /// Convenience for `map_request` where infallible direct mutation of request is acceptable. - pub fn mutate_request(mut self, f: F) -> Self - where - F: #{Fn}(&mut http::Request<#{SdkBody}>) + #{Send} + #{Sync} + 'static, - { - self.interceptors.push( - #{SharedInterceptor}::new( - #{MutateRequestInterceptor}::new(f), - ), - ); - self - } + impl CustomizableOperation { + /// Adds an [`Interceptor`](#{Interceptor}) that runs at specific stages of the request execution pipeline. + /// + /// Note that interceptors can also be added to `CustomizableOperation` by `config_override`, + /// `map_request`, and `mutate_request` (the last two are implemented via interceptors under the hood). + /// The order in which those user-specified operation interceptors are invoked should not be relied upon + /// as it is an implementation detail. + pub fn interceptor(mut self, interceptor: impl #{Interceptor} + #{Send} + #{Sync} + 'static) -> Self { + self.interceptors.push(#{SharedInterceptor}::new(interceptor)); + self + } - /// Overrides config for a single operation invocation. - /// - /// `config_override` is applied to the operation configuration level. - /// The fields in the builder that are `Some` override those applied to the service - /// configuration level. For instance, - /// - /// Config A overridden by Config B == Config C - /// field_1: None, field_1: Some(v2), field_1: Some(v2), - /// field_2: Some(v1), field_2: Some(v2), field_2: Some(v2), - /// field_3: Some(v1), field_3: None, field_3: Some(v1), - pub fn config_override( - mut self, - config_override: impl #{Into}, - ) -> Self { - self.config_override = Some(config_override.into()); - self - } + /// Allows for customizing the operation's request. + pub fn map_request(mut self, f: F) -> Self + where + F: #{Fn}(#{HttpRequest}) -> #{Result}<#{HttpRequest}, MapE> + + #{Send} + + #{Sync} + + 'static, + MapE: ::std::error::Error + #{Send} + #{Sync} + 'static, + { + self.interceptors.push( + #{SharedInterceptor}::new( + #{MapRequestInterceptor}::new(f), + ), + ); + self + } - /// Sends the request and returns the response. - pub async fn send( - self - ) -> #{Result}< - #{OperationOutput}, - #{SdkError}< - #{OperationError}, - #{HttpResponse} - > - > { - let mut config_override = if let Some(config_override) = self.config_override { - config_override - } else { - crate::config::Builder::new() - }; - - self.interceptors.into_iter().for_each(|interceptor| { - config_override.add_interceptor(interceptor); - }); - - self.fluent_builder - .config_override(config_override) - .send_orchestrator() - .await - } + /// Convenience for `map_request` where infallible direct mutation of request is acceptable. + pub fn mutate_request(mut self, f: F) -> Self + where + F: #{Fn}(&mut http::Request<#{SdkBody}>) + #{Send} + #{Sync} + 'static, + { + self.interceptors.push( + #{SharedInterceptor}::new( + #{MutateRequestInterceptor}::new(f), + ), + ); + self + } - #{additional_methods} + /// Overrides config for a single operation invocation. + /// + /// `config_override` is applied to the operation configuration level. + /// The fields in the builder that are `Some` override those applied to the service + /// configuration level. For instance, + /// + /// Config A overridden by Config B == Config C + /// field_1: None, field_1: Some(v2), field_1: Some(v2), + /// field_2: Some(v1), field_2: Some(v2), field_2: Some(v2), + /// field_3: Some(v1), field_3: None, field_3: Some(v1), + pub fn config_override( + mut self, + config_override: impl #{Into}, + ) -> Self { + self.config_override = Some(config_override.into()); + self + } + + /// Sends the request and returns the response. + pub async fn send( + self, + ) -> #{SendResult} + where + E: std::error::Error + #{Send} + #{Sync} + 'static, + { + let mut config_override = if let Some(config_override) = self.config_override { + config_override + } else { + crate::config::Builder::new() + }; + + self.interceptors.into_iter().for_each(|interceptor| { + config_override.add_interceptor(interceptor); + }); + + (self.customizable_send)(config_override).await + } + + #{additional_methods} + } + """, + *codegenScope, + "additional_methods" to writable { + writeCustomizations( + customizations, + CustomizableOperationSection.CustomizableOperationImpl(true), + ) + }, + ) } - """, - *codegenScope, - "additional_methods" to writable { - writeCustomizations(customizations, CustomizableOperationSection.CustomizableOperationImpl(operation)) - }, - ) + } + } + + private fun renderConvenienceAliases(parentModule: RustModule, writer: RustWriter) { + writer.withInlineModule(RustModule.new("internal", Visibility.PUBCRATE, true, parentModule), null) { + rustTemplate( + """ + pub type BoxFuture = ::std::pin::Pin<#{Box} + #{Send}>>; + + pub type SendResult = #{Result}< + T, + #{SdkError}< + E, + #{HttpResponse}, + >, + >; + + pub trait CustomizableSend: + #{FnOnce}(crate::config::Builder) -> BoxFuture> + {} + + impl CustomizableSend for F + where + F: #{FnOnce}(crate::config::Builder) -> BoxFuture> + {} + """, + *preludeScope, + "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::HttpResponse"), + "SdkError" to RuntimeType.sdkError(runtimeConfig), + ) + } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 40e43425c4..5461979470 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -88,13 +88,13 @@ class FluentClientGenerator( operations.forEach { operation -> crate.withModule(symbolProvider.moduleForBuilder(operation)) { renderFluentBuilder(operation) - if (codegenContext.smithyRuntimeMode.generateOrchestrator) { - customizableOperationGenerator.renderForOrchestrator(this, operation) - } } } customizableOperationGenerator.render(crate) + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + customizableOperationGenerator.renderForOrchestrator(crate) + } } private fun renderFluentClient(crate: RustCrate) { @@ -450,13 +450,15 @@ class FluentClientGenerator( if (smithyRuntimeMode.generateOrchestrator) { val orchestratorScope = arrayOf( *preludeScope, - "CustomizableOperation" to symbolProvider.moduleForBuilder(operation).toType() - .resolve("CustomizableOperation"), + "CustomizableOperation" to ClientRustModule.Client.customize.toType() + .resolve("orchestrator::CustomizableOperation"), "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::orchestrator::HttpResponse"), "Operation" to operationSymbol, "OperationError" to errorType, "OperationOutput" to outputType, + "SendResult" to ClientRustModule.Client.customize.toType() + .resolve("internal::SendResult"), "SdkError" to RuntimeType.sdkError(runtimeConfig), ) rustTemplate( @@ -469,8 +471,24 @@ class FluentClientGenerator( ##[doc(hidden)] // TODO(enableNewSmithyRuntime): Remove `async` once we switch to orchestrator - pub async fn customize_orchestrator(self) -> #{CustomizableOperation} { - #{CustomizableOperation} { fluent_builder: self, config_override: None, interceptors: vec![] } + pub async fn customize_orchestrator( + self, + ) -> #{CustomizableOperation}< + #{OperationOutput}, + #{OperationError}, + > + { + #{CustomizableOperation} { + customizable_send: #{Box}::new(move |config_override| { + #{Box}::pin(async { + self.config_override(config_override) + .send_orchestrator() + .await + }) + }), + config_override: None, + interceptors: vec![], + } } """, *orchestratorScope, @@ -493,10 +511,16 @@ class FluentClientGenerator( /// Consumes this builder, creating a customizable operation that can be modified before being /// sent. // TODO(enableNewSmithyRuntime): Remove `async` and `Result` once we switch to orchestrator - pub async fn customize(self) -> #{Result}< - #{CustomizableOperation}, - #{SdkError}<#{OperationError}> - > { + pub async fn customize( + self, + ) -> #{Result}< + #{CustomizableOperation}< + #{OperationOutput}, + #{OperationError}, + >, + #{SdkError}<#{OperationError}>, + > + { #{Ok}(self.customize_orchestrator().await) } """, From c6e537969eb8ce473b0ec9f3fd970c7bcd436f21 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 26 May 2023 09:46:39 -0700 Subject: [PATCH 115/253] Fix event streams in the orchestrator implementation (#2673) ## Motivation and Context This PR gets event streams working in the client orchestrator implementation, and depends on #2671. The orchestrator's `TypeErasedBox` enforces a `Send + Sync` requirement on inputs and outputs. For the most part, this isn't an issue since almost all generated inputs/outputs are `Send + Sync`, but it turns out the `EventStreamSender` wasn't `Sync` due to an omission of the `Sync` bound. Thus, this PR is a breaking change, as it adds a `Sync` requirement for anyone who passes a stream to an event stream operation. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- CHANGELOG.next.toml | 12 + aws/rust-runtime/aws-runtime/Cargo.toml | 5 + aws/rust-runtime/aws-runtime/src/auth.rs | 470 +------------- .../aws-runtime/src/auth/sigv4.rs | 609 ++++++++++++++++++ aws/rust-runtime/aws-sigv4/src/lib.rs | 12 + .../smithy/rustsdk/SigV4AuthDecorator.kt | 8 +- .../protocol/MakeOperationGenerator.kt | 8 +- .../protocol/RequestSerializerGenerator.kt | 13 +- .../protocols/HttpBoundProtocolGenerator.kt | 16 +- .../protocol/ProtocolTestGeneratorTest.kt | 10 +- .../generators/protocol/ProtocolGenerator.kt | 15 +- .../HttpBoundProtocolPayloadGenerator.kt | 39 +- .../src/event_stream/sender.rs | 19 +- .../src/client/orchestrator.rs | 12 +- .../src/client/orchestrator.rs | 2 +- .../src/client/test_util/serializer.rs | 6 +- 16 files changed, 757 insertions(+), 499 deletions(-) create mode 100644 aws/rust-runtime/aws-runtime/src/auth/sigv4.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 86fe2d7fe1..f81531ff73 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -50,3 +50,15 @@ See the changes in https://github.com/awslabs/smithy-rs/pull/2671 for an example references = ["smithy-rs#2671"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } author = "jdisanti" + +[[aws-sdk-rust]] +message = "For event stream operations such as S3 SelectObjectContent or Transcribe StartStreamTranscription, the `EventStreamSender` in the input now requires the passed in `Stream` impl to implement `Sync`." +references = ["smithy-rs#2673"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "jdisanti" + +[[smithy-rs]] +message = "For event stream operations, the `EventStreamSender` in inputs/outputs now requires the passed in `Stream` impl to implement `Sync`." +references = ["smithy-rs#2673"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all"} +author = "jdisanti" diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index cd0e4bc0cd..c5bb21ca17 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -7,10 +7,14 @@ edition = "2021" license = "Apache-2.0" repository = "https://github.com/awslabs/smithy-rs" +[features] +event-stream = ["dep:aws-smithy-eventstream", "aws-sigv4/sign-eventstream"] + [dependencies] aws-credential-types = { path = "../aws-credential-types" } aws-http = { path = "../aws-http" } aws-sigv4 = { path = "../aws-sigv4" } +aws-smithy-eventstream = { path = "../../../rust-runtime/aws-smithy-eventstream", optional = true } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } @@ -22,6 +26,7 @@ tracing = "0.1" uuid = { version = "1", features = ["v4", "fast-rng"] } [dev-dependencies] +aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-test" } proptest = "1" serde = { version = "1", features = ["derive"]} diff --git a/aws/rust-runtime/aws-runtime/src/auth.rs b/aws/rust-runtime/aws-runtime/src/auth.rs index f436a5f028..149466a58a 100644 --- a/aws/rust-runtime/aws-runtime/src/auth.rs +++ b/aws/rust-runtime/aws-runtime/src/auth.rs @@ -4,472 +4,4 @@ */ /// Auth implementations for SigV4. -pub mod sigv4 { - use aws_credential_types::Credentials; - use aws_sigv4::http_request::{ - sign, PayloadChecksumKind, PercentEncodingMode, SessionTokenMode, SignableBody, - SignableRequest, SignatureLocation, SigningParams, SigningSettings, - UriPathNormalizationMode, - }; - use aws_smithy_runtime_api::client::auth::{ - AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, - }; - use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; - use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpRequest}; - use aws_smithy_runtime_api::config_bag::ConfigBag; - use aws_smithy_types::Document; - use aws_types::region::{Region, SigningRegion}; - use aws_types::SigningService; - use std::borrow::Cow; - use std::error::Error as StdError; - use std::fmt; - use std::time::{Duration, SystemTime}; - - const EXPIRATION_WARNING: &str = "Presigned request will expire before the given \ - `expires_in` duration because the credentials used to sign it will expire first."; - - /// Auth scheme ID for SigV4. - pub const SCHEME_ID: AuthSchemeId = AuthSchemeId::new("sigv4"); - - struct EndpointAuthSchemeConfig { - signing_region_override: Option, - signing_service_override: Option, - } - - #[derive(Debug)] - enum SigV4SigningError { - MissingOperationSigningConfig, - MissingSigningRegion, - MissingSigningService, - WrongIdentityType(Identity), - BadTypeInEndpointAuthSchemeConfig(&'static str), - } - - impl fmt::Display for SigV4SigningError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use SigV4SigningError::*; - let mut w = |s| f.write_str(s); - match self { - MissingOperationSigningConfig => w("missing operation signing config for SigV4"), - MissingSigningRegion => w("missing signing region for SigV4 signing"), - MissingSigningService => w("missing signing service for SigV4 signing"), - WrongIdentityType(identity) => { - write!(f, "wrong identity type for SigV4: {identity:?}") - } - BadTypeInEndpointAuthSchemeConfig(field_name) => { - write!( - f, - "unexpected type for `{field_name}` in endpoint auth scheme config", - ) - } - } - } - } - - impl StdError for SigV4SigningError { - fn source(&self) -> Option<&(dyn StdError + 'static)> { - match self { - Self::MissingOperationSigningConfig => None, - Self::MissingSigningRegion => None, - Self::MissingSigningService => None, - Self::WrongIdentityType(_) => None, - Self::BadTypeInEndpointAuthSchemeConfig(_) => None, - } - } - } - - /// SigV4 auth scheme. - #[derive(Debug, Default)] - pub struct SigV4HttpAuthScheme { - signer: SigV4HttpRequestSigner, - } - - impl SigV4HttpAuthScheme { - /// Creates a new `SigV4HttpAuthScheme`. - pub fn new() -> Self { - Default::default() - } - } - - impl HttpAuthScheme for SigV4HttpAuthScheme { - fn scheme_id(&self) -> AuthSchemeId { - SCHEME_ID - } - - fn identity_resolver<'a>( - &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { - identity_resolvers.identity_resolver(self.scheme_id()) - } - - fn request_signer(&self) -> &dyn HttpRequestSigner { - &self.signer - } - } - - /// Type of SigV4 signature. - #[derive(Debug, Eq, PartialEq, Clone, Copy)] - pub enum HttpSignatureType { - /// A signature for a full http request should be computed, with header updates applied to the signing result. - HttpRequestHeaders, - - /// A signature for a full http request should be computed, with query param updates applied to the signing result. - /// - /// This is typically used for presigned URLs. - HttpRequestQueryParams, - } - - /// Signing options for SigV4. - #[derive(Clone, Debug, Eq, PartialEq)] - #[non_exhaustive] - pub struct SigningOptions { - /// Apply URI encoding twice. - pub double_uri_encode: bool, - /// Apply a SHA-256 payload checksum. - pub content_sha256_header: bool, - /// Normalize the URI path before signing. - pub normalize_uri_path: bool, - /// Omit the session token from the signature. - pub omit_session_token: bool, - /// Optional override for the payload to be used in signing. - pub payload_override: Option>, - /// Signature type. - pub signature_type: HttpSignatureType, - /// Whether or not the signature is optional. - pub signing_optional: bool, - /// Optional expiration (for presigning) - pub expires_in: Option, - } - - impl Default for SigningOptions { - fn default() -> Self { - Self { - double_uri_encode: true, - content_sha256_header: false, - normalize_uri_path: true, - omit_session_token: false, - payload_override: None, - signature_type: HttpSignatureType::HttpRequestHeaders, - signing_optional: false, - expires_in: None, - } - } - } - - /// SigV4 signing configuration for an operation - /// - /// Although these fields MAY be customized on a per request basis, they are generally static - /// for a given operation - #[derive(Clone, Debug, PartialEq, Eq)] - pub struct SigV4OperationSigningConfig { - /// AWS Region to sign for. - pub region: Option, - /// AWS Service to sign for. - pub service: Option, - /// Signing options. - pub signing_options: SigningOptions, - } - - /// SigV4 HTTP request signer. - #[derive(Debug, Default)] - pub struct SigV4HttpRequestSigner; - - impl SigV4HttpRequestSigner { - /// Creates a new signer instance. - pub fn new() -> Self { - Self - } - - fn settings(operation_config: &SigV4OperationSigningConfig) -> SigningSettings { - let mut settings = SigningSettings::default(); - settings.percent_encoding_mode = if operation_config.signing_options.double_uri_encode { - PercentEncodingMode::Double - } else { - PercentEncodingMode::Single - }; - settings.payload_checksum_kind = - if operation_config.signing_options.content_sha256_header { - PayloadChecksumKind::XAmzSha256 - } else { - PayloadChecksumKind::NoHeader - }; - settings.uri_path_normalization_mode = - if operation_config.signing_options.normalize_uri_path { - UriPathNormalizationMode::Enabled - } else { - UriPathNormalizationMode::Disabled - }; - settings.session_token_mode = if operation_config.signing_options.omit_session_token { - SessionTokenMode::Exclude - } else { - SessionTokenMode::Include - }; - settings.signature_location = match operation_config.signing_options.signature_type { - HttpSignatureType::HttpRequestHeaders => SignatureLocation::Headers, - HttpSignatureType::HttpRequestQueryParams => SignatureLocation::QueryParams, - }; - settings.expires_in = operation_config.signing_options.expires_in; - settings - } - - fn signing_params<'a>( - settings: SigningSettings, - credentials: &'a Credentials, - operation_config: &'a SigV4OperationSigningConfig, - request_timestamp: SystemTime, - ) -> Result, SigV4SigningError> { - if let Some(expires_in) = settings.expires_in { - if let Some(creds_expires_time) = credentials.expiry() { - let presigned_expires_time = request_timestamp + expires_in; - if presigned_expires_time > creds_expires_time { - tracing::warn!(EXPIRATION_WARNING); - } - } - } - - let mut builder = SigningParams::builder() - .access_key(credentials.access_key_id()) - .secret_key(credentials.secret_access_key()) - .region( - operation_config - .region - .as_ref() - .ok_or(SigV4SigningError::MissingSigningRegion)? - .as_ref(), - ) - .service_name( - operation_config - .service - .as_ref() - .ok_or(SigV4SigningError::MissingSigningService)? - .as_ref(), - ) - .time(request_timestamp) - .settings(settings); - builder.set_security_token(credentials.session_token()); - Ok(builder.build().expect("all required fields set")) - } - - fn extract_operation_config<'a>( - auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'a>, - config_bag: &'a ConfigBag, - ) -> Result, SigV4SigningError> { - let operation_config = config_bag - .get::() - .ok_or(SigV4SigningError::MissingOperationSigningConfig)?; - - let EndpointAuthSchemeConfig { - signing_region_override, - signing_service_override, - } = Self::extract_endpoint_auth_scheme_config(auth_scheme_endpoint_config)?; - - match (signing_region_override, signing_service_override) { - (None, None) => Ok(Cow::Borrowed(operation_config)), - (region, service) => { - let mut operation_config = operation_config.clone(); - if region.is_some() { - operation_config.region = region; - } - if service.is_some() { - operation_config.service = service; - } - Ok(Cow::Owned(operation_config)) - } - } - } - - fn extract_endpoint_auth_scheme_config( - endpoint_config: AuthSchemeEndpointConfig<'_>, - ) -> Result { - let (mut signing_region_override, mut signing_service_override) = (None, None); - if let Some(config) = endpoint_config.config().and_then(Document::as_object) { - use SigV4SigningError::BadTypeInEndpointAuthSchemeConfig as UnexpectedType; - signing_region_override = match config.get("signingRegion") { - Some(Document::String(s)) => Some(SigningRegion::from(Region::new(s.clone()))), - None => None, - _ => return Err(UnexpectedType("signingRegion")), - }; - signing_service_override = match config.get("signingName") { - Some(Document::String(s)) => Some(SigningService::from(s.to_string())), - None => None, - _ => return Err(UnexpectedType("signingName")), - }; - } - Ok(EndpointAuthSchemeConfig { - signing_region_override, - signing_service_override, - }) - } - } - - impl HttpRequestSigner for SigV4HttpRequestSigner { - fn sign_request( - &self, - request: &mut HttpRequest, - identity: &Identity, - auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, - config_bag: &ConfigBag, - ) -> Result<(), BoxError> { - let operation_config = - Self::extract_operation_config(auth_scheme_endpoint_config, config_bag)?; - let request_time = config_bag.request_time().unwrap_or_default().system_time(); - - let credentials = if let Some(creds) = identity.data::() { - creds - } else if operation_config.signing_options.signing_optional { - tracing::debug!("skipped SigV4 signing since signing is optional for this operation and there are no credentials"); - return Ok(()); - } else { - return Err(SigV4SigningError::WrongIdentityType(identity.clone()).into()); - }; - - let settings = Self::settings(&operation_config); - let signing_params = - Self::signing_params(settings, credentials, &operation_config, request_time)?; - - let (signing_instructions, _signature) = { - // A body that is already in memory can be signed directly. A body that is not in memory - // (any sort of streaming body or presigned request) will be signed via UNSIGNED-PAYLOAD. - let signable_body = operation_config - .signing_options - .payload_override - .as_ref() - // the payload_override is a cheap clone because it contains either a - // reference or a short checksum (we're not cloning the entire body) - .cloned() - .unwrap_or_else(|| { - request - .body() - .bytes() - .map(SignableBody::Bytes) - .unwrap_or(SignableBody::UnsignedPayload) - }); - - let signable_request = SignableRequest::new( - request.method(), - request.uri(), - request.headers(), - signable_body, - ); - sign(signable_request, &signing_params)? - } - .into_parts(); - - signing_instructions.apply_to_request(request); - Ok(()) - } - } - - #[cfg(test)] - mod tests { - use super::*; - use aws_credential_types::Credentials; - use aws_sigv4::http_request::SigningSettings; - use aws_types::region::SigningRegion; - use aws_types::SigningService; - use std::collections::HashMap; - use std::time::{Duration, SystemTime}; - use tracing_test::traced_test; - - #[test] - #[traced_test] - fn expiration_warning() { - let now = SystemTime::UNIX_EPOCH + Duration::from_secs(1000); - let creds_expire_in = Duration::from_secs(100); - - let mut settings = SigningSettings::default(); - settings.expires_in = Some(creds_expire_in - Duration::from_secs(10)); - - let credentials = Credentials::new( - "test-access-key", - "test-secret-key", - Some("test-session-token".into()), - Some(now + creds_expire_in), - "test", - ); - let operation_config = SigV4OperationSigningConfig { - region: Some(SigningRegion::from_static("test")), - service: Some(SigningService::from_static("test")), - signing_options: SigningOptions { - double_uri_encode: true, - content_sha256_header: true, - normalize_uri_path: true, - omit_session_token: true, - signature_type: HttpSignatureType::HttpRequestHeaders, - signing_optional: false, - expires_in: None, - payload_override: None, - }, - }; - SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now) - .unwrap(); - assert!(!logs_contain(EXPIRATION_WARNING)); - - let mut settings = SigningSettings::default(); - settings.expires_in = Some(creds_expire_in + Duration::from_secs(10)); - - SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now) - .unwrap(); - assert!(logs_contain(EXPIRATION_WARNING)); - } - - #[test] - fn endpoint_config_overrides_region_and_service() { - let mut cfg = ConfigBag::base(); - cfg.put(SigV4OperationSigningConfig { - region: Some(SigningRegion::from(Region::new("override-this-region"))), - service: Some(SigningService::from_static("override-this-service")), - signing_options: Default::default(), - }); - let config = Document::Object({ - let mut out = HashMap::new(); - out.insert("name".to_string(), "sigv4".to_string().into()); - out.insert( - "signingName".to_string(), - "qldb-override".to_string().into(), - ); - out.insert( - "signingRegion".to_string(), - "us-east-override".to_string().into(), - ); - out - }); - let config = AuthSchemeEndpointConfig::new(Some(&config)); - - let result = - SigV4HttpRequestSigner::extract_operation_config(config, &cfg).expect("success"); - - assert_eq!( - result.region, - Some(SigningRegion::from(Region::new("us-east-override"))) - ); - assert_eq!( - result.service, - Some(SigningService::from_static("qldb-override")) - ); - assert!(matches!(result, Cow::Owned(_))); - } - - #[test] - fn endpoint_config_supports_fallback_when_region_or_service_are_unset() { - let mut cfg = ConfigBag::base(); - cfg.put(SigV4OperationSigningConfig { - region: Some(SigningRegion::from(Region::new("us-east-1"))), - service: Some(SigningService::from_static("qldb")), - signing_options: Default::default(), - }); - let config = AuthSchemeEndpointConfig::empty(); - - let result = - SigV4HttpRequestSigner::extract_operation_config(config, &cfg).expect("success"); - - assert_eq!( - result.region, - Some(SigningRegion::from(Region::new("us-east-1"))) - ); - assert_eq!(result.service, Some(SigningService::from_static("qldb"))); - assert!(matches!(result, Cow::Borrowed(_))); - } - } -} +pub mod sigv4; diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs new file mode 100644 index 0000000000..8d72556843 --- /dev/null +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -0,0 +1,609 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_credential_types::Credentials; +use aws_sigv4::http_request::{ + sign, PayloadChecksumKind, PercentEncodingMode, SessionTokenMode, SignableBody, + SignableRequest, SignatureLocation, SigningParams, SigningSettings, UriPathNormalizationMode, +}; +use aws_smithy_runtime_api::client::auth::{ + AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, +}; +use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; +use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpRequest}; +use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::Document; +use aws_types::region::{Region, SigningRegion}; +use aws_types::SigningService; +use std::borrow::Cow; +use std::error::Error as StdError; +use std::fmt; +use std::time::{Duration, SystemTime}; + +const EXPIRATION_WARNING: &str = "Presigned request will expire before the given \ + `expires_in` duration because the credentials used to sign it will expire first."; + +/// Auth scheme ID for SigV4. +pub const SCHEME_ID: AuthSchemeId = AuthSchemeId::new("sigv4"); + +struct EndpointAuthSchemeConfig { + signing_region_override: Option, + signing_service_override: Option, +} + +#[derive(Debug)] +enum SigV4SigningError { + MissingOperationSigningConfig, + MissingSigningRegion, + MissingSigningService, + WrongIdentityType(Identity), + BadTypeInEndpointAuthSchemeConfig(&'static str), +} + +impl fmt::Display for SigV4SigningError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use SigV4SigningError::*; + let mut w = |s| f.write_str(s); + match self { + MissingOperationSigningConfig => w("missing operation signing config for SigV4"), + MissingSigningRegion => w("missing signing region for SigV4 signing"), + MissingSigningService => w("missing signing service for SigV4 signing"), + WrongIdentityType(identity) => { + write!(f, "wrong identity type for SigV4: {identity:?}") + } + BadTypeInEndpointAuthSchemeConfig(field_name) => { + write!( + f, + "unexpected type for `{field_name}` in endpoint auth scheme config", + ) + } + } + } +} + +impl StdError for SigV4SigningError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Self::MissingOperationSigningConfig => None, + Self::MissingSigningRegion => None, + Self::MissingSigningService => None, + Self::WrongIdentityType(_) => None, + Self::BadTypeInEndpointAuthSchemeConfig(_) => None, + } + } +} + +/// SigV4 auth scheme. +#[derive(Debug, Default)] +pub struct SigV4HttpAuthScheme { + signer: SigV4HttpRequestSigner, +} + +impl SigV4HttpAuthScheme { + /// Creates a new `SigV4HttpAuthScheme`. + pub fn new() -> Self { + Default::default() + } +} + +impl HttpAuthScheme for SigV4HttpAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + SCHEME_ID + } + + fn identity_resolver<'a>( + &self, + identity_resolvers: &'a IdentityResolvers, + ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers.identity_resolver(self.scheme_id()) + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + &self.signer + } +} + +/// Type of SigV4 signature. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum HttpSignatureType { + /// A signature for a full http request should be computed, with header updates applied to the signing result. + HttpRequestHeaders, + + /// A signature for a full http request should be computed, with query param updates applied to the signing result. + /// + /// This is typically used for presigned URLs. + HttpRequestQueryParams, +} + +/// Signing options for SigV4. +#[derive(Clone, Debug, Eq, PartialEq)] +#[non_exhaustive] +pub struct SigningOptions { + /// Apply URI encoding twice. + pub double_uri_encode: bool, + /// Apply a SHA-256 payload checksum. + pub content_sha256_header: bool, + /// Normalize the URI path before signing. + pub normalize_uri_path: bool, + /// Omit the session token from the signature. + pub omit_session_token: bool, + /// Optional override for the payload to be used in signing. + pub payload_override: Option>, + /// Signature type. + pub signature_type: HttpSignatureType, + /// Whether or not the signature is optional. + pub signing_optional: bool, + /// Optional expiration (for presigning) + pub expires_in: Option, +} + +impl Default for SigningOptions { + fn default() -> Self { + Self { + double_uri_encode: true, + content_sha256_header: false, + normalize_uri_path: true, + omit_session_token: false, + payload_override: None, + signature_type: HttpSignatureType::HttpRequestHeaders, + signing_optional: false, + expires_in: None, + } + } +} + +/// SigV4 signing configuration for an operation +/// +/// Although these fields MAY be customized on a per request basis, they are generally static +/// for a given operation +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SigV4OperationSigningConfig { + /// AWS Region to sign for. + pub region: Option, + /// AWS Service to sign for. + pub service: Option, + /// Signing options. + pub signing_options: SigningOptions, +} + +/// SigV4 HTTP request signer. +#[derive(Debug, Default)] +pub struct SigV4HttpRequestSigner; + +impl SigV4HttpRequestSigner { + /// Creates a new signer instance. + pub fn new() -> Self { + Self + } + + fn settings(operation_config: &SigV4OperationSigningConfig) -> SigningSettings { + let mut settings = SigningSettings::default(); + settings.percent_encoding_mode = if operation_config.signing_options.double_uri_encode { + PercentEncodingMode::Double + } else { + PercentEncodingMode::Single + }; + settings.payload_checksum_kind = if operation_config.signing_options.content_sha256_header { + PayloadChecksumKind::XAmzSha256 + } else { + PayloadChecksumKind::NoHeader + }; + settings.uri_path_normalization_mode = + if operation_config.signing_options.normalize_uri_path { + UriPathNormalizationMode::Enabled + } else { + UriPathNormalizationMode::Disabled + }; + settings.session_token_mode = if operation_config.signing_options.omit_session_token { + SessionTokenMode::Exclude + } else { + SessionTokenMode::Include + }; + settings.signature_location = match operation_config.signing_options.signature_type { + HttpSignatureType::HttpRequestHeaders => SignatureLocation::Headers, + HttpSignatureType::HttpRequestQueryParams => SignatureLocation::QueryParams, + }; + settings.expires_in = operation_config.signing_options.expires_in; + settings + } + + fn signing_params<'a>( + settings: SigningSettings, + credentials: &'a Credentials, + operation_config: &'a SigV4OperationSigningConfig, + request_timestamp: SystemTime, + ) -> Result, SigV4SigningError> { + if let Some(expires_in) = settings.expires_in { + if let Some(creds_expires_time) = credentials.expiry() { + let presigned_expires_time = request_timestamp + expires_in; + if presigned_expires_time > creds_expires_time { + tracing::warn!(EXPIRATION_WARNING); + } + } + } + + let mut builder = SigningParams::builder() + .access_key(credentials.access_key_id()) + .secret_key(credentials.secret_access_key()) + .region( + operation_config + .region + .as_ref() + .ok_or(SigV4SigningError::MissingSigningRegion)? + .as_ref(), + ) + .service_name( + operation_config + .service + .as_ref() + .ok_or(SigV4SigningError::MissingSigningService)? + .as_ref(), + ) + .time(request_timestamp) + .settings(settings); + builder.set_security_token(credentials.session_token()); + Ok(builder.build().expect("all required fields set")) + } + + fn extract_operation_config<'a>( + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'a>, + config_bag: &'a ConfigBag, + ) -> Result, SigV4SigningError> { + let operation_config = config_bag + .get::() + .ok_or(SigV4SigningError::MissingOperationSigningConfig)?; + + let EndpointAuthSchemeConfig { + signing_region_override, + signing_service_override, + } = Self::extract_endpoint_auth_scheme_config(auth_scheme_endpoint_config)?; + + match (signing_region_override, signing_service_override) { + (None, None) => Ok(Cow::Borrowed(operation_config)), + (region, service) => { + let mut operation_config = operation_config.clone(); + if region.is_some() { + operation_config.region = region; + } + if service.is_some() { + operation_config.service = service; + } + Ok(Cow::Owned(operation_config)) + } + } + } + + fn extract_endpoint_auth_scheme_config( + endpoint_config: AuthSchemeEndpointConfig<'_>, + ) -> Result { + let (mut signing_region_override, mut signing_service_override) = (None, None); + if let Some(config) = endpoint_config.config().and_then(Document::as_object) { + use SigV4SigningError::BadTypeInEndpointAuthSchemeConfig as UnexpectedType; + signing_region_override = match config.get("signingRegion") { + Some(Document::String(s)) => Some(SigningRegion::from(Region::new(s.clone()))), + None => None, + _ => return Err(UnexpectedType("signingRegion")), + }; + signing_service_override = match config.get("signingName") { + Some(Document::String(s)) => Some(SigningService::from(s.to_string())), + None => None, + _ => return Err(UnexpectedType("signingName")), + }; + } + Ok(EndpointAuthSchemeConfig { + signing_region_override, + signing_service_override, + }) + } +} + +impl HttpRequestSigner for SigV4HttpRequestSigner { + fn sign_request( + &self, + request: &mut HttpRequest, + identity: &Identity, + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + config_bag: &ConfigBag, + ) -> Result<(), BoxError> { + let operation_config = + Self::extract_operation_config(auth_scheme_endpoint_config, config_bag)?; + let request_time = config_bag.request_time().unwrap_or_default().system_time(); + + let credentials = if let Some(creds) = identity.data::() { + creds + } else if operation_config.signing_options.signing_optional { + tracing::debug!("skipped SigV4 signing since signing is optional for this operation and there are no credentials"); + return Ok(()); + } else { + return Err(SigV4SigningError::WrongIdentityType(identity.clone()).into()); + }; + + let settings = Self::settings(&operation_config); + let signing_params = + Self::signing_params(settings, credentials, &operation_config, request_time)?; + + let (signing_instructions, _signature) = { + // A body that is already in memory can be signed directly. A body that is not in memory + // (any sort of streaming body or presigned request) will be signed via UNSIGNED-PAYLOAD. + let signable_body = operation_config + .signing_options + .payload_override + .as_ref() + // the payload_override is a cheap clone because it contains either a + // reference or a short checksum (we're not cloning the entire body) + .cloned() + .unwrap_or_else(|| { + request + .body() + .bytes() + .map(SignableBody::Bytes) + .unwrap_or(SignableBody::UnsignedPayload) + }); + + let signable_request = SignableRequest::new( + request.method(), + request.uri(), + request.headers(), + signable_body, + ); + sign(signable_request, &signing_params)? + } + .into_parts(); + + // If this is an event stream operation, set up the event stream signer + #[cfg(feature = "event-stream")] + { + use aws_smithy_eventstream::frame::DeferredSignerSender; + use aws_smithy_runtime_api::client::orchestrator::RequestTime; + use event_stream::SigV4MessageSigner; + + if let Some(signer_sender) = config_bag.get::() { + let time_override = config_bag.get::().copied(); + signer_sender + .send(Box::new(SigV4MessageSigner::new( + _signature, + credentials.clone(), + Region::new(signing_params.region().to_string()).into(), + signing_params.service_name().to_string().into(), + time_override, + )) as _) + .expect("failed to send deferred signer"); + } + } + + signing_instructions.apply_to_request(request); + Ok(()) + } +} + +#[cfg(feature = "event-stream")] +mod event_stream { + use aws_credential_types::Credentials; + use aws_sigv4::event_stream::{sign_empty_message, sign_message}; + use aws_sigv4::SigningParams; + use aws_smithy_eventstream::frame::{Message, SignMessage, SignMessageError}; + use aws_smithy_runtime_api::client::orchestrator::RequestTime; + use aws_types::region::SigningRegion; + use aws_types::SigningService; + + /// Event Stream SigV4 signing implementation. + #[derive(Debug)] + pub(super) struct SigV4MessageSigner { + last_signature: String, + credentials: Credentials, + signing_region: SigningRegion, + signing_service: SigningService, + time: Option, + } + + impl SigV4MessageSigner { + pub(super) fn new( + last_signature: String, + credentials: Credentials, + signing_region: SigningRegion, + signing_service: SigningService, + time: Option, + ) -> Self { + Self { + last_signature, + credentials, + signing_region, + signing_service, + time, + } + } + + fn signing_params(&self) -> SigningParams<'_, ()> { + let mut builder = SigningParams::builder() + .access_key(self.credentials.access_key_id()) + .secret_key(self.credentials.secret_access_key()) + .region(self.signing_region.as_ref()) + .service_name(self.signing_service.as_ref()) + .time(self.time.unwrap_or_default().system_time()) + .settings(()); + builder.set_security_token(self.credentials.session_token()); + builder.build().unwrap() + } + } + + impl SignMessage for SigV4MessageSigner { + fn sign(&mut self, message: Message) -> Result { + let (signed_message, signature) = { + let params = self.signing_params(); + sign_message(&message, &self.last_signature, ¶ms).into_parts() + }; + self.last_signature = signature; + Ok(signed_message) + } + + fn sign_empty(&mut self) -> Option> { + let (signed_message, signature) = { + let params = self.signing_params(); + sign_empty_message(&self.last_signature, ¶ms).into_parts() + }; + self.last_signature = signature; + Some(Ok(signed_message)) + } + } + + #[cfg(test)] + mod tests { + use super::*; + use aws_credential_types::Credentials; + use aws_smithy_eventstream::frame::{HeaderValue, Message, SignMessage}; + use aws_types::region::Region; + use aws_types::region::SigningRegion; + use aws_types::SigningService; + use std::time::{Duration, UNIX_EPOCH}; + + fn check_send_sync(value: T) -> T { + value + } + + #[test] + fn sign_message() { + let region = Region::new("us-east-1"); + let mut signer = check_send_sync(SigV4MessageSigner::new( + "initial-signature".into(), + Credentials::for_tests(), + SigningRegion::from(region), + SigningService::from_static("transcribe"), + Some(RequestTime::new(UNIX_EPOCH + Duration::new(1611160427, 0))), + )); + let mut signatures = Vec::new(); + for _ in 0..5 { + let signed = signer + .sign(Message::new(&b"identical message"[..])) + .unwrap(); + if let HeaderValue::ByteArray(signature) = signed + .headers() + .iter() + .find(|h| h.name().as_str() == ":chunk-signature") + .unwrap() + .value() + { + signatures.push(signature.clone()); + } else { + panic!("failed to get the :chunk-signature") + } + } + for i in 1..signatures.len() { + assert_ne!(signatures[i - 1], signatures[i]); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_credential_types::Credentials; + use aws_sigv4::http_request::SigningSettings; + use aws_types::region::SigningRegion; + use aws_types::SigningService; + use std::collections::HashMap; + use std::time::{Duration, SystemTime}; + use tracing_test::traced_test; + + #[test] + #[traced_test] + fn expiration_warning() { + let now = SystemTime::UNIX_EPOCH + Duration::from_secs(1000); + let creds_expire_in = Duration::from_secs(100); + + let mut settings = SigningSettings::default(); + settings.expires_in = Some(creds_expire_in - Duration::from_secs(10)); + + let credentials = Credentials::new( + "test-access-key", + "test-secret-key", + Some("test-session-token".into()), + Some(now + creds_expire_in), + "test", + ); + let operation_config = SigV4OperationSigningConfig { + region: Some(SigningRegion::from_static("test")), + service: Some(SigningService::from_static("test")), + signing_options: SigningOptions { + double_uri_encode: true, + content_sha256_header: true, + normalize_uri_path: true, + omit_session_token: true, + signature_type: HttpSignatureType::HttpRequestHeaders, + signing_optional: false, + expires_in: None, + payload_override: None, + }, + }; + SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now) + .unwrap(); + assert!(!logs_contain(EXPIRATION_WARNING)); + + let mut settings = SigningSettings::default(); + settings.expires_in = Some(creds_expire_in + Duration::from_secs(10)); + + SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now) + .unwrap(); + assert!(logs_contain(EXPIRATION_WARNING)); + } + + #[test] + fn endpoint_config_overrides_region_and_service() { + let mut cfg = ConfigBag::base(); + cfg.put(SigV4OperationSigningConfig { + region: Some(SigningRegion::from(Region::new("override-this-region"))), + service: Some(SigningService::from_static("override-this-service")), + signing_options: Default::default(), + }); + let config = Document::Object({ + let mut out = HashMap::new(); + out.insert("name".to_string(), "sigv4".to_string().into()); + out.insert( + "signingName".to_string(), + "qldb-override".to_string().into(), + ); + out.insert( + "signingRegion".to_string(), + "us-east-override".to_string().into(), + ); + out + }); + let config = AuthSchemeEndpointConfig::new(Some(&config)); + + let result = + SigV4HttpRequestSigner::extract_operation_config(config, &cfg).expect("success"); + + assert_eq!( + result.region, + Some(SigningRegion::from(Region::new("us-east-override"))) + ); + assert_eq!( + result.service, + Some(SigningService::from_static("qldb-override")) + ); + assert!(matches!(result, Cow::Owned(_))); + } + + #[test] + fn endpoint_config_supports_fallback_when_region_or_service_are_unset() { + let mut cfg = ConfigBag::base(); + cfg.put(SigV4OperationSigningConfig { + region: Some(SigningRegion::from(Region::new("us-east-1"))), + service: Some(SigningService::from_static("qldb")), + signing_options: Default::default(), + }); + let config = AuthSchemeEndpointConfig::empty(); + + let result = + SigV4HttpRequestSigner::extract_operation_config(config, &cfg).expect("success"); + + assert_eq!( + result.region, + Some(SigningRegion::from(Region::new("us-east-1"))) + ); + assert_eq!(result.service, Some(SigningService::from_static("qldb"))); + assert!(matches!(result, Cow::Borrowed(_))); + } +} diff --git a/aws/rust-runtime/aws-sigv4/src/lib.rs b/aws/rust-runtime/aws-sigv4/src/lib.rs index 1d3b36ade9..d20df23994 100644 --- a/aws/rust-runtime/aws-sigv4/src/lib.rs +++ b/aws/rust-runtime/aws-sigv4/src/lib.rs @@ -49,6 +49,18 @@ pub struct SigningParams<'a, S> { pub(crate) settings: S, } +impl<'a, S> SigningParams<'a, S> { + /// Returns the region that will be used to sign + pub fn region(&self) -> &str { + self.region + } + + /// Returns the service name that will be used to sign + pub fn service_name(&self) -> &str { + self.service_name + } +} + impl<'a, S: fmt::Debug> fmt::Debug for SigningParams<'a, S> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SigningParams") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 3ca470fd0e..dbf6710a4c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -21,6 +21,7 @@ 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.util.hasEventStreamOperations import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.isInputEventStream import software.amazon.smithy.rust.codegen.core.util.letIf @@ -47,7 +48,7 @@ class SigV4AuthDecorator : ClientCodegenDecorator { } } -private class AuthServiceRuntimePluginCustomization(codegenContext: ClientCodegenContext) : +private class AuthServiceRuntimePluginCustomization(private val codegenContext: ClientCodegenContext) : ServiceRuntimePluginCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope by lazy { @@ -72,6 +73,11 @@ private class AuthServiceRuntimePluginCustomization(codegenContext: ClientCodege } is ServiceRuntimePluginSection.AdditionalConfig -> { + val serviceHasEventStream = codegenContext.serviceShape.hasEventStreamOperations(codegenContext.model) + if (serviceHasEventStream) { + // enable the aws-runtime `sign-eventstream` feature + addDependency(AwsCargoDependency.awsRuntime(runtimeConfig).withFeature("event-stream").toType().toSymbol()) + } section.putConfigValue(this) { rustTemplate("#{SigningService}::from_static(self.handle.conf.signing_service())", *codegenScope) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt index 8f2dcda292..ec806de95b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.generators.http.RequestBindingGenerator +import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientAdditionalPayloadContext import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.docs @@ -108,7 +109,12 @@ open class MakeOperationGenerator( // Clippy warning to make the codegen a little simpler in that case. Attribute.AllowClippyUselessConversion.render(this) withBlockTemplate("let body = #{SdkBody}::from(", ");", *codegenScope) { - bodyGenerator.generatePayload(this, "self", shape) + bodyGenerator.generatePayload( + this, + "self", + shape, + ClientAdditionalPayloadContext(propertyBagAvailable = true), + ) val streamingMember = shape.inputShape(model).findStreamingMember(model) val isBlobStreaming = streamingMember != null && model.expectShape(streamingMember.target) is BlobShape if (isBlobStreaming) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt index 22870147b5..90482e8e9a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.model.shapes.OperationShape 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.http.RequestBindingGenerator +import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientAdditionalPayloadContext import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -37,6 +38,7 @@ class RequestSerializerGenerator( val orchestrator = runtimeApi.resolve("client::orchestrator") arrayOf( "BoxError" to orchestrator.resolve("BoxError"), + "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), "HttpRequest" to orchestrator.resolve("HttpRequest"), "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, "Input" to interceptorContext.resolve("Input"), @@ -61,7 +63,7 @@ class RequestSerializerGenerator( struct ${operationName}RequestSerializer; impl #{RequestSerializer} for ${operationName}RequestSerializer { ##[allow(unused_mut, clippy::let_and_return, clippy::needless_borrow, clippy::useless_conversion)] - fn serialize_input(&self, input: #{Input}) -> Result<#{HttpRequest}, #{BoxError}> { + fn serialize_input(&self, input: #{Input}, _cfg: &mut #{ConfigBag}) -> Result<#{HttpRequest}, #{BoxError}> { let input = #{TypedBox}::<#{ConcreteInput}>::assume_from(input).expect("correct type").unwrap(); let mut request_builder = { #{create_http_request} @@ -76,7 +78,14 @@ class RequestSerializerGenerator( "ConcreteInput" to inputSymbol, "create_http_request" to createHttpRequest(operationShape), "generate_body" to writable { - val body = writable { bodyGenerator.generatePayload(this, "input", operationShape) } + val body = writable { + bodyGenerator.generatePayload( + this, + "input", + operationShape, + ClientAdditionalPayloadContext(propertyBagAvailable = false), + ) + } val streamingMember = inputShape.findStreamingMember(codegenContext.model) val isBlobStreaming = streamingMember != null && codegenContext.model.expectShape(streamingMember.target) is BlobShape diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index 69e0bdd480..6c30515ca7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -25,6 +25,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustom import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.AdditionalPayloadContext import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol @@ -51,19 +52,25 @@ class HttpBoundProtocolGenerator( HttpBoundProtocolTraitImplGenerator(codegenContext, protocol), ) +// TODO(enableNewSmithyRuntime): Completely delete `AdditionalPayloadContext` when switching to the orchestrator +data class ClientAdditionalPayloadContext( + val propertyBagAvailable: Boolean, +) : AdditionalPayloadContext + class ClientHttpBoundProtocolPayloadGenerator( codegenContext: ClientCodegenContext, protocol: Protocol, ) : ProtocolPayloadGenerator by HttpBoundProtocolPayloadGenerator( codegenContext, protocol, HttpMessageType.REQUEST, renderEventStreamBody = { writer, params -> + val propertyBagAvailable = (params.additionalPayloadContext as ClientAdditionalPayloadContext).propertyBagAvailable writer.rustTemplate( """ { let error_marshaller = #{errorMarshallerConstructorFn}(); let marshaller = #{marshallerConstructorFn}(); let (signer, signer_sender) = #{DeferredSigner}::new(); - properties.acquire_mut().insert(signer_sender); + #{insert_into_config} let adapter: #{aws_smithy_http}::event_stream::MessageStreamAdapter<_, _> = ${params.outerName}.${params.memberName}.into_body_stream(marshaller, error_marshaller, signer); let body: #{SdkBody} = #{hyper}::Body::wrap_stream(adapter).into(); @@ -76,6 +83,13 @@ class ClientHttpBoundProtocolPayloadGenerator( "DeferredSigner" to RuntimeType.smithyEventStream(codegenContext.runtimeConfig).resolve("frame::DeferredSigner"), "marshallerConstructorFn" to params.marshallerConstructorFn, "errorMarshallerConstructorFn" to params.errorMarshallerConstructorFn, + "insert_into_config" to writable { + if (propertyBagAvailable) { + rust("properties.acquire_mut().insert(signer_sender);") + } else { + rust("_cfg.put(signer_sender);") + } + }, ) }, ) diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt index b03f374403..36f6089169 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt @@ -22,6 +22,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.AdditionalPayloadContext import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol @@ -35,10 +36,15 @@ import software.amazon.smithy.rust.codegen.core.util.outputShape import java.nio.file.Path private class TestProtocolPayloadGenerator(private val body: String) : ProtocolPayloadGenerator { - override fun payloadMetadata(operationShape: OperationShape) = + override fun payloadMetadata(operationShape: OperationShape, additionalPayloadContext: AdditionalPayloadContext) = ProtocolPayloadGenerator.PayloadMetadata(takesOwnership = false) - override fun generatePayload(writer: RustWriter, shapeName: String, operationShape: OperationShape) { + override fun generatePayload( + writer: RustWriter, + shapeName: String, + operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext, + ) { writer.writeWithNoFormatting(body) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt index ceb385c6d1..f48a99d856 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt @@ -10,6 +10,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol +/** Allows for additional context to be given to the payload generator from where it is being called */ +interface AdditionalPayloadContext + /** * Payload Body Generator. * @@ -31,7 +34,10 @@ interface ProtocolPayloadGenerator { * Most operations will use the HTTP payload as a reference, but for operations that will consume the entire stream * later,they will need to take ownership and different code needs to be generated. */ - fun payloadMetadata(operationShape: OperationShape): PayloadMetadata + fun payloadMetadata( + operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext = object : AdditionalPayloadContext {}, + ): PayloadMetadata /** * Write the payload into [writer]. @@ -42,7 +48,12 @@ interface ProtocolPayloadGenerator { * - a `Vec` for non-streaming operations; or * - a `ByteStream` for streaming operations. */ - fun generatePayload(writer: RustWriter, shapeName: String, operationShape: OperationShape) + fun generatePayload( + writer: RustWriter, + shapeName: String, + operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext = object : AdditionalPayloadContext {}, + ) } /** diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt index 1e17beca4e..2c6ffc662c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/HttpBoundProtocolPayloadGenerator.kt @@ -25,6 +25,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.AdditionalPayloadContext import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.protocols.serialize.EventStreamErrorMarshallerGenerator @@ -46,6 +47,7 @@ data class EventStreamBodyParams( val memberName: String, val marshallerConstructorFn: RuntimeType, val errorMarshallerConstructorFn: RuntimeType, + val additionalPayloadContext: AdditionalPayloadContext, ) class HttpBoundProtocolPayloadGenerator( @@ -69,7 +71,10 @@ class HttpBoundProtocolPayloadGenerator( ) private val protocolFunctions = ProtocolFunctions(codegenContext) - override fun payloadMetadata(operationShape: OperationShape): ProtocolPayloadGenerator.PayloadMetadata { + override fun payloadMetadata( + operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext, + ): ProtocolPayloadGenerator.PayloadMetadata { val (shape, payloadMemberName) = when (httpMessageType) { HttpMessageType.RESPONSE -> operationShape.outputShape(model) to httpBindingResolver.responseMembers(operationShape, HttpLocation.PAYLOAD).firstOrNull()?.memberName @@ -98,32 +103,43 @@ class HttpBoundProtocolPayloadGenerator( } } - override fun generatePayload(writer: RustWriter, shapeName: String, operationShape: OperationShape) { + override fun generatePayload( + writer: RustWriter, + shapeName: String, + operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext, + ) { when (httpMessageType) { - HttpMessageType.RESPONSE -> generateResponsePayload(writer, shapeName, operationShape) - HttpMessageType.REQUEST -> generateRequestPayload(writer, shapeName, operationShape) + HttpMessageType.RESPONSE -> generateResponsePayload(writer, shapeName, operationShape, additionalPayloadContext) + HttpMessageType.REQUEST -> generateRequestPayload(writer, shapeName, operationShape, additionalPayloadContext) } } - private fun generateRequestPayload(writer: RustWriter, shapeName: String, operationShape: OperationShape) { + private fun generateRequestPayload( + writer: RustWriter, shapeName: String, operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext, + ) { val payloadMemberName = httpBindingResolver.requestMembers(operationShape, HttpLocation.PAYLOAD).firstOrNull()?.memberName if (payloadMemberName == null) { val serializerGenerator = protocol.structuredDataSerializer() generateStructureSerializer(writer, shapeName, serializerGenerator.operationInputSerializer(operationShape)) } else { - generatePayloadMemberSerializer(writer, shapeName, operationShape, payloadMemberName) + generatePayloadMemberSerializer(writer, shapeName, operationShape, payloadMemberName, additionalPayloadContext) } } - private fun generateResponsePayload(writer: RustWriter, shapeName: String, operationShape: OperationShape) { + private fun generateResponsePayload( + writer: RustWriter, shapeName: String, operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext, + ) { val payloadMemberName = httpBindingResolver.responseMembers(operationShape, HttpLocation.PAYLOAD).firstOrNull()?.memberName if (payloadMemberName == null) { val serializerGenerator = protocol.structuredDataSerializer() generateStructureSerializer(writer, shapeName, serializerGenerator.operationOutputSerializer(operationShape)) } else { - generatePayloadMemberSerializer(writer, shapeName, operationShape, payloadMemberName) + generatePayloadMemberSerializer(writer, shapeName, operationShape, payloadMemberName, additionalPayloadContext) } } @@ -132,16 +148,17 @@ class HttpBoundProtocolPayloadGenerator( shapeName: String, operationShape: OperationShape, payloadMemberName: String, + additionalPayloadContext: AdditionalPayloadContext, ) { val serializerGenerator = protocol.structuredDataSerializer() if (operationShape.isEventStream(model)) { if (operationShape.isInputEventStream(model) && target == CodegenTarget.CLIENT) { val payloadMember = operationShape.inputShape(model).expectMember(payloadMemberName) - writer.serializeViaEventStream(operationShape, payloadMember, serializerGenerator, shapeName) + writer.serializeViaEventStream(operationShape, payloadMember, serializerGenerator, shapeName, additionalPayloadContext) } else if (operationShape.isOutputEventStream(model) && target == CodegenTarget.SERVER) { val payloadMember = operationShape.outputShape(model).expectMember(payloadMemberName) - writer.serializeViaEventStream(operationShape, payloadMember, serializerGenerator, "output") + writer.serializeViaEventStream(operationShape, payloadMember, serializerGenerator, "output", additionalPayloadContext) } else { throw CodegenException("Payload serializer for event streams with an invalid configuration") } @@ -171,6 +188,7 @@ class HttpBoundProtocolPayloadGenerator( memberShape: MemberShape, serializerGenerator: StructuredDataSerializerGenerator, outerName: String, + additionalPayloadContext: AdditionalPayloadContext, ) { val memberName = symbolProvider.toMemberName(memberShape) val unionShape = model.expectShape(memberShape.target, UnionShape::class.java) @@ -207,6 +225,7 @@ class HttpBoundProtocolPayloadGenerator( memberName, marshallerConstructorFn, errorMarshallerConstructorFn, + additionalPayloadContext, ), ) } diff --git a/rust-runtime/aws-smithy-http/src/event_stream/sender.rs b/rust-runtime/aws-smithy-http/src/event_stream/sender.rs index a4faa3f236..d19690e727 100644 --- a/rust-runtime/aws-smithy-http/src/event_stream/sender.rs +++ b/rust-runtime/aws-smithy-http/src/event_stream/sender.rs @@ -17,12 +17,14 @@ use tracing::trace; /// Input type for Event Streams. pub struct EventStreamSender { - input_stream: Pin> + Send>>, + input_stream: Pin> + Send + Sync>>, } impl Debug for EventStreamSender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "EventStreamSender(Box)") + let name_t = std::any::type_name::(); + let name_e = std::any::type_name::(); + write!(f, "EventStreamSender<{name_t}, {name_e}>") } } @@ -40,7 +42,7 @@ impl EventStreamSender { impl From for EventStreamSender where - S: Stream> + Send + 'static, + S: Stream> + Send + Sync + 'static, { fn from(stream: S) -> Self { EventStreamSender { @@ -260,6 +262,17 @@ mod tests { } } + fn check_send_sync(value: T) -> T { + value + } + + #[test] + fn event_stream_sender_send_sync() { + check_send_sync(EventStreamSender::from(stream! { + yield Result::<_, SignMessageError>::Ok(TestMessage("test".into())); + })); + } + fn check_compatible_with_hyper_wrap_stream(stream: S) -> S where S: Stream> + Send + 'static, diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 3b9aaff2f3..3999c365b2 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -33,7 +33,7 @@ pub type BoxFuture = Pin> + Se pub type Future = NowOrLater, BoxFuture>; pub trait RequestSerializer: Send + Sync + fmt::Debug { - fn serialize_input(&self, input: Input) -> Result; + fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result; } pub trait ResponseDeserializer: Send + Sync + fmt::Debug { @@ -146,7 +146,7 @@ pub trait ConfigBagAccessors { fn http_auth_schemes(&self) -> &HttpAuthSchemes; fn set_http_auth_schemes(&mut self, http_auth_schemes: HttpAuthSchemes); - fn request_serializer(&self) -> &dyn RequestSerializer; + fn request_serializer(&self) -> Arc; fn set_request_serializer(&mut self, request_serializer: impl RequestSerializer + 'static); fn response_deserializer(&self) -> &dyn ResponseDeserializer; @@ -246,14 +246,14 @@ impl ConfigBagAccessors for ConfigBag { self.put::(http_auth_schemes); } - fn request_serializer(&self) -> &dyn RequestSerializer { - &**self - .get::>() + fn request_serializer(&self) -> Arc { + self.get::>() .expect("missing request serializer") + .clone() } fn set_request_serializer(&mut self, request_serializer: impl RequestSerializer + 'static) { - self.put::>(Box::new(request_serializer)); + self.put::>(Arc::new(request_serializer)); } fn response_deserializer(&self) -> &dyn ResponseDeserializer { diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 51cd3ee710..75bc21b508 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -107,7 +107,7 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: { let request_serializer = cfg.request_serializer(); let input = ctx.take_input().expect("input set at this point"); - let request = halt_on_err!([ctx] => request_serializer.serialize_input(input)); + let request = halt_on_err!([ctx] => request_serializer.serialize_input(input, cfg)); ctx.set_request(request); } diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs index d9a2659782..9895fd2ca5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -39,7 +39,11 @@ impl CannedRequestSerializer { } impl RequestSerializer for CannedRequestSerializer { - fn serialize_input(&self, _input: Input) -> Result { + fn serialize_input( + &self, + _input: Input, + _cfg: &mut ConfigBag, + ) -> Result { let req = self .take() .ok_or("CannedRequestSerializer's inner value has already been taken.")?; From d91232667fe4d59d45b69b035d900ef7d087c882 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 26 May 2023 10:12:55 -0700 Subject: [PATCH 116/253] Set up a default connector for generic clients in the orchestrator implementation (#2693) ## Motivation and Context This PR makes generic clients default to the built-in rustls HTTPS connector when no override is provided, but only for the new orchestrator implementation. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-config/Cargo.toml | 2 +- .../aws-config/external-types.toml | 1 + aws/rust-runtime/aws-config/src/connector.rs | 36 ++--------- .../ServiceRuntimePluginGenerator.kt | 21 +++++-- rust-runtime/aws-smithy-client/src/conns.rs | 60 +++++++++++++++++++ 5 files changed, 83 insertions(+), 37 deletions(-) diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index 2a350f3ef2..b15a573422 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/awslabs/smithy-rs" [features] client-hyper = ["aws-smithy-client/client-hyper"] -rustls = ["aws-smithy-client/rustls"] +rustls = ["aws-smithy-client/rustls", "client-hyper"] native-tls = [] allow-compilation = [] # our tests use `cargo test --all-features` and native-tls breaks CI rt-tokio = ["aws-smithy-async/rt-tokio", "tokio/rt"] diff --git a/aws/rust-runtime/aws-config/external-types.toml b/aws/rust-runtime/aws-config/external-types.toml index 7e0257f46d..fdcfb1b757 100644 --- a/aws/rust-runtime/aws-config/external-types.toml +++ b/aws/rust-runtime/aws-config/external-types.toml @@ -10,6 +10,7 @@ allowed_external_types = [ "aws_sdk_sts::types::_policy_descriptor_type::PolicyDescriptorType", "aws_smithy_async::rt::sleep::AsyncSleep", "aws_smithy_client::bounds::SmithyConnector", + "aws_smithy_client::conns::default_connector::default_connector", "aws_smithy_client::erase::DynConnector", "aws_smithy_client::erase::boxclone::BoxCloneService", "aws_smithy_client::http_connector::ConnectorSettings", diff --git a/aws/rust-runtime/aws-config/src/connector.rs b/aws/rust-runtime/aws-config/src/connector.rs index 82bda6e02e..634c7c7bfa 100644 --- a/aws/rust-runtime/aws-config/src/connector.rs +++ b/aws/rust-runtime/aws-config/src/connector.rs @@ -5,10 +5,7 @@ //! Functionality related to creating new HTTP Connectors -use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_client::erase::DynConnector; -use aws_smithy_client::http_connector::ConnectorSettings; -use std::sync::Arc; // unused when all crate features are disabled /// Unwrap an [`Option`](aws_smithy_client::erase::DynConnector), and panic with a helpful error message if it's `None` @@ -16,38 +13,17 @@ pub(crate) fn expect_connector(connector: Option) -> DynConnector connector.expect("No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this.") } +#[cfg(feature = "client-hyper")] +pub use aws_smithy_client::conns::default_connector; + #[cfg(all(feature = "native-tls", not(feature = "allow-compilation")))] compile_error!("Feature native-tls has been removed. For upgrade instructions, see: https://awslabs.github.io/smithy-rs/design/transport/connector.html"); -#[cfg(feature = "rustls")] -fn base( - settings: &ConnectorSettings, - sleep: Option>, -) -> aws_smithy_client::hyper_ext::Builder { - let mut hyper = - aws_smithy_client::hyper_ext::Adapter::builder().connector_settings(settings.clone()); - if let Some(sleep) = sleep { - hyper = hyper.sleep_impl(sleep); - } - hyper -} - -/// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. -#[cfg(feature = "rustls")] -pub fn default_connector( - settings: &ConnectorSettings, - sleep: Option>, -) -> Option { - tracing::trace!(settings = ?settings, sleep = ?sleep, "creating a new connector"); - let hyper = base(settings, sleep).build(aws_smithy_client::conns::https()); - Some(DynConnector::new(hyper)) -} - /// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. -#[cfg(not(feature = "rustls"))] +#[cfg(not(feature = "client-hyper"))] pub fn default_connector( - _settings: &ConnectorSettings, - _sleep: Option>, + _settings: &aws_smithy_client::http_connector::ConnectorSettings, + _sleep: Option>, ) -> Option { None } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index d00af9b66b..d903d527cd 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -63,6 +63,7 @@ class ServiceRuntimePluginGenerator( private val endpointTypesGenerator = EndpointTypesGenerator.fromContext(codegenContext) private val codegenScope = codegenContext.runtimeConfig.let { rc -> val http = RuntimeType.smithyHttp(rc) + val client = RuntimeType.smithyClient(rc) val runtime = RuntimeType.smithyRuntime(rc) val runtimeApi = RuntimeType.smithyRuntimeApi(rc) arrayOf( @@ -77,6 +78,7 @@ class ServiceRuntimePluginGenerator( "DefaultEndpointResolver" to runtime.resolve("client::orchestrator::endpoints::DefaultEndpointResolver"), "DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"), "HttpAuthSchemes" to runtimeApi.resolve("client::auth::HttpAuthSchemes"), + "HttpConnector" to client.resolve("http_connector::HttpConnector"), "IdentityResolvers" to runtimeApi.resolve("client::identity::IdentityResolvers"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "NeverRetryStrategy" to runtime.resolve("client::retries::strategy::NeverRetryStrategy"), @@ -85,6 +87,8 @@ class ServiceRuntimePluginGenerator( "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), "SharedEndpointResolver" to http.resolve("endpoint::SharedEndpointResolver"), "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), + "default_connector" to client.resolve("conns::default_connector"), + "require_connector" to client.resolve("conns::require_connector"), ) } @@ -121,15 +125,20 @@ class ServiceRuntimePluginGenerator( #{SharedEndpointResolver}::from(self.handle.conf.endpoint_resolver())); cfg.set_endpoint_resolver(endpoint_resolver); - // TODO(RuntimePlugins): Wire up standard retry + // TODO(enableNewSmithyRuntime): Wire up standard retry cfg.set_retry_strategy(#{NeverRetryStrategy}::new()); - // TODO(RuntimePlugins): Replace this with the correct long-term solution let sleep_impl = self.handle.conf.sleep_impl(); - let connection: #{Box} = self.handle.conf.http_connector() - .and_then(move |c| c.connector(&#{ConnectorSettings}::default(), sleep_impl)) - .map(|c| #{Box}::new(#{DynConnectorAdapter}::new(c)) as _) - .expect("connection set"); + let timeout_config = self.handle.conf.timeout_config(); + let connector_settings = timeout_config.map(|c| #{ConnectorSettings}::from_timeout_config(c)).unwrap_or_default(); + let connection: #{Box} = #{Box}::new(#{DynConnectorAdapter}::new( + // TODO(enableNewSmithyRuntime): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation + #{require_connector}( + self.handle.conf.http_connector() + .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) + .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) + )? + )) as _; cfg.set_connection(connection); #{additional_config} diff --git a/rust-runtime/aws-smithy-client/src/conns.rs b/rust-runtime/aws-smithy-client/src/conns.rs index 041ba85089..21d6f5e80e 100644 --- a/rust-runtime/aws-smithy-client/src/conns.rs +++ b/rust-runtime/aws-smithy-client/src/conns.rs @@ -5,6 +5,8 @@ //! Type aliases for standard connection types. +use crate::erase::DynConnector; + #[cfg(feature = "rustls")] /// A `hyper` connector that uses the `rustls` crate for TLS. To use this in a smithy client, /// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter). @@ -49,6 +51,64 @@ lazy_static::lazy_static! { }; } +mod default_connector { + use crate::erase::DynConnector; + use crate::http_connector::ConnectorSettings; + use aws_smithy_async::rt::sleep::AsyncSleep; + use std::sync::Arc; + + #[cfg(feature = "rustls")] + fn base( + settings: &ConnectorSettings, + sleep: Option>, + ) -> crate::hyper_ext::Builder { + let mut hyper = crate::hyper_ext::Adapter::builder().connector_settings(settings.clone()); + if let Some(sleep) = sleep { + hyper = hyper.sleep_impl(sleep); + } + hyper + } + + /// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. + pub fn default_connector( + settings: &ConnectorSettings, + sleep: Option>, + ) -> Option { + #[cfg(feature = "rustls")] + { + tracing::trace!(settings = ?settings, sleep = ?sleep, "creating a new default connector"); + let hyper = base(settings, sleep).build(super::https()); + Some(DynConnector::new(hyper)) + } + #[cfg(not(feature = "rustls"))] + { + tracing::trace!(settings = ?settings, sleep = ?sleep, "no default connector available"); + None + } + } +} +pub use default_connector::default_connector; + +/// Error that indicates a connector is required. +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct ConnectorRequiredError; + +impl std::fmt::Display for ConnectorRequiredError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this.") + } +} + +impl std::error::Error for ConnectorRequiredError {} + +/// Converts an optional connector to a result. +pub fn require_connector( + connector: Option, +) -> Result { + connector.ok_or(ConnectorRequiredError) +} + #[cfg(feature = "rustls")] /// Return a default HTTPS connector backed by the `rustls` crate. /// From 9fe5d55c7c028e2bae643819c2cd65d5418abecd Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 26 May 2023 13:34:53 -0700 Subject: [PATCH 117/253] Update the orchestrator CI test script (#2732) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../rustsdk/AwsCustomizableOperationDecorator.kt | 16 ++++++++++++++++ .../s3control/tests/signing-it.rs | 1 + tools/ci-scripts/check-aws-sdk-orchestrator-impl | 5 +++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index 884fefc829..ed9a6271a9 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -69,6 +69,16 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : self.interceptors.push(#{SharedInterceptor}::new(interceptor)); self } + + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn remove_invocation_id_for_tests(mut self) -> Self { + let interceptor = #{TestParamsSetterInterceptor}::new(|context: &mut #{BeforeTransmitInterceptorContextMut}<'_>, _: &mut #{ConfigBag}| { + context.request_mut().headers_mut().remove("amz-sdk-invocation-id"); + }); + self.interceptors.push(#{SharedInterceptor}::new(interceptor)); + self + } """, *codegenScope, ) @@ -89,6 +99,12 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : self.operation.properties_mut().insert(#{AwsUserAgent}::for_tests()); self } + + ##[doc(hidden)] + // This is a temporary method for testing. NEVER use it in production + pub fn remove_invocation_id_for_tests(self) -> Self { + self + } """, *codegenScope, ) diff --git a/aws/sdk/integration-tests/s3control/tests/signing-it.rs b/aws/sdk/integration-tests/s3control/tests/signing-it.rs index 88dc2debfb..c868491a42 100644 --- a/aws/sdk/integration-tests/s3control/tests/signing-it.rs +++ b/aws/sdk/integration-tests/s3control/tests/signing-it.rs @@ -39,6 +39,7 @@ async fn test_signer() { .unwrap() .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1636751225)) .user_agent_for_tests() + .remove_invocation_id_for_tests() .send() .await .expect_err("empty response"); diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index 483e3075bf..41b0eafff9 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -15,13 +15,12 @@ cd smithy-rs # TODO(enableNewSmithyRuntime): Move these into `services_that_compile` as more progress is made services_that_fail_compile=(\ "s3"\ - "s3control"\ - "transcribestreaming"\ "polly"\ ) # TODO(enableNewSmithyRuntime): Move these into `services_that_pass_tests` as more progress is made services_that_compile=(\ + "aws-config"\ "dynamodb"\ "route53"\ "sts"\ @@ -36,7 +35,9 @@ services_that_pass_tests=(\ "kms"\ "lambda"\ "qldbsession"\ + "s3control"\ "sso"\ + "transcribestreaming"\ ) ./gradlew aws:sdk:assemble -Psmithy.runtime.mode=orchestrator From 79ead4884c206f1c41eacb5e61feab2a96c06173 Mon Sep 17 00:00:00 2001 From: Chris Holcombe Date: Fri, 26 May 2023 14:02:36 -0700 Subject: [PATCH 118/253] Make SigningInstructions public (#2730) ## Motivation and Context I'm building an s3 server in rust and I need signature verification to work from the server side when receiving requests. To do that the server needs to generate a signature and then compare it to the one that the client has already sent along. I believe that the sign module in aws-sigv4 http_request contains the functions required to do that. ## Description Exported the `SigningInstructions` struct in the sign module of http_request for aws-sigv4. ## Testing _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Chris Holcombe Co-authored-by: John DiSanti --- CHANGELOG.next.toml | 6 ++++++ aws/rust-runtime/aws-sigv4/src/http_request/mod.rs | 2 +- aws/rust-runtime/aws-sigv4/src/http_request/sign.rs | 8 ++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index f81531ff73..51319b5986 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -62,3 +62,9 @@ message = "For event stream operations, the `EventStreamSender` in inputs/output references = ["smithy-rs#2673"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all"} author = "jdisanti" + +[[aws-sdk-rust]] +message = "The `SigningInstructions` in the `aws-sigv4` module are now public. This allows them to be named in a function signature." +references = ["smithy-rs#2730"] +author = "cholcombe973" +meta = { "breaking" = false, "tada" = false, "bug" = true } diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs b/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs index 023c142997..db021bbee5 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/mod.rs @@ -56,4 +56,4 @@ pub use settings::{ PayloadChecksumKind, PercentEncodingMode, SessionTokenMode, SignatureLocation, SigningParams, SigningSettings, UriPathNormalizationMode, }; -pub use sign::{sign, SignableBody, SignableRequest}; +pub use sign::{sign, SignableBody, SignableRequest, SigningInstructions}; diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs b/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs index b716b9fd40..69d646daa2 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs @@ -103,6 +103,7 @@ pub enum SignableBody<'a> { StreamingUnsignedPayloadTrailer, } +/// Instructions for applying a signature to an HTTP request. #[derive(Debug)] pub struct SigningInstructions { headers: Option>, @@ -117,20 +118,27 @@ impl SigningInstructions { Self { headers, params } } + /// Returns a reference to the headers that should be added to the request. pub fn headers(&self) -> Option<&HeaderMap> { self.headers.as_ref() } + + /// Returns the headers and sets the internal value to `None`. pub fn take_headers(&mut self) -> Option> { self.headers.take() } + /// Returns a reference to the query parameters that should be added to the request. pub fn params(&self) -> Option<&Vec<(&'static str, Cow<'static, str>)>> { self.params.as_ref() } + + /// Returns the query parameters and sets the internal value to `None`. pub fn take_params(&mut self) -> Option)>> { self.params.take() } + /// Applies the instructions to the given `request`. pub fn apply_to_request(mut self, request: &mut http::Request) { if let Some(new_headers) = self.take_headers() { for (name, value) in new_headers.into_iter() { From ec874d5a80000e50ed50feb1e1786242adb64408 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 30 May 2023 12:26:42 +0200 Subject: [PATCH 119/253] Error out if `ignoreUnsupportedConstraintTraits` has no effect (#2539) Now that constraint traits are supported in server SDKs (with some corner case caveats, see #1401), this flag will almost always be useless for those early adopters of constraint traits. It is thus convenient to inform the user that they should remove it. See https://github.com/awslabs/smithy-rs/pull/2516#issuecomment-1490393056. ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ --- CHANGELOG.next.toml | 6 ++++ codegen-server-test/python/build.gradle.kts | 7 ++--- .../smithy/ValidateUnsupportedConstraints.kt | 31 ++++++++++++------- ...ateUnsupportedConstraintsAreNotUsedTest.kt | 26 ++++++++++++++-- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 51319b5986..eac30bfe36 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -68,3 +68,9 @@ message = "The `SigningInstructions` in the `aws-sigv4` module are now public. T references = ["smithy-rs#2730"] author = "cholcombe973" meta = { "breaking" = false, "tada" = false, "bug" = true } + +[[smithy-rs]] +message = "Code generation will abort if the `ignoreUnsupportedConstraints` codegen flag has no effect, that is, if all constraint traits used in your model are well-supported. Please remove the flag in such case." +references = ["smithy-rs#2539"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } +author = "david-perez" diff --git a/codegen-server-test/python/build.gradle.kts b/codegen-server-test/python/build.gradle.kts index 423d8a4619..469e0e6bd7 100644 --- a/codegen-server-test/python/build.gradle.kts +++ b/codegen-server-test/python/build.gradle.kts @@ -47,21 +47,18 @@ val allCodegenTests = "../../codegen-core/common-test-models".let { commonModels imports = listOf("$commonModels/pokemon.smithy", "$commonModels/pokemon-common.smithy"), ), CodegenTest( - "com.amazonaws.ebs#Ebs", "ebs", + "com.amazonaws.ebs#Ebs", + "ebs", imports = listOf("$commonModels/ebs.json"), - extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, ), CodegenTest( "aws.protocoltests.misc#MiscService", "misc", imports = listOf("$commonModels/misc.smithy"), - // TODO(https://github.com/awslabs/smithy-rs/issues/1401) `@uniqueItems` is used. - extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, ), CodegenTest( "aws.protocoltests.json#JsonProtocol", "json_rpc11", - extraConfig = """, "codegen": { "ignoreUnsupportedConstraints": true } """, ), CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt index 26f298c191..f0f0ee8227 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt @@ -22,7 +22,6 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.ShortShape import software.amazon.smithy.model.traits.LengthTrait import software.amazon.smithy.model.traits.RangeTrait -import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.model.traits.Trait import software.amazon.smithy.model.traits.UniqueItemsTrait @@ -158,8 +157,6 @@ data class LogMessage(val level: Level, val message: String) data class ValidationResult(val shouldAbort: Boolean, val messages: List) : Throwable(message = messages.joinToString("\n") { it.message }) -private val unsupportedConstraintsOnMemberShapes = allConstraintTraits - RequiredTrait::class.java - /** * Validate that all constrained operations have the shape [validationExceptionShapeId] shape attached to their errors. */ @@ -280,16 +277,28 @@ fun validateUnsupportedConstraints( .toSet() val messages = - unsupportedLengthTraitOnStreamingBlobShapeSet.map { - it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) - } + - unsupportedConstraintShapeReachableViaAnEventStreamSet.map { + ( + unsupportedLengthTraitOnStreamingBlobShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - unsupportedRangeTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + - mapShapeReachableFromUniqueItemsListShapeSet.map { - it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) - } + unsupportedConstraintShapeReachableViaAnEventStreamSet.map { + it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) + } + + unsupportedRangeTraitOnShapeSet.map { it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) } + + mapShapeReachableFromUniqueItemsListShapeSet.map { + it.intoLogMessage(codegenConfig.ignoreUnsupportedConstraints) + } + ).toMutableList() + + if (messages.isEmpty() && codegenConfig.ignoreUnsupportedConstraints) { + messages += LogMessage( + Level.SEVERE, + """ + The `ignoreUnsupportedConstraints` flag in the `codegen` configuration is set to `true`, but it has no + effect. All the constraint traits used in the model are well-supported, please remove this flag. + """.trimIndent().replace("\n", " "), + ) + } return ValidationResult(shouldAbort = messages.any { it.level == Level.SEVERE }, messages) } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt index 68b88a7978..3c5e62e40f 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt @@ -19,6 +19,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamN import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionConversionGenerator +import java.io.File import java.util.logging.Level internal class ValidateUnsupportedConstraintsAreNotUsedTest { @@ -37,7 +38,7 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { """ private fun validateModel(model: Model, serverCodegenConfig: ServerCodegenConfig = ServerCodegenConfig()): ValidationResult { - val service = model.lookup("test#TestService") + val service = model.serviceShapes.first() return validateUnsupportedConstraints(model, service, serverCodegenConfig) } @@ -100,7 +101,7 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { """.trimIndent().replace("\n", " ") } - val constrainedShapesInEventStreamModel = + private val constrainedShapesInEventStreamModel = """ $baseModel @@ -242,4 +243,25 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { validationResult.messages shouldHaveAtLeastSize 1 validationResult.messages.shouldForAll { it.level shouldBe Level.WARNING } } + + @Test + fun `it should abort when ignoreUnsupportedConstraints is true and all used constraints are supported`() { + val allConstraintTraitsAreSupported = File("../codegen-core/common-test-models/constraints.smithy") + .readText() + .asSmithyModel() + + val validationResult = validateModel( + allConstraintTraitsAreSupported, + ServerCodegenConfig().copy(ignoreUnsupportedConstraints = true), + ) + + validationResult.messages shouldHaveSize 1 + validationResult.shouldAbort shouldBe true + validationResult.messages[0].message shouldContain( + """ + The `ignoreUnsupportedConstraints` flag in the `codegen` configuration is set to `true`, but it has no + effect. All the constraint traits used in the model are well-supported, please remove this flag. + """.trimIndent().replace("\n", " ") + ) + } } From 5126a6148966c3d1d17895631575f0495477b760 Mon Sep 17 00:00:00 2001 From: Thor Bjorgvinsson <64786777+Thor-Bjorgvinsson@users.noreply.github.com> Date: Tue, 30 May 2023 07:46:07 -0400 Subject: [PATCH 120/253] Fix tiny_map from_iter where one operation was being dropped (#2733) ## Motivation and Context 15th operation was being dropped when iterator was being read into a TinyMap ## Description The 15th operation was being dropped when the if statement was caught because the operation had been popped of the iterator but hadn't been added to the vec of operations before the two iterators were being chained and collected into a ## Testing Added unit tests that reproduced the issue and verified that the issue is fixed ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --------- Co-authored-by: david-perez Co-authored-by: Fahad Zubair --- CHANGELOG.next.toml | 6 ++++++ .../src/routing/tiny_map.rs | 16 +++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index eac30bfe36..bed2068945 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -11,6 +11,12 @@ # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} # author = "rcoh" +[[smithy-rs]] +message = "Fix bug in AWS JSON 1.x routers where, if a service had more than 14 operations, the router was created without the route for the 15th operation." +author = "thor-bjorgvinsson" +references = ["smithy-rs#2733"] +meta = { "breaking" = false, "tada" = false, "bug" = true, "target" ="server" } + [[aws-sdk-rust]] message = "Remove native-tls and add a migration guide." author = "82marbag" diff --git a/rust-runtime/aws-smithy-http-server/src/routing/tiny_map.rs b/rust-runtime/aws-smithy-http-server/src/routing/tiny_map.rs index 236da27797..7011515641 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/tiny_map.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/tiny_map.rs @@ -78,13 +78,13 @@ where // Populate the `Vec` while let Some((index, pair)) = iter.next() { + vec.push(pair); + // If overflow `CUTOFF` then return a `HashMap` instead if index == CUTOFF { let inner = TinyMapInner::HashMap(vec.into_iter().chain(iter.map(|(_, pair)| pair)).collect()); return TinyMap { inner }; } - - vec.push(pair); } TinyMap { @@ -158,19 +158,25 @@ mod tests { #[test] fn get_small_success() { let tiny_map: TinyMap<_, _, CUTOFF> = SMALL_VALUES.into_iter().collect(); - assert_eq!(tiny_map.get("a"), Some(&0)) + SMALL_VALUES.into_iter().for_each(|(op, val)| { + assert_eq!(tiny_map.get(op), Some(&val)); + }); } #[test] fn get_medium_success() { let tiny_map: TinyMap<_, _, CUTOFF> = MEDIUM_VALUES.into_iter().collect(); - assert_eq!(tiny_map.get("d"), Some(&3)) + MEDIUM_VALUES.into_iter().for_each(|(op, val)| { + assert_eq!(tiny_map.get(op), Some(&val)); + }); } #[test] fn get_large_success() { let tiny_map: TinyMap<_, _, CUTOFF> = LARGE_VALUES.into_iter().collect(); - assert_eq!(tiny_map.get("h"), Some(&7)) + LARGE_VALUES.into_iter().for_each(|(op, val)| { + assert_eq!(tiny_map.get(op), Some(&val)); + }); } #[test] From cf292d5817bf7355e15563e1d64a90b4687a745d Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 30 May 2023 16:47:20 +0200 Subject: [PATCH 121/253] Bump `lambda_http` dependency of `aws-smithy-http-server` to 0.8.0 (#2685) (#2691) This is https://github.com/awslabs/smithy-rs/pull/2685 again, which was merged prematurely and reverted in https://github.com/awslabs/smithy-rs/pull/2690. This time we won't merge it until it's due time. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- CHANGELOG.next.toml | 6 ++++++ examples/pokemon-service-lambda/Cargo.toml | 2 +- .../aws-smithy-http-server-python/Cargo.toml | 7 +------ rust-runtime/aws-smithy-http-server/Cargo.toml | 4 ++-- .../src/routing/lambda_handler.rs | 16 ++++++++++------ 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index bed2068945..436b87bb4e 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -80,3 +80,9 @@ message = "Code generation will abort if the `ignoreUnsupportedConstraints` code references = ["smithy-rs#2539"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } author = "david-perez" + +[[smithy-rs]] +message = "Bump dependency on `lambda_http` by `aws-smithy-http-server` to 0.8.0. This version of `aws-smithy-http-server` is only guaranteed to be compatible with 0.8.0, or semver-compatible versions of 0.8.0 of the `lambda_http` crate. It will not work with versions prior to 0.8.0 _at runtime_, making requests to your smithy-rs service unroutable, so please make sure you're running your service in a compatible configuration" +author = "david-perez" +references = ["smithy-rs#2676", "smithy-rs#2685"] +meta = { "breaking" = true, "tada" = false, "bug" = false } diff --git a/examples/pokemon-service-lambda/Cargo.toml b/examples/pokemon-service-lambda/Cargo.toml index 72c1d4cd01..b806c53891 100644 --- a/examples/pokemon-service-lambda/Cargo.toml +++ b/examples/pokemon-service-lambda/Cargo.toml @@ -16,7 +16,7 @@ tracing = "0.1" # `aws-smithy-http-server` is only guaranteed to be compatible with this # version of `lambda_http`, or semver-compatible versions of this version. # Depending on other versions of `lambda_http` may not work. -lambda_http = "0.7.3" +lambda_http = "0.8.0" # Local paths aws-smithy-http-server = { path = "../../rust-runtime/aws-smithy-http-server", features = ["aws-lambda"] } diff --git a/rust-runtime/aws-smithy-http-server-python/Cargo.toml b/rust-runtime/aws-smithy-http-server-python/Cargo.toml index 738b86418b..334b049191 100644 --- a/rust-runtime/aws-smithy-http-server-python/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server-python/Cargo.toml @@ -25,12 +25,7 @@ hyper = { version = "0.14.20", features = ["server", "http1", "http2", "tcp", "s tls-listener = { version = "0.7.0", features = ["rustls", "hyper-h2"] } rustls-pemfile = "1.0.1" tokio-rustls = "0.24.0" -lambda_http = { version = "0.7.1" } -# There is a breaking change in `lambda_runtime` between `0.7.0` and `0.7.1`, -# and `lambda_http` depends on `0.7` which by default resolves to `0.7.1` but in our CI -# we are running `minimal-versions` which downgrades `lambda_runtime` to `0.7.0` and fails to compile -# because of the breaking change. Here we are forcing it to use `lambda_runtime = 0.7.1`. -lambda_runtime = { version = "0.7.1" } +lambda_http = { version = "0.8.0" } num_cpus = "1.13.1" parking_lot = "0.12.1" pin-project-lite = "0.2" diff --git a/rust-runtime/aws-smithy-http-server/Cargo.toml b/rust-runtime/aws-smithy-http-server/Cargo.toml index 96ee56f79b..57f6bc761a 100644 --- a/rust-runtime/aws-smithy-http-server/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server/Cargo.toml @@ -28,8 +28,8 @@ futures-util = { version = "0.3.16", default-features = false } http = "0.2" http-body = "0.4" hyper = { version = "0.14.12", features = ["server", "http1", "http2", "tcp", "stream"] } -lambda_http = { version = "0.7.1", optional = true } -mime = "0.3" +lambda_http = { version = "0.8.0", optional = true } +mime = "0.3.4" nom = "7" pin-project-lite = "0.2" once_cell = "1.13" diff --git a/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs b/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs index 6e3348187c..44704e2dd1 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/lambda_handler.rs @@ -58,12 +58,12 @@ where /// /// [API Gateway Stage]: https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-stages.html fn convert_event(request: Request) -> HyperRequest { - let raw_path = request.raw_http_path(); - let (mut parts, body) = request.into_parts(); - let mut path = String::from(parts.uri.path()); + let raw_path: &str = request.extensions().raw_http_path(); + let path: &str = request.uri().path(); - if !raw_path.is_empty() && raw_path != path { - path = raw_path; + let (parts, body) = if !raw_path.is_empty() && raw_path != path { + let mut path = raw_path.to_owned(); // Clone only when we need to strip out the stage. + let (mut parts, body) = request.into_parts(); let uri_parts: uri::Parts = parts.uri.into(); let path_and_query = uri_parts @@ -81,7 +81,11 @@ fn convert_event(request: Request) -> HyperRequest { .path_and_query(path) .build() .expect("unable to construct new URI"); - } + + (parts, body) + } else { + request.into_parts() + }; let body = match body { lambda_http::Body::Empty => hyper::Body::empty(), From b30b063e1ef3781641bc5a65fa7c0913517303bf Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 30 May 2023 11:52:11 -0400 Subject: [PATCH 122/253] Integrate TimeSource into the orchestrator and middleware (breaking change warning is spurious) (#2728) ## Motivation and Context - #2262 - #2087 - #2707 This adds `TimeSource` in SDK and service configs so we can effectively control time when executing requests with the SDK at the client level. _note:_ the breaking change is in a trait implementation of a struct we're going to delete, not a real breaking change ## Description - Add `SharedTimeSource` and use it in SdkConfig / `aws-config` / ::Config - Wire up the signer and other uses of `SystemTime::now()` that I could find - track down broken tests ## Testing - [x] various unit tests that all still pass ## Checklist - [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._ --- CHANGELOG.next.toml | 19 ++++++++ .../aws-config/external-types.toml | 1 + .../aws-config/src/imds/client/token.rs | 8 ++-- .../aws-config/src/imds/credentials.rs | 9 ++-- aws/rust-runtime/aws-config/src/lib.rs | 15 +++++- .../src/profile/credentials/exec.rs | 18 ++++--- .../aws-config/src/provider_config.rs | 14 +++--- aws/rust-runtime/aws-config/src/sts.rs | 1 + .../aws-config/src/sts/assume_role.rs | 7 +-- aws/rust-runtime/aws-config/src/sts/util.rs | 7 ++- .../aws-config/src/web_identity_token.rs | 9 ++-- .../aws-credential-types/src/time_source.rs | 36 +++++++------- aws/rust-runtime/aws-inlineable/Cargo.toml | 3 +- .../tests/middleware_e2e_test.rs | 5 +- aws/rust-runtime/aws-runtime/Cargo.toml | 2 + .../aws-runtime/src/auth/sigv4.rs | 17 ++++--- aws/rust-runtime/aws-sig-auth/Cargo.toml | 4 +- .../aws-sig-auth/src/middleware.rs | 26 +++++----- .../aws-types/external-types.toml | 2 + aws/rust-runtime/aws-types/src/sdk_config.rs | 23 +++++++++ .../AwsCustomizableOperationDecorator.kt | 10 ++-- .../smithy/rustsdk/AwsPresigningDecorator.kt | 7 +-- .../smithy/rustsdk/SdkConfigDecorator.kt | 1 + aws/sdk/integration-tests/kms/Cargo.toml | 3 +- .../integration-tests/s3/tests/checksums.rs | 2 +- .../aws-sdk-s3/tests/interceptors.rs | 6 +-- .../tests/request_information_headers.rs | 8 ++-- .../aws-sdk-s3/tests/util.rs | 8 ++-- .../ResiliencyConfigCustomization.kt | 1 + .../customize/RequiredCustomizations.kt | 9 ++-- .../config/ServiceConfigGenerator.kt | 19 ++++++-- .../generators/config/TimeSourceConfig.kt | 40 ++++++++++++++++ .../aws-smithy-async/src/test_util.rs | 47 ++++++++++++++++++- rust-runtime/aws-smithy-async/src/time.rs | 31 ++++++++++++ .../src/client/orchestrator.rs | 37 +++------------ rust-runtime/aws-smithy-runtime/Cargo.toml | 2 +- .../src/client/test_util/interceptor.rs | 9 ++-- 37 files changed, 325 insertions(+), 141 deletions(-) create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 436b87bb4e..4c7d2e950e 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -81,8 +81,27 @@ references = ["smithy-rs#2539"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } author = "david-perez" +[[smithy-rs]] +message = "Time is now controlled by the `TimeSource` trait. This facilitates testing as well as use cases like WASM where `SystemTime::now()` is not supported." +references = ["smithy-rs#2728", "smithy-rs#2262", "aws-sdk-rust#2087"] +meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } +author = "rcoh" + +[[smithy-rs]] +message = "The property bag type for Time is now `SharedTimeSource`, not `SystemTime`. If your code relies on setting request time, use `aws_smithy_async::time::SharedTimeSource`." +references = ["smithy-rs#2728", "smithy-rs#2262", "aws-sdk-rust#2087"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } +author = "rcoh" + +[[aws-sdk-rust]] +message = "Time is now controlled by the `TimeSource` trait. This facilitates testing as well as use cases like WASM where `SystemTime::now()` is not supported." +references = ["smithy-rs#2728", "smithy-rs#2262", "aws-sdk-rust#2087"] +meta = { "breaking" = false, "tada" = false, "bug" = false } +author = "rcoh" + [[smithy-rs]] message = "Bump dependency on `lambda_http` by `aws-smithy-http-server` to 0.8.0. This version of `aws-smithy-http-server` is only guaranteed to be compatible with 0.8.0, or semver-compatible versions of 0.8.0 of the `lambda_http` crate. It will not work with versions prior to 0.8.0 _at runtime_, making requests to your smithy-rs service unroutable, so please make sure you're running your service in a compatible configuration" author = "david-perez" references = ["smithy-rs#2676", "smithy-rs#2685"] meta = { "breaking" = true, "tada" = false, "bug" = false } + diff --git a/aws/rust-runtime/aws-config/external-types.toml b/aws/rust-runtime/aws-config/external-types.toml index fdcfb1b757..b90a84885c 100644 --- a/aws/rust-runtime/aws-config/external-types.toml +++ b/aws/rust-runtime/aws-config/external-types.toml @@ -9,6 +9,7 @@ allowed_external_types = [ "aws_credential_types::provider::SharedCredentialsProvider", "aws_sdk_sts::types::_policy_descriptor_type::PolicyDescriptorType", "aws_smithy_async::rt::sleep::AsyncSleep", + "aws_smithy_async::time::TimeSource", "aws_smithy_client::bounds::SmithyConnector", "aws_smithy_client::conns::default_connector::default_connector", "aws_smithy_client::erase::DynConnector", diff --git a/aws/rust-runtime/aws-config/src/imds/client/token.rs b/aws/rust-runtime/aws-config/src/imds/client/token.rs index 4cd3f80075..213243a8cd 100644 --- a/aws/rust-runtime/aws-config/src/imds/client/token.rs +++ b/aws/rust-runtime/aws-config/src/imds/client/token.rs @@ -17,9 +17,9 @@ use crate::imds::client::error::{ImdsError, TokenError, TokenErrorKind}; use crate::imds::client::ImdsResponseRetryClassifier; use aws_credential_types::cache::ExpiringCache; -use aws_credential_types::time_source::TimeSource; use aws_http::user_agent::UserAgentStage; use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::time::SharedTimeSource; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::retry; use aws_smithy_http::body::SdkBody; @@ -65,7 +65,7 @@ pub(super) struct TokenMiddleware { client: Arc>>, token_parser: GetTokenResponseHandler, token: ExpiringCache, - time_source: TimeSource, + time_source: SharedTimeSource, endpoint: Uri, token_ttl: Duration, } @@ -79,7 +79,7 @@ impl Debug for TokenMiddleware { impl TokenMiddleware { pub(super) fn new( connector: DynConnector, - time_source: TimeSource, + time_source: SharedTimeSource, endpoint: Uri, token_ttl: Duration, retry_config: retry::Config, @@ -170,7 +170,7 @@ impl AsyncMapRequest for TokenMiddleware { #[derive(Clone)] struct GetTokenResponseHandler { - time: TimeSource, + time: SharedTimeSource, } impl ParseStrictResponse for GetTokenResponseHandler { diff --git a/aws/rust-runtime/aws-config/src/imds/credentials.rs b/aws/rust-runtime/aws-config/src/imds/credentials.rs index 83265c37a5..9cdc47b7c9 100644 --- a/aws/rust-runtime/aws-config/src/imds/credentials.rs +++ b/aws/rust-runtime/aws-config/src/imds/credentials.rs @@ -14,8 +14,8 @@ use crate::imds::client::LazyClient; use crate::json_credentials::{parse_json_credentials, JsonCredentials, RefreshableCredentials}; use crate::provider_config::ProviderConfig; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; -use aws_credential_types::time_source::TimeSource; use aws_credential_types::Credentials; +use aws_smithy_async::time::SharedTimeSource; use aws_types::os_shim_internal::Env; use std::borrow::Cow; use std::error::Error as StdError; @@ -53,7 +53,7 @@ pub struct ImdsCredentialsProvider { client: LazyClient, env: Env, profile: Option, - time_source: TimeSource, + time_source: SharedTimeSource, last_retrieved_credentials: Arc>>, } @@ -390,7 +390,10 @@ mod test { .build(); let creds = provider.provide_credentials().await.expect("valid creds"); // The expiry should be equal to what is originally set (==2021-09-21T04:16:53Z). - assert!(creds.expiry() == UNIX_EPOCH.checked_add(Duration::from_secs(1632197813))); + assert_eq!( + creds.expiry(), + UNIX_EPOCH.checked_add(Duration::from_secs(1632197813)) + ); connection.assert_requests_match(&[]); // There should not be logs indicating credentials are extended for stability. diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 2d237a5b8f..5364d1bcb5 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -155,6 +155,7 @@ mod loader { use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::{ProvideCredentials, SharedCredentialsProvider}; use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; + use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; @@ -192,6 +193,7 @@ mod loader { profile_files_override: Option, use_fips: Option, use_dual_stack: Option, + time_source: Option, } impl ConfigLoader { @@ -262,6 +264,12 @@ mod loader { self } + /// Set the time source used for tasks like signing requests + pub fn time_source(mut self, time_source: impl TimeSource + 'static) -> Self { + self.time_source = Some(SharedTimeSource::new(time_source)); + self + } + /// Override the [`HttpConnector`] for this [`ConfigLoader`]. The connector will be used when /// sending operations. This **does not set** the HTTP connector used by config providers. /// To change that connector, use [ConfigLoader::configure]. @@ -563,7 +571,9 @@ mod loader { .unwrap_or_else(|| HttpConnector::ConnectorFn(Arc::new(default_connector))); let credentials_cache = self.credentials_cache.unwrap_or_else(|| { - let mut builder = CredentialsCache::lazy_builder().time_source(conf.time_source()); + let mut builder = CredentialsCache::lazy_builder().time_source( + aws_credential_types::time_source::TimeSource::shared(conf.time_source()), + ); builder.set_sleep(conf.sleep()); builder.into_credentials_cache() }); @@ -588,12 +598,15 @@ mod loader { SharedCredentialsProvider::new(builder.build().await) }; + let ts = self.time_source.unwrap_or_default(); + let mut builder = SdkConfig::builder() .region(region) .retry_config(retry_config) .timeout_config(timeout_config) .credentials_cache(credentials_cache) .credentials_provider(credentials_provider) + .time_source(ts) .http_connector(http_connector); builder.set_app_name(app_name); diff --git a/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs b/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs index 86622d0c17..838007ad1e 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials/exec.rs @@ -14,6 +14,7 @@ use crate::web_identity_token::{StaticConfiguration, WebIdentityTokenCredentials use aws_credential_types::provider::{self, error::CredentialsError, ProvideCredentials}; use aws_sdk_sts::config::{Builder as StsConfigBuilder, Credentials}; use aws_sdk_sts::Client as StsClient; +use aws_smithy_async::time::SharedTimeSource; use std::fmt::Debug; use std::sync::Arc; @@ -22,6 +23,7 @@ pub(super) struct AssumeRoleProvider { role_arn: String, external_id: Option, session_name: Option, + time_source: SharedTimeSource, } impl AssumeRoleProvider { @@ -35,11 +37,9 @@ impl AssumeRoleProvider { .credentials_provider(input_credentials) .build(); let client = StsClient::from_conf(config); - let session_name = &self - .session_name - .as_ref() - .cloned() - .unwrap_or_else(|| sts::util::default_session_name("assume-role-from-profile")); + let session_name = &self.session_name.as_ref().cloned().unwrap_or_else(|| { + sts::util::default_session_name("assume-role-from-profile", self.time_source.now()) + }); let assume_role_creds = client .assume_role() .role_arn(&self.role_arn) @@ -97,7 +97,12 @@ impl ProviderChain { web_identity_token_file: web_identity_token_file.into(), role_arn: role_arn.to_string(), session_name: session_name.map(|sess| sess.to_string()).unwrap_or_else( - || sts::util::default_session_name("web-identity-token-profile"), + || { + sts::util::default_session_name( + "web-identity-token-profile", + provider_config.time_source().now(), + ) + }, ), }) .configure(provider_config) @@ -140,6 +145,7 @@ impl ProviderChain { role_arn: role_arn.role_arn.into(), external_id: role_arn.external_id.map(|id| id.into()), session_name: role_arn.session_name.map(|id| id.into()), + time_source: provider_config.time_source(), } }) .collect(); diff --git a/aws/rust-runtime/aws-config/src/provider_config.rs b/aws/rust-runtime/aws-config/src/provider_config.rs index 57fa11a621..7d596b2c2e 100644 --- a/aws/rust-runtime/aws-config/src/provider_config.rs +++ b/aws/rust-runtime/aws-config/src/provider_config.rs @@ -7,6 +7,7 @@ use aws_credential_types::time_source::TimeSource; use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; +use aws_smithy_async::time::SharedTimeSource; use aws_smithy_client::erase::DynConnector; use aws_smithy_types::error::display::DisplayErrorContext; use aws_types::os_shim_internal::{Env, Fs}; @@ -38,7 +39,7 @@ use crate::profile::{ProfileFileLoadError, ProfileSet}; pub struct ProviderConfig { env: Env, fs: Fs, - time_source: TimeSource, + time_source: SharedTimeSource, connector: HttpConnector, sleep: Option>, region: Option, @@ -72,7 +73,7 @@ impl Default for ProviderConfig { Self { env: Env::default(), fs: Fs::default(), - time_source: TimeSource::default(), + time_source: SharedTimeSource::default(), connector, sleep: default_async_sleep(), region: None, @@ -90,7 +91,6 @@ impl ProviderConfig { /// Unlike [`ProviderConfig::empty`] where `env` and `fs` will use their non-mocked implementations, /// this method will use an empty mock environment and an empty mock file system. pub fn no_configuration() -> Self { - use aws_credential_types::time_source::TestingTimeSource; use std::collections::HashMap; use std::time::UNIX_EPOCH; let fs = Fs::from_raw_map(HashMap::new()); @@ -100,7 +100,7 @@ impl ProviderConfig { profile_files: ProfileFiles::default(), env, fs, - time_source: TimeSource::testing(&TestingTimeSource::new(UNIX_EPOCH)), + time_source: SharedTimeSource::new(UNIX_EPOCH), connector: HttpConnector::Prebuilt(None), sleep: None, region: None, @@ -140,7 +140,7 @@ impl ProviderConfig { ProviderConfig { env: Env::default(), fs: Fs::default(), - time_source: TimeSource::default(), + time_source: SharedTimeSource::default(), connector: HttpConnector::Prebuilt(None), sleep: None, region: None, @@ -179,7 +179,7 @@ impl ProviderConfig { } #[allow(dead_code)] - pub(crate) fn time_source(&self) -> TimeSource { + pub(crate) fn time_source(&self) -> SharedTimeSource { self.time_source.clone() } @@ -293,7 +293,7 @@ impl ProviderConfig { #[doc(hidden)] pub fn with_time_source(self, time_source: TimeSource) -> Self { ProviderConfig { - time_source, + time_source: SharedTimeSource::new(time_source), ..self } } diff --git a/aws/rust-runtime/aws-config/src/sts.rs b/aws/rust-runtime/aws-config/src/sts.rs index 11a6ec591d..edaf1bfc15 100644 --- a/aws/rust-runtime/aws-config/src/sts.rs +++ b/aws/rust-runtime/aws-config/src/sts.rs @@ -22,6 +22,7 @@ impl crate::provider_config::ProviderConfig { .http_connector(expect_connector(self.connector(&Default::default()))) .retry_config(RetryConfig::standard()) .region(self.region()) + .time_source(self.time_source()) .credentials_cache(CredentialsCache::no_caching()); builder.set_sleep_impl(self.sleep()); builder diff --git a/aws/rust-runtime/aws-config/src/sts/assume_role.rs b/aws/rust-runtime/aws-config/src/sts/assume_role.rs index 84343059f9..6ae8f0a6ad 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -211,13 +211,14 @@ impl AssumeRoleProviderBuilder { let mut config = aws_sdk_sts::Config::builder() .credentials_cache(credentials_cache) .credentials_provider(provider) + .time_source(conf.time_source()) .region(self.region.clone()) .http_connector(expect_connector(conf.connector(&Default::default()))); config.set_sleep_impl(conf.sleep()); - let session_name = self - .session_name - .unwrap_or_else(|| super::util::default_session_name("assume-role-provider")); + let session_name = self.session_name.unwrap_or_else(|| { + super::util::default_session_name("assume-role-provider", conf.time_source().now()) + }); let sts_client = StsClient::from_conf(config.build()); let fluent_builder = sts_client diff --git a/aws/rust-runtime/aws-config/src/sts/util.rs b/aws/rust-runtime/aws-config/src/sts/util.rs index 426d3eb40a..c4fd630aef 100644 --- a/aws/rust-runtime/aws-config/src/sts/util.rs +++ b/aws/rust-runtime/aws-config/src/sts/util.rs @@ -7,6 +7,7 @@ use aws_credential_types::provider::{self, error::CredentialsError}; use aws_credential_types::Credentials as AwsCredentials; use aws_sdk_sts::types::Credentials as StsCredentials; +use aws_smithy_async::time::TimeSource; use std::convert::TryFrom; use std::time::{SystemTime, UNIX_EPOCH}; @@ -45,9 +46,7 @@ pub(crate) fn into_credentials( /// STS Assume Role providers MUST assign a name to their generated session. When a user does not /// provide a name for the session, the provider will choose a name composed of a base + a timestamp, /// e.g. `profile-file-provider-123456789` -pub(crate) fn default_session_name(base: &str) -> String { - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .expect("post epoch"); +pub(crate) fn default_session_name(base: &str, ts: SystemTime) -> String { + let now = ts.now().duration_since(UNIX_EPOCH).expect("post epoch"); format!("{}-{}", base, now.as_millis()) } diff --git a/aws/rust-runtime/aws-config/src/web_identity_token.rs b/aws/rust-runtime/aws-config/src/web_identity_token.rs index 15da88d6c5..7ea55fdf26 100644 --- a/aws/rust-runtime/aws-config/src/web_identity_token.rs +++ b/aws/rust-runtime/aws-config/src/web_identity_token.rs @@ -65,6 +65,7 @@ use crate::provider_config::ProviderConfig; use crate::sts; use aws_credential_types::provider::{self, error::CredentialsError, future, ProvideCredentials}; use aws_sdk_sts::Client as StsClient; +use aws_smithy_async::time::SharedTimeSource; use aws_smithy_types::error::display::DisplayErrorContext; use aws_types::os_shim_internal::{Env, Fs}; use std::borrow::Cow; @@ -80,6 +81,7 @@ const ENV_VAR_SESSION_NAME: &str = "AWS_ROLE_SESSION_NAME"; #[derive(Debug)] pub struct WebIdentityTokenCredentialsProvider { source: Source, + time_source: SharedTimeSource, fs: Fs, sts_client: StsClient, } @@ -131,9 +133,9 @@ impl WebIdentityTokenCredentialsProvider { "AWS_ROLE_ARN environment variable must be set", ) })?; - let session_name = env - .get(ENV_VAR_SESSION_NAME) - .unwrap_or_else(|_| sts::util::default_session_name("web-identity-token")); + let session_name = env.get(ENV_VAR_SESSION_NAME).unwrap_or_else(|_| { + sts::util::default_session_name("web-identity-token", self.time_source.now()) + }); Ok(Cow::Owned(StaticConfiguration { web_identity_token_file: token_file.into(), role_arn, @@ -203,6 +205,7 @@ impl Builder { source, fs: conf.fs(), sts_client: StsClient::from_conf(conf.sts_client_config().build()), + time_source: conf.time_source(), } } } diff --git a/aws/rust-runtime/aws-credential-types/src/time_source.rs b/aws/rust-runtime/aws-credential-types/src/time_source.rs index 2639d2f45a..212c7aa904 100644 --- a/aws/rust-runtime/aws-credential-types/src/time_source.rs +++ b/aws/rust-runtime/aws-credential-types/src/time_source.rs @@ -3,31 +3,22 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_async::time::{SharedTimeSource, TimeSource as TimeSourceTrait}; use std::ops::Deref; use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime}; +impl TimeSourceTrait for TimeSource { + fn now(&self) -> SystemTime { + self.now() + } +} + /// Time source abstraction /// /// Simple abstraction representing time either real-time or manually-specified for testing -/// -/// # Examples -/// -/// ```rust -/// # struct Client { -/// # // stub -/// # } -/// # -/// # impl Client { -/// # fn with_timesource(ts: TimeSource) -> Self { -/// # Client { } -/// # } -/// # } -/// use aws_credential_types::time_source::TimeSource; -/// let time = TimeSource::default(); -/// let client = Client::with_timesource(time); -/// ``` #[derive(Debug, Clone)] +// TODO(breakingChangeWindow): Delete this struct pub struct TimeSource(Inner); impl TimeSource { @@ -36,11 +27,17 @@ impl TimeSource { TimeSource(Inner::Testing(time_source.clone())) } + /// Creates `TimeSource` from a shared time source + pub fn shared(time_source: SharedTimeSource) -> Self { + TimeSource(Inner::Shared(time_source)) + } + /// Returns the current system time based on the mode. pub fn now(&self) -> SystemTime { match &self.0 { Inner::Default => SystemTime::now(), Inner::Testing(testing) => testing.now(), + Inner::Shared(ts) => ts.now(), } } } @@ -53,6 +50,8 @@ impl Default for TimeSource { } /// Time Source that can be manually moved for tests +/// > This has been superseded by [`aws_smithy_async::time::TimeSource`] and will be removed in a +/// > future release. /// /// # Examples /// @@ -112,12 +111,11 @@ impl TestingTimeSource { } } -// In the future, if needed we can add a time source trait, however, the testing time source -// should cover most test use cases. #[derive(Debug, Clone)] enum Inner { Default, Testing(TestingTimeSource), + Shared(SharedTimeSource), } #[cfg(test)] diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml index 8836e8c558..5586b75ae2 100644 --- a/aws/rust-runtime/aws-inlineable/Cargo.toml +++ b/aws/rust-runtime/aws-inlineable/Cargo.toml @@ -21,7 +21,7 @@ aws-sig-auth = { path = "../aws-sig-auth" } aws-smithy-checksums = { path = "../../../rust-runtime/aws-smithy-checksums" } aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client" } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } -aws-smithy-http-tower= { path = "../../../rust-runtime/aws-smithy-http-tower" } +aws-smithy-http-tower = { path = "../../../rust-runtime/aws-smithy-http-tower" } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } aws-types = { path = "../aws-types" } @@ -44,6 +44,7 @@ aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http", features = [ aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } tempfile = "3.2.0" tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] } [package.metadata.docs.rs] all-features = true diff --git a/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs b/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs index 3aefdf5093..3a5c3258d1 100644 --- a/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs +++ b/aws/rust-runtime/aws-inlineable/tests/middleware_e2e_test.rs @@ -28,6 +28,7 @@ use aws_http::retry::AwsResponseRetryClassifier; use aws_http::user_agent::AwsUserAgent; use aws_inlineable::middleware::DefaultMiddleware; use aws_sig_auth::signer::OperationSigningConfig; +use aws_smithy_async::time::SharedTimeSource; use aws_types::region::SigningRegion; use aws_types::SigningService; @@ -94,7 +95,9 @@ fn test_operation() -> Operation::Ok(req) }) diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index c5bb21ca17..e241ae92e7 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -19,6 +19,7 @@ aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-types = { path = "../aws-types" } http = "0.2.3" percent-encoding = "2.1.0" @@ -28,6 +29,7 @@ uuid = { version = "1", features = ["v4", "fast-rng"] } [dev-dependencies] aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-test" } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] } proptest = "1" serde = { version = "1", features = ["derive"]} serde_json = "1" diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs index 8d72556843..f9453eaf71 100644 --- a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -309,7 +309,7 @@ impl HttpRequestSigner for SigV4HttpRequestSigner { ) -> Result<(), BoxError> { let operation_config = Self::extract_operation_config(auth_scheme_endpoint_config, config_bag)?; - let request_time = config_bag.request_time().unwrap_or_default().system_time(); + let request_time = config_bag.request_time().unwrap_or_default().now(); let credentials = if let Some(creds) = identity.data::() { creds @@ -356,18 +356,17 @@ impl HttpRequestSigner for SigV4HttpRequestSigner { #[cfg(feature = "event-stream")] { use aws_smithy_eventstream::frame::DeferredSignerSender; - use aws_smithy_runtime_api::client::orchestrator::RequestTime; use event_stream::SigV4MessageSigner; if let Some(signer_sender) = config_bag.get::() { - let time_override = config_bag.get::().copied(); + let time_source = config_bag.request_time().unwrap_or_default(); signer_sender .send(Box::new(SigV4MessageSigner::new( _signature, credentials.clone(), Region::new(signing_params.region().to_string()).into(), signing_params.service_name().to_string().into(), - time_override, + time_source, )) as _) .expect("failed to send deferred signer"); } @@ -383,8 +382,8 @@ mod event_stream { use aws_credential_types::Credentials; use aws_sigv4::event_stream::{sign_empty_message, sign_message}; use aws_sigv4::SigningParams; + use aws_smithy_async::time::SharedTimeSource; use aws_smithy_eventstream::frame::{Message, SignMessage, SignMessageError}; - use aws_smithy_runtime_api::client::orchestrator::RequestTime; use aws_types::region::SigningRegion; use aws_types::SigningService; @@ -395,7 +394,7 @@ mod event_stream { credentials: Credentials, signing_region: SigningRegion, signing_service: SigningService, - time: Option, + time: SharedTimeSource, } impl SigV4MessageSigner { @@ -404,7 +403,7 @@ mod event_stream { credentials: Credentials, signing_region: SigningRegion, signing_service: SigningService, - time: Option, + time: SharedTimeSource, ) -> Self { Self { last_signature, @@ -421,7 +420,7 @@ mod event_stream { .secret_key(self.credentials.secret_access_key()) .region(self.signing_region.as_ref()) .service_name(self.signing_service.as_ref()) - .time(self.time.unwrap_or_default().system_time()) + .time(self.time.now()) .settings(()); builder.set_security_token(self.credentials.session_token()); builder.build().unwrap() @@ -470,7 +469,7 @@ mod event_stream { Credentials::for_tests(), SigningRegion::from(region), SigningService::from_static("transcribe"), - Some(RequestTime::new(UNIX_EPOCH + Duration::new(1611160427, 0))), + SharedTimeSource::new(UNIX_EPOCH + Duration::new(1611160427, 0)), )); let mut signatures = Vec::new(); for _ in 0..5 { diff --git a/aws/rust-runtime/aws-sig-auth/Cargo.toml b/aws/rust-runtime/aws-sig-auth/Cargo.toml index cb9695f7db..6ef8e4374f 100644 --- a/aws/rust-runtime/aws-sig-auth/Cargo.toml +++ b/aws/rust-runtime/aws-sig-auth/Cargo.toml @@ -15,6 +15,7 @@ aws-credential-types = { path = "../aws-credential-types" } aws-sigv4 = { path = "../aws-sigv4" } aws-smithy-eventstream = { path = "../../../rust-runtime/aws-smithy-eventstream", optional = true } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-types = { path = "../aws-types" } http = "0.2.2" tracing = "0.1" @@ -22,8 +23,9 @@ tracing = "0.1" [dev-dependencies] aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } aws-endpoint = { path = "../aws-endpoint" } -aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types"} +aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } tracing-test = "0.2.1" +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] } [package.metadata.docs.rs] all-features = true diff --git a/aws/rust-runtime/aws-sig-auth/src/middleware.rs b/aws/rust-runtime/aws-sig-auth/src/middleware.rs index c8c4794f0b..8dc84da5c2 100644 --- a/aws/rust-runtime/aws-sig-auth/src/middleware.rs +++ b/aws/rust-runtime/aws-sig-auth/src/middleware.rs @@ -5,7 +5,6 @@ use std::error::Error; use std::fmt::{Display, Formatter}; -use std::time::SystemTime; use aws_smithy_http::middleware::MapRequest; use aws_smithy_http::operation::Request; @@ -13,6 +12,7 @@ use aws_smithy_http::property_bag::PropertyBag; use aws_credential_types::Credentials; use aws_sigv4::http_request::SignableBody; +use aws_smithy_async::time::SharedTimeSource; use aws_types::region::SigningRegion; use aws_types::SigningService; @@ -54,11 +54,8 @@ impl AsRef for Signature { /// - [`Credentials`](Credentials): Credentials to sign with /// - [`OperationSigningConfig`](OperationSigningConfig): Operation specific signing configuration, e.g. /// changes to URL encoding behavior, or headers that must be omitted. +/// - [`SharedTimeSource`]: The time source to use when signing the request. /// If any of these fields are missing, the middleware will return an error. -/// -/// The following fields MAY be present in the property bag: -/// - [`SystemTime`](SystemTime): The timestamp to use when signing the request. If this field is not present -/// [`SystemTime::now`](SystemTime::now) will be used. #[derive(Clone, Debug)] pub struct SigV4SigningStage { signer: SigV4Signer, @@ -152,9 +149,10 @@ fn signing_config( let payload_override = config.get::>(); let request_config = RequestConfig { request_ts: config - .get::() - .copied() - .unwrap_or_else(SystemTime::now), + .get::() + .map(|t| t.now()) + // TODO(enableNewSmithyRuntime): Remove this fallback + .unwrap_or_else(|| SharedTimeSource::default().now()), region, payload_override, service: signing_service, @@ -192,7 +190,7 @@ impl MapRequest for SigV4SigningStage { // If this is an event stream operation, set up the event stream signer #[cfg(feature = "sign-eventstream")] if let Some(signer_sender) = config.get::() { - let time_override = config.get::().copied(); + let time_override = config.get::().map(|ts| ts.now()); signer_sender .send(Box::new(EventStreamSigV4Signer::new( signature.as_ref().into(), @@ -222,6 +220,7 @@ mod test { use aws_credential_types::Credentials; use aws_endpoint::AwsAuthStage; + use aws_smithy_async::time::SharedTimeSource; use aws_types::region::{Region, SigningRegion}; use aws_types::SigningService; @@ -262,7 +261,6 @@ mod test { fn sends_event_stream_signer_for_event_stream_operations() { use crate::event_stream::SigV4MessageSigner as EventStreamSigV4Signer; use aws_smithy_eventstream::frame::{DeferredSigner, SignMessage}; - use std::time::SystemTime; let (mut deferred_signer, deferred_signer_sender) = DeferredSigner::new(); let req = http::Request::builder() @@ -273,7 +271,9 @@ mod test { let req = operation::Request::new(req) .augment(|req, properties| { properties.insert(region.clone()); - properties.insert::(UNIX_EPOCH + Duration::new(1611160427, 0)); + properties.insert::(SharedTimeSource::new( + UNIX_EPOCH + Duration::new(1611160427, 0), + )); properties.insert(SigningService::from_static("kinesis")); properties.insert(OperationSigningConfig::default_config()); properties.insert(Credentials::for_tests()); @@ -315,7 +315,9 @@ mod test { let req = operation::Request::new(req) .augment(|req, conf| { conf.insert(region.clone()); - conf.insert(UNIX_EPOCH + Duration::new(1611160427, 0)); + conf.insert(SharedTimeSource::new( + UNIX_EPOCH + Duration::new(1611160427, 0), + )); conf.insert(SigningService::from_static("kinesis")); conf.insert(endpoint); Result::<_, Infallible>::Ok(req) diff --git a/aws/rust-runtime/aws-types/external-types.toml b/aws/rust-runtime/aws-types/external-types.toml index 71ce4acd8a..6c1d2b5aab 100644 --- a/aws/rust-runtime/aws-types/external-types.toml +++ b/aws/rust-runtime/aws-types/external-types.toml @@ -2,6 +2,8 @@ allowed_external_types = [ "aws_credential_types::cache::CredentialsCache", "aws_credential_types::provider::SharedCredentialsProvider", "aws_smithy_async::rt::sleep::AsyncSleep", + "aws_smithy_async::time::TimeSource", + "aws_smithy_async::time::SharedTimeSource", "aws_smithy_client::http_connector", "aws_smithy_client::http_connector::HttpConnector", "aws_smithy_http::endpoint::Endpoint", diff --git a/aws/rust-runtime/aws-types/src/sdk_config.rs b/aws/rust-runtime/aws-types/src/sdk_config.rs index f3a0301ae8..af60e88f5d 100644 --- a/aws/rust-runtime/aws-types/src/sdk_config.rs +++ b/aws/rust-runtime/aws-types/src/sdk_config.rs @@ -14,6 +14,7 @@ use std::sync::Arc; use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::SharedCredentialsProvider; use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; @@ -40,6 +41,8 @@ If no dual-stack endpoint is available the request MAY return an error. **Note**: Some services do not offer dual-stack as a configurable parameter (e.g. Code Catalyst). For these services, this setting has no effect" }; + + (time_source) => { "The time source use to use for this client. This only needs to be required for creating deterministic tests or platforms where `SystemTime::now()` is not supported." }; } } @@ -53,6 +56,7 @@ pub struct SdkConfig { endpoint_url: Option, retry_config: Option, sleep_impl: Option>, + time_source: Option, timeout_config: Option, http_connector: Option, use_fips: Option, @@ -73,6 +77,7 @@ pub struct Builder { endpoint_url: Option, retry_config: Option, sleep_impl: Option>, + time_source: Option, timeout_config: Option, http_connector: Option, use_fips: Option, @@ -499,6 +504,18 @@ impl Builder { self } + #[doc = docs_for!(time_source)] + pub fn time_source(mut self, time_source: impl TimeSource + 'static) -> Self { + self.set_time_source(Some(SharedTimeSource::new(time_source))); + self + } + + #[doc = docs_for!(time_source)] + pub fn set_time_source(&mut self, time_source: Option) -> &mut Self { + self.time_source = time_source; + self + } + /// Build a [`SdkConfig`](SdkConfig) from this builder pub fn build(self) -> SdkConfig { SdkConfig { @@ -513,6 +530,7 @@ impl Builder { http_connector: self.http_connector, use_fips: self.use_fips, use_dual_stack: self.use_dual_stack, + time_source: self.time_source, } } } @@ -554,6 +572,11 @@ impl SdkConfig { self.credentials_provider.as_ref() } + /// Configured time source + pub fn time_source(&self) -> Option<&SharedTimeSource> { + self.time_source.as_ref() + } + /// Configured app name pub fn app_name(&self) -> Option<&AppName> { self.app_name.as_ref() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index ed9a6271a9..754ac52310 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -23,11 +23,13 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : "BeforeTransmitInterceptorContextMut" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::interceptors::BeforeTransmitInterceptorContextMut"), "ConfigBag" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("config_bag::ConfigBag"), - "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::ConfigBagAccessors"), + "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::ConfigBagAccessors"), "http" to CargoDependency.Http.toType(), "InterceptorContext" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::interceptors::InterceptorContext"), - "RequestTime" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::RequestTime"), + "SharedTimeSource" to CargoDependency.smithyAsync(runtimeConfig).withFeature("test-util").toType() + .resolve("time::SharedTimeSource"), "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::interceptors::SharedInterceptor"), "TestParamsSetterInterceptor" to CargoDependency.smithyRuntime(runtimeConfig).withFeature("test-util") @@ -45,7 +47,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self { use #{ConfigBagAccessors}; let interceptor = #{TestParamsSetterInterceptor}::new(move |_: &mut #{BeforeTransmitInterceptorContextMut}<'_>, cfg: &mut #{ConfigBag}| { - cfg.set_request_time(#{RequestTime}::new(request_time)); + cfg.set_request_time(#{SharedTimeSource}::new(request_time)); }); self.interceptors.push(#{SharedInterceptor}::new(interceptor)); self @@ -89,7 +91,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : ##[doc(hidden)] // This is a temporary method for testing. NEVER use it in production pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self { - self.operation.properties_mut().insert(request_time); + self.operation.properties_mut().insert(#{SharedTimeSource}::new(request_time)); self } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index 4c9f6feffc..fb53f61165 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -25,7 +25,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientHttpBou import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs -import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate @@ -208,12 +207,14 @@ class AwsInputPresignedMethod( *codegenScope, ) rustBlock("") { - rust( + rustTemplate( """ // Change signature type to query params and wire up presigning config let mut props = request.properties_mut(); - props.insert(presigning_config.start_time()); + props.insert(#{SharedTimeSource}::new(presigning_config.start_time())); """, + "SharedTimeSource" to RuntimeType.smithyAsync(runtimeConfig) + .resolve("time::SharedTimeSource"), ) withBlock("props.insert(", ");") { rustTemplate( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt index f5d8afcbe2..b77b5c9bc7 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt @@ -77,6 +77,7 @@ class GenericSmithySdkConfigSettings : ClientCodegenDecorator { ${section.serviceConfigBuilder}.set_sleep_impl(${section.sdkConfig}.sleep_impl()); ${section.serviceConfigBuilder}.set_http_connector(${section.sdkConfig}.http_connector().cloned()); + ${section.serviceConfigBuilder}.set_time_source(${section.sdkConfig}.time_source().cloned()); """, ) }, diff --git a/aws/sdk/integration-tests/kms/Cargo.toml b/aws/sdk/integration-tests/kms/Cargo.toml index fbed1109f7..188a2d42dd 100644 --- a/aws/sdk/integration-tests/kms/Cargo.toml +++ b/aws/sdk/integration-tests/kms/Cargo.toml @@ -16,10 +16,11 @@ 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" } aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } +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" } aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api" } bytes = "1.0.0" http = "0.2.0" -tokio = { version = "1.23.1", features = ["full", "test-util"]} +tokio = { version = "1.23.1", features = ["full", "test-util"] } tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } diff --git a/aws/sdk/integration-tests/s3/tests/checksums.rs b/aws/sdk/integration-tests/s3/tests/checksums.rs index ae533964a1..be9c860628 100644 --- a/aws/sdk/integration-tests/s3/tests/checksums.rs +++ b/aws/sdk/integration-tests/s3/tests/checksums.rs @@ -59,6 +59,7 @@ async fn test_checksum_on_streaming_response( ); let sdk_config = SdkConfig::builder() .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) + .time_source(UNIX_EPOCH + Duration::from_secs(1624036048)) .region(Region::new("us-east-1")) .http_connector(conn.clone()) .build(); @@ -73,7 +74,6 @@ async fn test_checksum_on_streaming_response( .customize() .await .unwrap() - .request_time_for_tests(UNIX_EPOCH + Duration::from_secs(1624036048)) .user_agent_for_tests() .send() .await diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs index b1e708aad1..b69d67b3f3 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs @@ -8,6 +8,7 @@ mod util; use aws_http::user_agent::AwsUserAgent; use aws_sdk_s3::config::{Credentials, Region}; use aws_sdk_s3::Client; +use aws_smithy_async::test_util::StaticTimeSource; use aws_smithy_client::dvr; use aws_smithy_client::dvr::MediaType; use aws_smithy_client::erase::DynConnector; @@ -15,7 +16,6 @@ use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, Interceptor, }; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; -use aws_smithy_runtime_api::client::orchestrator::RequestTime; use aws_smithy_runtime_api::config_bag::ConfigBag; use http::header::USER_AGENT; use http::HeaderValue; @@ -66,7 +66,7 @@ impl Interceptor for RequestTimeResetInterceptor { _context: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { - cfg.set_request_time(RequestTime::new(UNIX_EPOCH)); + cfg.set_request_time(StaticTimeSource::new(UNIX_EPOCH)); Ok(()) } @@ -81,7 +81,7 @@ impl Interceptor for RequestTimeAdvanceInterceptor { cfg: &mut ConfigBag, ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { let request_time = cfg.request_time().unwrap(); - let request_time = RequestTime::new(request_time.system_time() + self.0); + let request_time = StaticTimeSource::new(request_time.now() + self.0); cfg.set_request_time(request_time); Ok(()) diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs index a5138f40de..afaf9d2718 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs @@ -13,15 +13,14 @@ use aws_smithy_client::dvr::MediaType; use aws_smithy_client::erase::DynConnector; use aws_smithy_runtime::client::retries::strategy::FixedDelayRetryStrategy; use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; -use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; +use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::config_bag::ConfigBag; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::{Duration, UNIX_EPOCH}; #[derive(Debug)] struct FixupPlugin { client: Client, - timestamp: SystemTime, } // # One SDK operation invocation. @@ -44,7 +43,6 @@ async fn three_retries_and_then_success() { .bucket("test-bucket"); cfg.put(params_builder); - cfg.set_request_time(RequestTime::new(self.timestamp.clone())); cfg.put(AwsUserAgent::for_tests()); cfg.put(InvocationId::for_tests()); cfg.set_retry_strategy(FixedDelayRetryStrategy::one_second_delay()); @@ -58,11 +56,11 @@ async fn three_retries_and_then_success() { .credentials_provider(Credentials::for_tests()) .region(Region::new("us-east-1")) .http_connector(DynConnector::new(conn.clone())) + .time_source(UNIX_EPOCH + Duration::from_secs(1624036048)) .build(); let client = Client::from_conf(config); let fixup = FixupPlugin { client: client.clone(), - timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), }; let resp = dbg!( diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs index 7ce99e3af3..4c9864ae84 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs @@ -5,10 +5,11 @@ use aws_http::user_agent::AwsUserAgent; use aws_runtime::invocation_id::InvocationId; +use aws_smithy_async::test_util::StaticTimeSource; use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, Interceptor, InterceptorRegistrar, }; -use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; +use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::config_bag::ConfigBag; use http::header::USER_AGENT; @@ -18,16 +19,13 @@ use std::time::SystemTime; pub const X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent"); #[derive(Debug)] -pub struct FixupPlugin { - pub timestamp: SystemTime, -} +pub struct FixupPlugin; impl RuntimePlugin for FixupPlugin { fn configure( &self, cfg: &mut ConfigBag, _interceptors: &mut InterceptorRegistrar, ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { - cfg.set_request_time(RequestTime::new(self.timestamp.clone())); cfg.put(InvocationId::for_tests()); Ok(()) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 5fcab7bd33..13cc1c26ba 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -276,6 +276,7 @@ class ResiliencyServiceRuntimePluginCustomization : ServiceRuntimePluginCustomiz if let Some(timeout_config) = self.handle.conf.timeout_config() { ${section.configBagName}.put(timeout_config.clone()); } + ${section.configBagName}.put(self.handle.conf.time_source.clone()); """, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index d43c5706ec..c6116f7326 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -18,6 +18,8 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.Resilien import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.TimeSourceOperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.timeSourceCustomization import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customizations.AllowLintsCustomization @@ -47,7 +49,8 @@ class RequiredCustomizations : ClientCodegenDecorator { IdempotencyTokenGenerator(codegenContext, operation) + EndpointPrefixGenerator(codegenContext, operation) + HttpChecksumRequiredGenerator(codegenContext, operation) + - HttpVersionListCustomization(codegenContext, operation) + HttpVersionListCustomization(codegenContext, operation) + + TimeSourceOperationCustomization() override fun configCustomizations( codegenContext: ClientCodegenContext, @@ -57,9 +60,9 @@ class RequiredCustomizations : ClientCodegenDecorator { if (codegenContext.smithyRuntimeMode.generateOrchestrator) { baseCustomizations + ResiliencyConfigCustomization(codegenContext) + InterceptorConfigCustomization( codegenContext, - ) + ) + timeSourceCustomization(codegenContext) } else { - baseCustomizations + ResiliencyConfigCustomization(codegenContext) + baseCustomizations + ResiliencyConfigCustomization(codegenContext) + timeSourceCustomization(codegenContext) } override fun libRsCustomizations( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 3112798fe0..4af06de0bd 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -29,6 +29,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomizat import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.letIf /** * [ServiceConfig] is the parent type of sections that can be overridden when generating a config for a service. @@ -103,7 +104,13 @@ sealed class ServiceConfig(name: String) : Section(name) { data class DefaultForTests(val configBuilderRef: String) : ServiceConfig("DefaultForTests") } -data class ConfigParam(val name: String, val type: Symbol, val setterDocs: Writable?, val getterDocs: Writable? = null) +data class ConfigParam( + val name: String, + val type: Symbol, + val setterDocs: Writable?, + val getterDocs: Writable? = null, + val optional: Boolean = true, +) /** * Config customization for a config param with no special behavior: @@ -116,7 +123,11 @@ fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : Conf return when (section) { is ServiceConfig.ConfigStruct -> writable { docsOrFallback(param.getterDocs) - rust("pub (crate) ${param.name}: #T,", param.type.makeOptional()) + val t = when (param.optional) { + true -> param.type.makeOptional() + false -> param.type + } + rust("pub (crate) ${param.name}: #T,", t) } ServiceConfig.ConfigImpl -> emptySection @@ -148,7 +159,8 @@ fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : Conf } ServiceConfig.BuilderBuild -> writable { - rust("${param.name}: self.${param.name},") + val default = "".letIf(!param.optional) { ".unwrap_or_default() " } + rust("${param.name}: self.${param.name}$default,") } ServiceConfig.ToRuntimePlugin -> emptySection @@ -204,6 +216,7 @@ class ServiceConfigGenerator(private val customizations: List writable { + rust( + """ + ${section.request}.properties_mut().insert(${section.config}.time_source.clone()); + """, + ) + } + + else -> emptySection + } + } +} diff --git a/rust-runtime/aws-smithy-async/src/test_util.rs b/rust-runtime/aws-smithy-async/src/test_util.rs index 5a8e864ada..7d3b4f78ce 100644 --- a/rust-runtime/aws-smithy-async/src/test_util.rs +++ b/rust-runtime/aws-smithy-async/src/test_util.rs @@ -13,7 +13,7 @@ use tokio::sync::Barrier; use tokio::time::timeout; use crate::rt::sleep::{AsyncSleep, Sleep}; -use crate::time::TimeSource; +use crate::time::{SharedTimeSource, TimeSource}; /// Manually controlled time source #[derive(Debug, Clone)] @@ -24,7 +24,7 @@ pub struct ManualTimeSource { impl TimeSource for ManualTimeSource { fn now(&self) -> SystemTime { - self.start_time + dbg!(self.log.lock().unwrap()).iter().sum() + self.start_time + self.log.lock().unwrap().iter().sum() } } @@ -194,6 +194,49 @@ pub fn controlled_time_and_sleep( (ManualTimeSource { start_time, log }, sleep, gate) } +#[derive(Debug)] +/// Time source that always returns the same time +pub struct StaticTimeSource { + time: SystemTime, +} + +impl StaticTimeSource { + /// Creates a new static time source that always returns the same time + pub fn new(time: SystemTime) -> Self { + Self { time } + } +} + +impl TimeSource for StaticTimeSource { + fn now(&self) -> SystemTime { + self.time + } +} + +impl TimeSource for SystemTime { + fn now(&self) -> SystemTime { + *self + } +} + +impl From for SharedTimeSource { + fn from(value: StaticTimeSource) -> Self { + SharedTimeSource::new(value) + } +} + +impl From for SharedTimeSource { + fn from(value: SystemTime) -> Self { + SharedTimeSource::new(value) + } +} + +impl From for SharedTimeSource { + fn from(value: ManualTimeSource) -> Self { + SharedTimeSource::new(value) + } +} + #[cfg(test)] mod test { use crate::rt::sleep::AsyncSleep; diff --git a/rust-runtime/aws-smithy-async/src/time.rs b/rust-runtime/aws-smithy-async/src/time.rs index 85c2ff0519..d2e52c9a87 100644 --- a/rust-runtime/aws-smithy-async/src/time.rs +++ b/rust-runtime/aws-smithy-async/src/time.rs @@ -5,6 +5,7 @@ //! Time source abstraction to support WASM and testing use std::fmt::Debug; +use std::sync::Arc; use std::time::SystemTime; /// Trait with a `now()` function returning the current time @@ -30,3 +31,33 @@ impl TimeSource for SystemTimeSource { SystemTime::now() } } + +impl Default for SharedTimeSource { + fn default() -> Self { + SharedTimeSource(Arc::new(SystemTimeSource)) + } +} + +#[derive(Debug, Clone)] +/// Time source structure used inside SDK +/// +/// This implements Default—the default implementation will use `SystemTime::now()` +pub struct SharedTimeSource(Arc); + +impl SharedTimeSource { + /// Returns the current time + pub fn now(&self) -> SystemTime { + self.0.now() + } + + /// Creates a new shared time source + pub fn new(source: impl TimeSource + 'static) -> Self { + Self(Arc::new(source)) + } +} + +impl TimeSource for SharedTimeSource { + fn now(&self) -> SystemTime { + self.0.now() + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 3999c365b2..97ff322145 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -15,6 +15,7 @@ use crate::config_bag::ConfigBag; use crate::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_http::body::SdkBody; use aws_smithy_types::endpoint::Endpoint; use bytes::Bytes; @@ -22,7 +23,6 @@ use std::fmt; use std::future::Future as StdFuture; use std::pin::Pin; use std::sync::Arc; -use std::time::SystemTime; pub use error::OrchestratorError; @@ -78,29 +78,6 @@ pub trait EndpointResolver: Send + Sync + fmt::Debug { fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Result; } -/// Time that the request is being made (so that time can be overridden in the [`ConfigBag`]). -#[non_exhaustive] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct RequestTime(SystemTime); - -impl Default for RequestTime { - fn default() -> Self { - Self(SystemTime::now()) - } -} - -impl RequestTime { - /// Create a new [`RequestTime`]. - pub fn new(time: SystemTime) -> Self { - Self(time) - } - - /// Returns the request time as a [`SystemTime`]. - pub fn system_time(&self) -> SystemTime { - self.0 - } -} - /// Informs the orchestrator on whether or not the request body needs to be loaded into memory before transmit. /// /// This enum gets placed into the `ConfigBag` to change the orchestrator behavior. @@ -161,8 +138,8 @@ pub trait ConfigBagAccessors { fn retry_strategy(&self) -> &dyn RetryStrategy; fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static); - fn request_time(&self) -> Option; - fn set_request_time(&mut self, request_time: RequestTime); + fn request_time(&self) -> Option; + fn set_request_time(&mut self, time_source: impl TimeSource + 'static); fn sleep_impl(&self) -> Option>; fn set_sleep_impl(&mut self, async_sleep: Option>); @@ -288,12 +265,12 @@ impl ConfigBagAccessors for ConfigBag { self.put::>(Box::new(retry_strategy)); } - fn request_time(&self) -> Option { - self.get::().cloned() + fn request_time(&self) -> Option { + self.get::().cloned() } - fn set_request_time(&mut self, request_time: RequestTime) { - self.put::(request_time); + fn set_request_time(&mut self, request_time: impl TimeSource + 'static) { + self.put::(SharedTimeSource::new(request_time)); } fn sleep_impl(&self) -> Option> { diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 496d50d84b..aadbfcac83 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -30,7 +30,7 @@ tokio = { version = "1.25", features = [] } tracing = "0.1.37" [dev-dependencies] -aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio"] } +aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio", "test-util"] } tokio = { version = "1.25", features = ["macros", "rt", "test-util"] } tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } tracing-test = "0.2.1" diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs index 74fa60bd70..543777ba6a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs @@ -47,7 +47,7 @@ mod tests { use super::*; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; - use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, RequestTime}; + use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::type_erasure::TypedBox; use std::time::{Duration, UNIX_EPOCH}; @@ -64,15 +64,12 @@ mod tests { let interceptor = TestParamsSetterInterceptor::new({ let request_time = request_time.clone(); move |_: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag| { - cfg.set_request_time(RequestTime::new(request_time)); + cfg.set_request_time(request_time); } }); interceptor .modify_before_signing(&mut ctx, &mut cfg) .unwrap(); - assert_eq!( - request_time, - cfg.get::().unwrap().system_time() - ); + assert_eq!(cfg.request_time().unwrap().now(), request_time); } } From 840221dddb75ada42991224ddb224145c6cdbe37 Mon Sep 17 00:00:00 2001 From: Thomas Cameron Date: Wed, 31 May 2023 03:47:41 +0900 Subject: [PATCH 123/253] Add serde support to number type (#2645) ## Motivation and Context This is a child PR of https://github.com/awslabs/smithy-rs/pull/2616 The changes that this PR introduces is same as the ones that were merged to `unstable-serde-support` branch before. Initially, we tried to make commit to unstable-serde-support branch and merge changes one by one in small PRs. However, in order to make it up to date with the main branch, we would need to go through a large PR of over 700 files. Thus, I decided to create individual PRs that commits directly to `main` branch. ## Description - Implements `serde` support to `Number` ## Testing - Test checks whether the serialized/de-serialized data matches with the expected value ## Checklist NA ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti Co-authored-by: John DiSanti --- rust-runtime/aws-smithy-types/src/number.rs | 49 +++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/rust-runtime/aws-smithy-types/src/number.rs b/rust-runtime/aws-smithy-types/src/number.rs index bb771798d0..85488b1299 100644 --- a/rust-runtime/aws-smithy-types/src/number.rs +++ b/rust-runtime/aws-smithy-types/src/number.rs @@ -3,11 +3,33 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! A number type that implements Javascript / JSON semantics. + use crate::error::{TryFromNumberError, TryFromNumberErrorKind}; +#[cfg(all( + aws_sdk_unstable, + any(feature = "serde-serialize", feature = "serde-deserialize") +))] +use serde; /// A number type that implements Javascript / JSON semantics, modeled on serde_json: /// #[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr( + all(aws_sdk_unstable, feature = "serde-deserialize"), + derive(serde::Deserialize) +)] +#[cfg_attr( + all(aws_sdk_unstable, feature = "serde-serialize"), + derive(serde::Serialize) +)] +#[cfg_attr( + any( + all(aws_sdk_unstable, feature = "serde-deserialize"), + all(aws_sdk_unstable, feature = "serde-serialize") + ), + serde(untagged) +)] pub enum Number { /// Unsigned 64-bit integer value. PosInt(u64), @@ -441,4 +463,31 @@ mod test { 1452089100f32 ); } + + #[test] + #[cfg(all( + test, + aws_sdk_unstable, + feature = "serde-deserialize", + feature = "serde-serialize" + ))] + /// ensures that numbers are deserialized as expected + /// 0 <= PosInt + /// 0 > NegInt + /// non integer values == Float + fn number_serde() { + let n: Number = serde_json::from_str("1.1").unwrap(); + assert_eq!(n, Number::Float(1.1)); + let n: Number = serde_json::from_str("1").unwrap(); + assert_eq!(n, Number::PosInt(1)); + let n: Number = serde_json::from_str("0").unwrap(); + assert_eq!(n, Number::PosInt(0)); + let n: Number = serde_json::from_str("-1").unwrap(); + assert_eq!(n, Number::NegInt(-1)); + + assert_eq!("1.1", serde_json::to_string(&Number::Float(1.1)).unwrap()); + assert_eq!("1", serde_json::to_string(&Number::PosInt(1)).unwrap()); + assert_eq!("0", serde_json::to_string(&Number::PosInt(0)).unwrap()); + assert_eq!("-1", serde_json::to_string(&Number::NegInt(-1)).unwrap()); + } } From 7347c584581ef3e03ca087529ff8276687d0b606 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Tue, 30 May 2023 21:13:53 +0100 Subject: [PATCH 124/253] Remove `PollError` from server type parameters (#2457) ## Motivation and Context https://github.com/awslabs/smithy-rs/issues/2444 ## Description Remove `PollError` from the implementation and documentation. --- CHANGELOG.next.toml | 10 ++++ .../src/operation/handler.rs | 13 ++--- .../src/operation/mod.rs | 44 +++++------------ .../src/operation/operation_service.rs | 36 +++++++------- .../src/operation/shape.rs | 4 +- .../src/operation/upgrade.rs | 47 +++++++++---------- 6 files changed, 66 insertions(+), 88 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 4c7d2e950e..dfd80c74f2 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -105,3 +105,13 @@ author = "david-perez" references = ["smithy-rs#2676", "smithy-rs#2685"] meta = { "breaking" = true, "tada" = false, "bug" = false } +[[smithy-rs]] +message = """Remove `PollError` from an operations `Service::Error`. + +Any [`tower::Service`](https://docs.rs/tower/latest/tower/trait.Service.html) provided to +[`Operation::from_service`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/struct.Operation.html#method.from_service) +no longer requires `Service::Error = OperationError`, instead requiring just `Service::Error = Op::Error`. +""" +references = ["smithy-rs#2457"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } +author = "hlbarber" diff --git a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs index 7a30a9a12e..c40c0f7742 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs @@ -10,13 +10,10 @@ use std::{ task::{Context, Poll}, }; -use futures_util::{ - future::{Map, MapErr}, - FutureExt, TryFutureExt, -}; +use futures_util::{future::Map, FutureExt}; use tower::Service; -use super::{OperationError, OperationShape}; +use super::OperationShape; /// A utility trait used to provide an even interface for all operation handlers. /// @@ -143,14 +140,14 @@ where H: Handler, { type Response = Op::Output; - type Error = OperationError; - type Future = MapErr Self::Error>; + type Error = Op::Error; + type Future = H::Future; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, (input, exts): (Op::Input, Exts)) -> Self::Future { - self.handler.call(input, exts).map_err(OperationError::Model) + self.handler.call(input, exts) } } diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index 3abf9e2540..91b6c36f88 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -84,16 +84,13 @@ //! ## [`OperationService`] //! //! Similarly, the [`OperationService`] trait is implemented by all `Service<(Op::Input, ...)>` with -//! `Response = Op::Output`, and `Error = OperationError`. -//! -//! We use [`OperationError`], with a `PollError` not constrained by the model, to allow the user to provide a custom -//! [`Service::poll_ready`](tower::Service::poll_ready) implementation. +//! `Response = Op::Output`, and `Error = Op::Error`. //! //! The following are examples of [`Service`](tower::Service)s which implement [`OperationService`]: //! -//! - `Service>`. -//! - `Service<(CartIdentifier, Extension), Response = ShoppingCart, Error = OperationError>`. -//! - `Service<(CartIdentifier, Extension, Extension), Response = ShoppingCart, Error = OperationError)`. +//! - `Service`. +//! - `Service<(CartIdentifier, Extension), Response = ShoppingCart, Error = GetShoppingCartError>`. +//! - `Service<(CartIdentifier, Extension, Extension), Response = ShoppingCart, Error = GetShoppingCartError)`. //! //! Notice the parallels between [`OperationService`] and [`Handler`]. //! @@ -116,7 +113,7 @@ //! # type Output = ShoppingCart; //! # type Error = GetShoppingError; //! # } -//! # type OpFuture = std::future::Ready>>; +//! # type OpFuture = std::future::Ready>; //! // Construction of an `Operation` from a `Handler`. //! //! async fn op_handler(input: CartIdentifier) -> Result { @@ -127,13 +124,11 @@ //! //! // Construction of an `Operation` from a `Service`. //! -//! pub struct PollError; -//! //! pub struct OpService; //! //! impl Service for OpService { //! type Response = ShoppingCart; -//! type Error = OperationError; +//! type Error = GetShoppingError; //! type Future = OpFuture; //! //! fn poll_ready(&mut self, cx: &mut Context) -> Poll> { @@ -155,19 +150,13 @@ //! //! Both [`Handler`] and [`OperationService`] accept and return Smithy model structures. After an [`Operation`] is //! constructed they are converted to a canonical form -//! `Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError>`. The +//! `Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error>`. The //! [`UpgradeLayer`] acts upon such services by converting them to -//! `Service`. +//! `Service`. //! -//! Note that the `PollError` is still exposed, for two reasons: -//! -//! - Smithy is agnostic to `PollError` and therefore we have no prescribed way to serialize it to a [`http::Response`] -//! , unlike the operation errors. -//! - The intention of `PollError` is to signal that the underlying service is no longer able to take requests, so -//! should be discarded. See [`Service::poll_ready`](tower::Service::poll_ready). //! //! The [`UpgradeLayer`] and it's [`Layer::Service`](tower::Layer::Service) [`Upgrade`] are both parameterized by a -//! protocol. This allows for upgrading to `Service` to be +//! protocol. This allows for upgrading to `Service` to be //! protocol dependent. //! //! The [`Operation::upgrade`] will apply [`UpgradeLayer`] to `S` then apply the [`Layer`](tower::Layer) `L`. The @@ -208,15 +197,15 @@ impl Operation { } } -impl Operation> { +impl Operation> { /// Creates an [`Operation`] from a [`Service`](tower::Service). pub fn from_service(inner: S) -> Self where Op: OperationShape, - S: OperationService, + S: OperationService, { Self { - inner: inner.canonicalize(), + inner: inner.normalize(), layer: Identity::new(), } } @@ -235,12 +224,3 @@ impl Operation> { } } } - -/// The operation [`Service`](tower::Service) has two classes of failure modes - those specified by the Smithy model -/// and those associated with [`Service::poll_ready`](tower::Service::poll_ready). -pub enum OperationError { - /// An error modelled by the Smithy model occurred. - Model(ModelError), - /// A [`Service::poll_ready`](tower::Service::poll_ready) failure occurred. - PollReady(PollError), -} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs b/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs index 2dd7f6d92e..f759f297a2 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs @@ -10,7 +10,7 @@ use std::{ use tower::Service; -use super::{OperationError, OperationShape}; +use super::OperationShape; /// A utility trait used to provide an even interface for all operation services. /// @@ -19,8 +19,7 @@ use super::{OperationError, OperationShape}; /// [`IntoService`](super::IntoService). /// /// See [`operation`](crate::operation) documentation for more info. -pub trait OperationService: - Service> +pub trait OperationService: Service where Op: OperationShape, { @@ -31,10 +30,10 @@ where } // `Service` -impl OperationService for S +impl OperationService for S where Op: OperationShape, - S: Service>, + S: Service, { type Normalized = Op::Input; @@ -44,10 +43,10 @@ where } // `Service<(Op::Input, Ext0)>` -impl OperationService for S +impl OperationService for S where Op: OperationShape, - S: Service<(Op::Input, Ext0), Response = Op::Output, Error = OperationError>, + S: Service<(Op::Input, Ext0), Response = Op::Output, Error = Op::Error>, { type Normalized = (Op::Input, Ext0); @@ -57,10 +56,10 @@ where } // `Service<(Op::Input, Ext0, Ext1)>` -impl OperationService for S +impl OperationService for S where Op: OperationShape, - S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = OperationError>, + S: Service<(Op::Input, Ext0, Ext1), Response = Op::Output, Error = Op::Error>, { type Normalized = (Op::Input, Ext0, Ext1); @@ -70,39 +69,37 @@ where } /// An extension trait of [`OperationService`]. -pub trait OperationServiceExt: OperationService +pub trait OperationServiceExt: OperationService where Op: OperationShape, { /// Convert the [`OperationService`] into a canonicalized [`Service`]. - fn canonicalize(self) -> Normalize + fn normalize(self) -> Normalize where Self: Sized, { Normalize { inner: self, _operation: PhantomData, - _poll_error: PhantomData, } } } -impl OperationServiceExt for F +impl OperationServiceExt for F where Op: OperationShape, - F: OperationService, + F: OperationService, { } /// A [`Service`] normalizing the request type of a [`OperationService`]. #[derive(Debug)] -pub struct Normalize { +pub struct Normalize { inner: S, _operation: PhantomData, - _poll_error: PhantomData, } -impl Clone for Normalize +impl Clone for Normalize where S: Clone, { @@ -110,15 +107,14 @@ where Self { inner: self.inner.clone(), _operation: PhantomData, - _poll_error: PhantomData, } } } -impl Service<(Op::Input, Exts)> for Normalize +impl Service<(Op::Input, Exts)> for Normalize where Op: OperationShape, - S: OperationService, + S: OperationService, { type Response = S::Response; type Error = S::Error; diff --git a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs index 9990326279..18414fc30c 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs @@ -33,9 +33,9 @@ pub trait OperationShapeExt: OperationShape { } /// Creates a new [`Operation`] for well-formed [`Service`](tower::Service)s. - fn from_service(svc: S) -> Operation> + fn from_service(svc: S) -> Operation> where - S: OperationService, + S: OperationService, Self: Sized, { Operation::from_service(svc) diff --git a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs index 42c4a5b26b..d1c95672e4 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs @@ -13,7 +13,7 @@ use std::{ use futures_util::ready; use pin_project_lite::pin_project; -use tower::{layer::util::Stack, Layer, Service}; +use tower::{layer::util::Stack, util::Oneshot, Layer, Service, ServiceExt}; use tracing::error; use crate::{ @@ -25,7 +25,7 @@ use crate::{ runtime_error::InternalFailureException, }; -use super::{Operation, OperationError, OperationShape}; +use super::{Operation, OperationShape}; /// A [`Layer`] responsible for taking an operation [`Service`], accepting and returning Smithy /// types and converting it into a [`Service`] taking and returning [`http`] types. @@ -106,7 +106,8 @@ pin_project! { } } -type InnerAlias = Inner<<(Input, Exts) as FromRequest>::Future, Fut>; +type InnerAlias = + Inner<<(Input, Exts) as FromRequest>::Future, Oneshot>; pin_project! { /// The [`Service::Future`] of [`Upgrade`]. @@ -118,11 +119,11 @@ pin_project! { { service: S, #[pin] - inner: InnerAlias + inner: InnerAlias } } -impl Future for UpgradeFuture +impl Future for UpgradeFuture where // `Op` is used to specify the operation shape Op: OperationShape, @@ -131,15 +132,15 @@ where // Smithy output must convert into a HTTP response Op::Output: IntoResponse

, // Smithy error must convert into a HTTP response - OpError: IntoResponse

, + Op::Error: IntoResponse

, // Must be able to convert extensions Exts: FromParts

, // The signature of the inner service is correct - S: Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError>, + S: Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error> + Clone, { - type Output = Result, PollError>; + type Output = Result, Infallible>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { @@ -150,7 +151,7 @@ where InnerProj::FromRequest { inner } => { let result = ready!(inner.poll(cx)); match result { - Ok(ok) => this.service.call(ok), + Ok(ok) => this.service.clone().oneshot(ok), Err(err) => return Poll::Ready(Ok(err.into_response())), } } @@ -158,10 +159,7 @@ where let result = ready!(call.poll(cx)); let output = match result { Ok(ok) => ok.into_response(), - Err(OperationError::Model(err)) => err.into_response(), - Err(OperationError::PollReady(_)) => { - unreachable!("poll error should not be raised") - } + Err(err) => err.into_response(), }; return Poll::Ready(Ok(output)); } @@ -172,7 +170,7 @@ where } } -impl Service> for Upgrade +impl Service> for Upgrade where // `Op` is used to specify the operation shape Op: OperationShape, @@ -181,23 +179,20 @@ where // Smithy output must convert into a HTTP response Op::Output: IntoResponse

, // Smithy error must convert into a HTTP response - OpError: IntoResponse

, + Op::Error: IntoResponse

, // Must be able to convert extensions Exts: FromParts

, // The signature of the inner service is correct - S: Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError> + Clone, + S: Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error> + Clone, { type Response = http::Response; - type Error = PollError; + type Error = Infallible; type Future = UpgradeFuture; - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.inner.poll_ready(cx).map_err(|err| match err { - OperationError::PollReady(err) => err, - OperationError::Model(_) => unreachable!("operation error should not be raised"), - }) + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: http::Request) -> Self::Future { @@ -223,7 +218,7 @@ pub trait Upgradable { type UpgradedService = <>::Layer as Layer>::Service>>>::Service; -impl Upgradable for Operation +impl Upgradable for Operation where // `Op` is used to specify the operation shape Op: OperationShape, @@ -239,8 +234,7 @@ where Exts: FromParts

, // The signature of the inner service is correct - Pl::Service: - Service<(Op::Input, Exts), Response = Op::Output, Error = OperationError> + Clone, + Pl::Service: Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error> + Clone, // The plugin takes this operation as input Pl: Plugin, @@ -260,7 +254,8 @@ where fn upgrade(self, plugin: &Pl) -> Route { let mapped = plugin.map(self); let layer = Stack::new(UpgradeLayer::new(), mapped.layer); - Route::new(layer.layer(mapped.inner)) + let svc = layer.layer(mapped.inner); + Route::new(svc) } } From 8c045d271ca7d4d72a41dbb71f2c0d992afcf51e Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 31 May 2023 14:08:51 -0400 Subject: [PATCH 125/253] Add support for TimeStreamWrite and TimeStreamQuery (#2707) TODO: - [x] docs - [x] integration test (canary even?) - [x] customize README for timestream ## Motivation and Context - #613 - https://github.com/awslabs/aws-sdk-rust/issues/114 ## Description This adds support for TSW and TSQ by adding endpoint discovery as a customization. This is made much simpler by the fact that endpoint discovery for these services **has no parameters** which means that there is no complexity from caching the returned endpoint. Customers call `.enable_endpoint_discovery()` on the client to create a version of the client with endpoint discovery enabled. This returns a new client and a Reloader from which customers must spawn the reload task if they want endpoint discovery to rerun. ## Testing ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --- CHANGELOG.next.toml | 6 + aws/rust-runtime/aws-inlineable/Cargo.toml | 1 + .../aws-inlineable/src/endpoint_discovery.rs | 287 ++ aws/rust-runtime/aws-inlineable/src/lib.rs | 9 + .../smithy/rustsdk/AwsCodegenDecorator.kt | 3 + .../smithy/rustsdk/AwsCrateDocsDecorator.kt | 9 +- .../software/amazon/smithy/rustsdk/AwsDocs.kt | 27 +- .../rustsdk/IntegrationTestDependencies.kt | 3 + .../customize/ServiceSpecificDecorator.kt | 6 + .../timestream/TimestreamDecorator.kt | 106 + aws/sdk/aws-models/timestream-query.json | 3047 ++++++++++++++ aws/sdk/aws-models/timestream-write.json | 3663 +++++++++++++++++ aws/sdk/gradle.properties | 3 +- aws/sdk/integration-tests/Cargo.toml | 1 + .../timestreamquery/Cargo.toml | 20 + .../timestreamquery/tests/endpoint_disco.rs | 76 + .../timestreamquery/tests/traffic.json | 407 ++ .../integration-tests/aws-sdk-s3/Cargo.toml | 1 + .../src/main/kotlin/aws/sdk/ServiceLoader.kt | 2 +- .../codegen/client/smithy/endpoint/Util.kt | 2 + .../rust/codegen/core/rustlang/RustWriter.kt | 13 +- .../core/smithy/customize/Customization.kt | 15 + .../aws-smithy-client/src/dvr/replay.rs | 29 +- 23 files changed, 7717 insertions(+), 19 deletions(-) create mode 100644 aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs create mode 100644 aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt create mode 100644 aws/sdk/aws-models/timestream-query.json create mode 100644 aws/sdk/aws-models/timestream-write.json create mode 100644 aws/sdk/integration-tests/timestreamquery/Cargo.toml create mode 100644 aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs create mode 100644 aws/sdk/integration-tests/timestreamquery/tests/traffic.json diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index dfd80c74f2..bcc8dcb0a4 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -115,3 +115,9 @@ no longer requires `Service::Error = OperationError`, inst references = ["smithy-rs#2457"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } author = "hlbarber" + +[[aws-sdk-rust]] +message = "The SDK has added support for timestreamwrite and timestreamquery. Support for these services is considered experimental at this time. In order to use these services, you MUST call `.enable_endpoint_discovery()` on the `Client` after construction." +meta = { "breaking" = false, "tada" = true, "bug" = false } +references = ["smithy-rs#2707", "aws-sdk-rust#114"] +author = "rcoh" diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml index 5586b75ae2..727202f2aa 100644 --- a/aws/rust-runtime/aws-inlineable/Cargo.toml +++ b/aws/rust-runtime/aws-inlineable/Cargo.toml @@ -24,6 +24,7 @@ aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } aws-smithy-http-tower = { path = "../../../rust-runtime/aws-smithy-http-tower" } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-types = { path = "../aws-types" } bytes = "1" bytes-utils = "0.1.1" diff --git a/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs new file mode 100644 index 0000000000..e74ceddb00 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs @@ -0,0 +1,287 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Maintain a cache of discovered endpoints + +use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::time::TimeSource; +use aws_smithy_client::erase::boxclone::BoxFuture; +use aws_smithy_http::endpoint::{ResolveEndpoint, ResolveEndpointError}; +use aws_smithy_types::endpoint::Endpoint; +use std::fmt::{Debug, Formatter}; +use std::future::Future; +use std::sync::{Arc, Mutex}; +use std::time::{Duration, SystemTime}; +use tokio::sync::oneshot::error::TryRecvError; +use tokio::sync::oneshot::{Receiver, Sender}; + +/// Endpoint reloader +#[must_use] +pub struct ReloadEndpoint { + loader: Box BoxFuture<(Endpoint, SystemTime), ResolveEndpointError> + Send + Sync>, + endpoint: Arc>>, + error: Arc>>, + rx: Receiver<()>, + sleep: Arc, + time: Arc, +} + +impl Debug for ReloadEndpoint { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ReloadEndpoint").finish() + } +} + +impl ReloadEndpoint { + /// Reload the endpoint once + pub async fn reload_once(&self) { + match (self.loader)().await { + Ok((endpoint, expiry)) => { + *self.endpoint.lock().unwrap() = Some(ExpiringEndpoint { endpoint, expiry }) + } + Err(err) => *self.error.lock().unwrap() = Some(err), + } + } + + /// An infinite loop task that will reload the endpoint + /// + /// This task will terminate when the corresponding [`Client`](crate::Client) is dropped. + pub async fn reload_task(mut self) { + loop { + match self.rx.try_recv() { + Ok(_) | Err(TryRecvError::Closed) => break, + _ => {} + } + self.reload_increment(self.time.now()).await; + self.sleep.sleep(Duration::from_secs(60)).await; + } + } + + async fn reload_increment(&self, now: SystemTime) { + let should_reload = self + .endpoint + .lock() + .unwrap() + .as_ref() + .map(|e| e.is_expired(now)) + .unwrap_or(true); + if should_reload { + tracing::debug!("reloading endpoint, previous endpoint was expired"); + self.reload_once().await; + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct EndpointCache { + error: Arc>>, + endpoint: Arc>>, + // When the sender is dropped, this allows the reload loop to stop + _drop_guard: Arc>, +} + +impl ResolveEndpoint for EndpointCache { + fn resolve_endpoint(&self, _params: &T) -> aws_smithy_http::endpoint::Result { + self.resolve_endpoint() + } +} + +#[derive(Debug)] +struct ExpiringEndpoint { + endpoint: Endpoint, + expiry: SystemTime, +} + +impl ExpiringEndpoint { + fn is_expired(&self, now: SystemTime) -> bool { + tracing::debug!(expiry = ?self.expiry, now = ?now, delta = ?self.expiry.duration_since(now), "checking expiry status of endpoint"); + match self.expiry.duration_since(now) { + Err(_) => true, + Ok(t) => t < Duration::from_secs(120), + } + } +} + +pub(crate) async fn create_cache( + loader_fn: impl Fn() -> F + Send + Sync + 'static, + sleep: Arc, + time: Arc, +) -> Result<(EndpointCache, ReloadEndpoint), ResolveEndpointError> +where + F: Future> + Send + 'static, +{ + let error_holder = Arc::new(Mutex::new(None)); + let endpoint_holder = Arc::new(Mutex::new(None)); + let (tx, rx) = tokio::sync::oneshot::channel(); + let cache = EndpointCache { + error: error_holder.clone(), + endpoint: endpoint_holder.clone(), + _drop_guard: Arc::new(tx), + }; + let reloader = ReloadEndpoint { + loader: Box::new(move || Box::pin((loader_fn)()) as _), + endpoint: endpoint_holder, + error: error_holder, + rx, + sleep, + time, + }; + reloader.reload_once().await; + // if we didn't successfully get an endpoint, bail out so the client knows + // configuration failed to work + cache.resolve_endpoint()?; + Ok((cache, reloader)) +} + +impl EndpointCache { + fn resolve_endpoint(&self) -> aws_smithy_http::endpoint::Result { + self.endpoint + .lock() + .unwrap() + .as_ref() + .map(|e| e.endpoint.clone()) + .ok_or_else(|| { + self.error + .lock() + .unwrap() + .take() + .unwrap_or_else(|| ResolveEndpointError::message("no endpoint loaded")) + }) + } +} + +#[cfg(test)] +mod test { + use crate::endpoint_discovery::create_cache; + use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::test_util::controlled_time_and_sleep; + use aws_smithy_async::time::SystemTimeSource; + use aws_smithy_types::endpoint::Endpoint; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; + use std::time::{Duration, SystemTime, UNIX_EPOCH}; + use tokio::time::timeout; + + fn check_send_v(t: T) -> T { + t + } + + #[tokio::test] + #[allow(unused_must_use)] + async fn check_traits() { + let (cache, reloader) = create_cache( + || async { + Ok(( + Endpoint::builder().url("http://foo.com").build(), + SystemTime::now(), + )) + }, + Arc::new(TokioSleep::new()), + Arc::new(SystemTimeSource::new()), + ) + .await + .unwrap(); + check_send_v(reloader.reload_task()); + check_send_v(cache); + } + + #[tokio::test] + async fn erroring_endpoint_always_reloaded() { + let expiry = UNIX_EPOCH + Duration::from_secs(123456789); + let ct = Arc::new(AtomicUsize::new(0)); + let (cache, reloader) = create_cache( + move || { + let shared_ct = ct.clone(); + shared_ct.fetch_add(1, Ordering::AcqRel); + async move { + Ok(( + Endpoint::builder() + .url(format!("http://foo.com/{shared_ct:?}")) + .build(), + expiry, + )) + } + }, + Arc::new(TokioSleep::new()), + Arc::new(SystemTimeSource::new()), + ) + .await + .expect("returns an endpoint"); + assert_eq!( + cache.resolve_endpoint().expect("ok").url(), + "http://foo.com/1" + ); + // 120 second buffer + reloader + .reload_increment(expiry - Duration::from_secs(240)) + .await; + assert_eq!( + cache.resolve_endpoint().expect("ok").url(), + "http://foo.com/1" + ); + + reloader.reload_increment(expiry).await; + assert_eq!( + cache.resolve_endpoint().expect("ok").url(), + "http://foo.com/2" + ); + } + + #[tokio::test] + async fn test_advance_of_task() { + let expiry = UNIX_EPOCH + Duration::from_secs(123456789); + // expires in 8 minutes + let (time, sleep, mut gate) = controlled_time_and_sleep(expiry - Duration::from_secs(239)); + let ct = Arc::new(AtomicUsize::new(0)); + let (cache, reloader) = create_cache( + move || { + let shared_ct = ct.clone(); + shared_ct.fetch_add(1, Ordering::AcqRel); + async move { + Ok(( + Endpoint::builder() + .url(format!("http://foo.com/{shared_ct:?}")) + .build(), + expiry, + )) + } + }, + Arc::new(sleep.clone()), + Arc::new(time.clone()), + ) + .await + .expect("first load success"); + let reload_task = tokio::spawn(reloader.reload_task()); + assert!(!reload_task.is_finished()); + // expiry occurs after 2 sleeps + // t = 0 + assert_eq!( + gate.expect_sleep().await.duration(), + Duration::from_secs(60) + ); + assert_eq!(cache.resolve_endpoint().unwrap().url(), "http://foo.com/1"); + // t = 60 + + let sleep = gate.expect_sleep().await; + // we're still holding the drop guard, so we haven't expired yet. + assert_eq!(cache.resolve_endpoint().unwrap().url(), "http://foo.com/1"); + assert_eq!(sleep.duration(), Duration::from_secs(60)); + sleep.allow_progress(); + // t = 120 + + let sleep = gate.expect_sleep().await; + assert_eq!(cache.resolve_endpoint().unwrap().url(), "http://foo.com/2"); + sleep.allow_progress(); + + let sleep = gate.expect_sleep().await; + drop(cache); + sleep.allow_progress(); + + timeout(Duration::from_secs(1), reload_task) + .await + .expect("task finishes successfully") + .expect("finishes"); + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index c4b7d23684..465aaac302 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -45,3 +45,12 @@ pub mod route53_resource_id_preprocessor; /// Convert a streaming `SdkBody` into an aws-chunked streaming body with checksum trailers pub mod http_body_checksum; + +#[allow(dead_code)] +pub mod endpoint_discovery; + +// just so docs work +#[allow(dead_code)] +/// allow docs to work +#[derive(Debug)] +pub struct Client; 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 8959054d16..952cb35e60 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 @@ -21,6 +21,7 @@ import software.amazon.smithy.rustsdk.customize.s3.S3ExtendedRequestIdDecorator import software.amazon.smithy.rustsdk.customize.s3control.S3ControlDecorator import software.amazon.smithy.rustsdk.customize.sso.SSODecorator import software.amazon.smithy.rustsdk.customize.sts.STSDecorator +import software.amazon.smithy.rustsdk.customize.timestream.TimestreamDecorator import software.amazon.smithy.rustsdk.endpoints.AwsEndpointsStdLib import software.amazon.smithy.rustsdk.endpoints.OperationInputTestDecorator import software.amazon.smithy.rustsdk.endpoints.RequireEndpointRules @@ -69,6 +70,8 @@ val DECORATORS: List = listOf( S3ControlDecorator().onlyApplyTo("com.amazonaws.s3control#AWSS3ControlServiceV20180820"), STSDecorator().onlyApplyTo("com.amazonaws.sts#AWSSecurityTokenServiceV20110615"), SSODecorator().onlyApplyTo("com.amazonaws.sso#SWBPortalService"), + TimestreamDecorator().onlyApplyTo("com.amazonaws.timestreamwrite#Timestream_20181101"), + TimestreamDecorator().onlyApplyTo("com.amazonaws.timestreamquery#Timestream_20181101"), // Only build docs-rs for linux to reduce load on docs.rs listOf( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCrateDocsDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCrateDocsDecorator.kt index a810b3e8a5..f167015ac2 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCrateDocsDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCrateDocsDecorator.kt @@ -21,6 +21,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rawTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocSection import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations @@ -85,6 +86,10 @@ class AwsCrateDocsDecorator : ClientCodegenDecorator { SdkSettings.from(codegenContext.settings).generateReadme } +sealed class DocSection(name: String) : AdHocSection(name) { + data class CreateClient(val crateName: String, val clientName: String = "client", val indent: String) : DocSection("CustomExample") +} + internal class AwsCrateDocGenerator(private val codegenContext: ClientCodegenContext) { private val logger: Logger = Logger.getLogger(javaClass.name) private val awsConfigVersion by lazy { @@ -154,8 +159,7 @@ internal class AwsCrateDocGenerator(private val codegenContext: ClientCodegenCon ##[#{tokio}::main] async fn main() -> Result<(), $shortModuleName::Error> { - let config = #{aws_config}::load_from_env().await; - let client = $shortModuleName::Client::new(&config); + #{constructClient} // ... make some calls with the client @@ -171,6 +175,7 @@ internal class AwsCrateDocGenerator(private val codegenContext: ClientCodegenCon true -> AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).toDevDependency().toType() else -> writable { rust("aws_config") } }, + "constructClient" to AwsDocs.constructClient(codegenContext, indent = " "), ) template( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsDocs.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsDocs.kt index a8dcdc33d8..7166ce1144 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsDocs.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsDocs.kt @@ -9,6 +9,9 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docsTemplate +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.customize.writeCustomizationsOrElse import software.amazon.smithy.rust.codegen.core.util.toSnakeCase object AwsDocs { @@ -23,6 +26,26 @@ object AwsDocs { ShapeId.from("com.amazonaws.sso#SWBPortalService"), ).contains(codegenContext.serviceShape.id) + fun constructClient(codegenContext: ClientCodegenContext, indent: String): Writable { + val crateName = codegenContext.moduleName.toSnakeCase() + return writable { + writeCustomizationsOrElse( + codegenContext.rootDecorator.extraSections(codegenContext), + DocSection.CreateClient(crateName = crateName, indent = indent), + ) { + if (canRelyOnAwsConfig(codegenContext)) { + addDependency(AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).toDevDependency()) + } + rustTemplate( + """ + let config = aws_config::load_from_env().await; + let client = $crateName::Client::new(&config); + """.trimIndent().prependIndent(indent), + ) + } + } + } + fun clientConstructionDocs(codegenContext: ClientCodegenContext): Writable = { if (canRelyOnAwsConfig(codegenContext)) { val crateName = codegenContext.moduleName.toSnakeCase() @@ -40,8 +63,7 @@ object AwsDocs { In the simplest case, creating a client looks as follows: ```rust,no_run ## async fn wrapper() { - let config = #{aws_config}::load_from_env().await; - let client = $crateName::Client::new(&config); + #{constructClient} ## } ``` @@ -76,6 +98,7 @@ object AwsDocs { [builder pattern]: https://rust-lang.github.io/api-guidelines/type-safety.html##builders-enable-construction-of-complex-values-c-builder """.trimIndent(), "aws_config" to AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).toDevDependency().toType(), + "constructClient" to constructClient(codegenContext, indent = ""), ) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt index 556e71d2b0..744b79c441 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt @@ -77,7 +77,10 @@ class IntegrationTestDependencies( if (hasTests) { val smithyClient = CargoDependency.smithyClient(codegenContext.runtimeConfig) .copy(features = setOf("test-util"), scope = DependencyScope.Dev) + val smithyAsync = CargoDependency.smithyAsync(codegenContext.runtimeConfig) + .copy(features = setOf("test-util"), scope = DependencyScope.Dev) addDependency(smithyClient) + addDependency(smithyAsync) addDependency(CargoDependency.smithyProtocolTestHelpers(codegenContext.runtimeConfig)) addDependency(SerdeJson) addDependency(Tokio) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index cc9c34797d..ce4d3f88ae 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -20,6 +20,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.config.Confi import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator import software.amazon.smithy.rust.codegen.core.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization @@ -148,6 +149,11 @@ class ServiceSpecificDecorator( delegateTo.protocolTestGenerator(codegenContext, baseGenerator) } + override fun extraSections(codegenContext: ClientCodegenContext): List = + listOf().maybeApply(codegenContext.serviceShape) { + delegateTo.extraSections(codegenContext) + } + override fun operationRuntimePluginCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt new file mode 100644 index 0000000000..8b8bffe398 --- /dev/null +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt @@ -0,0 +1,106 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk.customize.timestream + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Types +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope +import software.amazon.smithy.rust.codegen.core.rustlang.Visibility +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.toType +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.smithy.RustCrate +import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization +import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization +import software.amazon.smithy.rustsdk.AwsCargoDependency +import software.amazon.smithy.rustsdk.DocSection +import software.amazon.smithy.rustsdk.InlineAwsDependency + +/** + * This decorator does two things: + * 1. Adds the `endpoint_discovery` inlineable + * 2. Adds a `enable_endpoint_discovery` method on client that returns a wrapped client with endpoint discovery enabled + */ +class TimestreamDecorator : ClientCodegenDecorator { + override val name: String = "Timestream" + override val order: Byte = -1 + + override fun extraSections(codegenContext: ClientCodegenContext): List { + return listOf( + adhocCustomization { + addDependency(AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).toDevDependency()) + rustTemplate( + """ + let config = aws_config::load_from_env().await; + // You MUST call `enable_endpoint_discovery` to produce a working client for this service. + let ${it.clientName} = ${it.crateName}::Client::new(&config).enable_endpoint_discovery().await; + """.replaceIndent(it.indent), + ) + }, + ) + } + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + val endpointDiscovery = InlineAwsDependency.forRustFile( + "endpoint_discovery", + Visibility.PUBLIC, + CargoDependency.Tokio.copy(scope = DependencyScope.Compile, features = setOf("sync")), + ) + rustCrate.lib { + // helper function to resolve an endpoint given a base client + rustTemplate( + """ + async fn resolve_endpoint(client: &crate::Client) -> Result<(#{Endpoint}, #{SystemTime}), #{ResolveEndpointError}> { + let describe_endpoints = + client.describe_endpoints().send().await.map_err(|e| { + #{ResolveEndpointError}::from_source("failed to call describe_endpoints", e) + })?; + let endpoint = describe_endpoints.endpoints().unwrap().get(0).unwrap(); + let expiry = client.conf().time_source.now() + #{Duration}::from_secs(endpoint.cache_period_in_minutes() as u64 * 60); + Ok(( + #{Endpoint}::builder() + .url(format!("https://{}", endpoint.address().unwrap())) + .build(), + expiry, + )) + } + + impl Client { + /// Enable endpoint discovery for this client + /// + /// This method MUST be called to construct a working client. + pub async fn enable_endpoint_discovery(self) -> #{Result}<(Self, #{endpoint_discovery}::ReloadEndpoint), #{ResolveEndpointError}> { + let mut new_conf = self.conf().clone(); + let sleep = self.conf().sleep_impl().expect("sleep impl must be provided"); + let time = ::std::sync::Arc::new(self.conf().time_source.clone()); + let (resolver, reloader) = #{endpoint_discovery}::create_cache( + move || { + let client = self.clone(); + async move { resolve_endpoint(&client).await } + }, + sleep, + time + ) + .await?; + new_conf.endpoint_resolver = ::std::sync::Arc::new(resolver); + Ok((Self::from_conf(new_conf), reloader)) + } + } + + """, + "endpoint_discovery" to endpointDiscovery.toType(), + "SystemTime" to RuntimeType.std.resolve("time::SystemTime"), + "Duration" to RuntimeType.std.resolve("time::Duration"), + "SystemTimeSource" to RuntimeType.smithyAsync(codegenContext.runtimeConfig) + .resolve("time::SystemTimeSource"), + *Types(codegenContext.runtimeConfig).toArray(), + *preludeScope, + ) + } + } +} diff --git a/aws/sdk/aws-models/timestream-query.json b/aws/sdk/aws-models/timestream-query.json new file mode 100644 index 0000000000..9545cf1b2b --- /dev/null +++ b/aws/sdk/aws-models/timestream-query.json @@ -0,0 +1,3047 @@ +{ + "smithy": "2.0", + "metadata": { + "suppressions": [ + { + "id": "HttpMethodSemantics", + "namespace": "*" + }, + { + "id": "HttpResponseCodeSemantics", + "namespace": "*" + }, + { + "id": "PaginatedTrait", + "namespace": "*" + }, + { + "id": "HttpHeaderTrait", + "namespace": "*" + }, + { + "id": "HttpUriConflict", + "namespace": "*" + }, + { + "id": "Service", + "namespace": "*" + } + ] + }, + "shapes": { + "com.amazonaws.timestreamquery#AccessDeniedException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ServiceErrorMessage" + } + }, + "traits": { + "aws.protocols#awsQueryError": { + "code": "AccessDenied", + "httpResponseCode": 403 + }, + "smithy.api#documentation": "

You are not authorized to perform this action.

", + "smithy.api#error": "client", + "smithy.api#httpError": 403 + } + }, + "com.amazonaws.timestreamquery#AmazonResourceName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + } + } + }, + "com.amazonaws.timestreamquery#CancelQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#CancelQueryRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#CancelQueryResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Cancels a query that has been issued. Cancellation is provided only if the query has\n not completed running before the cancellation request was issued. Because cancellation\n is an idempotent operation, subsequent cancellation requests will return a\n CancellationMessage, indicating that the query has already been\n canceled. See code\n sample for details.

", + "smithy.api#idempotent": {} + } + }, + "com.amazonaws.timestreamquery#CancelQueryRequest": { + "type": "structure", + "members": { + "QueryId": { + "target": "com.amazonaws.timestreamquery#QueryId", + "traits": { + "smithy.api#documentation": "

The ID of the query that needs to be cancelled. QueryID is returned as\n part of the query result.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#CancelQueryResponse": { + "type": "structure", + "members": { + "CancellationMessage": { + "target": "com.amazonaws.timestreamquery#String", + "traits": { + "smithy.api#documentation": "

A CancellationMessage is returned when a CancelQuery\n request for the query specified by QueryId has already been issued.

" + } + } + } + }, + "com.amazonaws.timestreamquery#ClientRequestToken": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 32, + "max": 128 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.timestreamquery#ClientToken": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 32, + "max": 128 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.timestreamquery#ColumnInfo": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamquery#String", + "traits": { + "smithy.api#documentation": "

The name of the result set column. The name of the result set is available for\n columns of all data types except for arrays.

" + } + }, + "Type": { + "target": "com.amazonaws.timestreamquery#Type", + "traits": { + "smithy.api#documentation": "

The data type of the result set column. The data type can be a scalar or complex.\n Scalar data types are integers, strings, doubles, Booleans, and others. Complex data\n types are types such as arrays, rows, and others.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains the metadata for query results such as the column names, data types, and\n other attributes.

" + } + }, + "com.amazonaws.timestreamquery#ColumnInfoList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#ColumnInfo" + } + }, + "com.amazonaws.timestreamquery#ConflictException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

Unable to poll results for a cancelled query.

", + "smithy.api#error": "client", + "smithy.api#httpError": 409 + } + }, + "com.amazonaws.timestreamquery#CreateScheduledQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#CreateScheduledQueryRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#CreateScheduledQueryResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#ConflictException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Create a scheduled query that will be run on your behalf at the configured schedule.\n Timestream assumes the execution role provided as part of the\n ScheduledQueryExecutionRoleArn parameter to run the query. You can use\n the NotificationConfiguration parameter to configure notification for your\n scheduled query operations.

", + "smithy.api#idempotent": {} + } + }, + "com.amazonaws.timestreamquery#CreateScheduledQueryRequest": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryName", + "traits": { + "smithy.api#documentation": "

Name of the scheduled query.

", + "smithy.api#required": {} + } + }, + "QueryString": { + "target": "com.amazonaws.timestreamquery#QueryString", + "traits": { + "smithy.api#documentation": "

The query string to run. Parameter\n names can be specified in the query string @ character followed by an\n identifier. The named Parameter @scheduled_runtime is reserved and can be used in the query to get the time at which the query is scheduled to run.

\n

The timestamp calculated according to the ScheduleConfiguration parameter, will be the value of @scheduled_runtime paramater for each query run. \n For example, consider an instance of a scheduled query executing on 2021-12-01 00:00:00. For this instance, the @scheduled_runtime parameter is \n initialized to the timestamp 2021-12-01 00:00:00 when invoking the query.

", + "smithy.api#required": {} + } + }, + "ScheduleConfiguration": { + "target": "com.amazonaws.timestreamquery#ScheduleConfiguration", + "traits": { + "smithy.api#documentation": "

The schedule configuration for the query.

", + "smithy.api#required": {} + } + }, + "NotificationConfiguration": { + "target": "com.amazonaws.timestreamquery#NotificationConfiguration", + "traits": { + "smithy.api#documentation": "

Notification configuration for the scheduled query. A notification is sent by\n Timestream when a query run finishes, when the state is updated or when you delete it.

", + "smithy.api#required": {} + } + }, + "TargetConfiguration": { + "target": "com.amazonaws.timestreamquery#TargetConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration used for writing the result of a query.

" + } + }, + "ClientToken": { + "target": "com.amazonaws.timestreamquery#ClientToken", + "traits": { + "smithy.api#documentation": "

Using a ClientToken makes the call to CreateScheduledQuery idempotent, in other words, making the same request repeatedly will produce the same result. Making \n multiple identical CreateScheduledQuery requests has the same effect as making a single request.\n\n

\n
    \n
  • \n

    If CreateScheduledQuery is called without a ClientToken, the\n Query SDK generates a ClientToken on your behalf.

    \n
  • \n
  • \n

    After 8 hours, any request with the same ClientToken is treated\n as a new request.

    \n
  • \n
", + "smithy.api#idempotencyToken": {} + } + }, + "ScheduledQueryExecutionRoleArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The ARN for the IAM role that Timestream will assume when running the scheduled query.

", + "smithy.api#required": {} + } + }, + "Tags": { + "target": "com.amazonaws.timestreamquery#TagList", + "traits": { + "smithy.api#documentation": "

A list of key-value pairs to label the scheduled query.

" + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamquery#StringValue2048", + "traits": { + "smithy.api#documentation": "

The Amazon KMS key used to encrypt the scheduled query resource, at-rest. If the Amazon KMS\n key is not specified, the scheduled query resource will be encrypted with a Timestream\n owned Amazon KMS key. To specify a KMS key, use the key ID, key ARN, alias name, or alias\n ARN. When using an alias name, prefix the name with alias/\n

\n

If ErrorReportConfiguration uses SSE_KMS as encryption type, the same KmsKeyId is used to encrypt the error report at rest.

" + } + }, + "ErrorReportConfiguration": { + "target": "com.amazonaws.timestreamquery#ErrorReportConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration for error reporting. Error reports will be generated when a problem is encountered when writing the query results.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#CreateScheduledQueryResponse": { + "type": "structure", + "members": { + "Arn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

ARN for the created scheduled query.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#Datum": { + "type": "structure", + "members": { + "ScalarValue": { + "target": "com.amazonaws.timestreamquery#ScalarValue", + "traits": { + "smithy.api#documentation": "

Indicates if the data point is a scalar value such as integer, string, double, or\n Boolean.

" + } + }, + "TimeSeriesValue": { + "target": "com.amazonaws.timestreamquery#TimeSeriesDataPointList", + "traits": { + "smithy.api#documentation": "

Indicates if the data point is a timeseries data type.

" + } + }, + "ArrayValue": { + "target": "com.amazonaws.timestreamquery#DatumList", + "traits": { + "smithy.api#documentation": "

Indicates if the data point is an array.

" + } + }, + "RowValue": { + "target": "com.amazonaws.timestreamquery#Row", + "traits": { + "smithy.api#documentation": "

Indicates if the data point is a row.

" + } + }, + "NullValue": { + "target": "com.amazonaws.timestreamquery#NullableBoolean", + "traits": { + "smithy.api#documentation": "

Indicates if the data point is null.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Datum represents a single data point in a query result.

" + } + }, + "com.amazonaws.timestreamquery#DatumList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#Datum" + } + }, + "com.amazonaws.timestreamquery#DeleteScheduledQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#DeleteScheduledQueryRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Deletes a given scheduled query. This is an irreversible operation.

", + "smithy.api#idempotent": {} + } + }, + "com.amazonaws.timestreamquery#DeleteScheduledQueryRequest": { + "type": "structure", + "members": { + "ScheduledQueryArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The ARN of the scheduled query.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#DescribeEndpoints": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#DescribeEndpointsRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#DescribeEndpointsResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

DescribeEndpoints returns a list of available endpoints to make Timestream\n API calls against. This API is available through both Write and Query.

\n

Because the Timestream SDKs are designed to transparently work with the\n service’s architecture, including the management and mapping of the service endpoints,\n it is not recommended that you use this API unless:

\n \n

For detailed information on how and when to use and implement DescribeEndpoints, see\n The Endpoint Discovery Pattern.

" + } + }, + "com.amazonaws.timestreamquery#DescribeEndpointsRequest": { + "type": "structure", + "members": {} + }, + "com.amazonaws.timestreamquery#DescribeEndpointsResponse": { + "type": "structure", + "members": { + "Endpoints": { + "target": "com.amazonaws.timestreamquery#Endpoints", + "traits": { + "smithy.api#documentation": "

An Endpoints object is returned when a DescribeEndpoints\n request is made.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#DescribeScheduledQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#DescribeScheduledQueryRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#DescribeScheduledQueryResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Provides detailed information about a scheduled query.

" + } + }, + "com.amazonaws.timestreamquery#DescribeScheduledQueryRequest": { + "type": "structure", + "members": { + "ScheduledQueryArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The ARN of the scheduled query.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#DescribeScheduledQueryResponse": { + "type": "structure", + "members": { + "ScheduledQuery": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryDescription", + "traits": { + "smithy.api#documentation": "

The scheduled query.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#DimensionMapping": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Column name from query result.

", + "smithy.api#required": {} + } + }, + "DimensionValueType": { + "target": "com.amazonaws.timestreamquery#DimensionValueType", + "traits": { + "smithy.api#documentation": "

Type for the dimension.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

This type is used to map column(s) from the query result to a dimension in the\n destination table.

" + } + }, + "com.amazonaws.timestreamquery#DimensionMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#DimensionMapping" + } + }, + "com.amazonaws.timestreamquery#DimensionValueType": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "VARCHAR", + "name": "VARCHAR" + } + ] + } + }, + "com.amazonaws.timestreamquery#Double": { + "type": "double", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamquery#Endpoint": { + "type": "structure", + "members": { + "Address": { + "target": "com.amazonaws.timestreamquery#String", + "traits": { + "smithy.api#documentation": "

An endpoint address.

", + "smithy.api#required": {} + } + }, + "CachePeriodInMinutes": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The TTL for the endpoint, in minutes.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents an available endpoint against which to make API calls against, as well as\n the TTL for that endpoint.

" + } + }, + "com.amazonaws.timestreamquery#Endpoints": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#Endpoint" + } + }, + "com.amazonaws.timestreamquery#ErrorMessage": { + "type": "string" + }, + "com.amazonaws.timestreamquery#ErrorReportConfiguration": { + "type": "structure", + "members": { + "S3Configuration": { + "target": "com.amazonaws.timestreamquery#S3Configuration", + "traits": { + "smithy.api#documentation": "

The S3 configuration for the error reports.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration required for error reporting.

" + } + }, + "com.amazonaws.timestreamquery#ErrorReportLocation": { + "type": "structure", + "members": { + "S3ReportLocation": { + "target": "com.amazonaws.timestreamquery#S3ReportLocation", + "traits": { + "smithy.api#documentation": "

The S3 location where error reports are written.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

This contains the location of the error report for a single scheduled query call.\n

" + } + }, + "com.amazonaws.timestreamquery#ExecuteScheduledQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#ExecuteScheduledQueryRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

You can use this API to run a scheduled query manually.

", + "smithy.api#idempotent": {} + } + }, + "com.amazonaws.timestreamquery#ExecuteScheduledQueryRequest": { + "type": "structure", + "members": { + "ScheduledQueryArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

ARN of the scheduled query.

", + "smithy.api#required": {} + } + }, + "InvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The timestamp in UTC. Query will be run as if it was invoked at this timestamp.

", + "smithy.api#required": {} + } + }, + "ClientToken": { + "target": "com.amazonaws.timestreamquery#ClientToken", + "traits": { + "smithy.api#documentation": "

Not used.

", + "smithy.api#idempotencyToken": {} + } + } + } + }, + "com.amazonaws.timestreamquery#ExecutionStats": { + "type": "structure", + "members": { + "ExecutionTimeInMillis": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Total time, measured in milliseconds, that was needed for the scheduled query run to complete.

" + } + }, + "DataWrites": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Data writes metered for records ingested in a single scheduled query run.

" + } + }, + "BytesMetered": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Bytes metered for a single scheduled query run.

" + } + }, + "RecordsIngested": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The number of records ingested for a single scheduled query run.

" + } + }, + "QueryResultRows": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Number of rows present in the output from running a query before ingestion to\n destination data source.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Statistics for a single scheduled query run.

" + } + }, + "com.amazonaws.timestreamquery#InternalServerException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

\n Timestream was unable to fully process this request because of an internal\n server error.

", + "smithy.api#error": "server", + "smithy.api#httpError": 500 + } + }, + "com.amazonaws.timestreamquery#InvalidEndpointException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

The requested endpoint was not valid.

", + "smithy.api#error": "client", + "smithy.api#httpError": 421 + } + }, + "com.amazonaws.timestreamquery#ListScheduledQueries": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#ListScheduledQueriesRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#ListScheduledQueriesResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Gets a list of all scheduled queries in the caller's Amazon account and Region. ListScheduledQueries is eventually consistent.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.timestreamquery#ListScheduledQueriesRequest": { + "type": "structure", + "members": { + "MaxResults": { + "target": "com.amazonaws.timestreamquery#MaxScheduledQueriesResults", + "traits": { + "smithy.api#documentation": "

The maximum number of items to return in the output. If the total number of items\n available is more than the value specified, a NextToken is provided in the\n output. To resume pagination, provide the NextToken value as the argument\n to the subsequent call to ListScheduledQueriesRequest.

" + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#NextScheduledQueriesResultsToken", + "traits": { + "smithy.api#documentation": "

A pagination token to resume pagination.

" + } + } + } + }, + "com.amazonaws.timestreamquery#ListScheduledQueriesResponse": { + "type": "structure", + "members": { + "ScheduledQueries": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryList", + "traits": { + "smithy.api#documentation": "

A list of scheduled queries.

", + "smithy.api#required": {} + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#NextScheduledQueriesResultsToken", + "traits": { + "smithy.api#documentation": "

A token to specify where to start paginating. This is the NextToken from a previously\n truncated response.

" + } + } + } + }, + "com.amazonaws.timestreamquery#ListTagsForResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#ListTagsForResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#ListTagsForResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

List all tags on a Timestream query resource.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.timestreamquery#ListTagsForResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The Timestream resource with tags to be listed. This value is an Amazon Resource Name\n (ARN).

", + "smithy.api#required": {} + } + }, + "MaxResults": { + "target": "com.amazonaws.timestreamquery#MaxTagsForResourceResult", + "traits": { + "smithy.api#documentation": "

The maximum number of tags to return.

" + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#NextTagsForResourceResultsToken", + "traits": { + "smithy.api#documentation": "

A pagination token to resume pagination.

" + } + } + } + }, + "com.amazonaws.timestreamquery#ListTagsForResourceResponse": { + "type": "structure", + "members": { + "Tags": { + "target": "com.amazonaws.timestreamquery#TagList", + "traits": { + "smithy.api#documentation": "

The tags currently associated with the Timestream resource.

", + "smithy.api#required": {} + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#NextTagsForResourceResultsToken", + "traits": { + "smithy.api#documentation": "

A pagination token to resume pagination with a subsequent call to\n ListTagsForResourceResponse.

" + } + } + } + }, + "com.amazonaws.timestreamquery#Long": { + "type": "long", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamquery#MaxQueryResults": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 1, + "max": 1000 + } + } + }, + "com.amazonaws.timestreamquery#MaxScheduledQueriesResults": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 1, + "max": 1000 + } + } + }, + "com.amazonaws.timestreamquery#MaxTagsForResourceResult": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 1, + "max": 200 + } + } + }, + "com.amazonaws.timestreamquery#MeasureValueType": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "BIGINT", + "name": "BIGINT" + }, + { + "value": "BOOLEAN", + "name": "BOOLEAN" + }, + { + "value": "DOUBLE", + "name": "DOUBLE" + }, + { + "value": "VARCHAR", + "name": "VARCHAR" + }, + { + "value": "MULTI", + "name": "MULTI" + } + ] + } + }, + "com.amazonaws.timestreamquery#MixedMeasureMapping": { + "type": "structure", + "members": { + "MeasureName": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Refers to the value of measure_name in a result row. This field is required if\n MeasureNameColumn is provided.

" + } + }, + "SourceColumn": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

This field refers to the source column from which measure-value is to be read for\n result materialization.

" + } + }, + "TargetMeasureName": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Target measure name to be used. If not provided, the target measure name by default\n would be measure-name if provided, or sourceColumn otherwise.

" + } + }, + "MeasureValueType": { + "target": "com.amazonaws.timestreamquery#MeasureValueType", + "traits": { + "smithy.api#documentation": "

Type of the value that is to be read from sourceColumn. If the mapping is for MULTI,\n use MeasureValueType.MULTI.

", + "smithy.api#required": {} + } + }, + "MultiMeasureAttributeMappings": { + "target": "com.amazonaws.timestreamquery#MultiMeasureAttributeMappingList", + "traits": { + "smithy.api#documentation": "

Required when measureValueType is MULTI. Attribute mappings for MULTI value\n measures.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

MixedMeasureMappings are mappings that can be used to ingest data into a mixture of\n narrow and multi measures in the derived table.

" + } + }, + "com.amazonaws.timestreamquery#MixedMeasureMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#MixedMeasureMapping" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamquery#MultiMeasureAttributeMapping": { + "type": "structure", + "members": { + "SourceColumn": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Source column from where the attribute value is to be read.

", + "smithy.api#required": {} + } + }, + "TargetMultiMeasureAttributeName": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Custom name to be used for attribute name in derived table. If not provided, source\n column name would be used.

" + } + }, + "MeasureValueType": { + "target": "com.amazonaws.timestreamquery#ScalarMeasureValueType", + "traits": { + "smithy.api#documentation": "

Type of the attribute to be read from the source column.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Attribute mapping for MULTI value measures.

" + } + }, + "com.amazonaws.timestreamquery#MultiMeasureAttributeMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#MultiMeasureAttributeMapping" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamquery#MultiMeasureMappings": { + "type": "structure", + "members": { + "TargetMultiMeasureName": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

The name of the target multi-measure name in the derived table. This input is required\n when measureNameColumn is not provided. If MeasureNameColumn is provided, then value\n from that column will be used as multi-measure name.

" + } + }, + "MultiMeasureAttributeMappings": { + "target": "com.amazonaws.timestreamquery#MultiMeasureAttributeMappingList", + "traits": { + "smithy.api#documentation": "

Required. Attribute mappings to be used for mapping query results to ingest data for\n multi-measure attributes.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Only one of MixedMeasureMappings or MultiMeasureMappings is to be provided.\n MultiMeasureMappings can be used to ingest data as multi measures in the derived\n table.

" + } + }, + "com.amazonaws.timestreamquery#NextScheduledQueriesResultsToken": { + "type": "string" + }, + "com.amazonaws.timestreamquery#NextTagsForResourceResultsToken": { + "type": "string" + }, + "com.amazonaws.timestreamquery#NotificationConfiguration": { + "type": "structure", + "members": { + "SnsConfiguration": { + "target": "com.amazonaws.timestreamquery#SnsConfiguration", + "traits": { + "smithy.api#documentation": "

Details on SNS configuration.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Notification configuration for a scheduled query. A notification is sent by\n Timestream when a scheduled query is created, its state is updated or when it is deleted.

" + } + }, + "com.amazonaws.timestreamquery#NullableBoolean": { + "type": "boolean" + }, + "com.amazonaws.timestreamquery#PaginationToken": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + } + } + }, + "com.amazonaws.timestreamquery#ParameterMapping": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamquery#String", + "traits": { + "smithy.api#documentation": "

Parameter name.

", + "smithy.api#required": {} + } + }, + "Type": { + "target": "com.amazonaws.timestreamquery#Type", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Mapping for named parameters.

" + } + }, + "com.amazonaws.timestreamquery#ParameterMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#ParameterMapping" + } + }, + "com.amazonaws.timestreamquery#PrepareQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#PrepareQueryRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#PrepareQueryResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

A synchronous operation that allows you to submit a query with parameters to be stored\n by Timestream for later running. Timestream only supports using this operation with the\n PrepareQueryRequest$ValidateOnly set to true.

", + "smithy.api#idempotent": {} + } + }, + "com.amazonaws.timestreamquery#PrepareQueryRequest": { + "type": "structure", + "members": { + "QueryString": { + "target": "com.amazonaws.timestreamquery#QueryString", + "traits": { + "smithy.api#documentation": "

The Timestream query string that you want to use as a prepared statement. Parameter\n names can be specified in the query string @ character followed by an\n identifier.

", + "smithy.api#required": {} + } + }, + "ValidateOnly": { + "target": "com.amazonaws.timestreamquery#NullableBoolean", + "traits": { + "smithy.api#documentation": "

By setting this value to true, Timestream will only validate that the\n query string is a valid Timestream query, and not store the prepared query for later\n use.

" + } + } + } + }, + "com.amazonaws.timestreamquery#PrepareQueryResponse": { + "type": "structure", + "members": { + "QueryString": { + "target": "com.amazonaws.timestreamquery#QueryString", + "traits": { + "smithy.api#documentation": "

The query string that you want prepare.

", + "smithy.api#required": {} + } + }, + "Columns": { + "target": "com.amazonaws.timestreamquery#SelectColumnList", + "traits": { + "smithy.api#documentation": "

A list of SELECT clause columns of the submitted query string.

", + "smithy.api#required": {} + } + }, + "Parameters": { + "target": "com.amazonaws.timestreamquery#ParameterMappingList", + "traits": { + "smithy.api#documentation": "

A list of parameters used in the submitted query string.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#Query": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#QueryRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#QueryResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#ConflictException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#QueryExecutionException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

\n Query is a synchronous operation that enables you to run a query against\n your Amazon Timestream data. Query will time out after 60 seconds.\n You must update the default timeout in the SDK to support a timeout of 60 seconds. See\n the code\n sample for details.

\n

Your query request will fail in the following cases:

\n
    \n
  • \n

    If you submit a Query request with the same client token outside\n of the 5-minute idempotency window.

    \n
  • \n
  • \n

    If you submit a Query request with the same client token, but\n change other parameters, within the 5-minute idempotency window.

    \n
  • \n
  • \n

    If the size of the row (including the query metadata) exceeds 1 MB, then the\n query will fail with the following error message:

    \n

    \n Query aborted as max page response size has been exceeded by the output\n result row\n

    \n
  • \n
  • \n

    If the IAM principal of the query initiator and the result reader are not the\n same and/or the query initiator and the result reader do not have the same query\n string in the query requests, the query will fail with an Invalid\n pagination token error.

    \n
  • \n
", + "smithy.api#idempotent": {}, + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "items": "Rows", + "pageSize": "MaxRows" + } + } + }, + "com.amazonaws.timestreamquery#QueryExecutionException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

\n Timestream was unable to run the query successfully.

", + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + }, + "com.amazonaws.timestreamquery#QueryId": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 64 + }, + "smithy.api#pattern": "^[a-zA-Z0-9]+$" + } + }, + "com.amazonaws.timestreamquery#QueryRequest": { + "type": "structure", + "members": { + "QueryString": { + "target": "com.amazonaws.timestreamquery#QueryString", + "traits": { + "smithy.api#documentation": "

The query to be run by Timestream.

", + "smithy.api#required": {} + } + }, + "ClientToken": { + "target": "com.amazonaws.timestreamquery#ClientRequestToken", + "traits": { + "smithy.api#documentation": "

Unique, case-sensitive string of up to 64 ASCII characters specified when a\n Query request is made. Providing a ClientToken makes the\n call to Query\n idempotent. This means that running the same query repeatedly will\n produce the same result. In other words, making multiple identical Query\n requests has the same effect as making a single request. When using\n ClientToken in a query, note the following:

\n
    \n
  • \n

    If the Query API is instantiated without a ClientToken, the\n Query SDK generates a ClientToken on your behalf.

    \n
  • \n
  • \n

    If the Query invocation only contains the\n ClientToken but does not include a NextToken, that\n invocation of Query is assumed to be a new query run.

    \n
  • \n
  • \n

    If the invocation contains NextToken, that particular invocation\n is assumed to be a subsequent invocation of a prior call to the Query API, and a\n result set is returned.

    \n
  • \n
  • \n

    After 4 hours, any request with the same ClientToken is treated\n as a new request.

    \n
  • \n
", + "smithy.api#idempotencyToken": {} + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#PaginationToken", + "traits": { + "smithy.api#documentation": "

A pagination token used to return a set of results. When the Query API\n is invoked using NextToken, that particular invocation is assumed to be a\n subsequent invocation of a prior call to Query, and a result set is\n returned. However, if the Query invocation only contains the\n ClientToken, that invocation of Query is assumed to be a\n new query run.

\n

Note the following when using NextToken in a query:

\n
    \n
  • \n

    A pagination token can be used for up to five Query invocations,\n OR for a duration of up to 1 hour – whichever comes first.

    \n
  • \n
  • \n

    Using the same NextToken will return the same set of records. To\n keep paginating through the result set, you must to use the most recent\n nextToken.

    \n
  • \n
  • \n

    Suppose a Query invocation returns two NextToken\n values, TokenA and TokenB. If TokenB is\n used in a subsequent Query invocation, then TokenA is\n invalidated and cannot be reused.

    \n
  • \n
  • \n

    To request a previous result set from a query after pagination has begun, you\n must re-invoke the Query API.

    \n
  • \n
  • \n

    The latest NextToken should be used to paginate until\n null is returned, at which point a new NextToken\n should be used.

    \n
  • \n
  • \n

    If the IAM principal of the query initiator and the result reader are not the\n same and/or the query initiator and the result reader do not have the same query\n string in the query requests, the query will fail with an Invalid\n pagination token error.

    \n
  • \n
" + } + }, + "MaxRows": { + "target": "com.amazonaws.timestreamquery#MaxQueryResults", + "traits": { + "smithy.api#documentation": "

The total number of rows to be returned in the Query output. The initial\n run of Query with a MaxRows value specified will return the\n result set of the query in two cases:

\n
    \n
  • \n

    The size of the result is less than 1MB.

    \n
  • \n
  • \n

    The number of rows in the result set is less than the value of\n maxRows.

    \n
  • \n
\n

Otherwise, the initial invocation of Query only returns a\n NextToken, which can then be used in subsequent calls to fetch the\n result set. To resume pagination, provide the NextToken value in the\n subsequent command.

\n

If the row size is large (e.g. a row has many columns), Timestream may return\n fewer rows to keep the response size from exceeding the 1 MB limit. If\n MaxRows is not provided, Timestream will send the necessary\n number of rows to meet the 1 MB limit.

" + } + } + } + }, + "com.amazonaws.timestreamquery#QueryResponse": { + "type": "structure", + "members": { + "QueryId": { + "target": "com.amazonaws.timestreamquery#QueryId", + "traits": { + "smithy.api#documentation": "

A unique ID for the given query.

", + "smithy.api#required": {} + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamquery#PaginationToken", + "traits": { + "smithy.api#documentation": "

A pagination token that can be used again on a Query call to get the\n next set of results.

" + } + }, + "Rows": { + "target": "com.amazonaws.timestreamquery#RowList", + "traits": { + "smithy.api#documentation": "

The result set rows returned by the query.

", + "smithy.api#required": {} + } + }, + "ColumnInfo": { + "target": "com.amazonaws.timestreamquery#ColumnInfoList", + "traits": { + "smithy.api#documentation": "

The column data types of the returned result set.

", + "smithy.api#required": {} + } + }, + "QueryStatus": { + "target": "com.amazonaws.timestreamquery#QueryStatus", + "traits": { + "smithy.api#documentation": "

Information about the status of the query, including progress and bytes\n scanned.

" + } + } + } + }, + "com.amazonaws.timestreamquery#QueryStatus": { + "type": "structure", + "members": { + "ProgressPercentage": { + "target": "com.amazonaws.timestreamquery#Double", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The progress of the query, expressed as a percentage.

" + } + }, + "CumulativeBytesScanned": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The amount of data scanned by the query in bytes. This is a cumulative sum and\n represents the total amount of bytes scanned since the query was started.

" + } + }, + "CumulativeBytesMetered": { + "target": "com.amazonaws.timestreamquery#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The amount of data scanned by the query in bytes that you will be charged for. This is\n a cumulative sum and represents the total amount of data that you will be charged for\n since the query was started. The charge is applied only once and is either applied when\n the query completes running or when the query is cancelled.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Information about the status of the query, including progress and bytes\n scanned.

" + } + }, + "com.amazonaws.timestreamquery#QueryString": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 262144 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.timestreamquery#ResourceName": { + "type": "string" + }, + "com.amazonaws.timestreamquery#ResourceNotFoundException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + }, + "ScheduledQueryArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The ARN of the scheduled query.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The requested resource could not be found.

", + "smithy.api#error": "client", + "smithy.api#httpError": 404 + } + }, + "com.amazonaws.timestreamquery#Row": { + "type": "structure", + "members": { + "Data": { + "target": "com.amazonaws.timestreamquery#DatumList", + "traits": { + "smithy.api#documentation": "

List of data points in a single row of the result set.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a single row in the query results.

" + } + }, + "com.amazonaws.timestreamquery#RowList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#Row" + } + }, + "com.amazonaws.timestreamquery#S3BucketName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 3, + "max": 63 + }, + "smithy.api#pattern": "^[a-z0-9][\\.\\-a-z0-9]{1,61}[a-z0-9]$" + } + }, + "com.amazonaws.timestreamquery#S3Configuration": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamquery#S3BucketName", + "traits": { + "smithy.api#documentation": "

Name of the S3 bucket under which error reports will be created.

", + "smithy.api#required": {} + } + }, + "ObjectKeyPrefix": { + "target": "com.amazonaws.timestreamquery#S3ObjectKeyPrefix", + "traits": { + "smithy.api#documentation": "

Prefix for the error report key. Timestream by default adds the following prefix to\n the error report path.

" + } + }, + "EncryptionOption": { + "target": "com.amazonaws.timestreamquery#S3EncryptionOption", + "traits": { + "smithy.api#documentation": "

Encryption at rest options for the error reports. If no encryption option is\n specified, Timestream will choose SSE_S3 as default.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details on S3 location for error reports that result from running a query.

" + } + }, + "com.amazonaws.timestreamquery#S3EncryptionOption": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "SSE_S3", + "name": "SSE_S3" + }, + { + "value": "SSE_KMS", + "name": "SSE_KMS" + } + ] + } + }, + "com.amazonaws.timestreamquery#S3ObjectKey": { + "type": "string" + }, + "com.amazonaws.timestreamquery#S3ObjectKeyPrefix": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 896 + }, + "smithy.api#pattern": "^[a-zA-Z0-9|!\\-_*'\\(\\)]([a-zA-Z0-9]|[!\\-_*'\\(\\)\\/.])+$" + } + }, + "com.amazonaws.timestreamquery#S3ReportLocation": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamquery#S3BucketName", + "traits": { + "smithy.api#documentation": "

S3 bucket name.

" + } + }, + "ObjectKey": { + "target": "com.amazonaws.timestreamquery#S3ObjectKey", + "traits": { + "smithy.api#documentation": "

S3 key.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

S3 report location for the scheduled query run.

" + } + }, + "com.amazonaws.timestreamquery#ScalarMeasureValueType": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "BIGINT", + "name": "BIGINT" + }, + { + "value": "BOOLEAN", + "name": "BOOLEAN" + }, + { + "value": "DOUBLE", + "name": "DOUBLE" + }, + { + "value": "VARCHAR", + "name": "VARCHAR" + }, + { + "value": "TIMESTAMP", + "name": "TIMESTAMP" + } + ] + } + }, + "com.amazonaws.timestreamquery#ScalarType": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "VARCHAR", + "name": "VARCHAR" + }, + { + "value": "BOOLEAN", + "name": "BOOLEAN" + }, + { + "value": "BIGINT", + "name": "BIGINT" + }, + { + "value": "DOUBLE", + "name": "DOUBLE" + }, + { + "value": "TIMESTAMP", + "name": "TIMESTAMP" + }, + { + "value": "DATE", + "name": "DATE" + }, + { + "value": "TIME", + "name": "TIME" + }, + { + "value": "INTERVAL_DAY_TO_SECOND", + "name": "INTERVAL_DAY_TO_SECOND" + }, + { + "value": "INTERVAL_YEAR_TO_MONTH", + "name": "INTERVAL_YEAR_TO_MONTH" + }, + { + "value": "UNKNOWN", + "name": "UNKNOWN" + }, + { + "value": "INTEGER", + "name": "INTEGER" + } + ] + } + }, + "com.amazonaws.timestreamquery#ScalarValue": { + "type": "string" + }, + "com.amazonaws.timestreamquery#ScheduleConfiguration": { + "type": "structure", + "members": { + "ScheduleExpression": { + "target": "com.amazonaws.timestreamquery#ScheduleExpression", + "traits": { + "smithy.api#documentation": "

An expression that denotes when to trigger the scheduled query run. This can be a cron\n expression or a rate expression.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration of the schedule of the query.

" + } + }, + "com.amazonaws.timestreamquery#ScheduleExpression": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 256 + } + } + }, + "com.amazonaws.timestreamquery#ScheduledQuery": { + "type": "structure", + "members": { + "Arn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The Amazon Resource Name.

", + "smithy.api#required": {} + } + }, + "Name": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryName", + "traits": { + "smithy.api#documentation": "

The name of the scheduled query.

", + "smithy.api#required": {} + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The creation time of the scheduled query.

" + } + }, + "State": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryState", + "traits": { + "smithy.api#documentation": "

State of scheduled query.

", + "smithy.api#required": {} + } + }, + "PreviousInvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The last time the scheduled query was run.

" + } + }, + "NextInvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The next time the scheduled query is to be run.

" + } + }, + "ErrorReportConfiguration": { + "target": "com.amazonaws.timestreamquery#ErrorReportConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration for scheduled query error reporting.

" + } + }, + "TargetDestination": { + "target": "com.amazonaws.timestreamquery#TargetDestination", + "traits": { + "smithy.api#documentation": "

Target data source where final scheduled query result will be written.

" + } + }, + "LastRunStatus": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryRunStatus", + "traits": { + "smithy.api#documentation": "

Status of the last scheduled query run.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Scheduled Query

" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryDescription": { + "type": "structure", + "members": { + "Arn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

Scheduled query ARN.

", + "smithy.api#required": {} + } + }, + "Name": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryName", + "traits": { + "smithy.api#documentation": "

Name of the scheduled query.

", + "smithy.api#required": {} + } + }, + "QueryString": { + "target": "com.amazonaws.timestreamquery#QueryString", + "traits": { + "smithy.api#documentation": "

The query to be run.

", + "smithy.api#required": {} + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

Creation time of the scheduled query.

" + } + }, + "State": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryState", + "traits": { + "smithy.api#documentation": "

State of the scheduled query.

", + "smithy.api#required": {} + } + }, + "PreviousInvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

Last time the query was run.

" + } + }, + "NextInvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The next time the scheduled query is scheduled to run.

" + } + }, + "ScheduleConfiguration": { + "target": "com.amazonaws.timestreamquery#ScheduleConfiguration", + "traits": { + "smithy.api#documentation": "

Schedule configuration.

", + "smithy.api#required": {} + } + }, + "NotificationConfiguration": { + "target": "com.amazonaws.timestreamquery#NotificationConfiguration", + "traits": { + "smithy.api#documentation": "

Notification configuration.

", + "smithy.api#required": {} + } + }, + "TargetConfiguration": { + "target": "com.amazonaws.timestreamquery#TargetConfiguration", + "traits": { + "smithy.api#documentation": "

Scheduled query target store configuration.

" + } + }, + "ScheduledQueryExecutionRoleArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

IAM role that Timestream uses to run the schedule query.

" + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamquery#StringValue2048", + "traits": { + "smithy.api#documentation": "

A customer provided KMS key used to encrypt the scheduled query resource.

" + } + }, + "ErrorReportConfiguration": { + "target": "com.amazonaws.timestreamquery#ErrorReportConfiguration", + "traits": { + "smithy.api#documentation": "

Error-reporting configuration for the scheduled query.

" + } + }, + "LastRunSummary": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryRunSummary", + "traits": { + "smithy.api#documentation": "

Runtime summary for the last scheduled query run.

" + } + }, + "RecentlyFailedRuns": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryRunSummaryList", + "traits": { + "smithy.api#documentation": "

Runtime summary for the last five failed scheduled query runs.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Structure that describes scheduled query.

" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#ScheduledQuery" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 64 + }, + "smithy.api#pattern": "^[a-zA-Z0-9_.-]+$" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryRunStatus": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "AUTO_TRIGGER_SUCCESS", + "name": "AUTO_TRIGGER_SUCCESS" + }, + { + "value": "AUTO_TRIGGER_FAILURE", + "name": "AUTO_TRIGGER_FAILURE" + }, + { + "value": "MANUAL_TRIGGER_SUCCESS", + "name": "MANUAL_TRIGGER_SUCCESS" + }, + { + "value": "MANUAL_TRIGGER_FAILURE", + "name": "MANUAL_TRIGGER_FAILURE" + } + ] + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryRunSummary": { + "type": "structure", + "members": { + "InvocationTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

InvocationTime for this run. This is the time at which the query is scheduled to run.\n Parameter @scheduled_runtime can be used in the query to get the value.

" + } + }, + "TriggerTime": { + "target": "com.amazonaws.timestreamquery#Time", + "traits": { + "smithy.api#documentation": "

The actual time when the query was run.

" + } + }, + "RunStatus": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryRunStatus", + "traits": { + "smithy.api#documentation": "

The status of a scheduled query run.

" + } + }, + "ExecutionStats": { + "target": "com.amazonaws.timestreamquery#ExecutionStats", + "traits": { + "smithy.api#documentation": "

Runtime statistics for a scheduled run.

" + } + }, + "ErrorReportLocation": { + "target": "com.amazonaws.timestreamquery#ErrorReportLocation", + "traits": { + "smithy.api#documentation": "

S3 location for error report.

" + } + }, + "FailureReason": { + "target": "com.amazonaws.timestreamquery#ErrorMessage", + "traits": { + "smithy.api#documentation": "

Error message for the scheduled query in case of failure. You might have to look at\n the error report to get more detailed error reasons.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Run summary for the scheduled query

" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryRunSummaryList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryRunSummary" + } + }, + "com.amazonaws.timestreamquery#ScheduledQueryState": { + "type": "string", + "traits": { + "smithy.api#enum": [ + { + "value": "ENABLED", + "name": "ENABLED" + }, + { + "value": "DISABLED", + "name": "DISABLED" + } + ] + } + }, + "com.amazonaws.timestreamquery#SchemaName": { + "type": "string" + }, + "com.amazonaws.timestreamquery#SelectColumn": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamquery#String", + "traits": { + "smithy.api#documentation": "

Name of the column.

" + } + }, + "Type": { + "target": "com.amazonaws.timestreamquery#Type" + }, + "DatabaseName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Database that has this column.

" + } + }, + "TableName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Table within the database that has this column.

" + } + }, + "Aliased": { + "target": "com.amazonaws.timestreamquery#NullableBoolean", + "traits": { + "smithy.api#documentation": "

True, if the column name was aliased by the query. False otherwise.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details of the column that is returned by the query.

" + } + }, + "com.amazonaws.timestreamquery#SelectColumnList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#SelectColumn" + } + }, + "com.amazonaws.timestreamquery#ServiceErrorMessage": { + "type": "string" + }, + "com.amazonaws.timestreamquery#ServiceQuotaExceededException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

You have exceeded the service quota.

", + "smithy.api#error": "client", + "smithy.api#httpError": 402 + } + }, + "com.amazonaws.timestreamquery#SnsConfiguration": { + "type": "structure", + "members": { + "TopicArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

SNS topic ARN that the scheduled query status notifications will be sent to.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Details on SNS that are required to send the notification.

" + } + }, + "com.amazonaws.timestreamquery#String": { + "type": "string" + }, + "com.amazonaws.timestreamquery#StringValue2048": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + } + } + }, + "com.amazonaws.timestreamquery#Tag": { + "type": "structure", + "members": { + "Key": { + "target": "com.amazonaws.timestreamquery#TagKey", + "traits": { + "smithy.api#documentation": "

The key of the tag. Tag keys are case sensitive.

", + "smithy.api#required": {} + } + }, + "Value": { + "target": "com.amazonaws.timestreamquery#TagValue", + "traits": { + "smithy.api#documentation": "

The value of the tag. Tag values are case sensitive and can be null.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A tag is a label that you assign to a Timestream database and/or table. Each tag\n consists of a key and an optional value, both of which you define. Tags enable you to\n categorize databases and/or tables, for example, by purpose, owner, or environment.\n

" + } + }, + "com.amazonaws.timestreamquery#TagKey": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 128 + } + } + }, + "com.amazonaws.timestreamquery#TagKeyList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#TagKey" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 200 + } + } + }, + "com.amazonaws.timestreamquery#TagList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#Tag" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 200 + } + } + }, + "com.amazonaws.timestreamquery#TagResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#TagResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#TagResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Associate a set of tags with a Timestream resource. You can then activate these\n user-defined tags so that they appear on the Billing and Cost Management console for\n cost allocation tracking.

" + } + }, + "com.amazonaws.timestreamquery#TagResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

Identifies the Timestream resource to which tags should be added. This value is an\n Amazon Resource Name (ARN).

", + "smithy.api#required": {} + } + }, + "Tags": { + "target": "com.amazonaws.timestreamquery#TagList", + "traits": { + "smithy.api#documentation": "

The tags to be assigned to the Timestream resource.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#TagResourceResponse": { + "type": "structure", + "members": {} + }, + "com.amazonaws.timestreamquery#TagValue": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 0, + "max": 256 + } + } + }, + "com.amazonaws.timestreamquery#TargetConfiguration": { + "type": "structure", + "members": { + "TimestreamConfiguration": { + "target": "com.amazonaws.timestreamquery#TimestreamConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration needed to write data into the Timestream database and table.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration used for writing the output of a query.

" + } + }, + "com.amazonaws.timestreamquery#TargetDestination": { + "type": "structure", + "members": { + "TimestreamDestination": { + "target": "com.amazonaws.timestreamquery#TimestreamDestination", + "traits": { + "smithy.api#documentation": "

Query result destination details for Timestream data source.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Destination details to write data for a target data source. Current supported data\n source is Timestream.

" + } + }, + "com.amazonaws.timestreamquery#ThrottlingException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

The request was denied due to request throttling.

", + "smithy.api#error": "client", + "smithy.api#httpError": 429 + } + }, + "com.amazonaws.timestreamquery#Time": { + "type": "timestamp" + }, + "com.amazonaws.timestreamquery#TimeSeriesDataPoint": { + "type": "structure", + "members": { + "Time": { + "target": "com.amazonaws.timestreamquery#Timestamp", + "traits": { + "smithy.api#documentation": "

The timestamp when the measure value was collected.

", + "smithy.api#required": {} + } + }, + "Value": { + "target": "com.amazonaws.timestreamquery#Datum", + "traits": { + "smithy.api#documentation": "

The measure value for the data point.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

The timeseries data type represents the values of a measure over time. A time series\n is an array of rows of timestamps and measure values, with rows sorted in ascending\n order of time. A TimeSeriesDataPoint is a single data point in the time series. It\n represents a tuple of (time, measure value) in a time series.

" + } + }, + "com.amazonaws.timestreamquery#TimeSeriesDataPointList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamquery#TimeSeriesDataPoint" + } + }, + "com.amazonaws.timestreamquery#Timestamp": { + "type": "string" + }, + "com.amazonaws.timestreamquery#TimestreamConfiguration": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Name of Timestream database to which the query result will be written.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Name of Timestream table that the query result will be written to. The table should\n be within the same database that is provided in Timestream configuration.

", + "smithy.api#required": {} + } + }, + "TimeColumn": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Column from query result that should be used as the time column in destination table.\n Column type for this should be TIMESTAMP.

", + "smithy.api#required": {} + } + }, + "DimensionMappings": { + "target": "com.amazonaws.timestreamquery#DimensionMappingList", + "traits": { + "smithy.api#documentation": "

This is to allow mapping column(s) from the query result to the dimension in the\n destination table.

", + "smithy.api#required": {} + } + }, + "MultiMeasureMappings": { + "target": "com.amazonaws.timestreamquery#MultiMeasureMappings", + "traits": { + "smithy.api#documentation": "

Multi-measure mappings.

" + } + }, + "MixedMeasureMappings": { + "target": "com.amazonaws.timestreamquery#MixedMeasureMappingList", + "traits": { + "smithy.api#documentation": "

Specifies how to map measures to multi-measure records.

" + } + }, + "MeasureNameColumn": { + "target": "com.amazonaws.timestreamquery#SchemaName", + "traits": { + "smithy.api#documentation": "

Name of the measure column.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Configuration to write data into Timestream database and table. This configuration\n allows the user to map the query result select columns into the destination table\n columns.

" + } + }, + "com.amazonaws.timestreamquery#TimestreamDestination": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Timestream database name.

" + } + }, + "TableName": { + "target": "com.amazonaws.timestreamquery#ResourceName", + "traits": { + "smithy.api#documentation": "

Timestream table name.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Destination for scheduled query.

" + } + }, + "com.amazonaws.timestreamquery#Timestream_20181101": { + "type": "service", + "version": "2018-11-01", + "operations": [ + { + "target": "com.amazonaws.timestreamquery#CancelQuery" + }, + { + "target": "com.amazonaws.timestreamquery#CreateScheduledQuery" + }, + { + "target": "com.amazonaws.timestreamquery#DeleteScheduledQuery" + }, + { + "target": "com.amazonaws.timestreamquery#DescribeEndpoints" + }, + { + "target": "com.amazonaws.timestreamquery#DescribeScheduledQuery" + }, + { + "target": "com.amazonaws.timestreamquery#ExecuteScheduledQuery" + }, + { + "target": "com.amazonaws.timestreamquery#ListScheduledQueries" + }, + { + "target": "com.amazonaws.timestreamquery#ListTagsForResource" + }, + { + "target": "com.amazonaws.timestreamquery#PrepareQuery" + }, + { + "target": "com.amazonaws.timestreamquery#Query" + }, + { + "target": "com.amazonaws.timestreamquery#TagResource" + }, + { + "target": "com.amazonaws.timestreamquery#UntagResource" + }, + { + "target": "com.amazonaws.timestreamquery#UpdateScheduledQuery" + } + ], + "traits": { + "aws.api#clientEndpointDiscovery": { + "operation": "com.amazonaws.timestreamquery#DescribeEndpoints", + "error": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + "aws.api#service": { + "sdkId": "Timestream Query", + "arnNamespace": "timestream", + "cloudFormationName": "TimestreamQuery", + "cloudTrailEventSource": "timestreamquery.amazonaws.com", + "endpointPrefix": "query.timestream" + }, + "aws.auth#sigv4": { + "name": "timestream" + }, + "aws.protocols#awsJson1_0": {}, + "smithy.api#documentation": "Amazon Timestream Query\n \n

", + "smithy.api#title": "Amazon Timestream Query", + "smithy.rules#endpointRuleSet": { + "version": "1.0", + "parameters": { + "Region": { + "builtIn": "AWS::Region", + "required": false, + "documentation": "The AWS region used to dispatch the request.", + "type": "String" + }, + "UseDualStack": { + "builtIn": "AWS::UseDualStack", + "required": true, + "default": false, + "documentation": "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.", + "type": "Boolean" + }, + "UseFIPS": { + "builtIn": "AWS::UseFIPS", + "required": true, + "default": false, + "documentation": "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.", + "type": "Boolean" + }, + "Endpoint": { + "builtIn": "SDK::Endpoint", + "required": false, + "documentation": "Override the endpoint used to send this request", + "type": "String" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "endpoint": { + "url": { + "ref": "Endpoint" + }, + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Region" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + }, + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://query.timestream-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://query.timestream-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://query.timestream.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://query.timestream.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + } + ] + }, + { + "conditions": [], + "error": "Invalid Configuration: Missing Region", + "type": "error" + } + ] + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": true + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-east-1.api.aws" + } + }, + "params": { + "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": false + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": false + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "Region": "cn-north-1", + "UseDualStack": false, + "UseFIPS": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-gov-east-1.api.aws" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseDualStack": true, + "UseFIPS": false + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "Region": "us-gov-east-1", + "UseDualStack": false, + "UseFIPS": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "Region": "us-iso-east-1", + "UseDualStack": false, + "UseFIPS": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream-fips.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://query.timestream.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "Region": "us-isob-east-1", + "UseDualStack": false, + "UseFIPS": false + } + }, + { + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips enabled and dualstack disabled", + "expect": { + "error": "Invalid Configuration: FIPS and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseDualStack": false, + "UseFIPS": true, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips disabled and dualstack enabled", + "expect": { + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" + }, + "params": { + "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": false, + "Endpoint": "https://example.com" + } + } + ], + "version": "1.0" + } + } + }, + "com.amazonaws.timestreamquery#Type": { + "type": "structure", + "members": { + "ScalarType": { + "target": "com.amazonaws.timestreamquery#ScalarType", + "traits": { + "smithy.api#documentation": "

Indicates if the column is of type string, integer, Boolean, double, timestamp, date,\n time.

" + } + }, + "ArrayColumnInfo": { + "target": "com.amazonaws.timestreamquery#ColumnInfo", + "traits": { + "smithy.api#documentation": "

Indicates if the column is an array.

" + } + }, + "TimeSeriesMeasureValueColumnInfo": { + "target": "com.amazonaws.timestreamquery#ColumnInfo", + "traits": { + "smithy.api#documentation": "

Indicates if the column is a timeseries data type.

" + } + }, + "RowColumnInfo": { + "target": "com.amazonaws.timestreamquery#ColumnInfoList", + "traits": { + "smithy.api#documentation": "

Indicates if the column is a row.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Contains the data type of a column in a query result set. The data type can be scalar\n or complex. The supported scalar data types are integers, Boolean, string, double,\n timestamp, date, time, and intervals. The supported complex data types are arrays, rows,\n and timeseries.

" + } + }, + "com.amazonaws.timestreamquery#UntagResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#UntagResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamquery#UntagResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Removes the association of tags from a Timestream query resource.

" + } + }, + "com.amazonaws.timestreamquery#UntagResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The Timestream resource that the tags will be removed from. This value is an Amazon\n Resource Name (ARN).

", + "smithy.api#required": {} + } + }, + "TagKeys": { + "target": "com.amazonaws.timestreamquery#TagKeyList", + "traits": { + "smithy.api#documentation": "

A list of tags keys. Existing tags of the resource whose keys are members of this list\n will be removed from the Timestream resource.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#UntagResourceResponse": { + "type": "structure", + "members": {} + }, + "com.amazonaws.timestreamquery#UpdateScheduledQuery": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamquery#UpdateScheduledQueryRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamquery#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamquery#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamquery#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamquery#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamquery#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamquery#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Update a scheduled query.

" + } + }, + "com.amazonaws.timestreamquery#UpdateScheduledQueryRequest": { + "type": "structure", + "members": { + "ScheduledQueryArn": { + "target": "com.amazonaws.timestreamquery#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

ARN of the scheuled query.

", + "smithy.api#required": {} + } + }, + "State": { + "target": "com.amazonaws.timestreamquery#ScheduledQueryState", + "traits": { + "smithy.api#documentation": "

State of the scheduled query.

", + "smithy.api#required": {} + } + } + } + }, + "com.amazonaws.timestreamquery#ValidationException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamquery#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

Invalid or malformed request.

", + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + } + } +} diff --git a/aws/sdk/aws-models/timestream-write.json b/aws/sdk/aws-models/timestream-write.json new file mode 100644 index 0000000000..568ab520a0 --- /dev/null +++ b/aws/sdk/aws-models/timestream-write.json @@ -0,0 +1,3663 @@ +{ + "smithy": "2.0", + "metadata": { + "suppressions": [ + { + "id": "HttpMethodSemantics", + "namespace": "*" + }, + { + "id": "HttpResponseCodeSemantics", + "namespace": "*" + }, + { + "id": "PaginatedTrait", + "namespace": "*" + }, + { + "id": "HttpHeaderTrait", + "namespace": "*" + }, + { + "id": "HttpUriConflict", + "namespace": "*" + }, + { + "id": "Service", + "namespace": "*" + } + ] + }, + "shapes": { + "com.amazonaws.timestreamwrite#AccessDeniedException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

You are not authorized to perform this action.

", + "smithy.api#error": "client", + "smithy.api#httpError": 403 + } + }, + "com.amazonaws.timestreamwrite#AmazonResourceName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 1011 + } + } + }, + "com.amazonaws.timestreamwrite#BatchLoadDataFormat": { + "type": "enum", + "members": { + "CSV": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CSV" + } + } + } + }, + "com.amazonaws.timestreamwrite#BatchLoadProgressReport": { + "type": "structure", + "members": { + "RecordsProcessed": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "RecordsIngested": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "ParseFailures": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "RecordIngestionFailures": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "FileFailures": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "BytesMetered": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details about the progress of a batch load task.

" + } + }, + "com.amazonaws.timestreamwrite#BatchLoadStatus": { + "type": "enum", + "members": { + "CREATED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "CREATED" + } + }, + "IN_PROGRESS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "IN_PROGRESS" + } + }, + "FAILED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "FAILED" + } + }, + "SUCCEEDED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SUCCEEDED" + } + }, + "PROGRESS_STOPPED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PROGRESS_STOPPED" + } + }, + "PENDING_RESUME": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "PENDING_RESUME" + } + } + } + }, + "com.amazonaws.timestreamwrite#BatchLoadTask": { + "type": "structure", + "members": { + "TaskId": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskId", + "traits": { + "smithy.api#documentation": "

The ID of the batch load task.

" + } + }, + "TaskStatus": { + "target": "com.amazonaws.timestreamwrite#BatchLoadStatus", + "traits": { + "smithy.api#documentation": "

Status of the batch load task.

" + } + }, + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

Database name for the database into which a batch load task loads data.

" + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

Table name for the table into which a batch load task loads data.

" + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream batch load task was created.

" + } + }, + "LastUpdatedTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream batch load task was last updated.

" + } + }, + "ResumableUntil": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

\n

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details about a batch load task.

" + } + }, + "com.amazonaws.timestreamwrite#BatchLoadTaskDescription": { + "type": "structure", + "members": { + "TaskId": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskId", + "traits": { + "smithy.api#documentation": "

The ID of the batch load task.

" + } + }, + "ErrorMessage": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

" + } + }, + "DataSourceConfiguration": { + "target": "com.amazonaws.timestreamwrite#DataSourceConfiguration", + "traits": { + "smithy.api#documentation": "

Configuration details about the data source for a batch load task.

" + } + }, + "ProgressReport": { + "target": "com.amazonaws.timestreamwrite#BatchLoadProgressReport", + "traits": { + "smithy.api#documentation": "

" + } + }, + "ReportConfiguration": { + "target": "com.amazonaws.timestreamwrite#ReportConfiguration", + "traits": { + "smithy.api#documentation": "

Report configuration for a batch load task. This contains details about where error reports are stored.

" + } + }, + "DataModelConfiguration": { + "target": "com.amazonaws.timestreamwrite#DataModelConfiguration", + "traits": { + "smithy.api#documentation": "

Data model configuration for a batch load task. This contains details about where a data model for a batch load task is stored.

" + } + }, + "TargetDatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "TargetTableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "TaskStatus": { + "target": "com.amazonaws.timestreamwrite#BatchLoadStatus", + "traits": { + "smithy.api#documentation": "

Status of the batch load task.

" + } + }, + "RecordVersion": { + "target": "com.amazonaws.timestreamwrite#RecordVersion", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

" + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream batch load task was created.

" + } + }, + "LastUpdatedTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream batch load task was last updated.

" + } + }, + "ResumableUntil": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

\n

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Details about a batch load task.

" + } + }, + "com.amazonaws.timestreamwrite#BatchLoadTaskId": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 3, + "max": 32 + }, + "smithy.api#pattern": "^[A-Z0-9]+$" + } + }, + "com.amazonaws.timestreamwrite#BatchLoadTaskList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTask" + } + }, + "com.amazonaws.timestreamwrite#Boolean": { + "type": "boolean" + }, + "com.amazonaws.timestreamwrite#ClientRequestToken": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 64 + }, + "smithy.api#sensitive": {} + } + }, + "com.amazonaws.timestreamwrite#ConflictException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Timestream was unable to process this request because it contains resource that\n already exists.

", + "smithy.api#error": "client", + "smithy.api#httpError": 409 + } + }, + "com.amazonaws.timestreamwrite#CreateBatchLoadTask": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#CreateBatchLoadTaskRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#CreateBatchLoadTaskResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#ConflictException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Creates a new Timestream batch load task. A batch load task processes data from\n a CSV source in an S3 location and writes to a Timestream table. A mapping from\n source to target is defined in a batch load task. Errors and events are written to a report\n at an S3 location. For the report, if the KMS key is not specified, the\n batch load task will be encrypted with a Timestream managed KMS key\n located in your account. For more information, see Amazon Web Services managed\n keys. Service quotas apply. For\n details, see code\n sample.

" + } + }, + "com.amazonaws.timestreamwrite#CreateBatchLoadTaskRequest": { + "type": "structure", + "members": { + "ClientToken": { + "target": "com.amazonaws.timestreamwrite#ClientRequestToken", + "traits": { + "smithy.api#documentation": "

", + "smithy.api#idempotencyToken": {} + } + }, + "DataModelConfiguration": { + "target": "com.amazonaws.timestreamwrite#DataModelConfiguration" + }, + "DataSourceConfiguration": { + "target": "com.amazonaws.timestreamwrite#DataSourceConfiguration", + "traits": { + "smithy.api#documentation": "

Defines configuration details about the data source for a batch load task.

", + "smithy.api#required": {} + } + }, + "ReportConfiguration": { + "target": "com.amazonaws.timestreamwrite#ReportConfiguration", + "traits": { + "smithy.api#required": {} + } + }, + "TargetDatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceCreateAPIName", + "traits": { + "smithy.api#documentation": "

Target Timestream database for a batch load task.

", + "smithy.api#required": {} + } + }, + "TargetTableName": { + "target": "com.amazonaws.timestreamwrite#ResourceCreateAPIName", + "traits": { + "smithy.api#documentation": "

Target Timestream table for a batch load task.

", + "smithy.api#required": {} + } + }, + "RecordVersion": { + "target": "com.amazonaws.timestreamwrite#RecordVersion", + "traits": { + "smithy.api#default": null, + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#CreateBatchLoadTaskResponse": { + "type": "structure", + "members": { + "TaskId": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskId", + "traits": { + "smithy.api#documentation": "

The ID of the batch load task.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#CreateDatabase": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#CreateDatabaseRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#CreateDatabaseResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#ConflictException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Creates a new Timestream database. If the KMS key is not\n specified, the database will be encrypted with a Timestream managed KMS key located in your account. For more information, see Amazon Web Services managed keys. Service quotas apply. For\n details, see code sample.\n

" + } + }, + "com.amazonaws.timestreamwrite#CreateDatabaseRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceCreateAPIName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

The KMS key for the database. If the KMS key is not\n specified, the database will be encrypted with a Timestream managed KMS key located in your account. For more information, see Amazon Web Services managed keys.

" + } + }, + "Tags": { + "target": "com.amazonaws.timestreamwrite#TagList", + "traits": { + "smithy.api#documentation": "

A list of key-value pairs to label the table.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#CreateDatabaseResponse": { + "type": "structure", + "members": { + "Database": { + "target": "com.amazonaws.timestreamwrite#Database", + "traits": { + "smithy.api#documentation": "

The newly created Timestream database.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#CreateTable": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#CreateTableRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#CreateTableResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#ConflictException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Adds a new table to an existing database in your account. In an Amazon Web Services account, table names must be at least unique within each Region if they are in the same\n database. You might have identical table names in the same Region if the tables are in\n separate databases. While creating the table, you must specify the table name, database\n name, and the retention properties. Service quotas apply. See\n code\n sample for details.

" + } + }, + "com.amazonaws.timestreamwrite#CreateTableRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceCreateAPIName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceCreateAPIName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

", + "smithy.api#required": {} + } + }, + "RetentionProperties": { + "target": "com.amazonaws.timestreamwrite#RetentionProperties", + "traits": { + "smithy.api#documentation": "

The duration for which your time-series data must be stored in the memory store and the\n magnetic store.

" + } + }, + "Tags": { + "target": "com.amazonaws.timestreamwrite#TagList", + "traits": { + "smithy.api#documentation": "

A list of key-value pairs to label the table.

" + } + }, + "MagneticStoreWriteProperties": { + "target": "com.amazonaws.timestreamwrite#MagneticStoreWriteProperties", + "traits": { + "smithy.api#documentation": "

Contains properties to set on the table when enabling magnetic store writes.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#CreateTableResponse": { + "type": "structure", + "members": { + "Table": { + "target": "com.amazonaws.timestreamwrite#Table", + "traits": { + "smithy.api#documentation": "

The newly created Timestream table.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#CsvConfiguration": { + "type": "structure", + "members": { + "ColumnSeparator": { + "target": "com.amazonaws.timestreamwrite#StringValue1", + "traits": { + "smithy.api#documentation": "

Column separator can be one of comma (','), pipe ('|), semicolon (';'), tab('/t'), or\n blank space (' ').

" + } + }, + "EscapeChar": { + "target": "com.amazonaws.timestreamwrite#StringValue1", + "traits": { + "smithy.api#documentation": "

Escape character can be one of

" + } + }, + "QuoteChar": { + "target": "com.amazonaws.timestreamwrite#StringValue1", + "traits": { + "smithy.api#documentation": "

Can be single quote (') or double quote (\").

" + } + }, + "NullValue": { + "target": "com.amazonaws.timestreamwrite#StringValue256", + "traits": { + "smithy.api#documentation": "

Can be blank space (' ').

" + } + }, + "TrimWhiteSpace": { + "target": "com.amazonaws.timestreamwrite#Boolean", + "traits": { + "smithy.api#documentation": "

Specifies to trim leading and trailing white space.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A delimited data format where the column separator can be a comma and the record\n separator is a newline character.

" + } + }, + "com.amazonaws.timestreamwrite#DataModel": { + "type": "structure", + "members": { + "TimeColumn": { + "target": "com.amazonaws.timestreamwrite#StringValue256", + "traits": { + "smithy.api#documentation": "

Source column to be mapped to time.

" + } + }, + "TimeUnit": { + "target": "com.amazonaws.timestreamwrite#TimeUnit", + "traits": { + "smithy.api#documentation": "

The granularity of the timestamp unit. It indicates if the time value is in seconds,\n milliseconds, nanoseconds, or other supported values. Default is MILLISECONDS.\n

" + } + }, + "DimensionMappings": { + "target": "com.amazonaws.timestreamwrite#DimensionMappings", + "traits": { + "smithy.api#documentation": "

Source to target mappings for dimensions.

", + "smithy.api#required": {} + } + }, + "MultiMeasureMappings": { + "target": "com.amazonaws.timestreamwrite#MultiMeasureMappings", + "traits": { + "smithy.api#documentation": "

Source to target mappings for multi-measure records.

" + } + }, + "MixedMeasureMappings": { + "target": "com.amazonaws.timestreamwrite#MixedMeasureMappingList", + "traits": { + "smithy.api#documentation": "

Source to target mappings for measures.

" + } + }, + "MeasureNameColumn": { + "target": "com.amazonaws.timestreamwrite#StringValue256", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Data model for a batch load task.

" + } + }, + "com.amazonaws.timestreamwrite#DataModelConfiguration": { + "type": "structure", + "members": { + "DataModel": { + "target": "com.amazonaws.timestreamwrite#DataModel", + "traits": { + "smithy.api#documentation": "

" + } + }, + "DataModelS3Configuration": { + "target": "com.amazonaws.timestreamwrite#DataModelS3Configuration", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#DataModelS3Configuration": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamwrite#S3BucketName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "ObjectKey": { + "target": "com.amazonaws.timestreamwrite#S3ObjectKey", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#DataSourceConfiguration": { + "type": "structure", + "members": { + "DataSourceS3Configuration": { + "target": "com.amazonaws.timestreamwrite#DataSourceS3Configuration", + "traits": { + "smithy.api#documentation": "

Configuration of an S3 location for a file which contains data to load.

", + "smithy.api#required": {} + } + }, + "CsvConfiguration": { + "target": "com.amazonaws.timestreamwrite#CsvConfiguration" + }, + "DataFormat": { + "target": "com.amazonaws.timestreamwrite#BatchLoadDataFormat", + "traits": { + "smithy.api#documentation": "

This is currently CSV.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Defines configuration details about the data source.

" + } + }, + "com.amazonaws.timestreamwrite#DataSourceS3Configuration": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamwrite#S3BucketName", + "traits": { + "smithy.api#documentation": "

The bucket name of the customer S3 bucket.

", + "smithy.api#required": {} + } + }, + "ObjectKeyPrefix": { + "target": "com.amazonaws.timestreamwrite#S3ObjectKey", + "traits": { + "smithy.api#documentation": "

\n

" + } + } + }, + "traits": { + "smithy.api#documentation": "

\n

" + } + }, + "com.amazonaws.timestreamwrite#Database": { + "type": "structure", + "members": { + "Arn": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

The Amazon Resource Name that uniquely identifies this database.

" + } + }, + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

" + } + }, + "TableCount": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The total number of tables found within a Timestream database.

" + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

The identifier of the KMS key used to encrypt the data stored in the\n database.

" + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the database was created, calculated from the Unix epoch time.

" + } + }, + "LastUpdatedTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The last time that this database was updated.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A top-level container for a table. Databases and tables are the fundamental management\n concepts in Amazon Timestream. All tables in a database are encrypted with the\n same KMS key.

" + } + }, + "com.amazonaws.timestreamwrite#DatabaseList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Database" + } + }, + "com.amazonaws.timestreamwrite#Date": { + "type": "timestamp" + }, + "com.amazonaws.timestreamwrite#DeleteDatabase": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DeleteDatabaseRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Deletes a given Timestream database. This is an irreversible\n operation. After a database is deleted, the time-series data from its tables cannot be\n recovered.\n

\n \n

All tables in the database must be deleted first, or a ValidationException error will\n be thrown.

\n

Due to the nature of distributed retries, the operation can return either success or\n a ResourceNotFoundException. Clients should consider them equivalent.

\n
\n

See code sample\n for details.

" + } + }, + "com.amazonaws.timestreamwrite#DeleteDatabaseRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database to be deleted.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DeleteTable": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DeleteTableRequest" + }, + "output": { + "target": "smithy.api#Unit" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Deletes a given Timestream table. This is an irreversible operation. After a\n Timestream database table is deleted, the time-series data stored in the table\n cannot be recovered.

\n \n

Due to the nature of distributed retries, the operation can return either success or\n a ResourceNotFoundException. Clients should consider them equivalent.

\n
\n

See code\n sample for details.

" + } + }, + "com.amazonaws.timestreamwrite#DeleteTableRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the database where the Timestream database is to be deleted.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table to be deleted.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeBatchLoadTask": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DescribeBatchLoadTaskRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#DescribeBatchLoadTaskResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Returns information about the batch load task, including configurations, mappings,\n progress, and other details. Service quotas apply. See\n code\n sample for details.

" + } + }, + "com.amazonaws.timestreamwrite#DescribeBatchLoadTaskRequest": { + "type": "structure", + "members": { + "TaskId": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskId", + "traits": { + "smithy.api#documentation": "

The ID of the batch load task.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeBatchLoadTaskResponse": { + "type": "structure", + "members": { + "BatchLoadTaskDescription": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskDescription", + "traits": { + "smithy.api#documentation": "

Description of the batch load task.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeDatabase": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DescribeDatabaseRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#DescribeDatabaseResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Returns information about the database, including the database name, time that the\n database was created, and the total number of tables found within the database. Service\n quotas apply. See code sample\n for details.

" + } + }, + "com.amazonaws.timestreamwrite#DescribeDatabaseRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeDatabaseResponse": { + "type": "structure", + "members": { + "Database": { + "target": "com.amazonaws.timestreamwrite#Database", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeEndpoints": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DescribeEndpointsRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#DescribeEndpointsResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "smithy.api#documentation": "

Returns a list of available endpoints to make Timestream API calls against.\n This API operation is available through both the Write and Query APIs.

\n

Because the Timestream SDKs are designed to transparently work with the\n service’s architecture, including the management and mapping of the service endpoints,\n we don't recommend that you use this API operation unless:

\n \n

For detailed information on how and when to use and implement DescribeEndpoints, see\n The\n Endpoint Discovery Pattern.

" + } + }, + "com.amazonaws.timestreamwrite#DescribeEndpointsRequest": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeEndpointsResponse": { + "type": "structure", + "members": { + "Endpoints": { + "target": "com.amazonaws.timestreamwrite#Endpoints", + "traits": { + "smithy.api#documentation": "

An Endpoints object is returned when a DescribeEndpoints\n request is made.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeTable": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#DescribeTableRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#DescribeTableResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Returns information about the table, including the table name, database name, retention\n duration of the memory store and the magnetic store. Service quotas apply. See\n code\n sample for details.

" + } + }, + "com.amazonaws.timestreamwrite#DescribeTableRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#DescribeTableResponse": { + "type": "structure", + "members": { + "Table": { + "target": "com.amazonaws.timestreamwrite#Table", + "traits": { + "smithy.api#documentation": "

The Timestream table.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#Dimension": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

Dimension represents the metadata attributes of the time series. For example, the name\n and Availability Zone of an EC2 instance or the name of the manufacturer of a wind turbine\n are dimensions.

\n

For constraints on dimension names, see Naming\n Constraints.

", + "smithy.api#required": {} + } + }, + "Value": { + "target": "com.amazonaws.timestreamwrite#SchemaValue", + "traits": { + "smithy.api#documentation": "

The value of the dimension.

", + "smithy.api#required": {} + } + }, + "DimensionValueType": { + "target": "com.amazonaws.timestreamwrite#DimensionValueType", + "traits": { + "smithy.api#documentation": "

The data type of the dimension for the time-series data point.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents the metadata attributes of the time series. For example, the name and\n Availability Zone of an EC2 instance or the name of the manufacturer of a wind turbine are\n dimensions.

" + } + }, + "com.amazonaws.timestreamwrite#DimensionMapping": { + "type": "structure", + "members": { + "SourceColumn": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "DestinationColumn": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

\n

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#DimensionMappings": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#DimensionMapping" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamwrite#DimensionValueType": { + "type": "enum", + "members": { + "VARCHAR": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "VARCHAR" + } + } + } + }, + "com.amazonaws.timestreamwrite#Dimensions": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Dimension" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 128 + } + } + }, + "com.amazonaws.timestreamwrite#Endpoint": { + "type": "structure", + "members": { + "Address": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

An endpoint address.

", + "smithy.api#required": {} + } + }, + "CachePeriodInMinutes": { + "target": "com.amazonaws.timestreamwrite#Long", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The TTL for the endpoint, in minutes.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents an available endpoint against which to make API calls against, as well as the\n TTL for that endpoint.

" + } + }, + "com.amazonaws.timestreamwrite#Endpoints": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Endpoint" + } + }, + "com.amazonaws.timestreamwrite#ErrorMessage": { + "type": "string" + }, + "com.amazonaws.timestreamwrite#Integer": { + "type": "integer", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamwrite#InternalServerException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

\n Timestream was unable to fully process this request because of an internal server\n error.

", + "smithy.api#error": "server", + "smithy.api#httpError": 500 + } + }, + "com.amazonaws.timestreamwrite#InvalidEndpointException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

The requested endpoint was not valid.

", + "smithy.api#error": "client", + "smithy.api#httpError": 421 + } + }, + "com.amazonaws.timestreamwrite#ListBatchLoadTasks": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#ListBatchLoadTasksRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#ListBatchLoadTasksResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Provides a list of batch load tasks, along with the name, status, when the task is\n resumable until, and other details. See code\n sample for details.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.timestreamwrite#ListBatchLoadTasksRequest": { + "type": "structure", + "members": { + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

A token to specify where to start paginating. This is the NextToken from a previously\n truncated response.

" + } + }, + "MaxResults": { + "target": "com.amazonaws.timestreamwrite#PageLimit", + "traits": { + "smithy.api#documentation": "

The total number of items to return in the output. If the total number of items\n available is more than the value specified, a NextToken is provided in the output. To\n resume pagination, provide the NextToken value as argument of a subsequent API\n invocation.

" + } + }, + "TaskStatus": { + "target": "com.amazonaws.timestreamwrite#BatchLoadStatus", + "traits": { + "smithy.api#documentation": "

Status of the batch load task.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#ListBatchLoadTasksResponse": { + "type": "structure", + "members": { + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

A token to specify where to start paginating. Provide the next\n ListBatchLoadTasksRequest.

" + } + }, + "BatchLoadTasks": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskList", + "traits": { + "smithy.api#documentation": "

A list of batch load task details.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#ListDatabases": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#ListDatabasesRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#ListDatabasesResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Returns a list of your Timestream databases. Service quotas apply. See\n code sample for\n details.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.timestreamwrite#ListDatabasesRequest": { + "type": "structure", + "members": { + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

The pagination token. To resume pagination, provide the NextToken value as argument of a\n subsequent API invocation.

" + } + }, + "MaxResults": { + "target": "com.amazonaws.timestreamwrite#PaginationLimit", + "traits": { + "smithy.api#documentation": "

The total number of items to return in the output. If the total number of items\n available is more than the value specified, a NextToken is provided in the output. To\n resume pagination, provide the NextToken value as argument of a subsequent API\n invocation.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#ListDatabasesResponse": { + "type": "structure", + "members": { + "Databases": { + "target": "com.amazonaws.timestreamwrite#DatabaseList", + "traits": { + "smithy.api#documentation": "

A list of database names.

" + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

The pagination token. This parameter is returned when the response is truncated.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#ListTables": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#ListTablesRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#ListTablesResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Provides a list of tables, along with the name, status, and retention properties of each\n table. See code sample\n for details.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.timestreamwrite#ListTablesRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

" + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

The pagination token. To resume pagination, provide the NextToken value as argument of a\n subsequent API invocation.

" + } + }, + "MaxResults": { + "target": "com.amazonaws.timestreamwrite#PaginationLimit", + "traits": { + "smithy.api#documentation": "

The total number of items to return in the output. If the total number of items\n available is more than the value specified, a NextToken is provided in the output. To\n resume pagination, provide the NextToken value as argument of a subsequent API\n invocation.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#ListTablesResponse": { + "type": "structure", + "members": { + "Tables": { + "target": "com.amazonaws.timestreamwrite#TableList", + "traits": { + "smithy.api#documentation": "

A list of tables.

" + } + }, + "NextToken": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

A token to specify where to start paginating. This is the NextToken from a previously\n truncated response.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#ListTagsForResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#ListTagsForResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#ListTagsForResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Lists all tags on a Timestream resource.

" + } + }, + "com.amazonaws.timestreamwrite#ListTagsForResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamwrite#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The Timestream resource with tags to be listed. This value is an Amazon\n Resource Name (ARN).

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#ListTagsForResourceResponse": { + "type": "structure", + "members": { + "Tags": { + "target": "com.amazonaws.timestreamwrite#TagList", + "traits": { + "smithy.api#documentation": "

The tags currently associated with the Timestream resource.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#Long": { + "type": "long", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamwrite#MagneticStoreRejectedDataLocation": { + "type": "structure", + "members": { + "S3Configuration": { + "target": "com.amazonaws.timestreamwrite#S3Configuration", + "traits": { + "smithy.api#documentation": "

Configuration of an S3 location to write error reports for records rejected,\n asynchronously, during magnetic store writes.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The location to write error reports for records rejected, asynchronously, during\n magnetic store writes.

" + } + }, + "com.amazonaws.timestreamwrite#MagneticStoreRetentionPeriodInDays": { + "type": "long", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": 1, + "max": 73000 + } + } + }, + "com.amazonaws.timestreamwrite#MagneticStoreWriteProperties": { + "type": "structure", + "members": { + "EnableMagneticStoreWrites": { + "target": "com.amazonaws.timestreamwrite#Boolean", + "traits": { + "smithy.api#documentation": "

A flag to enable magnetic store writes.

", + "smithy.api#required": {} + } + }, + "MagneticStoreRejectedDataLocation": { + "target": "com.amazonaws.timestreamwrite#MagneticStoreRejectedDataLocation", + "traits": { + "smithy.api#documentation": "

The location to write error reports for records rejected asynchronously during magnetic\n store writes.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The set of properties on a table for configuring magnetic store writes.

" + } + }, + "com.amazonaws.timestreamwrite#MeasureValue": { + "type": "structure", + "members": { + "Name": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

The name of the MeasureValue.

\n

For constraints on MeasureValue names, see Naming Constraints in the Amazon Timestream Developer Guide.

", + "smithy.api#required": {} + } + }, + "Value": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

The value for the MeasureValue.

", + "smithy.api#required": {} + } + }, + "Type": { + "target": "com.amazonaws.timestreamwrite#MeasureValueType", + "traits": { + "smithy.api#documentation": "

Contains the data type of the MeasureValue for the time-series data point.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents the data attribute of the time series. For example, the CPU utilization of\n an EC2 instance or the RPM of a wind turbine are measures. MeasureValue has both name and\n value.

\n

MeasureValue is only allowed for type MULTI. Using MULTI\n type, you can pass multiple data attributes associated with the same time series in a\n single record

" + } + }, + "com.amazonaws.timestreamwrite#MeasureValueType": { + "type": "enum", + "members": { + "DOUBLE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DOUBLE" + } + }, + "BIGINT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BIGINT" + } + }, + "VARCHAR": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "VARCHAR" + } + }, + "BOOLEAN": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BOOLEAN" + } + }, + "TIMESTAMP": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "TIMESTAMP" + } + }, + "MULTI": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MULTI" + } + } + } + }, + "com.amazonaws.timestreamwrite#MeasureValues": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#MeasureValue" + } + }, + "com.amazonaws.timestreamwrite#MemoryStoreRetentionPeriodInHours": { + "type": "long", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": 1, + "max": 8766 + } + } + }, + "com.amazonaws.timestreamwrite#MixedMeasureMapping": { + "type": "structure", + "members": { + "MeasureName": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "SourceColumn": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "TargetMeasureName": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "MeasureValueType": { + "target": "com.amazonaws.timestreamwrite#MeasureValueType", + "traits": { + "smithy.api#documentation": "

", + "smithy.api#required": {} + } + }, + "MultiMeasureAttributeMappings": { + "target": "com.amazonaws.timestreamwrite#MultiMeasureAttributeMappingList", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#MixedMeasureMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#MixedMeasureMapping" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamwrite#MultiMeasureAttributeMapping": { + "type": "structure", + "members": { + "SourceColumn": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

", + "smithy.api#required": {} + } + }, + "TargetMultiMeasureAttributeName": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "MeasureValueType": { + "target": "com.amazonaws.timestreamwrite#ScalarMeasureValueType", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#MultiMeasureAttributeMappingList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#MultiMeasureAttributeMapping" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamwrite#MultiMeasureMappings": { + "type": "structure", + "members": { + "TargetMultiMeasureName": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

" + } + }, + "MultiMeasureAttributeMappings": { + "target": "com.amazonaws.timestreamwrite#MultiMeasureAttributeMappingList", + "traits": { + "smithy.api#documentation": "

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#PageLimit": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 1, + "max": 100 + } + } + }, + "com.amazonaws.timestreamwrite#PaginationLimit": { + "type": "integer", + "traits": { + "smithy.api#range": { + "min": 1, + "max": 20 + } + } + }, + "com.amazonaws.timestreamwrite#Record": { + "type": "structure", + "members": { + "Dimensions": { + "target": "com.amazonaws.timestreamwrite#Dimensions", + "traits": { + "smithy.api#documentation": "

Contains the list of dimensions for time-series data points.

" + } + }, + "MeasureName": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

Measure represents the data attribute of the time series. For example, the CPU\n utilization of an EC2 instance or the RPM of a wind turbine are measures.

" + } + }, + "MeasureValue": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

Contains the measure value for the time-series data point.

" + } + }, + "MeasureValueType": { + "target": "com.amazonaws.timestreamwrite#MeasureValueType", + "traits": { + "smithy.api#documentation": "

Contains the data type of the measure value for the time-series data point. Default\n type is DOUBLE.

" + } + }, + "Time": { + "target": "com.amazonaws.timestreamwrite#StringValue256", + "traits": { + "smithy.api#documentation": "

Contains the time at which the measure value for the data point was collected. The time\n value plus the unit provides the time elapsed since the epoch. For example, if the time\n value is 12345 and the unit is ms, then 12345 ms\n have elapsed since the epoch.

" + } + }, + "TimeUnit": { + "target": "com.amazonaws.timestreamwrite#TimeUnit", + "traits": { + "smithy.api#documentation": "

The granularity of the timestamp unit. It indicates if the time value is in seconds,\n milliseconds, nanoseconds, or other supported values. Default is MILLISECONDS.\n

" + } + }, + "Version": { + "target": "com.amazonaws.timestreamwrite#RecordVersion", + "traits": { + "smithy.api#default": null, + "smithy.api#documentation": "

64-bit attribute used for record updates. Write requests for duplicate data with a\n higher version number will update the existing measure value and version. In cases where\n the measure value is the same, Version will still be updated. Default value is\n 1.

\n \n

\n Version must be 1 or greater, or you will receive a\n ValidationException error.

\n
" + } + }, + "MeasureValues": { + "target": "com.amazonaws.timestreamwrite#MeasureValues", + "traits": { + "smithy.api#documentation": "

Contains the list of MeasureValue for time-series data points.

\n

This is only allowed for type MULTI. For scalar values, use\n MeasureValue attribute of the record directly.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a time-series data point being written into Timestream. Each record\n contains an array of dimensions. Dimensions represent the metadata attributes of a\n time-series data point, such as the instance name or Availability Zone of an EC2 instance.\n A record also contains the measure name, which is the name of the measure being collected\n (for example, the CPU utilization of an EC2 instance). Additionally, a record contains the\n measure value and the value type, which is the data type of the measure value. Also, the\n record contains the timestamp of when the measure was collected and the timestamp unit,\n which represents the granularity of the timestamp.

\n

Records have a Version field, which is a 64-bit long that you\n can use for updating data points. Writes of a duplicate record with the same dimension,\n timestamp, and measure name but different measure value will only succeed if the\n Version attribute of the record in the write request is higher than that of\n the existing record. Timestream defaults to a Version of\n 1 for records without the Version field.

" + } + }, + "com.amazonaws.timestreamwrite#RecordIndex": { + "type": "integer", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamwrite#RecordVersion": { + "type": "long", + "traits": { + "smithy.api#default": 0 + } + }, + "com.amazonaws.timestreamwrite#Records": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Record" + }, + "traits": { + "smithy.api#length": { + "min": 1, + "max": 100 + } + } + }, + "com.amazonaws.timestreamwrite#RecordsIngested": { + "type": "structure", + "members": { + "Total": { + "target": "com.amazonaws.timestreamwrite#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Total count of successfully ingested records.

" + } + }, + "MemoryStore": { + "target": "com.amazonaws.timestreamwrite#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Count of records ingested into the memory store.

" + } + }, + "MagneticStore": { + "target": "com.amazonaws.timestreamwrite#Integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

Count of records ingested into the magnetic store.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Information on the records ingested by this request.

" + } + }, + "com.amazonaws.timestreamwrite#RejectedRecord": { + "type": "structure", + "members": { + "RecordIndex": { + "target": "com.amazonaws.timestreamwrite#RecordIndex", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The index of the record in the input request for WriteRecords. Indexes begin with 0.\n

" + } + }, + "Reason": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#documentation": "

The reason why a record was not successfully inserted into Timestream.\n Possible causes of failure include:

\n
    \n
  • \n

    Records with duplicate data where there are multiple records with the same\n dimensions, timestamps, and measure names but:

    \n
      \n
    • \n

      Measure values are different

      \n
    • \n
    • \n

      Version is not present in the request, or the value of\n version in the new record is equal to or lower than the existing value

      \n
    • \n
    \n

    If Timestream rejects data for this case, the\n ExistingVersion field in the RejectedRecords response\n will indicate the current record’s version. To force an update, you can resend the\n request with a version for the record set to a value greater than the\n ExistingVersion.

    \n
  • \n
  • \n

    Records with timestamps that lie outside the retention duration of the memory\n store.

    \n \n

    When the retention window is updated, you will receive a\n RejectedRecords exception if you immediately try to ingest data\n within the new window. To avoid a RejectedRecords exception, wait\n until the duration of the new window to ingest new data. For further information,\n see Best\n Practices for Configuring Timestream and the\n explanation of how storage works in Timestream.

    \n
    \n
  • \n
  • \n

    Records with dimensions or measures that exceed the Timestream defined\n limits.

    \n
  • \n
\n

For more information, see Access Management in the\n Timestream Developer Guide.

" + } + }, + "ExistingVersion": { + "target": "com.amazonaws.timestreamwrite#RecordVersion", + "traits": { + "smithy.api#default": null, + "smithy.api#documentation": "

The existing version of the record. This value is populated in scenarios where an\n identical record exists with a higher version than the version in the write request.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents records that were not successfully inserted into Timestream due to\n data validation issues that must be resolved before reinserting time-series data into the\n system.

" + } + }, + "com.amazonaws.timestreamwrite#RejectedRecords": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#RejectedRecord" + } + }, + "com.amazonaws.timestreamwrite#RejectedRecordsException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage" + }, + "RejectedRecords": { + "target": "com.amazonaws.timestreamwrite#RejectedRecords", + "traits": { + "smithy.api#documentation": "

\n

" + } + } + }, + "traits": { + "smithy.api#documentation": "

WriteRecords would throw this exception in the following cases:

\n
    \n
  • \n

    Records with duplicate data where there are multiple records with the same\n dimensions, timestamps, and measure names but:

    \n
      \n
    • \n

      Measure values are different

      \n
    • \n
    • \n

      Version is not present in the request or the value of\n version in the new record is equal to or lower than the existing value

      \n
    • \n
    \n

    In this case, if Timestream rejects data, the\n ExistingVersion field in the RejectedRecords response\n will indicate the current record’s version. To force an update, you can resend the\n request with a version for the record set to a value greater than the\n ExistingVersion.

    \n
  • \n
  • \n

    Records with timestamps that lie outside the retention duration of the memory\n store.

    \n
  • \n
  • \n

    Records with dimensions or measures that exceed the Timestream defined\n limits.

    \n
  • \n
\n

For more information, see Quotas in the Amazon Timestream Developer Guide.

", + "smithy.api#error": "client", + "smithy.api#httpError": 419 + } + }, + "com.amazonaws.timestreamwrite#ReportConfiguration": { + "type": "structure", + "members": { + "ReportS3Configuration": { + "target": "com.amazonaws.timestreamwrite#ReportS3Configuration", + "traits": { + "smithy.api#documentation": "

Configuration of an S3 location to write error reports and events for a batch\n load.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Report configuration for a batch load task. This contains details about where error reports are stored.

" + } + }, + "com.amazonaws.timestreamwrite#ReportS3Configuration": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamwrite#S3BucketName", + "traits": { + "smithy.api#documentation": "

", + "smithy.api#required": {} + } + }, + "ObjectKeyPrefix": { + "target": "com.amazonaws.timestreamwrite#S3ObjectKeyPrefix", + "traits": { + "smithy.api#documentation": "

" + } + }, + "EncryptionOption": { + "target": "com.amazonaws.timestreamwrite#S3EncryptionOption", + "traits": { + "smithy.api#documentation": "

" + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

" + } + } + }, + "traits": { + "smithy.api#documentation": "

" + } + }, + "com.amazonaws.timestreamwrite#ResourceCreateAPIName": { + "type": "string", + "traits": { + "smithy.api#pattern": "^[a-zA-Z0-9_.-]+$" + } + }, + "com.amazonaws.timestreamwrite#ResourceName": { + "type": "string" + }, + "com.amazonaws.timestreamwrite#ResourceNotFoundException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

The operation tried to access a nonexistent resource. The resource might not be\n specified correctly, or its status might not be ACTIVE.

", + "smithy.api#error": "client", + "smithy.api#httpError": 404 + } + }, + "com.amazonaws.timestreamwrite#ResumeBatchLoadTask": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#ResumeBatchLoadTaskRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#ResumeBatchLoadTaskResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

\n

" + } + }, + "com.amazonaws.timestreamwrite#ResumeBatchLoadTaskRequest": { + "type": "structure", + "members": { + "TaskId": { + "target": "com.amazonaws.timestreamwrite#BatchLoadTaskId", + "traits": { + "smithy.api#documentation": "

The ID of the batch load task to resume.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#ResumeBatchLoadTaskResponse": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#RetentionProperties": { + "type": "structure", + "members": { + "MemoryStoreRetentionPeriodInHours": { + "target": "com.amazonaws.timestreamwrite#MemoryStoreRetentionPeriodInHours", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The duration for which data must be stored in the memory store.

", + "smithy.api#required": {} + } + }, + "MagneticStoreRetentionPeriodInDays": { + "target": "com.amazonaws.timestreamwrite#MagneticStoreRetentionPeriodInDays", + "traits": { + "smithy.api#default": 0, + "smithy.api#documentation": "

The duration for which data must be stored in the magnetic store.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Retention properties contain the duration for which your time-series data must be stored\n in the magnetic store and the memory store.

" + } + }, + "com.amazonaws.timestreamwrite#S3BucketName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 3, + "max": 63 + }, + "smithy.api#pattern": "^[a-z0-9][\\.\\-a-z0-9]{1,61}[a-z0-9]$" + } + }, + "com.amazonaws.timestreamwrite#S3Configuration": { + "type": "structure", + "members": { + "BucketName": { + "target": "com.amazonaws.timestreamwrite#S3BucketName", + "traits": { + "smithy.api#documentation": "

The bucket name of the customer S3 bucket.

" + } + }, + "ObjectKeyPrefix": { + "target": "com.amazonaws.timestreamwrite#S3ObjectKeyPrefix", + "traits": { + "smithy.api#documentation": "

The object key preview for the customer S3 location.

" + } + }, + "EncryptionOption": { + "target": "com.amazonaws.timestreamwrite#S3EncryptionOption", + "traits": { + "smithy.api#documentation": "

The encryption option for the customer S3 location. Options are S3 server-side\n encryption with an S3 managed key or Amazon Web Services managed key.

" + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

The KMS key ID for the customer S3 location when encrypting with an\n Amazon Web Services managed key.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

The configuration that specifies an S3 location.

" + } + }, + "com.amazonaws.timestreamwrite#S3EncryptionOption": { + "type": "enum", + "members": { + "SSE_S3": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SSE_S3" + } + }, + "SSE_KMS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SSE_KMS" + } + } + } + }, + "com.amazonaws.timestreamwrite#S3ObjectKey": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 1024 + }, + "smithy.api#pattern": "^[a-zA-Z0-9|!\\-_*'\\(\\)]([a-zA-Z0-9]|[!\\-_*'\\(\\)\\/.])+$" + } + }, + "com.amazonaws.timestreamwrite#S3ObjectKeyPrefix": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 928 + }, + "smithy.api#pattern": "^[a-zA-Z0-9|!\\-_*'\\(\\)]([a-zA-Z0-9]|[!\\-_*'\\(\\)\\/.])+$" + } + }, + "com.amazonaws.timestreamwrite#ScalarMeasureValueType": { + "type": "enum", + "members": { + "DOUBLE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DOUBLE" + } + }, + "BIGINT": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BIGINT" + } + }, + "BOOLEAN": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "BOOLEAN" + } + }, + "VARCHAR": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "VARCHAR" + } + }, + "TIMESTAMP": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "TIMESTAMP" + } + } + } + }, + "com.amazonaws.timestreamwrite#SchemaName": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamwrite#SchemaValue": { + "type": "string" + }, + "com.amazonaws.timestreamwrite#ServiceQuotaExceededException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage" + } + }, + "traits": { + "smithy.api#documentation": "

The instance quota of resource exceeded for this account.

", + "smithy.api#error": "client", + "smithy.api#httpError": 402 + } + }, + "com.amazonaws.timestreamwrite#String": { + "type": "string" + }, + "com.amazonaws.timestreamwrite#StringValue1": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 1 + } + } + }, + "com.amazonaws.timestreamwrite#StringValue2048": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 2048 + } + } + }, + "com.amazonaws.timestreamwrite#StringValue256": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 256 + } + } + }, + "com.amazonaws.timestreamwrite#Table": { + "type": "structure", + "members": { + "Arn": { + "target": "com.amazonaws.timestreamwrite#String", + "traits": { + "smithy.api#documentation": "

The Amazon Resource Name that uniquely identifies this table.

" + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

" + } + }, + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database that contains this table.

" + } + }, + "TableStatus": { + "target": "com.amazonaws.timestreamwrite#TableStatus", + "traits": { + "smithy.api#documentation": "

The current state of the table:

\n
    \n
  • \n

    \n DELETING - The table is being deleted.

    \n
  • \n
  • \n

    \n ACTIVE - The table is ready for use.

    \n
  • \n
" + } + }, + "RetentionProperties": { + "target": "com.amazonaws.timestreamwrite#RetentionProperties", + "traits": { + "smithy.api#documentation": "

The retention duration for the memory store and magnetic store.

" + } + }, + "CreationTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream table was created.

" + } + }, + "LastUpdatedTime": { + "target": "com.amazonaws.timestreamwrite#Date", + "traits": { + "smithy.api#documentation": "

The time when the Timestream table was last updated.

" + } + }, + "MagneticStoreWriteProperties": { + "target": "com.amazonaws.timestreamwrite#MagneticStoreWriteProperties", + "traits": { + "smithy.api#documentation": "

Contains properties to set on the table when enabling magnetic store writes.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Represents a database table in Timestream. Tables contain one or more related\n time series. You can modify the retention duration of the memory store and the magnetic\n store for a table.

" + } + }, + "com.amazonaws.timestreamwrite#TableList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Table" + } + }, + "com.amazonaws.timestreamwrite#TableStatus": { + "type": "enum", + "members": { + "ACTIVE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ACTIVE" + } + }, + "DELETING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DELETING" + } + }, + "RESTORING": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RESTORING" + } + } + } + }, + "com.amazonaws.timestreamwrite#Tag": { + "type": "structure", + "members": { + "Key": { + "target": "com.amazonaws.timestreamwrite#TagKey", + "traits": { + "smithy.api#documentation": "

The key of the tag. Tag keys are case sensitive.

", + "smithy.api#required": {} + } + }, + "Value": { + "target": "com.amazonaws.timestreamwrite#TagValue", + "traits": { + "smithy.api#documentation": "

The value of the tag. Tag values are case-sensitive and can be null.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

A tag is a label that you assign to a Timestream database and/or table. Each\n tag consists of a key and an optional value, both of which you define. With tags, you can\n categorize databases and/or tables, for example, by purpose, owner, or environment.

" + } + }, + "com.amazonaws.timestreamwrite#TagKey": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 1, + "max": 128 + } + } + }, + "com.amazonaws.timestreamwrite#TagKeyList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#TagKey" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 200 + } + } + }, + "com.amazonaws.timestreamwrite#TagList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#Tag" + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 200 + } + } + }, + "com.amazonaws.timestreamwrite#TagResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#TagResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#TagResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Associates a set of tags with a Timestream resource. You can then activate\n these user-defined tags so that they appear on the Billing and Cost Management console for\n cost allocation tracking.

" + } + }, + "com.amazonaws.timestreamwrite#TagResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamwrite#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

Identifies the Timestream resource to which tags should be added. This value\n is an Amazon Resource Name (ARN).

", + "smithy.api#required": {} + } + }, + "Tags": { + "target": "com.amazonaws.timestreamwrite#TagList", + "traits": { + "smithy.api#documentation": "

The tags to be assigned to the Timestream resource.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#TagResourceResponse": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#TagValue": { + "type": "string", + "traits": { + "smithy.api#length": { + "min": 0, + "max": 256 + } + } + }, + "com.amazonaws.timestreamwrite#ThrottlingException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

Too many requests were made by a user and they exceeded the service quotas. The request\n was throttled.

", + "smithy.api#error": "client", + "smithy.api#httpError": 429 + } + }, + "com.amazonaws.timestreamwrite#TimeUnit": { + "type": "enum", + "members": { + "MILLISECONDS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MILLISECONDS" + } + }, + "SECONDS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "SECONDS" + } + }, + "MICROSECONDS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MICROSECONDS" + } + }, + "NANOSECONDS": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "NANOSECONDS" + } + } + } + }, + "com.amazonaws.timestreamwrite#Timestream_20181101": { + "type": "service", + "version": "2018-11-01", + "operations": [ + { + "target": "com.amazonaws.timestreamwrite#CreateBatchLoadTask" + }, + { + "target": "com.amazonaws.timestreamwrite#CreateDatabase" + }, + { + "target": "com.amazonaws.timestreamwrite#CreateTable" + }, + { + "target": "com.amazonaws.timestreamwrite#DeleteDatabase" + }, + { + "target": "com.amazonaws.timestreamwrite#DeleteTable" + }, + { + "target": "com.amazonaws.timestreamwrite#DescribeBatchLoadTask" + }, + { + "target": "com.amazonaws.timestreamwrite#DescribeDatabase" + }, + { + "target": "com.amazonaws.timestreamwrite#DescribeEndpoints" + }, + { + "target": "com.amazonaws.timestreamwrite#DescribeTable" + }, + { + "target": "com.amazonaws.timestreamwrite#ListBatchLoadTasks" + }, + { + "target": "com.amazonaws.timestreamwrite#ListDatabases" + }, + { + "target": "com.amazonaws.timestreamwrite#ListTables" + }, + { + "target": "com.amazonaws.timestreamwrite#ListTagsForResource" + }, + { + "target": "com.amazonaws.timestreamwrite#ResumeBatchLoadTask" + }, + { + "target": "com.amazonaws.timestreamwrite#TagResource" + }, + { + "target": "com.amazonaws.timestreamwrite#UntagResource" + }, + { + "target": "com.amazonaws.timestreamwrite#UpdateDatabase" + }, + { + "target": "com.amazonaws.timestreamwrite#UpdateTable" + }, + { + "target": "com.amazonaws.timestreamwrite#WriteRecords" + } + ], + "traits": { + "aws.api#clientEndpointDiscovery": { + "operation": "com.amazonaws.timestreamwrite#DescribeEndpoints", + "error": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + "aws.api#service": { + "sdkId": "Timestream Write", + "arnNamespace": "timestream", + "cloudFormationName": "TimestreamWrite", + "cloudTrailEventSource": "timestreamwrite.amazonaws.com", + "endpointPrefix": "ingest.timestream" + }, + "aws.auth#sigv4": { + "name": "timestream" + }, + "aws.protocols#awsJson1_0": {}, + "smithy.api#documentation": "Amazon Timestream Write\n

Amazon Timestream is a fast, scalable, fully managed time-series database service\n that makes it easy to store and analyze trillions of time-series data points per day. With\n Timestream, you can easily store and analyze IoT sensor data to derive insights\n from your IoT applications. You can analyze industrial telemetry to streamline equipment\n management and maintenance. You can also store and analyze log data and metrics to improve\n the performance and availability of your applications.

\n

Timestream is built from the ground up to effectively ingest, process, and\n store time-series data. It organizes data to optimize query processing. It automatically\n scales based on the volume of data ingested and on the query volume to ensure you receive\n optimal performance while inserting and querying data. As your data grows over time,\n Timestream’s adaptive query processing engine spans across storage tiers to\n provide fast analysis while reducing costs.

", + "smithy.api#title": "Amazon Timestream Write", + "smithy.rules#endpointRuleSet": { + "version": "1.0", + "parameters": { + "Region": { + "builtIn": "AWS::Region", + "required": false, + "documentation": "The AWS region used to dispatch the request.", + "type": "String" + }, + "UseDualStack": { + "builtIn": "AWS::UseDualStack", + "required": true, + "default": false, + "documentation": "When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.", + "type": "Boolean" + }, + "UseFIPS": { + "builtIn": "AWS::UseFIPS", + "required": true, + "default": false, + "documentation": "When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.", + "type": "Boolean" + }, + "Endpoint": { + "builtIn": "SDK::Endpoint", + "required": false, + "documentation": "Override the endpoint used to send this request", + "type": "String" + } + }, + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Endpoint" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "error": "Invalid Configuration: FIPS and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported", + "type": "error" + }, + { + "conditions": [], + "endpoint": { + "url": { + "ref": "Endpoint" + }, + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "isSet", + "argv": [ + { + "ref": "Region" + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "aws.partition", + "argv": [ + { + "ref": "Region" + } + ], + "assign": "PartitionResult" + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + }, + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + }, + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://ingest.timestream-fips.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS and DualStack are enabled, but this partition does not support one or both", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseFIPS" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsFIPS" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://ingest.timestream-fips.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "FIPS is enabled but this partition does not support FIPS", + "type": "error" + } + ] + }, + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + { + "ref": "UseDualStack" + }, + true + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [ + { + "fn": "booleanEquals", + "argv": [ + true, + { + "fn": "getAttr", + "argv": [ + { + "ref": "PartitionResult" + }, + "supportsDualStack" + ] + } + ] + } + ], + "type": "tree", + "rules": [ + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://ingest.timestream.{Region}.{PartitionResult#dualStackDnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + }, + { + "conditions": [], + "error": "DualStack is enabled but this partition does not support DualStack", + "type": "error" + } + ] + }, + { + "conditions": [], + "type": "tree", + "rules": [ + { + "conditions": [], + "endpoint": { + "url": "https://ingest.timestream.{Region}.{PartitionResult#dnsSuffix}", + "properties": {}, + "headers": {} + }, + "type": "endpoint" + } + ] + } + ] + } + ] + }, + { + "conditions": [], + "error": "Invalid Configuration: Missing Region", + "type": "error" + } + ] + } + ] + }, + "smithy.rules#endpointTests": { + "testCases": [ + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-east-1.api.aws" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": true, + "Region": "us-east-1" + } + }, + { + "documentation": "For region us-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-east-1.amazonaws.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-east-1" + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-east-1.api.aws" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-east-1" + } + }, + { + "documentation": "For region us-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-east-1.amazonaws.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-east-1" + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": true, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region cn-north-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.cn-north-1.api.amazonwebservices.com.cn" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region cn-north-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.cn-north-1.amazonaws.com.cn" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "cn-north-1" + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-gov-east-1.api.aws" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": true, + "Region": "us-gov-east-1" + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-gov-east-1" + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-gov-east-1.api.aws" + } + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-gov-east-1" + } + }, + { + "documentation": "For region us-gov-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-gov-east-1.amazonaws.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-gov-east-1" + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-iso-east-1" + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-iso-east-1.c2s.ic.gov" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-iso-east-1" + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream-fips.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-isob-east-1" + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack disabled", + "expect": { + "endpoint": { + "url": "https://ingest.timestream.us-isob-east-1.sc2s.sgov.gov" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-isob-east-1" + } + }, + { + "documentation": "For custom endpoint with region set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Region": "us-east-1", + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with region not set and fips disabled and dualstack disabled", + "expect": { + "endpoint": { + "url": "https://example.com" + } + }, + "params": { + "UseDualStack": false, + "UseFIPS": false, + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips enabled and dualstack disabled", + "expect": { + "error": "Invalid Configuration: FIPS and custom endpoint are not supported" + }, + "params": { + "UseDualStack": false, + "UseFIPS": true, + "Region": "us-east-1", + "Endpoint": "https://example.com" + } + }, + { + "documentation": "For custom endpoint with fips disabled and dualstack enabled", + "expect": { + "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" + }, + "params": { + "UseDualStack": true, + "UseFIPS": false, + "Region": "us-east-1", + "Endpoint": "https://example.com" + } + } + ], + "version": "1.0" + } + } + }, + "com.amazonaws.timestreamwrite#UntagResource": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#UntagResourceRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#UntagResourceResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Removes the association of tags from a Timestream resource.

" + } + }, + "com.amazonaws.timestreamwrite#UntagResourceRequest": { + "type": "structure", + "members": { + "ResourceARN": { + "target": "com.amazonaws.timestreamwrite#AmazonResourceName", + "traits": { + "smithy.api#documentation": "

The Timestream resource that the tags will be removed from. This value is an\n Amazon Resource Name (ARN).

", + "smithy.api#required": {} + } + }, + "TagKeys": { + "target": "com.amazonaws.timestreamwrite#TagKeyList", + "traits": { + "smithy.api#documentation": "

A list of tags keys. Existing tags of the resource whose keys are members of this list\n will be removed from the Timestream resource.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#UntagResourceResponse": { + "type": "structure", + "members": {}, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#UpdateDatabase": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#UpdateDatabaseRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#UpdateDatabaseResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ServiceQuotaExceededException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Modifies the KMS key for an existing database. While updating the\n database, you must specify the database name and the identifier of the new KMS key to be used (KmsKeyId). If there are any concurrent\n UpdateDatabase requests, first writer wins.

\n

See code sample\n for details.

" + } + }, + "com.amazonaws.timestreamwrite#UpdateDatabaseRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the database.

", + "smithy.api#required": {} + } + }, + "KmsKeyId": { + "target": "com.amazonaws.timestreamwrite#StringValue2048", + "traits": { + "smithy.api#documentation": "

The identifier of the new KMS key (KmsKeyId) to be used to\n encrypt the data stored in the database. If the KmsKeyId currently registered\n with the database is the same as the KmsKeyId in the request, there will not\n be any update.

\n

You can specify the KmsKeyId using any of the following:

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN:\n arn:aws:kms:us-east-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Alias name: alias/ExampleAlias\n

    \n
  • \n
  • \n

    Alias ARN:\n arn:aws:kms:us-east-1:111122223333:alias/ExampleAlias\n

    \n
  • \n
", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#UpdateDatabaseResponse": { + "type": "structure", + "members": { + "Database": { + "target": "com.amazonaws.timestreamwrite#Database" + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#UpdateTable": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#UpdateTableRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#UpdateTableResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Modifies the retention duration of the memory store and magnetic store for your Timestream table. Note that the change in retention duration takes effect immediately.\n For example, if the retention period of the memory store was initially set to 2 hours and\n then changed to 24 hours, the memory store will be capable of holding 24 hours of data, but\n will be populated with 24 hours of data 22 hours after this change was made. Timestream does not retrieve data from the magnetic store to populate the memory store.

\n

See code\n sample for details.

" + } + }, + "com.amazonaws.timestreamwrite#UpdateTableRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

", + "smithy.api#required": {} + } + }, + "RetentionProperties": { + "target": "com.amazonaws.timestreamwrite#RetentionProperties", + "traits": { + "smithy.api#documentation": "

The retention duration of the memory store and the magnetic store.

" + } + }, + "MagneticStoreWriteProperties": { + "target": "com.amazonaws.timestreamwrite#MagneticStoreWriteProperties", + "traits": { + "smithy.api#documentation": "

Contains properties to set on the table when enabling magnetic store writes.

" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#UpdateTableResponse": { + "type": "structure", + "members": { + "Table": { + "target": "com.amazonaws.timestreamwrite#Table", + "traits": { + "smithy.api#documentation": "

The updated Timestream table.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.timestreamwrite#ValidationException": { + "type": "structure", + "members": { + "Message": { + "target": "com.amazonaws.timestreamwrite#ErrorMessage", + "traits": { + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#documentation": "

An invalid or malformed request.

", + "smithy.api#error": "client", + "smithy.api#httpError": 400 + } + }, + "com.amazonaws.timestreamwrite#WriteRecords": { + "type": "operation", + "input": { + "target": "com.amazonaws.timestreamwrite#WriteRecordsRequest" + }, + "output": { + "target": "com.amazonaws.timestreamwrite#WriteRecordsResponse" + }, + "errors": [ + { + "target": "com.amazonaws.timestreamwrite#AccessDeniedException" + }, + { + "target": "com.amazonaws.timestreamwrite#InternalServerException" + }, + { + "target": "com.amazonaws.timestreamwrite#InvalidEndpointException" + }, + { + "target": "com.amazonaws.timestreamwrite#RejectedRecordsException" + }, + { + "target": "com.amazonaws.timestreamwrite#ResourceNotFoundException" + }, + { + "target": "com.amazonaws.timestreamwrite#ThrottlingException" + }, + { + "target": "com.amazonaws.timestreamwrite#ValidationException" + } + ], + "traits": { + "aws.api#clientDiscoveredEndpoint": { + "required": true + }, + "smithy.api#documentation": "

Enables you to write your time-series data into Timestream. You can specify a\n single data point or a batch of data points to be inserted into the system. Timestream offers you a flexible schema that auto detects the column names and data\n types for your Timestream tables based on the dimension names and data types of\n the data points you specify when invoking writes into the database.

\n

Timestream supports eventual consistency read semantics. This means that when\n you query data immediately after writing a batch of data into Timestream, the\n query results might not reflect the results of a recently completed write operation. The\n results may also include some stale data. If you repeat the query request after a short\n time, the results should return the latest data. Service quotas apply.

\n

See code sample for\n details.

\n

\n Upserts\n

\n

You can use the Version parameter in a WriteRecords request to\n update data points. Timestream tracks a version number with each record.\n Version defaults to 1 when it's not specified for the record\n in the request. Timestream updates an existing record’s measure value along with\n its Version when it receives a write request with a higher\n Version number for that record. When it receives an update request where\n the measure value is the same as that of the existing record, Timestream still\n updates Version, if it is greater than the existing value of\n Version. You can update a data point as many times as desired, as long as\n the value of Version continuously increases.

\n

For example, suppose you write a new record without indicating Version in\n the request. Timestream stores this record, and set Version to\n 1. Now, suppose you try to update this record with a\n WriteRecords request of the same record with a different measure value but,\n like before, do not provide Version. In this case, Timestream will\n reject this update with a RejectedRecordsException since the updated record’s\n version is not greater than the existing value of Version.

\n

However, if you were to resend the update request with Version set to\n 2, Timestream would then succeed in updating the record’s value,\n and the Version would be set to 2. Next, suppose you sent a\n WriteRecords request with this same record and an identical measure value,\n but with Version set to 3. In this case, Timestream\n would only update Version to 3. Any further updates would need to\n send a version number greater than 3, or the update requests would receive a\n RejectedRecordsException.

" + } + }, + "com.amazonaws.timestreamwrite#WriteRecordsRequest": { + "type": "structure", + "members": { + "DatabaseName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream database.

", + "smithy.api#required": {} + } + }, + "TableName": { + "target": "com.amazonaws.timestreamwrite#ResourceName", + "traits": { + "smithy.api#documentation": "

The name of the Timestream table.

", + "smithy.api#required": {} + } + }, + "CommonAttributes": { + "target": "com.amazonaws.timestreamwrite#Record", + "traits": { + "smithy.api#documentation": "

A record that contains the common measure, dimension, time, and version attributes\n shared across all the records in the request. The measure and dimension attributes\n specified will be merged with the measure and dimension attributes in the records object\n when the data is written into Timestream. Dimensions may not overlap, or a\n ValidationException will be thrown. In other words, a record must contain\n dimensions with unique names.

" + } + }, + "Records": { + "target": "com.amazonaws.timestreamwrite#Records", + "traits": { + "smithy.api#documentation": "

An array of records that contain the unique measure, dimension, time, and version\n attributes for each time-series data point.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.timestreamwrite#WriteRecordsResponse": { + "type": "structure", + "members": { + "RecordsIngested": { + "target": "com.amazonaws.timestreamwrite#RecordsIngested", + "traits": { + "smithy.api#documentation": "

Information on the records ingested by this request.

" + } + } + }, + "traits": { + "smithy.api#output": {} + } + } + } +} diff --git a/aws/sdk/gradle.properties b/aws/sdk/gradle.properties index 1cd163ac60..3bd3028606 100644 --- a/aws/sdk/gradle.properties +++ b/aws/sdk/gradle.properties @@ -3,8 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 # -# timestream requires endpoint discovery: https://github.com/awslabs/aws-sdk-rust/issues/114 -aws.services=-timestreamwrite,-timestreamquery +aws.services= # List of services to generate Event Stream operations for: aws.services.eventstream.allowlist=\ diff --git a/aws/sdk/integration-tests/Cargo.toml b/aws/sdk/integration-tests/Cargo.toml index 74a6f261dc..284bc1bcb1 100644 --- a/aws/sdk/integration-tests/Cargo.toml +++ b/aws/sdk/integration-tests/Cargo.toml @@ -15,5 +15,6 @@ members = [ "s3control", "sts", "transcribestreaming", + "timestreamquery", "webassembly", ] diff --git a/aws/sdk/integration-tests/timestreamquery/Cargo.toml b/aws/sdk/integration-tests/timestreamquery/Cargo.toml new file mode 100644 index 0000000000..b81b618c88 --- /dev/null +++ b/aws/sdk/integration-tests/timestreamquery/Cargo.toml @@ -0,0 +1,20 @@ +# This Cargo.toml is unused in generated code. It exists solely to enable these tests to compile in-situ +[package] +name = "timestream-tests" +version = "0.1.0" +authors = ["Russell Cohen "] +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[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" } +tokio = { version = "1.23.1", features = ["full", "test-util"] } +aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util"] } +aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] } +aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } +tracing-subscriber = "0.3.17" diff --git a/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs new file mode 100644 index 0000000000..a045840907 --- /dev/null +++ b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs @@ -0,0 +1,76 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_credential_types::provider::SharedCredentialsProvider; +use aws_sdk_timestreamquery as query; +use aws_sdk_timestreamquery::config::Credentials; +use aws_smithy_async::test_util::controlled_time_and_sleep; +use aws_smithy_async::time::{SharedTimeSource, TimeSource}; +use aws_smithy_client::dvr::{MediaType, ReplayingConnection}; +use aws_types::region::Region; +use aws_types::SdkConfig; +use std::sync::Arc; +use std::time::{Duration, UNIX_EPOCH}; + +#[tokio::test] +async fn do_endpoint_discovery() { + tracing_subscriber::fmt::init(); + let conn = ReplayingConnection::from_file("tests/traffic.json").unwrap(); + //let conn = aws_smithy_client::dvr::RecordingConnection::new(conn); + let start = UNIX_EPOCH + Duration::from_secs(1234567890); + let (ts, sleep, mut gate) = controlled_time_and_sleep(start); + let config = SdkConfig::builder() + .http_connector(conn.clone()) + .region(Region::from_static("us-west-2")) + .sleep_impl(Arc::new(sleep)) + .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) + .time_source(SharedTimeSource::new(ts.clone())) + .build(); + let conf = query::config::Builder::from(&config) + .make_token("0000-0000-0000") + .build(); + let (client, reloader) = query::Client::from_conf(conf) + .enable_endpoint_discovery() + .await + .expect("initial setup of endpoint discovery failed"); + + tokio::spawn(reloader.reload_task()); + + let _resp = client + .query() + .query_string("SELECT now() as time_now") + .send() + .await + .unwrap(); + + // wait 10 minutes for the endpoint to expire + while ts.now() < start + Duration::from_secs(60 * 10) { + assert_eq!( + gate.expect_sleep().await.duration(), + Duration::from_secs(60) + ); + } + + // the recording validates that this request hits another endpoint + let _resp = client + .query() + .query_string("SELECT now() as time_now") + .send() + .await + .unwrap(); + // if you want to update this test: + // conn.dump_to_file("tests/traffic.json").unwrap(); + conn.validate_body_and_headers( + Some(&[ + "x-amz-security-token", + "x-amz-date", + "content-type", + "x-amz-target", + ]), + MediaType::Json, + ) + .await + .unwrap(); +} diff --git a/aws/sdk/integration-tests/timestreamquery/tests/traffic.json b/aws/sdk/integration-tests/timestreamquery/tests/traffic.json new file mode 100644 index 0000000000..6b692ce1dc --- /dev/null +++ b/aws/sdk/integration-tests/timestreamquery/tests/traffic.json @@ -0,0 +1,407 @@ +{ + "events": [ + { + "connection_id": 0, + "action": { + "Request": { + "request": { + "uri": "https://query.timestream.us-west-2.amazonaws.com/", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head api/timestreamquery/0.0.0-local os/macos lang/rust/1.67.1" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20090213/us-west-2/timestream/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-security-token;x-amz-target;x-amz-user-agent, Signature=1b50e2545f06c8e1ca0e205c20f25a34b6aab82f3a47e4cc370e9a5fea01d08c" + ], + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "x-amz-target": [ + "Timestream_20181101.DescribeEndpoints" + ], + "user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head os/macos lang/rust/1.67.1" + ], + "x-amz-date": [ + "20090213T233130Z" + ] + }, + "method": "POST" + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "{}" + }, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 0, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "x-amzn-requestid": [ + "fcfdab03-2bb8-45e9-a284-b789cf7efb63" + ], + "content-type": [ + "application/x-amz-json-1.0" + ], + "date": [ + "Wed, 24 May 2023 15:51:07 GMT" + ], + "content-length": [ + "104" + ] + } + } + } + } + } + }, + { + "connection_id": 0, + "action": { + "Data": { + "data": { + "Utf8": "{\"Endpoints\":[{\"Address\":\"query-cell1.timestream.us-west-2.amazonaws.com\",\"CachePeriodInMinutes\":10}]}" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 0, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + }, + { + "connection_id": 1, + "action": { + "Request": { + "request": { + "uri": "https://query-cell1.timestream.us-west-2.amazonaws.com/", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "content-length": [ + "73" + ], + "x-amz-security-token": [ + "notarealsessiontoken" + ], + "user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head os/macos lang/rust/1.67.1" + ], + "x-amz-date": [ + "20090213T233130Z" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20090213/us-west-2/timestream/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-security-token;x-amz-target;x-amz-user-agent, Signature=8174b6ca0ece22834b562b60785f47ef354b2c1ddf7a541482f255006b5f98c2" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head api/timestreamquery/0.0.0-local os/macos lang/rust/1.67.1" + ], + "x-amz-target": [ + "Timestream_20181101.Query" + ] + }, + "method": "POST" + } + } + } + }, + { + "connection_id": 1, + "action": { + "Data": { + "data": { + "Utf8": "{\"QueryString\":\"SELECT now() as time_now\",\"ClientToken\":\"0000-0000-0000\"}" + }, + "direction": "Request" + } + } + }, + { + "connection_id": 1, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 1, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "date": [ + "Wed, 24 May 2023 15:51:08 GMT" + ], + "x-amzn-requestid": [ + "OFO47BQ6XAXGTIK3XH4S6GBFLQ" + ], + "content-length": [ + "318" + ] + } + } + } + } + } + }, + { + "connection_id": 1, + "action": { + "Data": { + "data": { + "Utf8": "{\"ColumnInfo\":[{\"Name\":\"time_now\",\"Type\":{\"ScalarType\":\"TIMESTAMP\"}}],\"QueryId\":\"AEDACANMQDLSTQR3SPDV6HEWYACX5IALTEWGK7JM3VSFQQ5J5F3HGKVEMNHBRHY\",\"QueryStatus\":{\"CumulativeBytesMetered\":10000000,\"CumulativeBytesScanned\":0,\"ProgressPercentage\":100.0},\"Rows\":[{\"Data\":[{\"ScalarValue\":\"2023-05-24 15:51:08.760000000\"}]}]}" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 1, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + }, + { + "connection_id": 2, + "action": { + "Request": { + "request": { + "uri": "https://query.timestream.us-west-2.amazonaws.com/", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head os/macos lang/rust/1.67.1" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head api/timestreamquery/0.0.0-local os/macos lang/rust/1.67.1" + ], + "x-amz-date": [ + "20090213T234030Z" + ], + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20090213/us-west-2/timestream/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-security-token;x-amz-target;x-amz-user-agent, Signature=32e468e574c514ba0fa4a0f0304cef82b72f82965b9e8792fd63f6efb67b297c" + ], + "x-amz-target": [ + "Timestream_20181101.DescribeEndpoints" + ], + "x-amz-security-token": [ + "notarealsessiontoken" + ] + }, + "method": "POST" + } + } + } + }, + { + "connection_id": 2, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "content-length": [ + "104" + ], + "date": [ + "Wed, 24 May 2023 15:51:07 GMT" + ], + "x-amzn-requestid": [ + "fcfdab03-2bb8-45e9-a284-b789cf7efb63" + ] + } + } + } + } + } + }, + { + "connection_id": 2, + "action": { + "Data": { + "data": { + "Utf8": "{}" + }, + "direction": "Request" + } + } + }, + { + "connection_id": 2, + "action": { + "Eof": { + "ok": true, + "direction": "Request" + } + } + }, + { + "connection_id": 2, + "action": { + "Data": { + "data": { + "Utf8": "{\"Endpoints\":[{\"Address\":\"query-cell2.timestream.us-west-2.amazonaws.com\",\"CachePeriodInMinutes\":10}]}" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 2, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + }, + { + "connection_id": 3, + "action": { + "Request": { + "request": { + "uri": "https://query-cell2.timestream.us-west-2.amazonaws.com/", + "headers": { + "authorization": [ + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20090213/us-west-2/timestream/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-security-token;x-amz-target;x-amz-user-agent, Signature=8b2abd7688aefb4004200e5fa087f6c154fde879f2df79d3f6c57934cdc8f62a" + ], + "x-amz-date": [ + "20090213T234130Z" + ], + "user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head os/macos lang/rust/1.67.1" + ], + "x-amz-target": [ + "Timestream_20181101.Query" + ], + "x-amz-user-agent": [ + "aws-sdk-rust/0.0.0-smithy-rs-head api/timestreamquery/0.0.0-local os/macos lang/rust/1.67.1" + ], + "content-type": [ + "application/x-amz-json-1.0" + ], + "content-length": [ + "73" + ], + "x-amz-security-token": [ + "notarealsessiontoken" + ] + }, + "method": "POST" + } + } + } + }, + { + "connection_id": 3, + "action": { + "Data": { + "data": { + "Utf8": "{\"QueryString\":\"SELECT now() as time_now\",\"ClientToken\":\"0000-0000-0000\"}" + }, + "direction": "Request" + } + } + }, + { + "connection_id": 3, + "action": { + "Response": { + "response": { + "Ok": { + "status": 200, + "version": "HTTP/1.1", + "headers": { + "content-type": [ + "application/x-amz-json-1.0" + ], + "date": [ + "Wed, 24 May 2023 15:51:08 GMT" + ], + "x-amzn-requestid": [ + "OFO47BQ6XAXGTIK3XH4S6GBFLQ" + ], + "content-length": [ + "318" + ] + } + } + } + } + } + }, + { + "connection_id": 3, + "action": { + "Data": { + "data": { + "Utf8": "{\"ColumnInfo\":[{\"Name\":\"time_now\",\"Type\":{\"ScalarType\":\"TIMESTAMP\"}}],\"QueryId\":\"AEDACANMQDLSTQR3SPDV6HEWYACX5IALTEWGK7JM3VSFQQ5J5F3HGKVEMNHBRHY\",\"QueryStatus\":{\"CumulativeBytesMetered\":10000000,\"CumulativeBytesScanned\":0,\"ProgressPercentage\":100.0},\"Rows\":[{\"Data\":[{\"ScalarValue\":\"2023-05-24 15:51:08.760000000\"}]}]}" + }, + "direction": "Response" + } + } + }, + { + "connection_id": 3, + "action": { + "Eof": { + "ok": true, + "direction": "Response" + } + } + } + ], + "docs": "todo docs", + "version": "V0" +} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml index d93edb0e84..67b619607c 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml +++ b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml @@ -12,6 +12,7 @@ aws-sdk-s3 = { path = "../../build/sdk/aws-sdk-s3", features = ["test-util"] } aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-runtime = { path = "../../../../rust-runtime/aws-smithy-runtime" } aws-smithy-runtime-api = { path = "../../../../rust-runtime/aws-smithy-runtime-api" } +aws-smithy-async = { path = "../../../../rust-runtime/aws-smithy-async", features = ["test-util"]} aws-types = { path = "../../../rust-runtime/aws-types" } criterion = { version = "0.4", features = ["async_tokio"] } http = "0.2.3" diff --git a/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt b/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt index a1cbe0dc99..c1f76be0c0 100644 --- a/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt +++ b/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt @@ -207,7 +207,7 @@ fun parseMembership(rawList: String): Membership { val inclusions = mutableSetOf() val exclusions = mutableSetOf() - rawList.split(",").map { it.trim() }.forEach { item -> + rawList.split(",").map { it.trim() }.filter { it.isNotEmpty() }.forEach { item -> when { item.startsWith('-') -> exclusions.add(item.substring(1)) item.startsWith('+') -> inclusions.add(item.substring(1)) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt index ed283bc6de..df909e9452 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt @@ -50,6 +50,8 @@ class Types(runtimeConfig: RuntimeConfig) { val resolveEndpoint = smithyHttpEndpointModule.resolve("ResolveEndpoint") val smithyEndpoint = smithyTypesEndpointModule.resolve("Endpoint") val resolveEndpointError = smithyHttpEndpointModule.resolve("ResolveEndpointError") + + fun toArray() = arrayOf("ResolveEndpointError" to resolveEndpointError, "Endpoint" to smithyEndpoint) } /** diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt index 007f5e24d3..a29d12f7b7 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt @@ -466,6 +466,7 @@ class RustWriter private constructor( val devDependenciesOnly: Boolean = false, ) : SymbolWriter(UseDeclarations(namespace)) { + companion object { fun root() = forModule(null) fun forModule(module: String?): RustWriter = if (module == null) { @@ -486,6 +487,7 @@ class RustWriter private constructor( debugMode = debugMode, devDependenciesOnly = true, ) + fileName == "package.json" -> rawWriter(fileName, debugMode = debugMode) fileName == "stubgen.sh" -> rawWriter(fileName, debugMode = debugMode) else -> RustWriter(fileName, namespace, debugMode = debugMode) @@ -515,6 +517,8 @@ class RustWriter private constructor( return super.write(content, *args) } + fun dirty() = super.toString().isNotBlank() || preamble.isNotEmpty() + /** Helper function to determine if a stack frame is relevant for debug purposes */ private fun StackTraceElement.isRelevant(): Boolean { if (this.className.contains("AbstractCodeWriter") || this.className.startsWith("java.lang")) { @@ -711,7 +715,8 @@ class RustWriter private constructor( override fun toString(): String { val contents = super.toString() val preheader = if (preamble.isNotEmpty()) { - val prewriter = RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) + val prewriter = + RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) preamble.forEach { it(prewriter) } prewriter.toString() } else { @@ -757,7 +762,8 @@ class RustWriter private constructor( @Suppress("UNCHECKED_CAST") val func = t as? Writable ?: throw CodegenException("RustWriteableInjector.apply choked on non-function t ($t)") - val innerWriter = RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) + val innerWriter = + RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) func(innerWriter) innerWriter.dependencies.forEach { addDependencyTestAware(it) } return innerWriter.toString().trimEnd() @@ -790,7 +796,8 @@ class RustWriter private constructor( @Suppress("UNCHECKED_CAST") val func = t as? Writable ?: throw CodegenException("Invalid function type (expected writable) ($t)") - val innerWriter = RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) + val innerWriter = + RustWriter(filename, namespace, printWarning = false, devDependenciesOnly = devDependenciesOnly) func(innerWriter) innerWriter.dependencies.forEach { addDependencyTestAware(it) } return innerWriter.toString().trimEnd() diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/Customization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/Customization.kt index d563349867..c174c81d95 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/Customization.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/Customization.kt @@ -60,3 +60,18 @@ fun RustWriter.writeCustomizations(customizations: List RustWriter.writeCustomizationsOrElse( + customizations: List>, + section: T, + orElse: Writable, +) { + val test = RustWriter.root() + test.writeCustomizations(customizations, section) + if (test.dirty()) { + writeCustomizations(customizations, section) + } else { + orElse(this) + } +} diff --git a/rust-runtime/aws-smithy-client/src/dvr/replay.rs b/rust-runtime/aws-smithy-client/src/dvr/replay.rs index fc5d867741..54065e5fc7 100644 --- a/rust-runtime/aws-smithy-client/src/dvr/replay.rs +++ b/rust-runtime/aws-smithy-client/src/dvr/replay.rs @@ -63,15 +63,7 @@ impl ReplayingConnection { /// Validate all headers and bodies pub async fn full_validate(self, media_type: MediaType) -> Result<(), Box> { - self.validate_base(None, |b1, b2| { - aws_smithy_protocol_test::validate_body( - b1, - std::str::from_utf8(b2).unwrap(), - media_type.clone(), - ) - .map_err(|e| Box::new(e) as _) - }) - .await + self.validate_body_and_headers(None, media_type).await } /// Validate actual requests against expected requests @@ -84,6 +76,25 @@ impl ReplayingConnection { .await } + /// Validate that the bodies match, using a given [`MediaType`] for comparison + /// + /// The specified headers are also validated + pub async fn validate_body_and_headers( + self, + checked_headers: Option<&[&str]>, + media_type: MediaType, + ) -> Result<(), Box> { + self.validate_base(checked_headers, |b1, b2| { + aws_smithy_protocol_test::validate_body( + b1, + std::str::from_utf8(b2).unwrap(), + media_type.clone(), + ) + .map_err(|e| Box::new(e) as _) + }) + .await + } + async fn validate_base( self, checked_headers: Option<&[&str]>, From 40612a9308b2c2e5d17277ee2e6569d944a0a0a3 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Wed, 31 May 2023 16:49:52 -0500 Subject: [PATCH 126/253] Move `config_bag` and `type_erasure` to `aws-smithy-types` (#2735) ## Motivation and Context This PR moves types and traits in those modules to the `aws-smithy-types` crate. ## Description This is to prepare for the upcoming refactoring where `SdkConfig` and service configs (and both of their builders) will be backed by `ConfigBag`. ## Testing - [x] Pass tests in CI ## Checklist - [x] ~~I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ --------- Co-authored-by: Yuki Saito Co-authored-by: John DiSanti --- .../src/apigateway_interceptors.rs | 2 +- .../src/glacier_interceptors.rs | 6 +- .../aws-runtime/src/auth/sigv4.rs | 2 +- aws/rust-runtime/aws-runtime/src/identity.rs | 2 +- .../aws-runtime/src/invocation_id.rs | 6 +- .../aws-runtime/src/recursion_detection.rs | 4 +- .../aws-runtime/src/request_info.rs | 6 +- .../aws-runtime/src/user_agent.rs | 6 +- .../AwsCustomizableOperationDecorator.kt | 2 +- .../rustsdk/RetryClassifierDecorator.kt | 2 +- .../integration-tests/aws-sdk-s3/Cargo.toml | 3 +- .../benches/middleware_vs_orchestrator.rs | 2 +- .../aws-sdk-s3/tests/interceptors.rs | 2 +- .../tests/request_information_headers.rs | 4 +- .../aws-sdk-s3/tests/util.rs | 2 +- .../InterceptorConfigCustomization.kt | 4 +- .../EndpointParamsInterceptorGenerator.kt | 3 +- .../OperationRuntimePluginGenerator.kt | 3 +- .../ServiceRuntimePluginGenerator.kt | 3 +- .../config/ServiceConfigGenerator.kt | 4 +- .../protocol/ClientProtocolGenerator.kt | 2 +- .../protocol/RequestSerializerGenerator.kt | 36 ++++---- .../protocol/ResponseDeserializerGenerator.kt | 2 +- .../aws-smithy-runtime-api/src/client/auth.rs | 4 +- .../src/client/identity.rs | 2 +- .../src/client/identity/http.rs | 2 +- .../src/client/interceptors.rs | 2 +- .../src/client/interceptors/context.rs | 6 +- .../src/client/orchestrator.rs | 4 +- .../src/client/orchestrator/error.rs | 2 +- .../src/client/retries.rs | 2 +- .../src/client/runtime_plugin.rs | 4 +- .../aws-smithy-runtime-api/src/lib.rs | 6 -- .../src/client/auth/http.rs | 2 +- .../src/client/identity/anonymous.rs | 2 +- .../src/client/interceptor.rs | 2 +- .../src/client/orchestrator.rs | 6 +- .../src/client/orchestrator/auth.rs | 4 +- .../src/client/orchestrator/endpoints.rs | 2 +- .../interceptors/request_attempts.rs | 2 +- .../interceptors/service_clock_skew.rs | 2 +- .../client/retries/strategy/fixed_delay.rs | 2 +- .../src/client/retries/strategy/never.rs | 2 +- .../client/runtime_plugin/anonymous_auth.rs | 2 +- .../src/client/test_util/deserializer.rs | 2 +- .../src/client/test_util/interceptor.rs | 4 +- .../src/client/test_util/serializer.rs | 2 +- .../aws-smithy-runtime/src/client/timeout.rs | 2 +- .../src/config_bag.rs | 88 ++++++++++++------- .../src/config_bag/typeid_map.rs | 0 rust-runtime/aws-smithy-types/src/lib.rs | 9 ++ .../src/type_erasure.rs | 16 ++-- 52 files changed, 163 insertions(+), 130 deletions(-) rename rust-runtime/{aws-smithy-runtime-api => aws-smithy-types}/src/config_bag.rs (86%) rename rust-runtime/{aws-smithy-runtime-api => aws-smithy-types}/src/config_bag/typeid_map.rs (100%) rename rust-runtime/{aws-smithy-runtime-api => aws-smithy-types}/src/type_erasure.rs (94%) diff --git a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs index b6e56890de..76953ac510 100644 --- a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs @@ -8,7 +8,7 @@ use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, BoxError, Interceptor, }; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use http::header::ACCEPT; use http::HeaderValue; diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs index 3dddba4381..de08e9f582 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -15,7 +15,7 @@ use aws_smithy_runtime_api::client::interceptors::{ Interceptor, }; use aws_smithy_runtime_api::client::orchestrator::LoadedRequestBody; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use bytes::Bytes; use http::header::{HeaderName, HeaderValue}; use http::Request; @@ -234,7 +234,7 @@ fn compute_hash_tree(mut hashes: Vec) -> Digest { mod account_id_autofill_tests { use super::*; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; - use aws_smithy_runtime_api::type_erasure::TypedBox; + use aws_smithy_types::type_erasure::TypedBox; #[test] fn autofill_account_id() { @@ -273,7 +273,7 @@ mod account_id_autofill_tests { mod api_version_tests { use super::*; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; - use aws_smithy_runtime_api::type_erasure::TypedBox; + use aws_smithy_types::type_erasure::TypedBox; #[test] fn api_version_interceptor() { diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs index f9453eaf71..1b4f85cb12 100644 --- a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -13,7 +13,7 @@ use aws_smithy_runtime_api::client::auth::{ }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpRequest}; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::Document; use aws_types::region::{Region, SigningRegion}; use aws_types::SigningService; diff --git a/aws/rust-runtime/aws-runtime/src/identity.rs b/aws/rust-runtime/aws-runtime/src/identity.rs index c120e4eb7f..ba3d5f9fb4 100644 --- a/aws/rust-runtime/aws-runtime/src/identity.rs +++ b/aws/rust-runtime/aws-runtime/src/identity.rs @@ -8,7 +8,7 @@ pub mod credentials { use aws_credential_types::cache::SharedCredentialsCache; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver}; use aws_smithy_runtime_api::client::orchestrator::{BoxError, Future}; - use aws_smithy_runtime_api::config_bag::ConfigBag; + use aws_smithy_types::config_bag::ConfigBag; /// Smithy identity resolver for AWS credentials. #[derive(Debug)] diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index b5234264eb..c92e1b9368 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -7,7 +7,7 @@ use aws_smithy_runtime_api::client::interceptors::error::BoxError; use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, Interceptor, }; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use http::{HeaderName, HeaderValue}; use uuid::Uuid; @@ -75,8 +75,8 @@ mod tests { use crate::invocation_id::InvocationIdInterceptor; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; - use aws_smithy_runtime_api::config_bag::ConfigBag; - use aws_smithy_runtime_api::type_erasure::TypedBox; + use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::type_erasure::TypedBox; use http::HeaderValue; fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a HeaderValue { diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs index 4c0d9f10b0..0aa4b72c0b 100644 --- a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -6,7 +6,7 @@ use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, BoxError, Interceptor, }; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use aws_types::os_shim_internal::Env; use http::HeaderValue; use percent_encoding::{percent_encode, CONTROLS}; @@ -75,7 +75,7 @@ mod tests { use aws_smithy_http::body::SdkBody; use aws_smithy_protocol_test::{assert_ok, validate_headers}; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; - use aws_smithy_runtime_api::type_erasure::TypedBox; + use aws_smithy_types::type_erasure::TypedBox; use aws_types::os_shim_internal::Env; use http::HeaderValue; use proptest::{prelude::*, proptest}; diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index 3896654ef6..f06f14ca58 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -7,7 +7,7 @@ use aws_smithy_runtime::client::orchestrator::interceptors::{RequestAttempts, Se use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, BoxError, Interceptor, }; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::date_time::Format; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; @@ -158,10 +158,10 @@ mod tests { use aws_smithy_http::body::SdkBody; use aws_smithy_runtime::client::orchestrator::interceptors::RequestAttempts; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; - use aws_smithy_runtime_api::config_bag::ConfigBag; - use aws_smithy_runtime_api::type_erasure::TypedBox; + use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; + use aws_smithy_types::type_erasure::TypedBox; use http::HeaderValue; use std::time::Duration; diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 326cc5ccae..829fa570cf 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -8,7 +8,7 @@ use aws_smithy_runtime_api::client::interceptors::error::BoxError; use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, Interceptor, }; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use aws_types::app_name::AppName; use aws_types::os_shim_internal::Env; use http::header::{InvalidHeaderValue, USER_AGENT}; @@ -110,9 +110,9 @@ mod tests { use super::*; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; - use aws_smithy_runtime_api::config_bag::ConfigBag; - use aws_smithy_runtime_api::type_erasure::TypedBox; + use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::error::display::DisplayErrorContext; + use aws_smithy_types::type_erasure::TypedBox; fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a str { context diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index 754ac52310..62d3f79368 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -22,7 +22,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : .resolve("user_agent::AwsUserAgent"), "BeforeTransmitInterceptorContextMut" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::interceptors::BeforeTransmitInterceptorContextMut"), - "ConfigBag" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("config_bag::ConfigBag"), + "ConfigBag" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag"), "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::orchestrator::ConfigBagAccessors"), "http" to CargoDependency.Http.toType(), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt index 78b288c92a..d7c2afd9d9 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt @@ -75,7 +75,7 @@ class OperationRetryClassifiersFeature( "OperationError" to codegenContext.symbolProvider.symbolForOperationError(operationShape), "OrchestratorError" to smithyRuntimeApi.resolve("client::orchestrator::OrchestratorError"), "SdkError" to RuntimeType.smithyHttp(runtimeConfig).resolve("result::SdkError"), - "ErasedError" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypeErasedError"), + "ErasedError" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypeErasedError"), ) override fun section(section: OperationRuntimePluginSection) = when (section) { diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml index 67b619607c..d8057c6de7 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml +++ b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml @@ -9,10 +9,11 @@ publish = false aws-http = { path = "../../../rust-runtime/aws-http" } aws-runtime = { path = "../../../rust-runtime/aws-runtime" } aws-sdk-s3 = { path = "../../build/sdk/aws-sdk-s3", features = ["test-util"] } +aws-smithy-async = { path = "../../../../rust-runtime/aws-smithy-async", features = ["test-util"]} aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-runtime = { path = "../../../../rust-runtime/aws-smithy-runtime" } aws-smithy-runtime-api = { path = "../../../../rust-runtime/aws-smithy-runtime-api" } -aws-smithy-async = { path = "../../../../rust-runtime/aws-smithy-async", features = ["test-util"]} +aws-smithy-types = { path = "../../../../rust-runtime/aws-smithy-types" } aws-types = { path = "../../../rust-runtime/aws-types" } criterion = { version = "0.4", features = ["async_tokio"] } http = "0.2.3" diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs index 7c875e18ab..23af022b79 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs @@ -8,7 +8,7 @@ extern crate criterion; use aws_sdk_s3 as s3; use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use criterion::{BenchmarkId, Criterion}; macro_rules! test_connection { diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs index b69d67b3f3..c65605c8eb 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs @@ -16,7 +16,7 @@ use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, Interceptor, }; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use http::header::USER_AGENT; use http::HeaderValue; use std::time::{Duration, SystemTime, UNIX_EPOCH}; diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs index afaf9d2718..43e62b4d42 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs @@ -13,9 +13,9 @@ use aws_smithy_client::dvr::MediaType; use aws_smithy_client::erase::DynConnector; use aws_smithy_runtime::client::retries::strategy::FixedDelayRetryStrategy; use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::client::orchestrator::ConfigBagAccessors; +use aws_smithy_types::config_bag::ConfigBag; use std::time::{Duration, UNIX_EPOCH}; #[derive(Debug)] diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs index 4c9864ae84..992f9a748e 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs @@ -11,7 +11,7 @@ use aws_smithy_runtime_api::client::interceptors::{ }; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use http::header::USER_AGENT; use http::{HeaderName, HeaderValue}; use std::time::SystemTime; diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt index 664880b96b..967bea65ac 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt @@ -71,7 +71,7 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus /// ## fn example() { /// use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; /// use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; - /// use aws_smithy_runtime_api::config_bag::ConfigBag; + /// use aws_smithy_types::config_bag::ConfigBag; /// use $moduleUseName::config::Config; /// /// fn base_url() -> String { @@ -122,7 +122,7 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus /// ## fn example() { /// use aws_smithy_runtime_api::client::interceptors::context::phase::BeforeTransmit; /// use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext, SharedInterceptor}; - /// use aws_smithy_runtime_api::config_bag::ConfigBag; + /// use aws_smithy_types::config_bag::ConfigBag; /// use $moduleUseName::config::{Builder, Config}; /// /// fn base_url() -> String { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 04eb32ca5a..1dc2bb16dd 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -40,9 +40,10 @@ class EndpointParamsInterceptorGenerator( val runtimeApi = CargoDependency.smithyRuntimeApi(rc).toType() val interceptors = runtimeApi.resolve("client::interceptors") val orchestrator = runtimeApi.resolve("client::orchestrator") + val smithyTypes = CargoDependency.smithyTypes(rc).toType() arrayOf( "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), - "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), + "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), "ContextAttachedError" to interceptors.resolve("error::ContextAttachedError"), "EndpointResolverParams" to orchestrator.resolve("EndpointResolverParams"), "HttpRequest" to orchestrator.resolve("HttpRequest"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index ac07a1d4b7..419c15522b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -72,10 +72,11 @@ class OperationRuntimePluginGenerator( ) { private val codegenScope = codegenContext.runtimeConfig.let { rc -> val runtimeApi = RuntimeType.smithyRuntimeApi(rc) + val smithyTypes = RuntimeType.smithyTypes(rc) arrayOf( "AuthOptionResolverParams" to runtimeApi.resolve("client::auth::AuthOptionResolverParams"), "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), - "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), + "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index d903d527cd..2a4a053c24 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -66,12 +66,13 @@ class ServiceRuntimePluginGenerator( val client = RuntimeType.smithyClient(rc) val runtime = RuntimeType.smithyRuntime(rc) val runtimeApi = RuntimeType.smithyRuntimeApi(rc) + val smithyTypes = RuntimeType.smithyTypes(rc) arrayOf( *preludeScope, "Arc" to RuntimeType.Arc, "AnonymousIdentityResolver" to runtimeApi.resolve("client::identity::AnonymousIdentityResolver"), "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), - "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), + "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), "Connection" to runtimeApi.resolve("client::orchestrator::Connection"), "ConnectorSettings" to RuntimeType.smithyClient(rc).resolve("http_connector::ConnectorSettings"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 4af06de0bd..a43d4f9e10 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -308,16 +308,18 @@ class ServiceConfigGenerator(private val customizations: List Result<(), #{BoxError}> """, "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), - "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), + "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), ) { rust("// TODO(enableNewSmithyRuntime): Put into `cfg` the fields in `self.config_override` that are not `None`") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt index 1c8a69bece..731fa56f05 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt @@ -164,7 +164,7 @@ open class ClientProtocolGenerator( } """, *codegenScope, - "TypedBox" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypedBox"), + "TypedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypedBox"), "invoke" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke"), "setup_runtime_plugins" to setupRuntimePluginsFn, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt index 90482e8e9a..e9391e1d7a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt @@ -33,24 +33,24 @@ class RequestSerializerGenerator( private val httpBindingResolver = protocol.httpBindingResolver private val symbolProvider = codegenContext.symbolProvider private val codegenScope by lazy { - CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig).toType().let { runtimeApi -> - val interceptorContext = runtimeApi.resolve("client::interceptors::context") - val orchestrator = runtimeApi.resolve("client::orchestrator") - arrayOf( - "BoxError" to orchestrator.resolve("BoxError"), - "ConfigBag" to runtimeApi.resolve("config_bag::ConfigBag"), - "HttpRequest" to orchestrator.resolve("HttpRequest"), - "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, - "Input" to interceptorContext.resolve("Input"), - "RequestSerializer" to orchestrator.resolve("RequestSerializer"), - "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), - "TypedBox" to runtimeApi.resolve("type_erasure::TypedBox"), - "config" to ClientRustModule.Config, - "header_util" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("header"), - "http" to RuntimeType.Http, - "operation" to RuntimeType.operationModule(codegenContext.runtimeConfig), - ) - } + val runtimeApi = CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig).toType() + val interceptorContext = runtimeApi.resolve("client::interceptors::context") + val orchestrator = runtimeApi.resolve("client::orchestrator") + val smithyTypes = CargoDependency.smithyTypes(codegenContext.runtimeConfig).toType() + arrayOf( + "BoxError" to orchestrator.resolve("BoxError"), + "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), + "HttpRequest" to orchestrator.resolve("HttpRequest"), + "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, + "Input" to interceptorContext.resolve("Input"), + "RequestSerializer" to orchestrator.resolve("RequestSerializer"), + "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), + "TypedBox" to smithyTypes.resolve("type_erasure::TypedBox"), + "config" to ClientRustModule.Config, + "header_util" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("header"), + "http" to RuntimeType.Http, + "operation" to RuntimeType.operationModule(codegenContext.runtimeConfig), + ) } fun render(writer: RustWriter, operationShape: OperationShape) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index d66f02c3da..968e335916 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -45,7 +45,7 @@ class ResponseDeserializerGenerator( "ResponseDeserializer" to orchestrator.resolve("ResponseDeserializer"), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), "SdkError" to RuntimeType.sdkError(runtimeConfig), - "TypedBox" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("type_erasure::TypedBox"), + "TypedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypedBox"), "debug_span" to RuntimeType.Tracing.resolve("debug_span"), "type_erase_result" to typeEraseResult(), ) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index ab9ee61db3..08d2586171 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -5,8 +5,8 @@ use crate::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use crate::client::orchestrator::{BoxError, HttpRequest}; -use crate::config_bag::ConfigBag; -use crate::type_erasure::{TypeErasedBox, TypedBox}; +use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_types::Document; use std::borrow::Cow; use std::fmt; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index 368d51ffff..341567d191 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -5,7 +5,7 @@ use crate::client::auth::AuthSchemeId; use crate::client::orchestrator::Future; -use crate::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use std::any::Any; use std::fmt::Debug; use std::sync::Arc; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs index 5866d772f4..878943c545 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity/http.rs @@ -7,7 +7,7 @@ use crate::client::identity::{Identity, IdentityResolver}; use crate::client::orchestrator::Future; -use crate::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use std::fmt::Debug; use std::sync::Arc; use std::time::SystemTime; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index ec322badf7..1477aa4489 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -9,7 +9,7 @@ pub mod error; use crate::client::interceptors::context::wrappers::{ FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, }; -use crate::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::error::display::DisplayErrorContext; pub use context::{ wrappers::{ diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 82481f437d..488cd1d389 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -32,9 +32,9 @@ pub mod phase; pub mod wrappers; use crate::client::orchestrator::{HttpRequest, HttpResponse, OrchestratorError}; -use crate::config_bag::ConfigBag; -use crate::type_erasure::{TypeErasedBox, TypeErasedError}; use aws_smithy_http::result::SdkError; +use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; use phase::Phase; use std::fmt::Debug; use std::mem; @@ -345,8 +345,8 @@ fn try_clone(request: &HttpRequest) -> Option { #[cfg(test)] mod tests { use super::*; - use crate::type_erasure::TypedBox; use aws_smithy_http::body::SdkBody; + use aws_smithy_types::type_erasure::TypedBox; use http::header::{AUTHORIZATION, CONTENT_LENGTH}; use http::{HeaderValue, Uri}; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 97ff322145..4585da8f35 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -11,13 +11,13 @@ use crate::client::identity::IdentityResolvers; use crate::client::interceptors::context::{Error, Input, Output}; use crate::client::retries::RetryClassifiers; use crate::client::retries::RetryStrategy; -use crate::config_bag::ConfigBag; -use crate::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_http::body::SdkBody; +use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::endpoint::Endpoint; +use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use bytes::Bytes; use std::fmt; use std::future::Future as StdFuture; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs index 2cc44a2647..87a0ac2a64 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs @@ -7,8 +7,8 @@ use super::BoxError; use crate::client::interceptors::context::phase::Phase; use crate::client::interceptors::InterceptorError; use crate::client::orchestrator::HttpResponse; -use crate::type_erasure::TypeErasedError; use aws_smithy_http::result::{ConnectorError, SdkError}; +use aws_smithy_types::type_erasure::TypeErasedError; use std::fmt::Debug; #[derive(Debug)] diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index fa42811ee1..33327eb291 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -6,7 +6,7 @@ use crate::client::interceptors::context::Error; use crate::client::interceptors::InterceptorContext; use crate::client::orchestrator::{BoxError, OrchestratorError}; -use crate::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::retry::ErrorKind; use std::fmt::Debug; use std::time::Duration; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index 2e913ac524..3c6802d05a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -4,7 +4,7 @@ */ use crate::client::interceptors::InterceptorRegistrar; -use crate::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use std::fmt::Debug; pub type BoxError = Box; @@ -90,7 +90,7 @@ impl RuntimePlugins { mod tests { use super::{BoxError, RuntimePlugin, RuntimePlugins}; use crate::client::interceptors::InterceptorRegistrar; - use crate::config_bag::ConfigBag; + use aws_smithy_types::config_bag::ConfigBag; #[derive(Debug)] struct SomeStruct; diff --git a/rust-runtime/aws-smithy-runtime-api/src/lib.rs b/rust-runtime/aws-smithy-runtime-api/src/lib.rs index 94e0efc9f3..1668685235 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/lib.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/lib.rs @@ -15,9 +15,3 @@ /// Smithy runtime for client orchestration. pub mod client; - -/// A typemap for storing configuration. -pub mod config_bag; - -/// Utilities for type erasure. -pub mod type_erasure; diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs index eaf14dd04a..18f713ae13 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs @@ -14,8 +14,8 @@ use aws_smithy_runtime_api::client::auth::{ use aws_smithy_runtime_api::client::identity::http::{Login, Token}; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpRequest}; -use aws_smithy_runtime_api::config_bag::ConfigBag; use aws_smithy_types::base64::encode; +use aws_smithy_types::config_bag::ConfigBag; use http::header::HeaderName; use http::HeaderValue; diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs b/rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs index 93fb7ad2e4..020f12dcbd 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs @@ -5,7 +5,7 @@ use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver}; use aws_smithy_runtime_api::client::orchestrator::Future; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; #[derive(Debug, Default)] pub struct AnonymousIdentity; diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs index bbd2263ddf..2eb0278840 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs @@ -8,7 +8,7 @@ use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, BoxError, Interceptor, }; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use std::error::Error as StdError; use std::fmt; use std::marker::PhantomData; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 75bc21b508..af885ed290 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -17,7 +17,7 @@ use aws_smithy_runtime_api::client::orchestrator::{ }; use aws_smithy_runtime_api::client::retries::ShouldAttempt; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use std::mem; use tracing::{debug_span, Instrument}; @@ -264,8 +264,8 @@ mod tests { }; use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, OrchestratorError}; use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin, RuntimePlugins}; - use aws_smithy_runtime_api::config_bag::ConfigBag; - use aws_smithy_runtime_api::type_erasure::TypeErasedBox; + use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::type_erasure::TypeErasedBox; use http::StatusCode; use tracing_test::traced_test; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index a336e1436c..d5ea86b3dc 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -6,7 +6,7 @@ use aws_smithy_runtime_api::client::auth::{AuthSchemeEndpointConfig, AuthSchemeId}; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors}; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::endpoint::Endpoint; use aws_smithy_types::Document; use std::borrow::Cow; @@ -140,7 +140,7 @@ mod tests { use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; - use aws_smithy_runtime_api::type_erasure::TypedBox; + use aws_smithy_types::type_erasure::TypedBox; use std::collections::HashMap; #[tokio::test] diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 139bef24bb..7430519978 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -12,7 +12,7 @@ use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, EndpointResolver, EndpointResolverParams, HttpRequest, }; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::endpoint::Endpoint; use http::header::HeaderName; use http::{HeaderValue, Uri}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs index 6d48abee6d..c42971cb21 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs @@ -6,7 +6,7 @@ use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, BoxError, Interceptor, }; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; #[derive(Debug, Clone, Default)] #[non_exhaustive] diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs index eb988c8572..61bab15478 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs @@ -6,7 +6,7 @@ use aws_smithy_runtime_api::client::interceptors::{ BeforeDeserializationInterceptorContextMut, BoxError, Interceptor, }; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::date_time::Format; use aws_smithy_types::DateTime; use std::time::{Duration, SystemTime}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs index d3f4abe569..19f23a3d41 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -9,7 +9,7 @@ use aws_smithy_runtime_api::client::orchestrator::BoxError; use aws_smithy_runtime_api::client::retries::{ ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, ShouldAttempt, }; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use std::time::Duration; // A retry policy used in tests. This relies on an error classifier already present in the config bag. diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs index f415af3691..599b6ea5af 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs @@ -6,7 +6,7 @@ use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::BoxError; use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; #[derive(Debug, Clone, Default)] pub struct NeverRetryStrategy {} diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs index f641e48379..ad207dfad7 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs @@ -16,7 +16,7 @@ use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, Ident use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpRequest}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; const ANONYMOUS_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("anonymous"); diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs index 7df0e2fe78..292227acd0 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs @@ -9,7 +9,7 @@ use aws_smithy_runtime_api::client::orchestrator::{ ConfigBagAccessors, HttpResponse, OrchestratorError, ResponseDeserializer, }; use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin}; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use std::sync::Mutex; #[derive(Default, Debug)] diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs index 543777ba6a..d8d8c6ec32 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs @@ -8,7 +8,7 @@ use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, BoxError, Interceptor, }; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use std::fmt; pub struct TestParamsSetterInterceptor { @@ -48,7 +48,7 @@ mod tests { use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; - use aws_smithy_runtime_api::type_erasure::TypedBox; + use aws_smithy_types::type_erasure::TypedBox; use std::time::{Duration, UNIX_EPOCH}; #[test] diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs index 9895fd2ca5..9c27e07603 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -9,7 +9,7 @@ use aws_smithy_runtime_api::client::orchestrator::{ ConfigBagAccessors, HttpRequest, RequestSerializer, }; use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin}; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use std::sync::Mutex; #[derive(Default, Debug)] diff --git a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs index 66c43d8eb3..b54447194c 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs @@ -7,7 +7,7 @@ use aws_smithy_async::future::timeout::Timeout; use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; use aws_smithy_client::SdkError; use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpResponse}; -use aws_smithy_runtime_api::config_bag::ConfigBag; +use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::timeout::TimeoutConfig; use pin_project_lite::pin_project; use std::future::Future; diff --git a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs similarity index 86% rename from rust-runtime/aws-smithy-runtime-api/src/config_bag.rs rename to rust-runtime/aws-smithy-types/src/config_bag.rs index 47806267b2..0100a99f97 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -76,14 +76,17 @@ impl Default for Value { } } -pub struct Layer { +/// A named layer comprising a config bag +struct Layer { name: Cow<'static, str>, props: TypeIdMap, } /// Trait defining how types can be stored and loaded from the config bag pub trait Store: Sized + Send + Sync + 'static { + /// Denote the returned type when loaded from the config bag type ReturnedType<'a>: Send + Sync; + /// Denote the stored type when stored into the config bag type StoredType: Send + Sync + Debug; /// Create a returned type from an iterable of items @@ -93,12 +96,24 @@ pub trait Store: Sized + Send + Sync + 'static { /// Store an item in the config bag by replacing the existing value #[non_exhaustive] pub struct StoreReplace(PhantomData); +impl Debug for StoreReplace { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "StoreReplace") + } +} /// Store an item in the config bag by effectively appending it to a list #[non_exhaustive] pub struct StoreAppend(PhantomData); +impl Debug for StoreAppend { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "StoreAppend") + } +} +/// Trait that marks the implementing types as able to be stored in the config bag pub trait Storable: Send + Sync + Debug + 'static { + /// Specify how an item is stored in the config bag, e.g. [`StoreReplace`] and [`StoreAppend`] type Storer: Store; } @@ -131,6 +146,11 @@ pub struct AppendItemIter<'a, U> { inner: ItemIter<'a, StoreAppend>, cur: Option>>, } +impl<'a, U> Debug for AppendItemIter<'a, U> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "AppendItemIter") + } +} impl<'a, U: 'a> Iterator for AppendItemIter<'a, U> where @@ -172,25 +192,30 @@ impl Debug for Layer { } impl Layer { - pub fn put(&mut self, value: T::StoredType) -> &mut Self { + /// Inserts `value` into the layer + fn put(&mut self, value: T::StoredType) -> &mut Self { self.props .insert(TypeId::of::(), TypeErasedBox::new(value)); self } - pub fn get(&self) -> Option<&T::StoredType> { + /// Retrieves the value of type `T` from this layer if exists + fn get(&self) -> Option<&T::StoredType> { self.props .get(&TypeId::of::()) .map(|t| t.downcast_ref().expect("typechecked")) } - pub fn get_mut(&mut self) -> Option<&mut T::StoredType> { + /// Returns a mutable reference to `T` if it is stored in this layer + fn get_mut(&mut self) -> Option<&mut T::StoredType> { self.props .get_mut(&TypeId::of::()) .map(|t| t.downcast_mut().expect("typechecked")) } - pub fn get_mut_or_default(&mut self) -> &mut T::StoredType + /// Returns a mutable reference to `T` if it is stored in this layer, otherwise returns the + /// [`Default`] implementation of `T` + fn get_mut_or_default(&mut self) -> &mut T::StoredType where T::StoredType: Default, { @@ -200,23 +225,6 @@ impl Layer { .downcast_mut() .expect("typechecked") } - - pub fn is_empty(&self) -> bool { - self.props.is_empty() - } - - pub fn len(&self) -> usize { - self.props.len() - } -} - -pub trait Accessor { - type Setter: Setter; - fn config(&self) -> &ConfigBag; -} - -pub trait Setter { - fn config(&mut self) -> &mut ConfigBag; } fn no_op(_: &mut ConfigBag) {} @@ -236,7 +244,7 @@ impl FrozenConfigBag { /// /// # Examples /// ``` - /// use aws_smithy_runtime_api::config_bag::ConfigBag; + /// use aws_smithy_types::config_bag::ConfigBag; /// fn add_more_config(bag: &mut ConfigBag) { /* ... */ } /// let bag = ConfigBag::base().with_fn("first layer", |_| { /* add a property */ }); /// let mut bag = bag.add_layer("second layer"); @@ -267,6 +275,9 @@ impl FrozenConfigBag { } impl ConfigBag { + /// Creates a new config bag "base". Configuration may then be "layered" onto the base by calling + /// [`ConfigBag::store_put`], [`ConfigBag::store_or_unset`], [`ConfigBag::store_append`]. Layers + /// of configuration may then be "frozen" (made immutable) by calling [`ConfigBag::freeze`]. pub fn base() -> Self { ConfigBag { head: Layer { @@ -277,6 +288,7 @@ impl ConfigBag { } } + /// Stores `item` of type `T` into the config bag, overriding a previous value of the same type pub fn store_put(&mut self, item: T) -> &mut Self where T: Storable>, @@ -285,6 +297,8 @@ impl ConfigBag { self } + /// Stores `item` of type `T` into the config bag, overriding a previous value of the same type, + /// or unsets it by passing a `None` pub fn store_or_unset(&mut self, item: Option) -> &mut Self where T: Storable>, @@ -299,7 +313,7 @@ impl ConfigBag { /// This can only be used for types that use [`StoreAppend`] /// ``` - /// use aws_smithy_runtime_api::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; + /// use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; /// let mut bag = ConfigBag::base(); /// #[derive(Debug, PartialEq, Eq)] /// struct Interceptor(&'static str); @@ -326,6 +340,10 @@ impl ConfigBag { self } + /// Clears the value of type `T` from the config bag + /// + /// This internally marks the item of type `T` as cleared as opposed to wiping it out from the + /// config bag. pub fn clear(&mut self) where T: Storable>, @@ -334,6 +352,7 @@ impl ConfigBag { .put::>(Value::ExplicitlyUnset(type_name::())); } + /// Load a value (or values) of type `T` depending on how `T` implements [`Storable`] pub fn load(&self) -> ::ReturnedType<'_> { self.sourced_get::() } @@ -344,7 +363,7 @@ impl ConfigBag { out } - /// Returns a mutable reference to `T` if it is stored in the top layer of the bag + /// Return a mutable reference to `T` if it is stored in the top layer of the bag pub fn get_mut(&mut self) -> Option<&mut T> where T: Storable>, @@ -433,7 +452,7 @@ impl ConfigBag { /// /// Hint: If you want to re-use this layer, call `freeze` first. /// ``` - /// use aws_smithy_runtime_api::config_bag::ConfigBag; + /// use aws_smithy_types::config_bag::ConfigBag; /// let bag = ConfigBag::base(); /// let first_layer = bag.with_fn("a", |b: &mut ConfigBag| { b.put("a"); }).freeze(); /// let second_layer = first_layer.with_fn("other", |b: &mut ConfigBag| { b.put(1i32); }); @@ -449,10 +468,15 @@ impl ConfigBag { self.freeze().with_fn(name, next) } + /// Add a new layer with `name` after freezing the top layer so far pub fn add_layer(self, name: impl Into>) -> ConfigBag { self.freeze().add_layer(name) } + /// Return a value (or values) of type `T` depending on how it has been stored in a `ConfigBag` + /// + /// It flexibly chooses to return a single value vs. an iterator of values depending on how + /// `T` implements a [`Store`] trait. pub fn sourced_get(&self) -> T::ReturnedType<'_> { let stored_type_iter = ItemIter { inner: self.layers(), @@ -471,6 +495,11 @@ pub struct ItemIter<'a, T> { inner: BagIter<'a>, t: PhantomData, } +impl<'a, T> Debug for ItemIter<'a, T> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "ItemIter") + } +} impl<'a, T: 'a> Iterator for ItemIter<'a, T> where T: Store, @@ -508,13 +537,6 @@ impl From for FrozenConfigBag { } } -#[derive(Debug)] -pub enum SourceInfo { - Set { layer: &'static str, value: String }, - Unset { layer: &'static str }, - Inherit { layer: &'static str }, -} - #[cfg(test)] mod test { use super::ConfigBag; diff --git a/rust-runtime/aws-smithy-runtime-api/src/config_bag/typeid_map.rs b/rust-runtime/aws-smithy-types/src/config_bag/typeid_map.rs similarity index 100% rename from rust-runtime/aws-smithy-runtime-api/src/config_bag/typeid_map.rs rename to rust-runtime/aws-smithy-types/src/config_bag/typeid_map.rs diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index ffa1c86ea5..3df626f1f9 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -14,6 +14,10 @@ unreachable_pub )] pub mod base64; +//TODO(enableNewSmithyRuntime): Unhide this module when switching to the orchestrator +#[doc(hidden)] +/// A typemap for storing configuration. +pub mod config_bag; pub mod date_time; pub mod endpoint; pub mod error; @@ -21,6 +25,11 @@ pub mod primitive; pub mod retry; pub mod timeout; +//TODO(enableNewSmithyRuntime): Unhide this module when switching to the orchestrator +#[doc(hidden)] +/// Utilities for type erasure. +pub mod type_erasure; + mod blob; mod document; mod number; diff --git a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs b/rust-runtime/aws-smithy-types/src/type_erasure.rs similarity index 94% rename from rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs rename to rust-runtime/aws-smithy-types/src/type_erasure.rs index 5f0f626050..b27d9df347 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/type_erasure.rs +++ b/rust-runtime/aws-smithy-types/src/type_erasure.rs @@ -29,7 +29,7 @@ impl TypedBox where T: fmt::Debug + Send + Sync + 'static, { - // Creates a new `TypedBox`. + /// Creates a new `TypedBox` from `inner` of type `T` pub fn new(inner: T) -> Self { Self { inner: TypeErasedBox::new(inner), @@ -37,10 +37,10 @@ where } } - // Tries to create a `TypedBox` from a `TypeErasedBox`. - // - // If the `TypedBox` can't be created due to the `TypeErasedBox`'s value consisting - // of another type, then the original `TypeErasedBox` will be returned in the `Err` variant. + /// Tries to create a `TypedBox` from a `TypeErasedBox`. + /// + /// If the `TypedBox` can't be created due to the `TypeErasedBox`'s value consisting + /// of another type, then the original `TypeErasedBox` will be returned in the `Err` variant. pub fn assume_from(type_erased: TypeErasedBox) -> Result, TypeErasedBox> { if type_erased.downcast_ref::().is_some() { Ok(TypedBox { @@ -114,6 +114,7 @@ impl fmt::Debug for TypeErasedBox { } impl TypeErasedBox { + /// Create a new `TypeErasedBox` from `value` of type `T` pub fn new(value: T) -> Self { let debug = |value: &Box, f: &mut fmt::Formatter<'_>| { fmt::Debug::fmt(value.downcast_ref::().expect("typechecked"), f) @@ -124,7 +125,7 @@ impl TypeErasedBox { } } - // Downcast into a `Box`, or return `Self` if it is not a `T`. + /// Downcast into a `Box`, or return `Self` if it is not a `T`. pub fn downcast(self) -> Result, Self> { let TypeErasedBox { field, debug } = self; field.downcast().map_err(|field| Self { field, debug }) @@ -181,6 +182,7 @@ impl StdError for TypeErasedError { } impl TypeErasedError { + /// Create a new `TypeErasedError` from `value` of type `T` pub fn new(value: T) -> Self { let debug = |value: &Box, f: &mut fmt::Formatter<'_>| { fmt::Debug::fmt(value.downcast_ref::().expect("typechecked"), f) @@ -194,7 +196,7 @@ impl TypeErasedError { } } - // Downcast into a `Box`, or return `Self` if it is not a `T`. + /// Downcast into a `Box`, or return `Self` if it is not a `T`. pub fn downcast( self, ) -> Result, Self> { From 0cba3d892ae5a0b438a8ace20fa545d0b1cff549 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Thu, 1 Jun 2023 10:59:49 -0500 Subject: [PATCH 127/253] Update retry classifiers to act on InterceptorContext instead of OrchestratorError (#2737) ## Description - update retry classifiers to act directly on the `InterceptorContext` - add codegen method for inserting retry classifiers into the config bag - update `InterceptorContext` to return options instead of panicking ## Testing Tests were updated as needed. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-runtime/src/invocation_id.rs | 7 +- .../aws-runtime/src/recursion_detection.rs | 2 +- .../aws-runtime/src/request_info.rs | 1 + .../aws-runtime/src/retries/classifier.rs | 134 +++++++++---- .../aws-runtime/src/user_agent.rs | 1 + .../rustsdk/RetryClassifierDecorator.kt | 137 ++----------- .../kms/tests/retryable_errors.rs | 20 +- .../customizations/HttpAuthDecorator.kt | 2 + .../ServiceRuntimePluginGenerator.kt | 18 ++ .../aws-smithy-runtime-api/Cargo.toml | 1 + .../src/client/interceptors/context.rs | 76 ++++---- .../client/interceptors/context/wrappers.rs | 4 +- .../src/client/orchestrator/error.rs | 9 + .../src/client/retries.rs | 63 +++++- .../src/client/orchestrator.rs | 9 +- .../src/client/orchestrator/auth.rs | 22 ++- .../src/client/orchestrator/endpoints.rs | 4 +- .../src/client/retries/classifier.rs | 184 ++++++++++++------ .../client/retries/strategy/fixed_delay.rs | 19 +- 19 files changed, 427 insertions(+), 286 deletions(-) diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index c92e1b9368..899f76d753 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -80,7 +80,12 @@ mod tests { use http::HeaderValue; fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a HeaderValue { - context.request().headers().get(header_name).unwrap() + context + .request() + .expect("request is set") + .headers() + .get(header_name) + .unwrap() } #[test] diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs index 0aa4b72c0b..9437dce76b 100644 --- a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -159,7 +159,7 @@ mod tests { RecursionDetectionInterceptor { env } .modify_before_signing(&mut ctx, &mut config) .expect("interceptor must succeed"); - let mutated_request = context.request(); + let mutated_request = context.request().expect("request is set"); for name in mutated_request.headers().keys() { assert_eq!( mutated_request.headers().get_all(name).iter().count(), diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index f06f14ca58..140ac28aea 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -168,6 +168,7 @@ mod tests { fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a str { context .request() + .expect("request is set") .headers() .get(header_name) .unwrap() diff --git a/aws/rust-runtime/aws-runtime/src/retries/classifier.rs b/aws/rust-runtime/aws-runtime/src/retries/classifier.rs index 7e9c10d59b..0cbeb902a0 100644 --- a/aws/rust-runtime/aws-runtime/src/retries/classifier.rs +++ b/aws/rust-runtime/aws-runtime/src/retries/classifier.rs @@ -4,10 +4,13 @@ */ use aws_smithy_http::http::HttpHeaders; -use aws_smithy_http::result::SdkError; -use aws_smithy_runtime_api::client::retries::RetryReason; +use aws_smithy_runtime_api::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; +use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::error::metadata::ProvideErrorMetadata; use aws_smithy_types::retry::ErrorKind; +use std::error::Error as StdError; +use std::marker::PhantomData; /// AWS error codes that represent throttling errors. pub const THROTTLING_ERRORS: &[&str] = &[ @@ -31,16 +34,31 @@ pub const THROTTLING_ERRORS: &[&str] = &[ pub const TRANSIENT_ERRORS: &[&str] = &["RequestTimeout", "RequestTimeoutException"]; /// A retry classifier for determining if the response sent by an AWS service requires a retry. -#[derive(Debug)] -pub struct AwsErrorCodeClassifier; - -impl AwsErrorCodeClassifier { - /// Classify an error code to check if represents a retryable error. The codes of retryable - /// errors are defined [here](THROTTLING_ERRORS) and [here](TRANSIENT_ERRORS). - pub fn classify_error( - &self, - error: &SdkError, - ) -> Option { +#[derive(Debug, Default)] +pub struct AwsErrorCodeClassifier { + _inner: PhantomData, +} + +impl AwsErrorCodeClassifier { + /// Create a new AwsErrorCodeClassifier + pub fn new() -> Self { + Self { + _inner: PhantomData, + } + } +} + +impl ClassifyRetry for AwsErrorCodeClassifier +where + E: StdError + ProvideErrorMetadata + Send + Sync + 'static, +{ + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + let error = ctx + .output_or_error()? + .err() + .and_then(OrchestratorError::as_operation_error)? + .downcast_ref::()?; + if let Some(error_code) = error.code() { if THROTTLING_ERRORS.contains(&error_code) { return Some(RetryReason::Error(ErrorKind::ThrottlingError)); @@ -51,18 +69,27 @@ impl AwsErrorCodeClassifier { None } + + fn name(&self) -> &'static str { + "AWS Error Code" + } } /// A retry classifier that checks for `x-amz-retry-after` headers. If one is found, a /// [`RetryReason::Explicit`] is returned containing the duration to wait before retrying. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct AmzRetryAfterHeaderClassifier; impl AmzRetryAfterHeaderClassifier { - /// Classify an AWS responses error code to determine how (and if) it should be retried. - pub fn classify_error(&self, error: &SdkError) -> Option { - error - .raw_response() + /// Create a new `AmzRetryAfterHeaderClassifier`. + pub fn new() -> Self { + Self + } +} + +impl ClassifyRetry for AmzRetryAfterHeaderClassifier { + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + ctx.response() .and_then(|res| res.http_headers().get("x-amz-retry-after")) .and_then(|header| header.to_str().ok()) .and_then(|header| header.parse::().ok()) @@ -70,18 +97,23 @@ impl AmzRetryAfterHeaderClassifier { RetryReason::Explicit(std::time::Duration::from_millis(retry_after_delay)) }) } + + fn name(&self) -> &'static str { + "'Retry After' Header" + } } #[cfg(test)] mod test { use super::{AmzRetryAfterHeaderClassifier, AwsErrorCodeClassifier}; use aws_smithy_http::body::SdkBody; - use aws_smithy_http::operation; - use aws_smithy_http::result::SdkError; - use aws_smithy_runtime_api::client::retries::RetryReason; + use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; + use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::error::metadata::ProvideErrorMetadata; use aws_smithy_types::error::ErrorMetadata; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; + use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; use std::fmt; use std::time::Duration; @@ -96,6 +128,17 @@ mod test { impl std::error::Error for UnmodeledError {} + impl ProvideErrorKind for UnmodeledError { + fn retryable_error_kind(&self) -> Option { + None + } + + fn code(&self) -> Option<&str> { + None + } + } + + #[derive(Debug)] struct CodedError { metadata: ErrorMetadata, } @@ -108,16 +151,14 @@ mod test { } } - impl ProvideErrorKind for UnmodeledError { - fn retryable_error_kind(&self) -> Option { - None - } - - fn code(&self) -> Option<&str> { - None + impl fmt::Display for CodedError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Coded Error") } } + impl std::error::Error for CodedError {} + impl ProvideErrorMetadata for CodedError { fn meta(&self) -> &ErrorMetadata { &self.metadata @@ -126,31 +167,39 @@ mod test { #[test] fn classify_by_error_code() { - let policy = AwsErrorCodeClassifier; - let res = http::Response::new("OK"); - let err = SdkError::service_error(CodedError::new("Throttling"), res); + let policy = AwsErrorCodeClassifier::::new(); + let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new( + CodedError::new("Throttling"), + )))); assert_eq!( - policy.classify_error(&err), + policy.classify_retry(&ctx), Some(RetryReason::Error(ErrorKind::ThrottlingError)) ); - let res = http::Response::new("OK"); - let err = SdkError::service_error(CodedError::new("RequestTimeout"), res); + let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new( + CodedError::new("RequestTimeout"), + )))); assert_eq!( - policy.classify_error(&err), + policy.classify_retry(&ctx), Some(RetryReason::Error(ErrorKind::TransientError)) ) } #[test] fn classify_generic() { - let policy = AwsErrorCodeClassifier; - let res = http::Response::new("OK"); + let policy = AwsErrorCodeClassifier::::new(); let err = aws_smithy_types::Error::builder().code("SlowDown").build(); - let err = SdkError::service_error(err, res); + let test_response = http::Response::new("OK").map(SdkBody::from); + + let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + ctx.set_response(test_response); + ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new(err)))); + assert_eq!( - policy.classify_error(&err), + policy.classify_retry(&ctx), Some(RetryReason::Error(ErrorKind::ThrottlingError)) ); } @@ -163,11 +212,14 @@ mod test { .body("retry later") .unwrap() .map(SdkBody::from); - let res = operation::Response::new(res); - let err = SdkError::service_error(UnmodeledError, res); + let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + ctx.set_response(res); + ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new( + UnmodeledError, + )))); assert_eq!( - policy.classify_error(&err), + policy.classify_retry(&ctx), Some(RetryReason::Explicit(Duration::from_millis(5000))), ); } diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 829fa570cf..51bace059a 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -117,6 +117,7 @@ mod tests { fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a str { context .request() + .expect("request is set") .headers() .get(header_name) .unwrap() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt index d7c2afd9d9..9d87e68d1a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt @@ -10,8 +10,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginSection -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.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable @@ -19,6 +17,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection +import software.amazon.smithy.rust.codegen.core.util.letIf class RetryClassifierDecorator : ClientCodegenDecorator { override val name: String = "RetryPolicy" @@ -35,8 +34,11 @@ class RetryClassifierDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = - baseCustomizations + OperationRetryClassifiersFeature(codegenContext, operation) + ): List { + return baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + OperationRetryClassifiersFeature(codegenContext, operation) + } + } } class RetryClassifierFeature(private val runtimeConfig: RuntimeConfig) : OperationCustomization() { @@ -57,133 +59,36 @@ class RetryClassifierFeature(private val runtimeConfig: RuntimeConfig) : Operati class OperationRetryClassifiersFeature( codegenContext: ClientCodegenContext, - operationShape: OperationShape, + operation: OperationShape, ) : OperationRuntimePluginCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) private val smithyRuntime = RuntimeType.smithyRuntime(runtimeConfig) private val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) private val codegenScope = arrayOf( - "HttpStatusCodeClassifier" to smithyRuntime.resolve("client::retries::classifier::HttpStatusCodeClassifier"), - "AwsErrorCodeClassifier" to awsRuntime.resolve("retries::classifier::AwsErrorCodeClassifier"), - "ModeledAsRetryableClassifier" to smithyRuntime.resolve("client::retries::classifier::ModeledAsRetryableClassifier"), - "AmzRetryAfterHeaderClassifier" to awsRuntime.resolve("retries::classifier::AmzRetryAfterHeaderClassifier"), + // Classifiers "SmithyErrorClassifier" to smithyRuntime.resolve("client::retries::classifier::SmithyErrorClassifier"), - "RetryReason" to smithyRuntimeApi.resolve("client::retries::RetryReason"), + "AmzRetryAfterHeaderClassifier" to awsRuntime.resolve("retries::classifier::AmzRetryAfterHeaderClassifier"), + "ModeledAsRetryableClassifier" to smithyRuntime.resolve("client::retries::classifier::ModeledAsRetryableClassifier"), + "AwsErrorCodeClassifier" to awsRuntime.resolve("retries::classifier::AwsErrorCodeClassifier"), + "HttpStatusCodeClassifier" to smithyRuntime.resolve("client::retries::classifier::HttpStatusCodeClassifier"), + // Other Types "ClassifyRetry" to smithyRuntimeApi.resolve("client::retries::ClassifyRetry"), - "RetryClassifiers" to smithyRuntimeApi.resolve("client::retries::RetryClassifiers"), - "OperationError" to codegenContext.symbolProvider.symbolForOperationError(operationShape), + "InterceptorContext" to smithyRuntimeApi.resolve("client::interceptor::InterceptorContext"), + "OperationError" to codegenContext.symbolProvider.symbolForOperationError(operation), "OrchestratorError" to smithyRuntimeApi.resolve("client::orchestrator::OrchestratorError"), - "SdkError" to RuntimeType.smithyHttp(runtimeConfig).resolve("result::SdkError"), - "ErasedError" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypeErasedError"), + "RetryReason" to smithyRuntimeApi.resolve("client::retries::RetryReason"), ) override fun section(section: OperationRuntimePluginSection) = when (section) { - is OperationRuntimePluginSection.RuntimePluginSupportingTypes -> writable { - Attribute(derive(RuntimeType.Debug)).render(this) - rustTemplate( - """ - struct HttpStatusCodeClassifier(#{HttpStatusCodeClassifier}); - impl HttpStatusCodeClassifier { - fn new() -> Self { - Self(#{HttpStatusCodeClassifier}::default()) - } - } - impl #{ClassifyRetry} for HttpStatusCodeClassifier { - fn classify_retry(&self, _error: &#{OrchestratorError}<#{ErasedError}>) -> Option<#{RetryReason}> { - // TODO(enableNewSmithyRuntime): classify the error with self.0 - None - } - } - """, - *codegenScope, - ) - - Attribute(derive(RuntimeType.Debug)).render(this) - rustTemplate( - """ - struct AwsErrorCodeClassifier(#{AwsErrorCodeClassifier}); - impl AwsErrorCodeClassifier { - fn new() -> Self { - Self(#{AwsErrorCodeClassifier}) - } - } - impl #{ClassifyRetry} for AwsErrorCodeClassifier { - fn classify_retry(&self, _error: &#{OrchestratorError}<#{ErasedError}>) -> Option<#{RetryReason}> { - // TODO(enableNewSmithyRuntime): classify the error with self.0 - None - } - } - """, - *codegenScope, - ) - - Attribute(derive(RuntimeType.Debug)).render(this) - rustTemplate( - """ - struct ModeledAsRetryableClassifier(#{ModeledAsRetryableClassifier}); - impl ModeledAsRetryableClassifier { - fn new() -> Self { - Self(#{ModeledAsRetryableClassifier}) - } - } - impl #{ClassifyRetry} for ModeledAsRetryableClassifier { - fn classify_retry(&self, _error: &#{OrchestratorError}<#{ErasedError}>) -> Option<#{RetryReason}> { - // TODO(enableNewSmithyRuntime): classify the error with self.0 - None - } - } - """, - *codegenScope, - ) - - Attribute(derive(RuntimeType.Debug)).render(this) - rustTemplate( - """ - struct AmzRetryAfterHeaderClassifier(#{AmzRetryAfterHeaderClassifier}); - impl AmzRetryAfterHeaderClassifier { - fn new() -> Self { - Self(#{AmzRetryAfterHeaderClassifier}) - } - } - impl #{ClassifyRetry} for AmzRetryAfterHeaderClassifier { - fn classify_retry(&self, _error: &#{OrchestratorError}<#{ErasedError}>) -> Option<#{RetryReason}> { - // TODO(enableNewSmithyRuntime): classify the error with self.0 - None - } - } - """, - *codegenScope, - ) - - Attribute(derive(RuntimeType.Debug)).render(this) - rustTemplate( - """ - struct SmithyErrorClassifier(#{SmithyErrorClassifier}); - impl SmithyErrorClassifier { - fn new() -> Self { - Self(#{SmithyErrorClassifier}) - } - } - impl #{ClassifyRetry} for SmithyErrorClassifier { - fn classify_retry(&self, _error: &#{OrchestratorError}<#{ErasedError}>) -> Option<#{RetryReason}> { - // TODO(enableNewSmithyRuntime): classify the error with self.0 - None - } - } - """, - *codegenScope, - ) - } - is OperationRuntimePluginSection.RetryClassifier -> writable { rustTemplate( """ - .with_classifier(SmithyErrorClassifier::new()) - .with_classifier(AmzRetryAfterHeaderClassifier::new()) - .with_classifier(ModeledAsRetryableClassifier::new()) - .with_classifier(AwsErrorCodeClassifier::new()) - .with_classifier(HttpStatusCodeClassifier::new()) + .with_classifier(#{SmithyErrorClassifier}::<#{OperationError}>::new()) + .with_classifier(#{AmzRetryAfterHeaderClassifier}) + .with_classifier(#{ModeledAsRetryableClassifier}::<#{OperationError}>::new()) + .with_classifier(#{AwsErrorCodeClassifier}::<#{OperationError}>::new()) + .with_classifier(#{HttpStatusCodeClassifier}::default()) """, *codegenScope, ) diff --git a/aws/sdk/integration-tests/kms/tests/retryable_errors.rs b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs index 5c859da51b..e052dc28f2 100644 --- a/aws/sdk/integration-tests/kms/tests/retryable_errors.rs +++ b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs @@ -73,9 +73,11 @@ mod orchestrator_mode_tests { use aws_sdk_kms as kms; use aws_smithy_client::test_connection::infallible_connection_fn; use aws_smithy_http::result::SdkError; - use aws_smithy_runtime_api::client::orchestrator::HttpResponse; - use aws_smithy_runtime_api::client::retries::RetryReason; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::orchestrator::{HttpResponse, OrchestratorError}; + use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::retry::ErrorKind; + use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; use bytes::Bytes; use kms::operation::create_alias::CreateAliasError; @@ -110,8 +112,11 @@ mod orchestrator_mode_tests { .await; dbg!(&err); - let classifier = AwsErrorCodeClassifier; - let retry_kind = classifier.classify_error(&err); + let classifier = AwsErrorCodeClassifier::::new(); + let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + let err = err.into_service_error(); + ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new(err)))); + let retry_kind = classifier.classify_retry(&ctx); assert_eq!( Some(RetryReason::Error(ErrorKind::ThrottlingError)), retry_kind @@ -129,8 +134,11 @@ mod orchestrator_mode_tests { .await; dbg!(&err); - let classifier = AwsErrorCodeClassifier; - let retry_kind = classifier.classify_error(&err); + let classifier = AwsErrorCodeClassifier::::new(); + let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + let err = err.into_service_error(); + ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new(err)))); + let retry_kind = classifier.classify_retry(&ctx); assert_eq!( Some(RetryReason::Error(ErrorKind::ThrottlingError)), retry_kind diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index 5b0eba15da..2ec542d1ab 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -193,6 +193,8 @@ private class HttpAuthServiceRuntimePluginCustomization( rust("cfg.set_identity_resolvers(self.handle.conf.identity_resolvers().clone());") } } + + else -> emptySection } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 2a4a053c24..1c7904a386 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -30,6 +30,15 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { */ data class HttpAuthScheme(val configBagName: String) : ServiceRuntimePluginSection("HttpAuthScheme") + /** + * Hook for adding retry classifiers to an operation's `RetryClassifiers` bundle. + * + * Should emit code that looks like the following: + ``` + .with_classifier(AwsErrorCodeClassifier::new()) + */ + data class RetryClassifier(val configBagName: String) : ServiceRuntimePluginSection("RetryClassifier") + /** * Hook for adding additional things to config inside service runtime plugins. */ @@ -83,6 +92,7 @@ class ServiceRuntimePluginGenerator( "IdentityResolvers" to runtimeApi.resolve("client::identity::IdentityResolvers"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "NeverRetryStrategy" to runtime.resolve("client::retries::strategy::NeverRetryStrategy"), + "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), "Params" to endpointTypesGenerator.paramsStruct(), "ResolveEndpoint" to http.resolve("endpoint::ResolveEndpoint"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), @@ -126,6 +136,11 @@ class ServiceRuntimePluginGenerator( #{SharedEndpointResolver}::from(self.handle.conf.endpoint_resolver())); cfg.set_endpoint_resolver(endpoint_resolver); + // TODO(enableNewSmithyRuntime): Use the `store_append` method of ConfigBag to insert classifiers + let retry_classifiers = #{RetryClassifiers}::new() + #{retry_classifier_customizations}; + cfg.set_retry_classifiers(retry_classifiers); + // TODO(enableNewSmithyRuntime): Wire up standard retry cfg.set_retry_strategy(#{NeverRetryStrategy}::new()); @@ -155,6 +170,9 @@ class ServiceRuntimePluginGenerator( "http_auth_scheme_customizations" to writable { writeCustomizations(customizations, ServiceRuntimePluginSection.HttpAuthScheme("cfg")) }, + "retry_classifier_customizations" to writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.RetryClassifier("cfg")) + }, "additional_config" to writable { writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg", "_interceptors")) }, diff --git a/rust-runtime/aws-smithy-runtime-api/Cargo.toml b/rust-runtime/aws-smithy-runtime-api/Cargo.toml index 726b923482..ec17790c1c 100644 --- a/rust-runtime/aws-smithy-runtime-api/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime-api/Cargo.toml @@ -12,6 +12,7 @@ repository = "https://github.com/awslabs/smithy-rs" [features] default = [] http-auth = ["dep:zeroize"] +test-util = [] [dependencies] aws-smithy-async = { path = "../aws-smithy-async" } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 488cd1d389..c393f2e766 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -118,17 +118,13 @@ where } /// Retrieve the input for the operation being invoked. - pub fn input(&self) -> &I { - self.input - .as_ref() - .expect("input is present in 'before serialization'") + pub fn input(&self) -> Option<&I> { + self.input.as_ref() } /// Retrieve the input for the operation being invoked. - pub fn input_mut(&mut self) -> &mut I { - self.input - .as_mut() - .expect("input is present in 'before serialization'") + pub fn input_mut(&mut self) -> Option<&mut I> { + self.input.as_mut() } /// Takes ownership of the input. @@ -143,18 +139,14 @@ where /// Retrieve the transmittable request for the operation being invoked. /// This will only be available once request marshalling has completed. - pub fn request(&self) -> &Request { - self.request - .as_ref() - .expect("request populated in 'before transmit'") + pub fn request(&self) -> Option<&Request> { + self.request.as_ref() } /// Retrieve the transmittable request for the operation being invoked. /// This will only be available once request marshalling has completed. - pub fn request_mut(&mut self) -> &mut Request { - self.request - .as_mut() - .expect("request populated in 'before transmit'") + pub fn request_mut(&mut self) -> Option<&mut Request> { + self.request.as_mut() } /// Takes ownership of the request. @@ -170,17 +162,13 @@ where } /// Returns the response. - pub fn response(&self) -> &Response { - self.response.as_ref().expect( - "response set in 'before deserialization' and available in the phases following it", - ) + pub fn response(&self) -> Option<&Response> { + self.response.as_ref() } /// Returns a mutable reference to the response. - pub fn response_mut(&mut self) -> &mut Response { - self.response.as_mut().expect( - "response is set in 'before deserialization' and available in the following phases", - ) + pub fn response_mut(&mut self) -> Option<&mut Response> { + self.response.as_mut() } /// Set the output or error for the operation being invoked. @@ -189,18 +177,13 @@ where } /// Returns the deserialized output or error. - pub fn output_or_error(&self) -> Result<&O, &OrchestratorError> { - self.output_or_error - .as_ref() - .expect("output set in Phase::AfterDeserialization") - .as_ref() + pub fn output_or_error(&self) -> Option>> { + self.output_or_error.as_ref().map(|res| res.as_ref()) } /// Returns the mutable reference to the deserialized output or error. - pub fn output_or_error_mut(&mut self) -> &mut Result> { - self.output_or_error - .as_mut() - .expect("output set in 'after deserialization'") + pub fn output_or_error_mut(&mut self) -> Option<&mut Result>> { + self.output_or_error.as_mut() } /// Advance to the Serialization phase. @@ -228,7 +211,10 @@ where self.request.is_some(), "request must be set before calling enter_before_transmit_phase" ); - self.request_checkpoint = try_clone(self.request()); + self.request_checkpoint = try_clone( + self.request() + .expect("request is set before calling enter_before_transmit_phase"), + ); self.tainted = true; self.phase = Phase::BeforeTransmit; } @@ -356,7 +342,13 @@ mod tests { let output = TypedBox::new("output".to_string()).erase(); let mut context = InterceptorContext::new(input); - assert_eq!("input", context.input().downcast_ref::().unwrap()); + assert_eq!( + "input", + context + .input() + .map(|i| i.downcast_ref::().unwrap()) + .unwrap() + ); context.input_mut(); context.enter_serialization_phase(); @@ -408,7 +400,13 @@ mod tests { let error = TypedBox::new(Error).erase_error(); let mut context = InterceptorContext::new(input); - assert_eq!("input", context.input().downcast_ref::().unwrap()); + assert_eq!( + "input", + context + .input() + .map(|i| i.downcast_ref::().unwrap()) + .unwrap() + ); context.enter_serialization_phase(); let _ = context.take_input(); @@ -421,8 +419,8 @@ mod tests { context.enter_before_transmit_phase(); // Modify the test header post-checkpoint to simulate modifying the request for signing or a mutating interceptor - context.request_mut().headers_mut().remove("test"); - context.request_mut().headers_mut().insert( + context.request_mut().unwrap().headers_mut().remove("test"); + context.request_mut().unwrap().headers_mut().insert( "test", HeaderValue::from_static("request-modified-after-signing"), ); @@ -444,7 +442,7 @@ mod tests { // Now after rewinding, the test header should be its original value assert_eq!( "the-original-un-mutated-request", - context.request().headers().get("test").unwrap() + context.request().unwrap().headers().get("test").unwrap() ); context.enter_transmit_phase(); diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs index b9db1e4042..a72df5bea8 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs @@ -45,13 +45,13 @@ macro_rules! declare_method { (&mut $name:ident, $doc:literal, $($tt:tt)+) => { #[doc=$doc] pub fn $name(&mut self) -> output!(&mut $($tt)+) { - self.inner.$name() + self.inner.$name().expect("wrapper type cannot be created unless this is set") } }; (&$name:ident, $doc:literal, $($tt:tt)+) => { #[doc=$doc] pub fn $name(&self) -> output!(&$($tt)+) { - self.inner.$name() + self.inner.$name().expect("wrapper type cannot be created unless this is set") } }; } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs index 87a0ac2a64..34296dbcac 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs @@ -38,6 +38,15 @@ impl OrchestratorError { Self::Interceptor { err } } + /// Convert the `OrchestratorError` into `Some` operation specific error if it is one. Otherwise, + /// return `None`. + pub fn as_operation_error(&self) -> Option<&E> { + match self { + Self::Operation { err } => Some(err), + _ => None, + } + } + /// Convert the `OrchestratorError` into an [`SdkError`]. pub fn into_sdk_error( self, diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index 33327eb291..ca9699075e 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -3,14 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::client::interceptors::context::Error; use crate::client::interceptors::InterceptorContext; -use crate::client::orchestrator::{BoxError, OrchestratorError}; +use crate::client::orchestrator::BoxError; use aws_smithy_types::config_bag::ConfigBag; -use aws_smithy_types::retry::ErrorKind; use std::fmt::Debug; use std::time::Duration; +use tracing::trace; +pub use aws_smithy_types::retry::ErrorKind; + +#[derive(Debug, Clone, PartialEq, Eq)] /// An answer to the question "should I make a request attempt?" pub enum ShouldAttempt { Yes, @@ -35,11 +37,14 @@ pub enum RetryReason { Explicit(Duration), } -/// Classifies what kind of retry is needed for a given [`Error`]. +/// Classifies what kind of retry is needed for a given an [`InterceptorContext`]. pub trait ClassifyRetry: Send + Sync + Debug { /// Run this classifier against an error to determine if it should be retried. Returns /// `Some(RetryKind)` if the error should be retried; Otherwise returns `None`. - fn classify_retry(&self, error: &OrchestratorError) -> Option; + fn classify_retry(&self, ctx: &InterceptorContext) -> Option; + + /// The name that this classifier should report for debugging purposes. + fn name(&self) -> &'static str; } #[derive(Debug)] @@ -67,8 +72,52 @@ impl RetryClassifiers { } impl ClassifyRetry for RetryClassifiers { - fn classify_retry(&self, error: &OrchestratorError) -> Option { + fn classify_retry(&self, error: &InterceptorContext) -> Option { // return the first non-None result - self.inner.iter().find_map(|cr| cr.classify_retry(error)) + self.inner.iter().find_map(|cr| { + let maybe_reason = cr.classify_retry(error); + + match maybe_reason.as_ref() { + Some(reason) => trace!( + "\"{}\" classifier classified error as {:?}", + cr.name(), + reason + ), + None => trace!("\"{}\" classifier ignored the error", cr.name()), + }; + + maybe_reason + }) + } + + fn name(&self) -> &'static str { + "Collection of Classifiers" } } + +#[cfg(feature = "test-util")] +mod test_util { + use super::{ClassifyRetry, ErrorKind, RetryReason}; + use crate::client::interceptors::InterceptorContext; + use tracing::trace; + + /// A retry classifier for testing purposes. This classifier always returns + /// `Some(RetryReason::Error(ErrorKind))` where `ErrorKind` is the value provided when creating + /// this classifier. + #[derive(Debug)] + pub struct AlwaysRetry(pub ErrorKind); + + impl ClassifyRetry for AlwaysRetry { + fn classify_retry(&self, error: &InterceptorContext) -> Option { + trace!("Retrying error {:?} as an {:?}", error, self.0); + Some(RetryReason::Error(self.0)) + } + + fn name(&self) -> &'static str { + "Always Retry" + } + } +} + +#[cfg(feature = "test-util")] +pub use test_util::AlwaysRetry; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index af885ed290..42d23e2bcd 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -114,9 +114,10 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: // Load the request body into memory if configured to do so if let LoadedRequestBody::Requested = cfg.loaded_request_body() { let mut body = SdkBody::taken(); - mem::swap(&mut body, ctx.request_mut().body_mut()); + let req = ctx.request_mut().expect("request exists"); + mem::swap(&mut body, req.body_mut()); let loaded_body = halt_on_err!([ctx] => ByteStream::new(body).collect().await).into_bytes(); - *ctx.request_mut().body_mut() = SdkBody::from(loaded_body.clone()); + *req.body_mut() = SdkBody::from(loaded_body.clone()); cfg.set_loaded_request_body(LoadedRequestBody::Loaded(loaded_body)); } @@ -200,7 +201,9 @@ async fn try_attempt( ctx.enter_deserialization_phase(); let output_or_error = async { - let response = ctx.response_mut(); + let response = ctx + .response_mut() + .ok_or("No response was present in the InterceptorContext")?; let response_deserializer = cfg.response_deserializer(); match response_deserializer.deserialize_streaming(response) { Some(output_or_error) => Ok(output_or_error), diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index d5ea86b3dc..37b21f731a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -85,7 +85,9 @@ pub(super) async fn orchestrate_auth( extract_endpoint_auth_scheme_config(endpoint, scheme_id)?; let identity = identity_resolver.resolve_identity(cfg).await?; - let request = ctx.request_mut(); + let request = ctx + .request_mut() + .expect("request is present before orchestrate_auth is called"); request_signer.sign_request( request, &identity, @@ -219,7 +221,11 @@ mod tests { assert_eq!( "success!", - ctx.request().headers().get("Authorization").unwrap() + ctx.request() + .expect("request is set") + .headers() + .get("Authorization") + .unwrap() ); } @@ -263,7 +269,11 @@ mod tests { assert_eq!( // "YTpi" == "a:b" in base64 "Basic YTpi", - ctx.request().headers().get("Authorization").unwrap() + ctx.request() + .expect("request is set") + .headers() + .get("Authorization") + .unwrap() ); // Next, test the presence of a bearer token and absence of basic auth @@ -281,7 +291,11 @@ mod tests { orchestrate_auth(&mut ctx, &cfg).await.expect("success"); assert_eq!( "Bearer t", - ctx.request().headers().get("Authorization").unwrap() + ctx.request() + .expect("request is set") + .headers() + .get("Authorization") + .unwrap() ); } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 7430519978..e78b3beac5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -93,7 +93,9 @@ pub(super) fn orchestrate_endpoint( ) -> Result<(), BoxError> { let params = cfg.endpoint_resolver_params(); let endpoint_prefix = cfg.get::(); - let request = ctx.request_mut(); + let request = ctx + .request_mut() + .expect("request is present before orchestrate_endpoint is called"); let endpoint_resolver = cfg.endpoint_resolver(); let endpoint = endpoint_resolver.resolve_endpoint(params)?; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs index 64960707ad..fd9754cff2 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs @@ -4,36 +4,84 @@ */ use aws_smithy_http::result::SdkError; -use aws_smithy_runtime_api::client::retries::RetryReason; +use aws_smithy_runtime_api::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; use std::borrow::Cow; +use std::error::Error as StdError; +use std::marker::PhantomData; /// A retry classifier for checking if an error is modeled as retryable. -#[derive(Debug)] -pub struct ModeledAsRetryableClassifier; - -impl ModeledAsRetryableClassifier { - /// Check if an error is modeled as retryable, returning a [`RetryReason::Error`] if it is. - pub fn classify_error( - &self, - error: &SdkError, - ) -> Option { - match error { - SdkError::ServiceError(inner) => { - inner.err().retryable_error_kind().map(RetryReason::Error) - } - _ => None, +#[derive(Debug, Default)] +pub struct ModeledAsRetryableClassifier { + _inner: PhantomData, +} + +impl ModeledAsRetryableClassifier { + /// Create a new `ModeledAsRetryableClassifier` + pub fn new() -> Self { + Self { + _inner: PhantomData, } } } -#[derive(Debug)] -pub struct SmithyErrorClassifier; +impl ClassifyRetry for ModeledAsRetryableClassifier +where + E: StdError + ProvideErrorKind + Send + Sync + 'static, +{ + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + // Check for a result + let output_or_error = ctx.output_or_error()?; + // Check for an error + let error = match output_or_error { + Ok(_) => return None, + Err(err) => err, + }; + // Check that the error is an operation error + let error = error.as_operation_error()?; + // Downcast the error + let error = error.downcast_ref::()?; + // Check if the error is retryable + error.retryable_error_kind().map(RetryReason::Error) + } -impl SmithyErrorClassifier { - pub fn classify_error(&self, result: &SdkError) -> Option { - match result { - SdkError::TimeoutError(_err) => Some(RetryReason::Error(ErrorKind::TransientError)), + fn name(&self) -> &'static str { + "Errors Modeled As Retryable" + } +} + +#[derive(Debug, Default)] +pub struct SmithyErrorClassifier { + _inner: PhantomData, +} + +impl SmithyErrorClassifier { + /// Create a new `SmithyErrorClassifier` + pub fn new() -> Self { + Self { + _inner: PhantomData, + } + } +} + +impl ClassifyRetry for SmithyErrorClassifier +where + E: StdError + Send + Sync + 'static, +{ + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + let output_or_error = ctx.output_or_error()?; + // Check for an error + let error = match output_or_error { + Ok(_) => return None, + Err(err) => err, + }; + // Check that the error is an operation error + let error = error.as_operation_error()?; + // Downcast the error + let error = error.downcast_ref::>()?; + match error { + SdkError::TimeoutError(_) => Some(RetryReason::Error(ErrorKind::TransientError)), SdkError::ResponseError { .. } => Some(RetryReason::Error(ErrorKind::TransientError)), SdkError::DispatchFailure(err) if (err.is_timeout() || err.is_io()) => { Some(RetryReason::Error(ErrorKind::TransientError)) @@ -42,6 +90,10 @@ impl SmithyErrorClassifier { _ => None, } } + + fn name(&self) -> &'static str { + "Retryable Smithy Errors" + } } const TRANSIENT_ERROR_STATUS_CODES: &[u16] = &[500, 502, 503, 504]; @@ -53,30 +105,34 @@ pub struct HttpStatusCodeClassifier { retryable_status_codes: Cow<'static, [u16]>, } +impl Default for HttpStatusCodeClassifier { + fn default() -> Self { + Self::new_from_codes(TRANSIENT_ERROR_STATUS_CODES.to_owned()) + } +} + impl HttpStatusCodeClassifier { - /// Given a `Vec` where the `u16`s represent status codes, create a retry classifier that will - /// treat HTTP response with those status codes as retryable. The `Default` version will retry - /// 500, 502, 503, and 504 errors. + /// Given a `Vec` where the `u16`s represent status codes, create a `HttpStatusCodeClassifier` + /// that will treat HTTP response with those status codes as retryable. The `Default` version + /// will retry 500, 502, 503, and 504 errors. pub fn new_from_codes(retryable_status_codes: impl Into>) -> Self { Self { retryable_status_codes: retryable_status_codes.into(), } } +} - /// Classify an HTTP response based on its status code. - pub fn classify_error(&self, error: &SdkError) -> Option { - error - .raw_response() - .map(|res| res.http().status().as_u16()) +impl ClassifyRetry for HttpStatusCodeClassifier { + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + ctx.response() + .map(|res| res.status().as_u16()) .map(|status| self.retryable_status_codes.contains(&status)) .unwrap_or_default() .then_some(RetryReason::Error(ErrorKind::TransientError)) } -} -impl Default for HttpStatusCodeClassifier { - fn default() -> Self { - Self::new_from_codes(TRANSIENT_ERROR_STATUS_CODES.to_owned()) + fn name(&self) -> &'static str { + "HTTP Status Code" } } @@ -87,25 +143,27 @@ impl Default for HttpStatusCodeClassifier { // .with_classifier(ModeledAsRetryableClassifier::new()) // .with_classifier(HttpStatusCodeClassifier::new()) // } -// This ordering is different than the default AWS ordering because the old generic client classifer +// This ordering is different than the default AWS ordering because the old generic client classifier // was the same. #[cfg(test)] mod test { - use std::fmt; - use crate::client::retries::classifier::{ HttpStatusCodeClassifier, ModeledAsRetryableClassifier, }; use aws_smithy_http::body::SdkBody; use aws_smithy_http::operation; use aws_smithy_http::result::SdkError; - use aws_smithy_runtime_api::client::retries::RetryReason; + use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; + use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; + use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; + use std::fmt; use super::SmithyErrorClassifier; - #[derive(Debug)] + #[derive(Debug, PartialEq, Eq, Clone)] struct UnmodeledError; impl fmt::Display for UnmodeledError { @@ -124,10 +182,10 @@ mod test { .body("error!") .unwrap() .map(SdkBody::from); - let res = operation::Response::new(res); - let err = SdkError::service_error(UnmodeledError, res); + let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + ctx.set_response(res); assert_eq!( - policy.classify_error(&err), + policy.classify_retry(&ctx), Some(RetryReason::Error(ErrorKind::TransientError)) ); } @@ -140,17 +198,23 @@ mod test { .body("error!") .unwrap() .map(SdkBody::from); - let res = operation::Response::new(res); - let err = SdkError::service_error(UnmodeledError, res); - - assert_eq!(policy.classify_error(&err), None); + let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + ctx.set_response(res); + assert_eq!(policy.classify_retry(&ctx), None); } #[test] fn classify_by_error_kind() { - struct ModeledRetries; + #[derive(Debug)] + struct RetryableError; - impl ProvideErrorKind for ModeledRetries { + impl fmt::Display for RetryableError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Some retryable error") + } + } + + impl ProvideErrorKind for RetryableError { fn retryable_error_kind(&self) -> Option { Some(ErrorKind::ClientError) } @@ -161,34 +225,42 @@ mod test { } } - let policy = ModeledAsRetryableClassifier; - let res = http::Response::new("OK"); - let err = SdkError::service_error(ModeledRetries, res); + impl std::error::Error for RetryableError {} + + let policy = ModeledAsRetryableClassifier::::new(); + let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new( + RetryableError, + )))); assert_eq!( - policy.classify_error(&err), + policy.classify_retry(&ctx), Some(RetryReason::Error(ErrorKind::ClientError)), ); } #[test] fn classify_response_error() { - let policy = SmithyErrorClassifier; + let policy = SmithyErrorClassifier::::new(); let test_response = http::Response::new("OK").map(SdkBody::from); let err: SdkError = SdkError::response_error(UnmodeledError, operation::Response::new(test_response)); + let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new(err)))); assert_eq!( - policy.classify_error(&err), + policy.classify_retry(&ctx), Some(RetryReason::Error(ErrorKind::TransientError)), ); } #[test] fn test_timeout_error() { - let policy = SmithyErrorClassifier; - let err: SdkError = SdkError::timeout_error("blah"); + let policy = SmithyErrorClassifier::::new(); + let err: SdkError = SdkError::timeout_error("blah"); + let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new(err)))); assert_eq!( - policy.classify_error(&err), + policy.classify_retry(&ctx), Some(RetryReason::Error(ErrorKind::TransientError)), ); } diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs index 19f23a3d41..243c0a3298 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -44,18 +44,19 @@ impl RetryStrategy for FixedDelayRetryStrategy { cfg: &ConfigBag, ) -> Result { // Look a the result. If it's OK then we're done; No retry required. Otherwise, we need to inspect it - let error = match ctx.output_or_error() { - Ok(_) => { - tracing::trace!("request succeeded, no retry necessary"); - return Ok(ShouldAttempt::No); - } - Err(err) => err, - }; + let output_or_error = ctx.output_or_error().expect( + "This must never be called without reaching the point where the result exists.", + ); + if output_or_error.is_ok() { + tracing::trace!("request succeeded, no retry necessary"); + return Ok(ShouldAttempt::No); + } + // Check if we're out of attempts let request_attempts: &RequestAttempts = cfg .get() .expect("at least one request attempt is made before any retry is attempted"); - if request_attempts.attempts() == self.max_attempts { + if request_attempts.attempts() >= self.max_attempts { tracing::trace!( attempts = request_attempts.attempts(), max_attempts = self.max_attempts, @@ -67,7 +68,7 @@ impl RetryStrategy for FixedDelayRetryStrategy { let retry_classifiers = cfg .get::() .expect("a retry classifier is set"); - let retry_reason = retry_classifiers.classify_retry(error); + let retry_reason = retry_classifiers.classify_retry(ctx); let backoff = match retry_reason { Some(RetryReason::Explicit(_)) => self.fixed_delay, From 303d99b688ba0082876009966e8e0601c7f63c01 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Thu, 1 Jun 2023 13:44:05 -0500 Subject: [PATCH 128/253] Fix various small issues with the orchestrator (#2736) ## Motivation and Context This change fixes many of the smaller issues I ran into during the implementation of standard retries for the orchestrator. Merges from `main` were getting difficult with my [other PR](https://github.com/awslabs/smithy-rs/pull/2725) so I'm breaking things up. ## Description - when orchestrator attempt timeout occurs, error is now set in context - update test connection to allow defining connection events with optional latency simulation update orchestrator attempt loop to track iteration count - set request attempts from the attempt loop - add comment explaining "rewind" step of making request attempts add `doesnt_matter` method to `TypeErasedBox`, useful when testing update tests to use the new `TypeErasedBox::doesnt_matter` method - add more doc comments - add `set_subsec_nanos` method to `DateTime`. - I added this to make it easier to string-format a datetime that didn't include the nanos. - fix Invocation ID interceptor not inserting the expected header update input type for `OperationError::other` to be more user-friendly - add `test-util` feature to `aws-smithy-runtime-api` - add `test-util` feature to `aws-runtime` - fix presigining inlineable to pull in tower dep during codegen ## Testing tests have been updated where necessary ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-runtime/Cargo.toml | 7 +- .../aws-runtime/src/invocation_id.rs | 172 +++++++++++++----- .../aws-runtime/src/recursion_detection.rs | 4 +- .../aws-runtime/src/request_info.rs | 29 +-- .../aws-runtime/src/user_agent.rs | 4 +- .../AwsCustomizableOperationDecorator.kt | 2 +- .../amazon/smithy/rustsdk/AwsRuntimeType.kt | 9 +- .../smithy/rustsdk/InvocationIdDecorator.kt | 21 ++- .../RetryInformationHeaderDecorator.kt | 5 - .../transcribestreaming/Cargo.toml | 1 + .../tests/request_information_headers.rs | 1 + .../aws-sdk-s3/tests/sra_test.rs | 4 +- .../ServiceRuntimePluginGenerator.kt | 2 +- .../client/CustomizableOperationGenerator.kt | 4 +- .../common-test-models/pokemon.smithy | 2 +- .../smithy/ValidateUnsupportedConstraints.kt | 2 +- ...ateUnsupportedConstraintsAreNotUsedTest.kt | 2 +- .../aws-smithy-runtime-api/src/client.rs | 3 + .../src/client/interceptors.rs | 4 + .../src/client/interceptors/context.rs | 94 ++++++++-- .../src/client/orchestrator/error.rs | 5 +- .../src/client/request_attempts.rs | 26 +++ rust-runtime/aws-smithy-runtime/Cargo.toml | 1 + .../aws-smithy-runtime/external-types.toml | 1 + rust-runtime/aws-smithy-runtime/src/client.rs | 2 +- .../src/client/connections/test_connection.rs | 114 ++++++++---- .../{interceptor.rs => interceptors.rs} | 0 .../src/client/orchestrator.rs | 54 ++++-- .../src/client/orchestrator/interceptors.rs | 2 - .../interceptors/request_attempts.rs | 69 ------- .../client/retries/strategy/fixed_delay.rs | 4 +- .../src/client/test_util.rs | 2 +- .../{interceptor.rs => interceptors.rs} | 7 +- rust-runtime/aws-smithy-types/Cargo.toml | 3 + .../aws-smithy-types/src/config_bag.rs | 6 +- .../aws-smithy-types/src/date_time/mod.rs | 12 ++ .../aws-smithy-types/src/type_erasure.rs | 13 +- 37 files changed, 457 insertions(+), 236 deletions(-) create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs rename rust-runtime/aws-smithy-runtime/src/client/{interceptor.rs => interceptors.rs} (100%) delete mode 100644 rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs rename rust-runtime/aws-smithy-runtime/src/client/test_util/{interceptor.rs => interceptors.rs} (94%) diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index e241ae92e7..08411f4584 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -9,17 +9,18 @@ repository = "https://github.com/awslabs/smithy-rs" [features] event-stream = ["dep:aws-smithy-eventstream", "aws-sigv4/sign-eventstream"] +test-util = [] [dependencies] aws-credential-types = { path = "../aws-credential-types" } aws-http = { path = "../aws-http" } aws-sigv4 = { path = "../aws-sigv4" } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-smithy-eventstream = { path = "../../../rust-runtime/aws-smithy-eventstream", optional = true } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } -aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-types = { path = "../aws-types" } http = "0.2.3" percent-encoding = "2.1.0" @@ -28,8 +29,10 @@ uuid = { version = "1", features = ["v4", "fast-rng"] } [dev-dependencies] aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } -aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-test" } aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] } +aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types", features = ["test-util"] } +aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-test" } +aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["test-util"] } proptest = "1" serde = { version = "1", features = ["derive"]} serde_json = "1" diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 899f76d753..420cedb73d 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -9,17 +9,26 @@ use aws_smithy_runtime_api::client::interceptors::{ }; use aws_smithy_types::config_bag::ConfigBag; use http::{HeaderName, HeaderValue}; +use std::fmt::Debug; use uuid::Uuid; +#[cfg(feature = "test-util")] +pub use test_util::{NoInvocationIdGenerator, PredefinedInvocationIdGenerator}; + #[allow(clippy::declare_interior_mutable_const)] // we will never mutate this const AMZ_SDK_INVOCATION_ID: HeaderName = HeaderName::from_static("amz-sdk-invocation-id"); +/// A generator for returning new invocation IDs on demand. +pub trait InvocationIdGenerator: Debug + Send + Sync { + /// Call this function to receive a new [`InvocationId`] or an error explaining why one couldn't + /// be provided. + fn generate(&self) -> Result, BoxError>; +} + /// This interceptor generates a UUID and attaches it to all request attempts made as part of this operation. #[non_exhaustive] -#[derive(Debug)] -pub struct InvocationIdInterceptor { - id: InvocationId, -} +#[derive(Debug, Default)] +pub struct InvocationIdInterceptor {} impl InvocationIdInterceptor { /// Creates a new `InvocationIdInterceptor` @@ -28,39 +37,50 @@ impl InvocationIdInterceptor { } } -impl Default for InvocationIdInterceptor { - fn default() -> Self { - Self { - id: InvocationId::from_uuid(), - } - } -} - impl Interceptor for InvocationIdInterceptor { fn modify_before_retry_loop( &self, - context: &mut BeforeTransmitInterceptorContextMut<'_>, - _cfg: &mut ConfigBag, + _ctx: &mut BeforeTransmitInterceptorContextMut<'_>, + cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let headers = context.request_mut().headers_mut(); - let id = _cfg.get::().unwrap_or(&self.id); + let id = cfg + .get::>() + .map(|gen| gen.generate()) + .transpose()? + .flatten(); + cfg.put::(id.unwrap_or_default()); + + Ok(()) + } + + fn modify_before_transmit( + &self, + ctx: &mut BeforeTransmitInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let headers = ctx.request_mut().headers_mut(); + let id = cfg + .get::() + .ok_or("Expected an InvocationId in the ConfigBag but none was present")?; headers.append(AMZ_SDK_INVOCATION_ID, id.0.clone()); Ok(()) } } /// InvocationId provides a consistent ID across retries -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct InvocationId(HeaderValue); + impl InvocationId { - /// A test invocation id to allow deterministic requests - pub fn for_tests() -> Self { - InvocationId(HeaderValue::from_static( - "00000000-0000-4000-8000-000000000000", - )) + /// Create a new, random, invocation ID. + pub fn new() -> Self { + Self::default() } +} - fn from_uuid() -> Self { +/// Defaults to a random UUID. +impl Default for InvocationId { + fn default() -> Self { let id = Uuid::new_v4(); let id = id .to_string() @@ -70,45 +90,107 @@ impl InvocationId { } } +#[cfg(feature = "test-util")] +mod test_util { + use super::*; + use std::sync::{Arc, Mutex}; + + impl InvocationId { + /// Create a new invocation ID from a `&'static str`. + pub fn new_from_str(uuid: &'static str) -> Self { + InvocationId(HeaderValue::from_static(uuid)) + } + } + + /// A "generator" that returns [`InvocationId`]s from a predefined list. + #[derive(Debug)] + pub struct PredefinedInvocationIdGenerator { + pre_generated_ids: Arc>>, + } + + impl PredefinedInvocationIdGenerator { + /// Given a `Vec`, create a new [`PredefinedInvocationIdGenerator`]. + pub fn new(mut invocation_ids: Vec) -> Self { + // We're going to pop ids off of the end of the list, so we need to reverse the list or else + // we'll be popping the ids in reverse order, confusing the poor test writer. + invocation_ids.reverse(); + + Self { + pre_generated_ids: Arc::new(Mutex::new(invocation_ids)), + } + } + } + + impl InvocationIdGenerator for PredefinedInvocationIdGenerator { + fn generate(&self) -> Result, BoxError> { + Ok(Some( + self.pre_generated_ids + .lock() + .expect("this will never be under contention") + .pop() + .expect("testers will provide enough invocation IDs"), + )) + } + } + + /// A "generator" that always returns `None`. + #[derive(Debug, Default)] + pub struct NoInvocationIdGenerator; + + impl NoInvocationIdGenerator { + /// Create a new [`NoInvocationIdGenerator`]. + pub fn new() -> Self { + Self::default() + } + } + + impl InvocationIdGenerator for NoInvocationIdGenerator { + fn generate(&self) -> Result, BoxError> { + Ok(None) + } + } +} + #[cfg(test)] mod tests { - use crate::invocation_id::InvocationIdInterceptor; + use crate::invocation_id::{InvocationId, InvocationIdInterceptor}; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; + use aws_smithy_runtime_api::client::interceptors::{ + BeforeTransmitInterceptorContextMut, Interceptor, InterceptorContext, + }; use aws_smithy_types::config_bag::ConfigBag; - use aws_smithy_types::type_erasure::TypedBox; + use aws_smithy_types::type_erasure::TypeErasedBox; use http::HeaderValue; - fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a HeaderValue { - context - .request() - .expect("request is set") - .headers() - .get(header_name) - .unwrap() + fn expect_header<'a>( + context: &'a BeforeTransmitInterceptorContextMut<'_>, + header_name: &str, + ) -> &'a HeaderValue { + context.request().headers().get(header_name).unwrap() } #[test] fn test_id_is_generated_and_set() { - let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); - context.enter_serialization_phase(); - context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); - let _ = context.take_input(); - context.enter_before_transmit_phase(); + let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + ctx.enter_serialization_phase(); + ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = ctx.take_input(); + ctx.enter_before_transmit_phase(); - let mut config = ConfigBag::base(); + let mut cfg = ConfigBag::base(); let interceptor = InvocationIdInterceptor::new(); - let mut ctx = Into::into(&mut context); + let mut ctx = Into::into(&mut ctx); interceptor - .modify_before_signing(&mut ctx, &mut config) + .modify_before_retry_loop(&mut ctx, &mut cfg) .unwrap(); interceptor - .modify_before_retry_loop(&mut ctx, &mut config) + .modify_before_transmit(&mut ctx, &mut cfg) .unwrap(); - let header = expect_header(&context, "amz-sdk-invocation-id"); - assert_eq!(&interceptor.id.0, header); + let expected = cfg.get::().expect("invocation ID was set"); + let header = expect_header(&ctx, "amz-sdk-invocation-id"); + assert_eq!(expected.0, header, "the invocation ID in the config bag must match the invocation ID in the request header"); // UUID should include 32 chars and 4 dashes - assert_eq!(interceptor.id.0.len(), 36); + assert_eq!(header.len(), 36); } } diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs index 9437dce76b..d7101e686a 100644 --- a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -75,7 +75,7 @@ mod tests { use aws_smithy_http::body::SdkBody; use aws_smithy_protocol_test::{assert_ok, validate_headers}; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; - use aws_smithy_types::type_erasure::TypedBox; + use aws_smithy_types::type_erasure::TypeErasedBox; use aws_types::os_shim_internal::Env; use http::HeaderValue; use proptest::{prelude::*, proptest}; @@ -148,7 +148,7 @@ mod tests { request = request.header(name, value); } let request = request.body(SdkBody::empty()).expect("must be valid"); - let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + let mut context = InterceptorContext::new(TypeErasedBox::doesnt_matter()); context.enter_serialization_phase(); context.set_request(request); let _ = context.take_input(); diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index 140ac28aea..0b41310536 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -3,10 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime::client::orchestrator::interceptors::{RequestAttempts, ServiceClockSkew}; +use aws_smithy_runtime::client::orchestrator::interceptors::ServiceClockSkew; use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, BoxError, Interceptor, }; +use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::date_time::Format; use aws_smithy_types::retry::RetryConfig; @@ -44,7 +45,7 @@ impl RequestInfoInterceptor { let request_attempts = cfg .get::() .map(|r_a| r_a.attempts()) - .unwrap_or(1); + .unwrap_or(0); let request_attempts = request_attempts.to_string(); Some((Cow::Borrowed("attempt"), Cow::Owned(request_attempts))) } @@ -68,11 +69,19 @@ impl RequestInfoInterceptor { let estimated_skew: Duration = cfg.get::().cloned()?.into(); let current_time = SystemTime::now(); let ttl = current_time.checked_add(socket_read + estimated_skew)?; - let timestamp = DateTime::from(ttl); - let formatted_timestamp = timestamp + let mut timestamp = DateTime::from(ttl); + // Set subsec_nanos to 0 so that the formatted `DateTime` won't have fractional seconds. + timestamp.set_subsec_nanos(0); + let mut formatted_timestamp = timestamp .fmt(Format::DateTime) .expect("the resulting DateTime will always be valid"); + // Remove dashes and colons + formatted_timestamp = formatted_timestamp + .chars() + .filter(|&c| c != '-' && c != ':') + .collect(); + Some((Cow::Borrowed("ttl"), Cow::Owned(formatted_timestamp))) } } @@ -84,13 +93,13 @@ impl Interceptor for RequestInfoInterceptor { cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let mut pairs = RequestPairs::new(); - if let Some(pair) = self.build_attempts_pair(cfg) { + if let Some(pair) = self.build_ttl_pair(cfg) { pairs = pairs.with_pair(pair); } - if let Some(pair) = self.build_max_attempts_pair(cfg) { + if let Some(pair) = self.build_attempts_pair(cfg) { pairs = pairs.with_pair(pair); } - if let Some(pair) = self.build_ttl_pair(cfg) { + if let Some(pair) = self.build_max_attempts_pair(cfg) { pairs = pairs.with_pair(pair); } @@ -156,12 +165,11 @@ mod tests { use super::RequestInfoInterceptor; use crate::request_info::RequestPairs; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime::client::orchestrator::interceptors::RequestAttempts; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; - use aws_smithy_types::type_erasure::TypedBox; + use aws_smithy_types::type_erasure::TypeErasedBox; use http::HeaderValue; use std::time::Duration; @@ -178,7 +186,7 @@ mod tests { #[test] fn test_request_pairs_for_initial_attempt() { - let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + let mut context = InterceptorContext::new(TypeErasedBox::doesnt_matter()); context.enter_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); @@ -189,7 +197,6 @@ mod tests { .read_timeout(Duration::from_secs(30)) .build(), ); - config.put(RequestAttempts::new()); let _ = context.take_input(); context.enter_before_transmit_phase(); diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 51bace059a..52c6cdb4da 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -112,7 +112,7 @@ mod tests { use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::error::display::DisplayErrorContext; - use aws_smithy_types::type_erasure::TypedBox; + use aws_smithy_types::type_erasure::TypeErasedBox; fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a str { context @@ -126,7 +126,7 @@ mod tests { } fn context() -> InterceptorContext { - let mut context = InterceptorContext::new(TypedBox::new("doesntmatter").erase()); + let mut context = InterceptorContext::new(TypeErasedBox::doesnt_matter()); context.enter_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let _ = context.take_input(); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index 62d3f79368..1b4243c65c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -33,7 +33,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::interceptors::SharedInterceptor"), "TestParamsSetterInterceptor" to CargoDependency.smithyRuntime(runtimeConfig).withFeature("test-util") - .toType().resolve("client::test_util::interceptor::TestParamsSetterInterceptor"), + .toType().resolve("client::test_util::interceptors::TestParamsSetterInterceptor"), ) override fun section(section: CustomizableOperationSection): Writable = diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt index dc2afd1a35..b0ae6a70eb 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt @@ -41,8 +41,13 @@ fun RuntimeConfig.awsRoot(): RuntimeCrateLocation { } object AwsRuntimeType { - fun presigning(): RuntimeType = - RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning", visibility = Visibility.PUBLIC)) + fun presigning(): RuntimeType = RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "presigning", + visibility = Visibility.PUBLIC, + CargoDependency.Tower, + ), + ) // TODO(enableNewSmithyRuntime): Delete defaultMiddleware and middleware.rs, and remove tower dependency from inlinables, when cleaning up middleware fun RuntimeConfig.defaultMiddleware() = RuntimeType.forInlineDependency( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt index c608f1c573..220b57169a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt @@ -10,7 +10,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.util.letIf @@ -29,15 +29,20 @@ class InvocationIdDecorator : ClientCodegenDecorator { private class InvocationIdRuntimePluginCustomization( private val codegenContext: ClientCodegenContext, ) : ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) + private val codegenScope = arrayOf( + "InvocationIdInterceptor" to awsRuntime.resolve("invocation_id::InvocationIdInterceptor"), + ) + override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.AdditionalConfig) { - section.registerInterceptor(codegenContext.runtimeConfig, this) { - rust( - "#T::new()", - AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig) - .resolve("invocation_id::InvocationIdInterceptor"), - ) + when (section) { + is ServiceRuntimePluginSection.AdditionalConfig -> { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rustTemplate("#{InvocationIdInterceptor}::new()", *codegenScope) + } } + else -> emptySection } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt index d930693988..4f8a342e31 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt @@ -41,11 +41,6 @@ private class AddRetryInformationHeaderInterceptors(codegenContext: ClientCodege rust("#T::new()", smithyRuntime.resolve("client::orchestrator::interceptors::ServiceClockSkewInterceptor")) } - // Track the number of request attempts made. - section.registerInterceptor(runtimeConfig, this) { - rust("#T::new()", smithyRuntime.resolve("client::orchestrator::interceptors::RequestAttemptsInterceptor")) - } - // Add request metadata to outgoing requests. Sets a header. section.registerInterceptor(runtimeConfig, this) { rust("#T::new()", awsRuntime.resolve("request_info::RequestInfoInterceptor")) diff --git a/aws/sdk/integration-tests/transcribestreaming/Cargo.toml b/aws/sdk/integration-tests/transcribestreaming/Cargo.toml index 38b1018cc5..181ba493cb 100644 --- a/aws/sdk/integration-tests/transcribestreaming/Cargo.toml +++ b/aws/sdk/integration-tests/transcribestreaming/Cargo.toml @@ -22,4 +22,5 @@ hound = "3.4.0" http = "0.2.0" serde_json = "1.0.0" tokio = { version = "1.23.1", features = ["full", "test-util"] } +tracing = "0.1" tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs index 43e62b4d42..28a8df6c04 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs @@ -13,6 +13,7 @@ use aws_smithy_client::dvr::MediaType; use aws_smithy_client::erase::DynConnector; use aws_smithy_runtime::client::retries::strategy::FixedDelayRetryStrategy; use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; +use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::client::orchestrator::ConfigBagAccessors; use aws_smithy_types::config_bag::ConfigBag; diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs index 69f89d1a9d..3e6c64be5d 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs @@ -27,9 +27,7 @@ async fn sra_test() { .interceptor(util::TestUserAgentInterceptor) .build(); let client = Client::from_conf(config); - let fixup = util::FixupPlugin { - timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), - }; + let fixup = util::FixupPlugin; let resp = dbg!( client diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 1c7904a386..5351a10e56 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -146,7 +146,7 @@ class ServiceRuntimePluginGenerator( let sleep_impl = self.handle.conf.sleep_impl(); let timeout_config = self.handle.conf.timeout_config(); - let connector_settings = timeout_config.map(|c| #{ConnectorSettings}::from_timeout_config(c)).unwrap_or_default(); + let connector_settings = timeout_config.map(#{ConnectorSettings}::from_timeout_config).unwrap_or_default(); let connection: #{Box} = #{Box}::new(#{DynConnectorAdapter}::new( // TODO(enableNewSmithyRuntime): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation #{require_connector}( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index eab249a9c0..8a64e514c1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -154,9 +154,9 @@ class CustomizableOperationGenerator( "Interceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::interceptors::Interceptor"), "MapRequestInterceptor" to RuntimeType.smithyRuntime(runtimeConfig) - .resolve("client::interceptor::MapRequestInterceptor"), + .resolve("client::interceptors::MapRequestInterceptor"), "MutateRequestInterceptor" to RuntimeType.smithyRuntime(runtimeConfig) - .resolve("client::interceptor::MutateRequestInterceptor"), + .resolve("client::interceptors::MutateRequestInterceptor"), "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), "SendResult" to ClientRustModule.Client.customize.toType() .resolve("internal::SendResult"), diff --git a/codegen-core/common-test-models/pokemon.smithy b/codegen-core/common-test-models/pokemon.smithy index 745f51d93c..014ee61c41 100644 --- a/codegen-core/common-test-models/pokemon.smithy +++ b/codegen-core/common-test-models/pokemon.smithy @@ -162,4 +162,4 @@ structure StreamPokemonRadioOutput { } @streaming -blob StreamingBlob \ No newline at end of file +blob StreamingBlob diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt index f0f0ee8227..192facc2ba 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt @@ -294,7 +294,7 @@ fun validateUnsupportedConstraints( messages += LogMessage( Level.SEVERE, """ - The `ignoreUnsupportedConstraints` flag in the `codegen` configuration is set to `true`, but it has no + The `ignoreUnsupportedConstraints` flag in the `codegen` configuration is set to `true`, but it has no effect. All the constraint traits used in the model are well-supported, please remove this flag. """.trimIndent().replace("\n", " "), ) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt index 3c5e62e40f..a140322e36 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraintsAreNotUsedTest.kt @@ -259,7 +259,7 @@ internal class ValidateUnsupportedConstraintsAreNotUsedTest { validationResult.shouldAbort shouldBe true validationResult.messages[0].message shouldContain( """ - The `ignoreUnsupportedConstraints` flag in the `codegen` configuration is set to `true`, but it has no + The `ignoreUnsupportedConstraints` flag in the `codegen` configuration is set to `true`, but it has no effect. All the constraint traits used in the model are well-supported, please remove this flag. """.trimIndent().replace("\n", " ") ) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client.rs b/rust-runtime/aws-smithy-runtime-api/src/client.rs index 1c4a97a62b..5627d8ffc7 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client.rs @@ -24,3 +24,6 @@ pub mod runtime_plugin; /// Smithy auth runtime plugins pub mod auth; + +/// A type to track the number of requests sent by the orchestrator for a given operation. +pub mod request_attempts; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index 1477aa4489..12722aef6c 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -612,6 +612,10 @@ impl Deref for SharedInterceptor { pub struct InterceptorRegistrar(Vec); impl InterceptorRegistrar { + /// Register an interceptor with this `InterceptorRegistrar`. + /// + /// When this `InterceptorRegistrar` is passed to an orchestrator, the orchestrator will run the + /// registered interceptor for all the "hooks" that it implements. pub fn register(&mut self, interceptor: SharedInterceptor) { self.0.push(interceptor); } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index c393f2e766..9b58b816db 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -36,8 +36,7 @@ use aws_smithy_http::result::SdkError; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; use phase::Phase; -use std::fmt::Debug; -use std::mem; +use std::{fmt, mem}; use tracing::{error, trace}; pub type Input = TypeErasedBox; @@ -56,7 +55,7 @@ type Response = HttpResponse; #[derive(Debug)] pub struct InterceptorContext where - E: Debug, + E: fmt::Debug, { pub(crate) input: Option, pub(crate) output_or_error: Option>>, @@ -84,7 +83,7 @@ impl InterceptorContext { impl InterceptorContext where - E: Debug, + E: fmt::Debug, { /// Decomposes the context into its constituent parts. #[doc(hidden)] @@ -113,7 +112,7 @@ where .. } = self; output_or_error - .expect("output_or_error must always beset before finalize is called.") + .expect("output_or_error must always be set before finalize is called.") .map_err(|error| OrchestratorError::into_sdk_error(error, &phase, response)) } @@ -189,6 +188,7 @@ where /// Advance to the Serialization phase. #[doc(hidden)] pub fn enter_serialization_phase(&mut self) { + trace!("entering \'serialization\' phase"); debug_assert!( self.phase.is_before_serialization(), "called enter_serialization_phase but phase is not before 'serialization'" @@ -199,6 +199,7 @@ where /// Advance to the BeforeTransmit phase. #[doc(hidden)] pub fn enter_before_transmit_phase(&mut self) { + trace!("entering \'before transmit\' phase"); debug_assert!( self.phase.is_serialization(), "called enter_before_transmit_phase but phase is not 'serialization'" @@ -215,13 +216,13 @@ where self.request() .expect("request is set before calling enter_before_transmit_phase"), ); - self.tainted = true; self.phase = Phase::BeforeTransmit; } /// Advance to the Transmit phase. #[doc(hidden)] pub fn enter_transmit_phase(&mut self) { + trace!("entering \'transmit\' phase"); debug_assert!( self.phase.is_before_transmit(), "called enter_transmit_phase but phase is not before transmit" @@ -232,6 +233,7 @@ where /// Advance to the BeforeDeserialization phase. #[doc(hidden)] pub fn enter_before_deserialization_phase(&mut self) { + trace!("entering \'before deserialization\' phase"); debug_assert!( self.phase.is_transmit(), "called enter_before_deserialization_phase but phase is not 'transmit'" @@ -250,6 +252,7 @@ where /// Advance to the Deserialization phase. #[doc(hidden)] pub fn enter_deserialization_phase(&mut self) { + trace!("entering \'deserialization\' phase"); debug_assert!( self.phase.is_before_deserialization(), "called enter_deserialization_phase but phase is not 'before deserialization'" @@ -260,6 +263,7 @@ where /// Advance to the AfterDeserialization phase. #[doc(hidden)] pub fn enter_after_deserialization_phase(&mut self) { + trace!("entering \'after deserialization\' phase"); debug_assert!( self.phase.is_deserialization(), "called enter_after_deserialization_phase but phase is not 'deserialization'" @@ -271,23 +275,45 @@ where self.phase = Phase::AfterDeserialization; } - // Returns false if rewinding isn't possible - pub fn rewind(&mut self, _cfg: &mut ConfigBag) -> bool { - // If before transmit was never touched, then we don't need to rewind - if !self.tainted { - return true; + /// Set the request checkpoint. This should only be called once, right before entering the retry loop. + #[doc(hidden)] + pub fn save_checkpoint(&mut self) { + trace!("saving request checkpoint..."); + self.request_checkpoint = self.request().and_then(try_clone); + match self.request_checkpoint.as_ref() { + Some(_) => trace!("successfully saved request checkpoint"), + None => trace!("failed to save request checkpoint: request body could not be cloned"), + } + } + + /// Returns false if rewinding isn't possible + #[doc(hidden)] + pub fn rewind(&mut self, _cfg: &mut ConfigBag) -> RewindResult { + // If request_checkpoint was never set, but we've already made one attempt, + // then this is not a retryable request + if self.request_checkpoint.is_none() && self.tainted { + return RewindResult::Impossible; } - // If request_checkpoint was never set, then this is not a retryable request - if self.request_checkpoint.is_none() { - return false; + + if !self.tainted { + // The first call to rewind() happens before the request is ever touched, so we don't need + // to clone it then. However, the request must be marked as tainted so that subsequent calls + // to rewind() properly reload the saved request checkpoint. + self.tainted = true; + return RewindResult::Unnecessary; } - // Otherwise, rewind back to the beginning of BeforeTransmit + + // Otherwise, rewind to the saved request checkpoint // TODO(enableNewSmithyRuntime): Also rewind the ConfigBag self.phase = Phase::BeforeTransmit; self.request = try_clone(self.request_checkpoint.as_ref().expect("checked above")); + assert!( + self.request.is_some(), + "if the request wasn't cloneable, then we should have already return from this method." + ); self.response = None; self.output_or_error = None; - true + RewindResult::Occurred } /// Mark this context as failed due to errors during the operation. Any errors already contained @@ -313,6 +339,33 @@ where } } +/// The result of attempting to rewind a request. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[doc(hidden)] +pub enum RewindResult { + /// The request couldn't be rewound because it wasn't cloneable. + Impossible, + /// The request wasn't rewound because it was unnecessary. + Unnecessary, + /// The request was rewound successfully. + Occurred, +} + +impl fmt::Display for RewindResult { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RewindResult::Impossible => write!( + f, + "The request couldn't be rewound because it wasn't cloneable." + ), + RewindResult::Unnecessary => { + write!(f, "The request wasn't rewound because it was unnecessary.") + } + RewindResult::Occurred => write!(f, "The request was rewound successfully."), + } + } +} + fn try_clone(request: &HttpRequest) -> Option { let cloned_body = request.body().try_clone()?; let mut cloned_request = ::http::Request::builder() @@ -346,7 +399,7 @@ mod tests { "input", context .input() - .map(|i| i.downcast_ref::().unwrap()) + .and_then(|i| i.downcast_ref::()) .unwrap() ); context.input_mut(); @@ -404,7 +457,7 @@ mod tests { "input", context .input() - .map(|i| i.downcast_ref::().unwrap()) + .and_then(|i| i.downcast_ref::()) .unwrap() ); @@ -417,7 +470,8 @@ mod tests { .unwrap(), ); context.enter_before_transmit_phase(); - + context.save_checkpoint(); + assert_eq!(context.rewind(&mut cfg), RewindResult::Unnecessary); // Modify the test header post-checkpoint to simulate modifying the request for signing or a mutating interceptor context.request_mut().unwrap().headers_mut().remove("test"); context.request_mut().unwrap().headers_mut().insert( @@ -437,7 +491,7 @@ mod tests { context.enter_deserialization_phase(); context.set_output_or_error(Err(OrchestratorError::operation(error))); - assert!(context.rewind(&mut cfg)); + assert_eq!(context.rewind(&mut cfg), RewindResult::Occurred); // Now after rewinding, the test header should be its original value assert_eq!( diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs index 34296dbcac..56e6b9f4cc 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs @@ -24,7 +24,8 @@ pub enum OrchestratorError { impl OrchestratorError { /// Create a new `OrchestratorError` from a [`BoxError`]. - pub fn other(err: BoxError) -> Self { + pub fn other(err: impl Into>) -> Self { + let err = err.into(); Self::Other { err } } @@ -130,6 +131,6 @@ where E: Debug + std::error::Error + 'static, { fn from(err: aws_smithy_http::byte_stream::error::Error) -> Self { - Self::other(err.into()) + Self::other(err) } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs b/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs new file mode 100644 index 0000000000..3cfe306f10 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs @@ -0,0 +1,26 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[derive(Debug, Clone, Copy)] +pub struct RequestAttempts { + attempts: usize, +} + +impl RequestAttempts { + #[cfg(any(feature = "test-util", test))] + pub fn new(attempts: usize) -> Self { + Self { attempts } + } + + pub fn attempts(&self) -> usize { + self.attempts + } +} + +impl From for RequestAttempts { + fn from(attempts: usize) -> Self { + Self { attempts } + } +} diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index aadbfcac83..d1fd93c93b 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -31,6 +31,7 @@ tracing = "0.1.37" [dev-dependencies] aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio", "test-util"] } +aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api", features = ["test-util"] } tokio = { version = "1.25", features = ["macros", "rt", "test-util"] } tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } tracing-test = "0.2.1" diff --git a/rust-runtime/aws-smithy-runtime/external-types.toml b/rust-runtime/aws-smithy-runtime/external-types.toml index 92360e722a..a639820020 100644 --- a/rust-runtime/aws-smithy-runtime/external-types.toml +++ b/rust-runtime/aws-smithy-runtime/external-types.toml @@ -1,5 +1,6 @@ allowed_external_types = [ "aws_smithy_runtime_api::*", + "aws_smithy_async::*", "aws_smithy_http::*", "aws_smithy_types::*", "aws_smithy_client::erase::DynConnector", diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index 1558db8bfe..0c8a1705f7 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -32,4 +32,4 @@ pub mod runtime_plugin; pub mod identity; /// Interceptors for Smithy clients. -pub mod interceptor; +pub mod interceptors; diff --git a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs b/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs index f948daf746..2ca2db43b1 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs @@ -5,6 +5,7 @@ //! Module with client connectors useful for testing. +use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; @@ -13,9 +14,9 @@ use aws_smithy_runtime_api::client::orchestrator::{ }; use http::header::{HeaderName, CONTENT_TYPE}; use std::fmt::Debug; -use std::future::ready; use std::ops::Deref; use std::sync::{Arc, Mutex}; +use std::time::Duration; use tokio::sync::oneshot; /// Test Connection to capture a single request @@ -92,7 +93,44 @@ pub fn capture_request( ) } -type ConnectVec = Vec<(HttpRequest, HttpResponse)>; +type ConnectionEvents = Vec; + +#[derive(Debug)] +pub struct ConnectionEvent { + latency: Duration, + req: HttpRequest, + res: HttpResponse, +} + +impl ConnectionEvent { + pub fn new(req: HttpRequest, res: HttpResponse) -> Self { + Self { + res, + req, + latency: Duration::from_secs(0), + } + } + + /// Add simulated latency to this `ConnectionEvent` + pub fn with_latency(mut self, latency: Duration) -> Self { + self.latency = latency; + self + } + + pub fn req(&self) -> &HttpRequest { + &self.req + } + + pub fn res(&self) -> &HttpResponse { + &self.res + } +} + +impl From<(HttpRequest, HttpResponse)> for ConnectionEvent { + fn from((req, res): (HttpRequest, HttpResponse)) -> Self { + Self::new(req, res) + } +} #[derive(Debug)] pub struct ValidateRequest { @@ -101,20 +139,23 @@ pub struct ValidateRequest { } impl ValidateRequest { - pub fn assert_matches(&self, ignore_headers: &[HeaderName]) { + pub fn assert_matches(&self, index: usize, ignore_headers: &[HeaderName]) { let (actual, expected) = (&self.actual, &self.expected); - assert_eq!(actual.uri(), expected.uri()); + assert_eq!( + actual.uri(), + expected.uri(), + "Request #{index} - URI doesn't match expected value" + ); 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)); + .unwrap_or_else(|| panic!("Request #{index} - Header {name:?} is missing")); assert_eq!( actual_header.to_str().unwrap(), value.to_str().unwrap(), - "Header mismatch for {:?}", - name + "Request #{index} - Header {name:?} doesn't match expected value", ); } } @@ -132,7 +173,11 @@ impl ValidateRequest { }; match (actual_str, expected_str) { (Ok(actual), Ok(expected)) => assert_ok(validate_body(actual, expected, media_type)), - _ => assert_eq!(actual.body().bytes(), expected.body().bytes()), + _ => assert_eq!( + actual.body().bytes(), + expected.body().bytes(), + "Request #{index} - Body contents didn't match expected value" + ), }; } } @@ -142,28 +187,20 @@ impl ValidateRequest { /// A basic test connection. It will: /// - Respond to requests with a preloaded series of responses /// - Record requests for future examination -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TestConnection { - data: Arc>, + data: Arc>, requests: Arc>>, -} - -// Need a clone impl that ignores `B` -impl Clone for TestConnection { - fn clone(&self) -> Self { - TestConnection { - data: self.data.clone(), - requests: self.requests.clone(), - } - } + sleep_impl: Arc, } impl TestConnection { - pub fn new(mut data: ConnectVec) -> Self { + pub fn new(mut data: ConnectionEvents, sleep_impl: Arc) -> Self { data.reverse(); TestConnection { data: Arc::new(Mutex::new(data)), requests: Default::default(), + sleep_impl, } } @@ -173,33 +210,40 @@ impl TestConnection { #[track_caller] pub fn assert_requests_match(&self, ignore_headers: &[HeaderName]) { - for req in self.requests().iter() { - req.assert_matches(ignore_headers) + for (i, req) in self.requests().iter().enumerate() { + req.assert_matches(i, ignore_headers) } - let remaining_requests = self.data.lock().unwrap().len(); + let remaining_requests = self.data.lock().unwrap(); + let number_of_remaining_requests = remaining_requests.len(); let actual_requests = self.requests().len(); - assert_eq!( - remaining_requests, 0, - "Expected {} additional requests ({} were made)", - remaining_requests, actual_requests + assert!( + remaining_requests.is_empty(), + "Expected {number_of_remaining_requests} additional requests (only {actual_requests} sent)", ); } } impl Connection for TestConnection { fn call(&self, request: HttpRequest) -> BoxFuture { - // TODO(orchestrator) Validate request - - let res = if let Some((expected, resp)) = self.data.lock().unwrap().pop() { + // TODO(enableNewSmithyRuntime) Validate request + let (res, simulated_latency) = if let Some(event) = self.data.lock().unwrap().pop() { self.requests.lock().unwrap().push(ValidateRequest { - expected, + expected: event.req, actual: request, }); - Ok(resp.map(SdkBody::from)) + + (Ok(event.res.map(SdkBody::from)), event.latency) } else { - Err(ConnectorError::other("No more data".into(), None).into()) + ( + Err(ConnectorError::other("No more data".into(), None).into()), + Duration::from_secs(0), + ) }; - Box::pin(ready(res)) + let sleep = self.sleep_impl.sleep(simulated_latency); + Box::pin(async move { + sleep.await; + res + }) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptor.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs similarity index 100% rename from rust-runtime/aws-smithy-runtime/src/client/interceptor.rs rename to rust-runtime/aws-smithy-runtime/src/client/interceptors.rs diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 42d23e2bcd..12abb526ea 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -10,16 +10,17 @@ use crate::client::timeout::{MaybeTimeout, ProvideMaybeTimeoutConfig, TimeoutKin use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream::ByteStream; use aws_smithy_http::result::SdkError; -use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output}; +use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output, RewindResult}; use aws_smithy_runtime_api::client::interceptors::{InterceptorContext, Interceptors}; use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, ConfigBagAccessors, HttpResponse, LoadedRequestBody, + BoxError, ConfigBagAccessors, HttpResponse, LoadedRequestBody, OrchestratorError, }; +use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::ShouldAttempt; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; use aws_smithy_types::config_bag::ConfigBag; use std::mem; -use tracing::{debug_span, Instrument}; +use tracing::{debug, debug_span, instrument, trace, Instrument}; mod auth; /// Defines types that implement a trait for endpoint resolution @@ -29,6 +30,7 @@ pub mod interceptors; macro_rules! halt { ([$ctx:ident] => $err:expr) => {{ + trace!("encountered orchestrator error, continuing"); $ctx.fail($err.into()); return; }}; @@ -46,6 +48,7 @@ macro_rules! halt_on_err { macro_rules! continue_on_err { ([$ctx:ident] => $expr:expr) => { if let Err(err) = $expr { + trace!("encountered orchestrator error, continuing"); $ctx.fail(err.into()); } }; @@ -82,6 +85,7 @@ pub async fn invoke( /// Apply configuration is responsible for apply runtime plugins to the config bag, as well as running /// `read_before_execution` interceptors. If a failure occurs due to config construction, `invoke` /// will raise it to the user. If an interceptor fails, then `invoke` +#[instrument(skip_all)] fn apply_configuration( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, @@ -97,6 +101,7 @@ fn apply_configuration( Ok(()) } +#[instrument(skip_all)] async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: &Interceptors) { // Before serialization halt_on_err!([ctx] => interceptors.read_before_serialization(ctx, cfg)); @@ -129,10 +134,10 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: let retry_strategy = cfg.retry_strategy(); match retry_strategy.should_attempt_initial_request(cfg) { // Yes, let's make a request - Ok(ShouldAttempt::Yes) => { /* Keep going */ } + Ok(ShouldAttempt::Yes) => trace!("retry strategy has OKed initial request"), // No, this request shouldn't be sent Ok(ShouldAttempt::No) => { - let err: BoxError = "The retry strategy indicates that an initial request shouldn't be made, but it did specify why.".into(); + let err: BoxError = "The retry strategy indicates that an initial request shouldn't be made, but it didn't specify why.".into(); halt!([ctx] => err); } // No, we shouldn't make a request because... @@ -142,16 +147,36 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: } } - loop { + // Save a request checkpoint before we make the request. This will allow us to "rewind" + // the request in the case of retry attempts. + ctx.save_checkpoint(); + for i in 0usize.. { + trace!("beginning attempt #{i}"); + // Break from the loop if we can't rewind the request's state. This will always succeed the + // first time, but will fail on subsequent iterations if the request body wasn't retryable. + match ctx.rewind(cfg) { + r @ RewindResult::Impossible => { + debug!("{r}"); + break; + } + r @ RewindResult::Occurred => debug!("{r}"), + r @ RewindResult::Unnecessary => debug!("{r}"), + } + // Track which attempt we're currently on. + cfg.put::(i.into()); let attempt_timeout_config = cfg.maybe_timeout_config(TimeoutKind::OperationAttempt); - async { + let maybe_timeout = async { try_attempt(ctx, cfg, interceptors).await; finally_attempt(ctx, cfg, interceptors).await; Result::<_, SdkError>::Ok(()) } .maybe_timeout_with_config(attempt_timeout_config) .await - .expect("These are infallible; The retry strategy will decide whether to stop or not."); + .map_err(OrchestratorError::other); + + // We continue when encountering a timeout error. The retry classifier will decide what to do with it. + continue_on_err!([ctx] => maybe_timeout); + let retry_strategy = cfg.retry_strategy(); let should_attempt = halt_on_err!([ctx] => retry_strategy.should_attempt_retry(ctx, cfg)); match should_attempt { @@ -159,16 +184,21 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: ShouldAttempt::Yes => continue, // No, this request shouldn't be retried ShouldAttempt::No => { + trace!("this error is not retryable, exiting attempt loop"); break; } - ShouldAttempt::YesAfterDelay(_delay) => { - // TODO(enableNewSmithyRuntime): implement retries with explicit delay - todo!("implement retries with an explicit delay.") + ShouldAttempt::YesAfterDelay(delay) => { + let sleep_impl = halt_on_err!([ctx] => cfg.sleep_impl().ok_or(OrchestratorError::other( + "The retry strategy requested a delay before sending the next request, but no 'async sleep' implementation was set." + ))); + sleep_impl.sleep(delay).await; + continue; } } } } +#[instrument(skip_all)] async fn try_attempt( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, @@ -221,6 +251,7 @@ async fn try_attempt( halt_on_err!([ctx] => interceptors.read_after_deserialization(ctx, cfg)); } +#[instrument(skip_all)] async fn finally_attempt( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, @@ -230,6 +261,7 @@ async fn finally_attempt( continue_on_err!([ctx] => interceptors.read_after_attempt(ctx, cfg)); } +#[instrument(skip_all)] async fn finally_op( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs index 081b9b3bd7..b9de2daa37 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs @@ -3,8 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -mod request_attempts; mod service_clock_skew; -pub use request_attempts::{RequestAttempts, RequestAttemptsInterceptor}; pub use service_clock_skew::{ServiceClockSkew, ServiceClockSkewInterceptor}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs deleted file mode 100644 index c42971cb21..0000000000 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/request_attempts.rs +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_smithy_runtime_api::client::interceptors::{ - BeforeTransmitInterceptorContextMut, BoxError, Interceptor, -}; -use aws_smithy_types::config_bag::ConfigBag; - -#[derive(Debug, Clone, Default)] -#[non_exhaustive] -pub struct RequestAttempts { - attempts: u32, -} - -impl RequestAttempts { - pub fn new() -> Self { - Self::default() - } - - // There is no legitimate reason to set this unless you're testing things. - // Therefore, this is only available for tests. - #[cfg(test)] - pub fn new_with_attempts(attempts: u32) -> Self { - Self { attempts } - } - - pub fn attempts(&self) -> u32 { - self.attempts - } - - fn increment(mut self) -> Self { - self.attempts += 1; - self - } -} - -#[derive(Debug, Default)] -#[non_exhaustive] -pub struct RequestAttemptsInterceptor {} - -impl RequestAttemptsInterceptor { - pub fn new() -> Self { - Self::default() - } -} - -impl Interceptor for RequestAttemptsInterceptor { - fn modify_before_retry_loop( - &self, - _ctx: &mut BeforeTransmitInterceptorContextMut<'_>, - cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { - cfg.put(RequestAttempts::new()); - Ok(()) - } - - fn modify_before_transmit( - &self, - _ctx: &mut BeforeTransmitInterceptorContextMut<'_>, - cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { - if let Some(request_attempts) = cfg.get::().cloned() { - cfg.put(request_attempts.increment()); - } - Ok(()) - } -} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs index 243c0a3298..66db997eae 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::client::orchestrator::interceptors::RequestAttempts; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::BoxError; +use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, ShouldAttempt, }; @@ -56,7 +56,7 @@ impl RetryStrategy for FixedDelayRetryStrategy { let request_attempts: &RequestAttempts = cfg .get() .expect("at least one request attempt is made before any retry is attempted"); - if request_attempts.attempts() >= self.max_attempts { + if request_attempts.attempts() >= self.max_attempts as usize { tracing::trace!( attempts = request_attempts.attempts(), max_attempts = self.max_attempts, diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs index 71afb382e4..101c971eff 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs @@ -5,5 +5,5 @@ pub mod connector; pub mod deserializer; -pub mod interceptor; +pub mod interceptors; pub mod serializer; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs similarity index 94% rename from rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs rename to rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs index d8d8c6ec32..07681ba274 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptor.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs @@ -61,12 +61,11 @@ mod tests { ctx.enter_before_transmit_phase(); let mut ctx = Into::into(&mut ctx); let request_time = UNIX_EPOCH + Duration::from_secs(1624036048); - let interceptor = TestParamsSetterInterceptor::new({ - let request_time = request_time.clone(); + let interceptor = TestParamsSetterInterceptor::new( move |_: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag| { cfg.set_request_time(request_time); - } - }); + }, + ); interceptor .modify_before_signing(&mut ctx, &mut cfg) .unwrap(); diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index 7ebd3ece23..aa2462d6d0 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -7,6 +7,9 @@ edition = "2021" license = "Apache-2.0" repository = "https://github.com/awslabs/smithy-rs" +[features] +test-util = [] + [dependencies] itoa = "1.0.0" num-integer = "0.1.44" diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index 0100a99f97..f307d927b8 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -275,7 +275,9 @@ impl FrozenConfigBag { } impl ConfigBag { - /// Creates a new config bag "base". Configuration may then be "layered" onto the base by calling + /// Create a new config bag "base". + /// + /// Configuration may then be "layered" onto the base by calling /// [`ConfigBag::store_put`], [`ConfigBag::store_or_unset`], [`ConfigBag::store_append`]. Layers /// of configuration may then be "frozen" (made immutable) by calling [`ConfigBag::freeze`]. pub fn base() -> Self { @@ -495,11 +497,13 @@ pub struct ItemIter<'a, T> { inner: BagIter<'a>, t: PhantomData, } + impl<'a, T> Debug for ItemIter<'a, T> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "ItemIter") } } + impl<'a, T: 'a> Iterator for ItemIter<'a, T> where T: Store, diff --git a/rust-runtime/aws-smithy-types/src/date_time/mod.rs b/rust-runtime/aws-smithy-types/src/date_time/mod.rs index fd9555b5e6..84ac29799b 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/mod.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs @@ -176,6 +176,12 @@ impl DateTime { self.seconds } + /// Set the seconds component of this `DateTime`. + pub fn set_seconds(&mut self, seconds: i64) -> &mut Self { + self.seconds = seconds; + self + } + /// Returns the sub-second nanos component of the `DateTime`. /// /// _Note: this does not include the number of seconds since the epoch._ @@ -183,6 +189,12 @@ impl DateTime { self.subsecond_nanos } + /// Set the "sub-second" nanoseconds of this `DateTime`. + pub fn set_subsec_nanos(&mut self, subsec_nanos: u32) -> &mut Self { + self.subsecond_nanos = subsec_nanos; + self + } + /// Converts the `DateTime` to the number of milliseconds since the Unix epoch. /// /// This is fallible since `DateTime` holds more precision than an `i64`, and will diff --git a/rust-runtime/aws-smithy-types/src/type_erasure.rs b/rust-runtime/aws-smithy-types/src/type_erasure.rs index b27d9df347..99025d4617 100644 --- a/rust-runtime/aws-smithy-types/src/type_erasure.rs +++ b/rust-runtime/aws-smithy-types/src/type_erasure.rs @@ -106,6 +106,17 @@ pub struct TypeErasedBox { >, } +#[cfg(feature = "test-util")] +impl TypeErasedBox { + /// Often, when testing the orchestrator or its components, it's necessary to provide a + /// `TypeErasedBox` to serve as an `Input` for `invoke`. In cases where the type won't actually + /// be accessed during testing, use this method to generate a `TypeErasedBox` that makes it + /// clear that "for the purpose of this test, the `Input` doesn't matter." + pub fn doesnt_matter() -> Self { + Self::new("doesn't matter") + } +} + impl fmt::Debug for TypeErasedBox { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("TypeErasedBox:")?; @@ -117,7 +128,7 @@ impl TypeErasedBox { /// Create a new `TypeErasedBox` from `value` of type `T` pub fn new(value: T) -> Self { let debug = |value: &Box, f: &mut fmt::Formatter<'_>| { - fmt::Debug::fmt(value.downcast_ref::().expect("typechecked"), f) + fmt::Debug::fmt(value.downcast_ref::().expect("type-checked"), f) }; Self { field: Box::new(value), From 7ea3ed92f849e29a74137fe999cb06b05f219051 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Fri, 2 Jun 2023 10:18:10 -0500 Subject: [PATCH 129/253] No really, add the standard retry strategy to the orchestrator (#2741) ## Description This PR adds support for the Standard retry strategy. The standard strategy will be inserted into a service's config bag if a retry config was set. Otherwise, unless another retry strategy was already set, a `NeverRetryStrategy` will be set. This seemed like a reasonable default, but I'm open to suggestions. ## Testing tests are included --- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../ServiceRuntimePluginGenerator.kt | 12 +- .../src/client/orchestrator.rs | 8 +- rust-runtime/aws-smithy-runtime/Cargo.toml | 2 + .../src/client/orchestrator.rs | 15 +- .../src/client/retries/strategy.rs | 2 + .../src/client/retries/strategy/standard.rs | 221 ++++++++++++++++++ 6 files changed, 250 insertions(+), 10 deletions(-) create mode 100644 rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 5351a10e56..e495aeae2c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -91,6 +91,7 @@ class ServiceRuntimePluginGenerator( "HttpConnector" to client.resolve("http_connector::HttpConnector"), "IdentityResolvers" to runtimeApi.resolve("client::identity::IdentityResolvers"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), + "StandardRetryStrategy" to runtime.resolve("client::retries::strategy::StandardRetryStrategy"), "NeverRetryStrategy" to runtime.resolve("client::retries::strategy::NeverRetryStrategy"), "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), "Params" to endpointTypesGenerator.paramsStruct(), @@ -141,11 +142,16 @@ class ServiceRuntimePluginGenerator( #{retry_classifier_customizations}; cfg.set_retry_classifiers(retry_classifiers); - // TODO(enableNewSmithyRuntime): Wire up standard retry - cfg.set_retry_strategy(#{NeverRetryStrategy}::new()); - let sleep_impl = self.handle.conf.sleep_impl(); let timeout_config = self.handle.conf.timeout_config(); + let retry_config = self.handle.conf.retry_config(); + + if let Some(retry_config) = retry_config { + cfg.set_retry_strategy(#{StandardRetryStrategy}::new(retry_config)); + } else if cfg.retry_strategy().is_none() { + cfg.set_retry_strategy(#{NeverRetryStrategy}::new()); + } + let connector_settings = timeout_config.map(#{ConnectorSettings}::from_timeout_config).unwrap_or_default(); let connection: #{Box} = #{Box}::new(#{DynConnectorAdapter}::new( // TODO(enableNewSmithyRuntime): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 4585da8f35..6e041566ea 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -135,7 +135,7 @@ pub trait ConfigBagAccessors { fn retry_classifiers(&self) -> &RetryClassifiers; fn set_retry_classifiers(&mut self, retry_classifier: RetryClassifiers); - fn retry_strategy(&self) -> &dyn RetryStrategy; + fn retry_strategy(&self) -> Option<&dyn RetryStrategy>; fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static); fn request_time(&self) -> Option; @@ -255,10 +255,8 @@ impl ConfigBagAccessors for ConfigBag { self.put::(retry_classifiers); } - fn retry_strategy(&self) -> &dyn RetryStrategy { - &**self - .get::>() - .expect("a retry strategy must be set") + fn retry_strategy(&self) -> Option<&dyn RetryStrategy> { + self.get::>().map(|rs| &**rs) } fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static) { diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index d1fd93c93b..3510ba2807 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -28,10 +28,12 @@ pin-project-lite = "0.2.7" pin-utils = "0.1.0" tokio = { version = "1.25", features = [] } tracing = "0.1.37" +fastrand = "1.4" [dev-dependencies] aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio", "test-util"] } aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api", features = ["test-util"] } +aws-smithy-types = { path = "../aws-smithy-types", features = ["test-util"] } tokio = { version = "1.25", features = ["macros", "rt", "test-util"] } tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } tracing-test = "0.2.1" diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 12abb526ea..f626816dce 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -132,7 +132,12 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: halt_on_err!([ctx] => interceptors.modify_before_retry_loop(ctx, cfg)); let retry_strategy = cfg.retry_strategy(); - match retry_strategy.should_attempt_initial_request(cfg) { + // If we got a retry strategy from the bag, ask it what to do. + // Otherwise, assume we should attempt the initial request. + let should_attempt = retry_strategy + .map(|rs| rs.should_attempt_initial_request(cfg)) + .unwrap_or(Ok(ShouldAttempt::Yes)); + match should_attempt { // Yes, let's make a request Ok(ShouldAttempt::Yes) => trace!("retry strategy has OKed initial request"), // No, this request shouldn't be sent @@ -178,7 +183,13 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: continue_on_err!([ctx] => maybe_timeout); let retry_strategy = cfg.retry_strategy(); - let should_attempt = halt_on_err!([ctx] => retry_strategy.should_attempt_retry(ctx, cfg)); + // If we got a retry strategy from the bag, ask it what to do. + // If no strategy was set, we won't retry. + let should_attempt = halt_on_err!( + [ctx] => retry_strategy + .map(|rs| rs.should_attempt_retry(ctx, cfg)) + .unwrap_or(Ok(ShouldAttempt::No) + )); match should_attempt { // Yes, let's retry the request ShouldAttempt::Yes => continue, diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs index 6f1e71d39e..3805834b12 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs @@ -6,7 +6,9 @@ #[cfg(feature = "test-util")] mod fixed_delay; mod never; +mod standard; #[cfg(feature = "test-util")] pub use fixed_delay::FixedDelayRetryStrategy; pub use never::NeverRetryStrategy; +pub use standard::StandardRetryStrategy; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs new file mode 100644 index 0000000000..d19cddf9d2 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -0,0 +1,221 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors}; +use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; +use aws_smithy_runtime_api::client::retries::{ + ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt, +}; +use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::retry::RetryConfig; +use std::time::Duration; + +const DEFAULT_MAX_ATTEMPTS: usize = 4; + +#[derive(Debug)] +pub struct StandardRetryStrategy { + max_attempts: usize, + initial_backoff: Duration, + max_backoff: Duration, + base: fn() -> f64, +} + +impl StandardRetryStrategy { + pub fn new(retry_config: &RetryConfig) -> Self { + // TODO(enableNewSmithyRuntime) add support for `retry_config.reconnect_mode()` here or in the orchestrator flow. + Self::default() + .with_max_attempts(retry_config.max_attempts() as usize) + .with_initial_backoff(retry_config.initial_backoff()) + } + + pub fn with_base(mut self, base: fn() -> f64) -> Self { + self.base = base; + self + } + + pub fn with_max_attempts(mut self, max_attempts: usize) -> Self { + self.max_attempts = max_attempts; + self + } + + pub fn with_initial_backoff(mut self, initial_backoff: Duration) -> Self { + self.initial_backoff = initial_backoff; + self + } +} + +impl Default for StandardRetryStrategy { + fn default() -> Self { + Self { + max_attempts: DEFAULT_MAX_ATTEMPTS, + max_backoff: Duration::from_secs(20), + // by default, use a random base for exponential backoff + base: fastrand::f64, + initial_backoff: Duration::from_secs(1), + } + } +} + +impl RetryStrategy for StandardRetryStrategy { + // TODO(token-bucket) add support for optional cross-request token bucket + fn should_attempt_initial_request(&self, _cfg: &ConfigBag) -> Result { + Ok(ShouldAttempt::Yes) + } + + fn should_attempt_retry( + &self, + ctx: &InterceptorContext, + cfg: &ConfigBag, + ) -> Result { + // Look a the result. If it's OK then we're done; No retry required. Otherwise, we need to inspect it + let output_or_error = ctx.output_or_error().expect( + "This must never be called without reaching the point where the result exists.", + ); + if output_or_error.is_ok() { + tracing::debug!("request succeeded, no retry necessary"); + return Ok(ShouldAttempt::No); + } + + // Check if we're out of attempts + let request_attempts: &RequestAttempts = cfg + .get() + .expect("at least one request attempt is made before any retry is attempted"); + if request_attempts.attempts() >= self.max_attempts { + tracing::trace!( + attempts = request_attempts.attempts(), + max_attempts = self.max_attempts, + "not retrying because we are out of attempts" + ); + return Ok(ShouldAttempt::No); + } + + // Run the classifiers against the context to determine if we should retry + let retry_classifiers = cfg.retry_classifiers(); + let retry_reason = retry_classifiers.classify_retry(ctx); + let backoff = match retry_reason { + Some(RetryReason::Explicit(dur)) => dur, + Some(RetryReason::Error(_)) => { + let backoff = calculate_exponential_backoff( + // Generate a random base multiplier to create jitter + (self.base)(), + // Get the backoff time multiplier in seconds (with fractional seconds) + self.initial_backoff.as_secs_f64(), + // `self.local.attempts` tracks number of requests made including the initial request + // The initial attempt shouldn't count towards backoff calculations so we subtract it + (request_attempts.attempts() - 1) as u32, + ); + Duration::from_secs_f64(backoff).min(self.max_backoff) + } + Some(_) => { + unreachable!("RetryReason is non-exhaustive. Therefore, we need to cover this unreachable case.") + } + None => { + tracing::trace!( + attempts = request_attempts.attempts(), + max_attempts = self.max_attempts, + "encountered unretryable error" + ); + return Ok(ShouldAttempt::No); + } + }; + + tracing::debug!( + "attempt {} failed with {:?}; retrying after {:?}", + request_attempts.attempts(), + retry_reason.expect("the match statement above ensures this is not None"), + backoff + ); + + Ok(ShouldAttempt::YesAfterDelay(backoff)) + } +} + +fn calculate_exponential_backoff(base: f64, initial_backoff: f64, retry_attempts: u32) -> f64 { + base * initial_backoff * 2_u32.pow(retry_attempts) as f64 +} + +#[cfg(test)] +mod tests { + use super::{ShouldAttempt, StandardRetryStrategy}; + use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, OrchestratorError}; + use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; + use aws_smithy_runtime_api::client::retries::{AlwaysRetry, RetryClassifiers, RetryStrategy}; + use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::retry::ErrorKind; + use aws_smithy_types::type_erasure::TypeErasedBox; + use std::time::Duration; + + #[test] + fn no_retry_necessary_for_ok_result() { + let cfg = ConfigBag::base(); + let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + let strategy = StandardRetryStrategy::default(); + ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); + let actual = strategy + .should_attempt_retry(&ctx, &cfg) + .expect("method is infallible for this use"); + assert_eq!(ShouldAttempt::No, actual); + } + + fn set_up_cfg_and_context( + error_kind: ErrorKind, + current_request_attempts: usize, + ) -> (InterceptorContext, ConfigBag) { + let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + ctx.set_output_or_error(Err(OrchestratorError::other("doesn't matter"))); + let mut cfg = ConfigBag::base(); + cfg.set_retry_classifiers(RetryClassifiers::new().with_classifier(AlwaysRetry(error_kind))); + cfg.put(RequestAttempts::new(current_request_attempts)); + + (ctx, cfg) + } + + // Test that error kinds produce the correct "retry after X seconds" output. + // All error kinds are handled in the same way for the standard strategy. + fn test_should_retry_error_kind(error_kind: ErrorKind) { + let (ctx, cfg) = set_up_cfg_and_context(error_kind, 3); + let strategy = StandardRetryStrategy::default().with_base(|| 1.0); + let actual = strategy + .should_attempt_retry(&ctx, &cfg) + .expect("method is infallible for this use"); + assert_eq!(ShouldAttempt::YesAfterDelay(Duration::from_secs(4)), actual); + } + + #[test] + fn should_retry_transient_error_result_after_2s() { + test_should_retry_error_kind(ErrorKind::TransientError); + } + + #[test] + fn should_retry_client_error_result_after_2s() { + test_should_retry_error_kind(ErrorKind::ClientError); + } + + #[test] + fn should_retry_server_error_result_after_2s() { + test_should_retry_error_kind(ErrorKind::ServerError); + } + + #[test] + fn should_retry_throttling_error_result_after_2s() { + test_should_retry_error_kind(ErrorKind::ThrottlingError); + } + + #[test] + fn dont_retry_when_out_of_attempts() { + let current_attempts = 4; + let max_attempts = current_attempts; + let (ctx, cfg) = set_up_cfg_and_context(ErrorKind::TransientError, current_attempts); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(max_attempts); + let actual = strategy + .should_attempt_retry(&ctx, &cfg) + .expect("method is infallible for this use"); + assert_eq!(ShouldAttempt::No, actual); + } +} From 500aef3957755015e4a7219cb8bf8ee3debfacbc Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 2 Jun 2023 15:33:23 -0500 Subject: [PATCH 130/253] Add SharedAsyncSleep wrapping Arc (#2742) ## Motivation and Context Add `SharedAsyncSleep` wrapping `Arc` and update call sites of `Arc` to use `SharedAsyncSleep` ## Description Public APIs that take/return a trait object for `AsyncSleep` have been using `Arc` in the codebase. This has a downside of exposing a bare `Arc`, making it difficult to change the APIs in the future without breaking backwards compatibility. This PR adds a newtype wrapper `SharedAsyncSleep` that hides `Arc` to alleviate the said issue (the idiom of having `SharedXXX` has been seen in the codebase, e.g. `SharedTimeSource`, `SharedCredentialsProvider`, and so on). ## Testing - [x] Passed tests in CI ## Checklist - [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._ --------- Co-authored-by: Yuki Saito Co-authored-by: John DiSanti --- CHANGELOG.next.toml | 12 +++++++ aws/rust-runtime/aws-config/src/connector.rs | 4 +-- .../aws-config/src/imds/client/token.rs | 4 +-- aws/rust-runtime/aws-config/src/lib.rs | 6 ++-- .../aws-config/src/provider_config.rs | 13 ++++--- .../aws-credential-types/external-types.toml | 2 +- .../src/cache/lazy_caching.rs | 28 +++++++-------- .../aws-inlineable/src/endpoint_discovery.rs | 28 +++++++-------- .../aws-types/external-types.toml | 2 +- aws/rust-runtime/aws-types/src/sdk_config.rs | 31 ++++++++-------- .../smithy/rustsdk/CredentialProviders.kt | 2 +- .../smithy/rustsdk/SdkConfigDecorator.kt | 2 +- .../timestream/TimestreamDecorator.kt | 2 +- .../dynamodb/tests/timeouts.rs | 7 ++-- .../tests/client-construction.rs | 4 ++- .../s3/tests/alternative-async-runtime.rs | 21 +++++------ .../s3/tests/make-connector-override.rs | 6 ++-- .../integration-tests/s3/tests/reconnects.rs | 7 ++-- .../s3/tests/sleep_impl_use_cases.rs | 12 ++++--- .../integration-tests/s3/tests/timeouts.rs | 5 ++- .../timestreamquery/tests/endpoint_disco.rs | 4 +-- .../ResiliencyConfigCustomization.kt | 24 ++++++------- rust-runtime/aws-smithy-async/src/rt/sleep.rs | 35 +++++++++++++++++-- rust-runtime/aws-smithy-client/src/builder.rs | 15 ++++---- rust-runtime/aws-smithy-client/src/conns.rs | 9 +++-- .../aws-smithy-client/src/http_connector.rs | 6 ++-- .../aws-smithy-client/src/hyper_ext.rs | 35 +++++++------------ rust-runtime/aws-smithy-client/src/lib.rs | 5 ++- rust-runtime/aws-smithy-client/src/retry.rs | 8 ++--- rust-runtime/aws-smithy-client/src/timeout.rs | 12 +++---- .../aws-smithy-client/tests/e2e_test.rs | 5 ++- .../tests/reconnect_on_transient_error.rs | 5 ++- .../src/client/orchestrator.rs | 16 ++++----- .../src/client/orchestrator.rs | 1 + .../aws-smithy-runtime/src/client/timeout.rs | 9 +++-- 35 files changed, 205 insertions(+), 182 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index bcc8dcb0a4..c31d53f166 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -121,3 +121,15 @@ message = "The SDK has added support for timestreamwrite and timestreamquery. Su meta = { "breaking" = false, "tada" = true, "bug" = false } references = ["smithy-rs#2707", "aws-sdk-rust#114"] author = "rcoh" + +[[smithy-rs]] +message = "A newtype wrapper `SharedAsyncSleep` has been introduced and occurrences of `Arc` that appear in public APIs have been replaced with it." +references = ["smithy-rs#2742"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } +author = "ysaito1001" + +[[aws-sdk-rust]] +message = "A newtype wrapper `SharedAsyncSleep` has been introduced and occurrences of `Arc` that appear in public APIs have been replaced with it." +references = ["smithy-rs#2742"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "ysaito1001" diff --git a/aws/rust-runtime/aws-config/src/connector.rs b/aws/rust-runtime/aws-config/src/connector.rs index 634c7c7bfa..c924a072bf 100644 --- a/aws/rust-runtime/aws-config/src/connector.rs +++ b/aws/rust-runtime/aws-config/src/connector.rs @@ -19,11 +19,11 @@ pub use aws_smithy_client::conns::default_connector; #[cfg(all(feature = "native-tls", not(feature = "allow-compilation")))] compile_error!("Feature native-tls has been removed. For upgrade instructions, see: https://awslabs.github.io/smithy-rs/design/transport/connector.html"); -/// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. +/// Given `ConnectorSettings` and a [`SharedAsyncSleep`](aws_smithy_async::rt::sleep::SharedAsyncSleep), create a `DynConnector` from defaults depending on what cargo features are activated. #[cfg(not(feature = "client-hyper"))] pub fn default_connector( _settings: &aws_smithy_client::http_connector::ConnectorSettings, - _sleep: Option>, + _sleep: Option, ) -> Option { None } diff --git a/aws/rust-runtime/aws-config/src/imds/client/token.rs b/aws/rust-runtime/aws-config/src/imds/client/token.rs index 213243a8cd..41e96777b4 100644 --- a/aws/rust-runtime/aws-config/src/imds/client/token.rs +++ b/aws/rust-runtime/aws-config/src/imds/client/token.rs @@ -18,7 +18,7 @@ use crate::imds::client::error::{ImdsError, TokenError, TokenErrorKind}; use crate::imds::client::ImdsResponseRetryClassifier; use aws_credential_types::cache::ExpiringCache; use aws_http::user_agent::UserAgentStage; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_async::time::SharedTimeSource; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::retry; @@ -84,7 +84,7 @@ impl TokenMiddleware { token_ttl: Duration, retry_config: retry::Config, timeout_config: TimeoutConfig, - sleep_impl: Option>, + sleep_impl: Option, ) -> Self { let mut inner_builder = aws_smithy_client::Client::builder() .connector(connector) diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 5364d1bcb5..5871762204 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -154,7 +154,7 @@ mod loader { use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::{ProvideCredentials, SharedCredentialsProvider}; - use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; + use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep}; use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_types::retry::RetryConfig; @@ -185,7 +185,7 @@ mod loader { endpoint_url: Option, region: Option>, retry_config: Option, - sleep: Option>, + sleep: Option, timeout_config: Option, provider_config: Option, http_connector: Option, @@ -260,7 +260,7 @@ mod loader { /// is used to create timeout futures. pub fn sleep_impl(mut self, sleep: impl AsyncSleep + 'static) -> Self { // it's possible that we could wrapping an `Arc in an `Arc` and that's OK - self.sleep = Some(Arc::new(sleep)); + self.sleep = Some(SharedAsyncSleep::new(sleep)); self } diff --git a/aws/rust-runtime/aws-config/src/provider_config.rs b/aws/rust-runtime/aws-config/src/provider_config.rs index 7d596b2c2e..afa2989811 100644 --- a/aws/rust-runtime/aws-config/src/provider_config.rs +++ b/aws/rust-runtime/aws-config/src/provider_config.rs @@ -6,7 +6,7 @@ //! Configuration Options for Credential Providers use aws_credential_types::time_source::TimeSource; -use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; +use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep}; use aws_smithy_async::time::SharedTimeSource; use aws_smithy_client::erase::DynConnector; use aws_smithy_types::error::display::DisplayErrorContext; @@ -41,7 +41,7 @@ pub struct ProviderConfig { fs: Fs, time_source: SharedTimeSource, connector: HttpConnector, - sleep: Option>, + sleep: Option, region: Option, /// An AWS profile created from `ProfileFiles` and a `profile_name` parsed_profile: Arc>>, @@ -65,7 +65,7 @@ impl Debug for ProviderConfig { impl Default for ProviderConfig { fn default() -> Self { let connector = HttpConnector::ConnectorFn(Arc::new( - |settings: &ConnectorSettings, sleep: Option>| { + |settings: &ConnectorSettings, sleep: Option| { default_connector(settings, sleep) }, )); @@ -195,7 +195,7 @@ impl ProviderConfig { } #[allow(dead_code)] - pub(crate) fn sleep(&self) -> Option> { + pub(crate) fn sleep(&self) -> Option { self.sleep.clone() } @@ -332,8 +332,7 @@ impl ProviderConfig { C::Future: Unpin + Send + 'static, C::Error: Into>, { - let connector_fn = move |settings: &ConnectorSettings, - sleep: Option>| { + let connector_fn = move |settings: &ConnectorSettings, sleep: Option| { let mut builder = aws_smithy_client::hyper_ext::Adapter::builder() .connector_settings(settings.clone()); if let Some(sleep) = sleep { @@ -350,7 +349,7 @@ impl ProviderConfig { /// Override the sleep implementation for this configuration pub fn with_sleep(self, sleep: impl AsyncSleep + 'static) -> Self { ProviderConfig { - sleep: Some(Arc::new(sleep)), + sleep: Some(SharedAsyncSleep::new(sleep)), ..self } } diff --git a/aws/rust-runtime/aws-credential-types/external-types.toml b/aws/rust-runtime/aws-credential-types/external-types.toml index 83527f561e..20337b394d 100644 --- a/aws/rust-runtime/aws-credential-types/external-types.toml +++ b/aws/rust-runtime/aws-credential-types/external-types.toml @@ -1,3 +1,3 @@ allowed_external_types = [ - "aws_smithy_async::rt::sleep::AsyncSleep", + "aws_smithy_async::rt::sleep::SharedAsyncSleep", ] diff --git a/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs b/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs index 1081b8f336..ae1a6e2d36 100644 --- a/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs +++ b/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs @@ -5,11 +5,10 @@ //! Lazy, credentials cache implementation -use std::sync::Arc; use std::time::{Duration, Instant}; use aws_smithy_async::future::timeout::Timeout; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; use tracing::{debug, info, info_span, Instrument}; use crate::cache::{ExpiringCache, ProvideCachedCredentials}; @@ -25,7 +24,7 @@ const DEFAULT_BUFFER_TIME_JITTER_FRACTION: fn() -> f64 = fastrand::f64; #[derive(Debug)] pub(crate) struct LazyCredentialsCache { time: TimeSource, - sleeper: Arc, + sleeper: SharedAsyncSleep, cache: ExpiringCache, provider: SharedCredentialsProvider, load_timeout: Duration, @@ -37,7 +36,7 @@ pub(crate) struct LazyCredentialsCache { impl LazyCredentialsCache { fn new( time: TimeSource, - sleeper: Arc, + sleeper: SharedAsyncSleep, provider: SharedCredentialsProvider, load_timeout: Duration, buffer_time: Duration, @@ -133,12 +132,11 @@ use crate::Credentials; pub use builder::Builder; mod builder { - use std::sync::Arc; use std::time::Duration; use crate::cache::{CredentialsCache, Inner}; use crate::provider::SharedCredentialsProvider; - use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; + use aws_smithy_async::rt::sleep::{default_async_sleep, SharedAsyncSleep}; use super::TimeSource; use super::{ @@ -160,7 +158,7 @@ mod builder { /// `build` to create a `LazyCredentialsCache`. #[derive(Clone, Debug, Default)] pub struct Builder { - sleep: Option>, + sleep: Option, time_source: Option, load_timeout: Option, buffer_time: Option, @@ -174,22 +172,22 @@ mod builder { Default::default() } - /// Implementation of [`AsyncSleep`] to use for timeouts. + /// Implementation of [`AsyncSleep`](aws_smithy_async::rt::sleep::AsyncSleep) to use for timeouts. /// /// This enables use of the `LazyCredentialsCache` with other async runtimes. /// If using Tokio as the async runtime, this should be set to an instance of /// [`TokioSleep`](aws_smithy_async::rt::sleep::TokioSleep). - pub fn sleep(mut self, sleep: Arc) -> Self { + pub fn sleep(mut self, sleep: SharedAsyncSleep) -> Self { self.set_sleep(Some(sleep)); self } - /// Implementation of [`AsyncSleep`] to use for timeouts. + /// Implementation of [`AsyncSleep`](aws_smithy_async::rt::sleep::AsyncSleep) to use for timeouts. /// /// This enables use of the `LazyCredentialsCache` with other async runtimes. /// If using Tokio as the async runtime, this should be set to an instance of /// [`TokioSleep`](aws_smithy_async::rt::sleep::TokioSleep). - pub fn set_sleep(&mut self, sleep: Option>) -> &mut Self { + pub fn set_sleep(&mut self, sleep: Option) -> &mut Self { self.sleep = sleep; self } @@ -347,7 +345,7 @@ mod tests { use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; - use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use tracing::info; use tracing_test::traced_test; @@ -372,7 +370,7 @@ mod tests { let load_list = Arc::new(Mutex::new(load_list)); LazyCredentialsCache::new( time, - Arc::new(TokioSleep::new()), + SharedAsyncSleep::new(TokioSleep::new()), SharedCredentialsProvider::new(provide_credentials_fn(move || { let list = load_list.clone(); async move { @@ -414,7 +412,7 @@ mod tests { })); let credentials_cache = LazyCredentialsCache::new( TimeSource::testing(&time), - Arc::new(TokioSleep::new()), + SharedAsyncSleep::new(TokioSleep::new()), provider, DEFAULT_LOAD_TIMEOUT, DEFAULT_BUFFER_TIME, @@ -534,7 +532,7 @@ mod tests { let time = TestingTimeSource::new(epoch_secs(100)); let credentials_cache = LazyCredentialsCache::new( TimeSource::testing(&time), - Arc::new(TokioSleep::new()), + SharedAsyncSleep::new(TokioSleep::new()), SharedCredentialsProvider::new(provide_credentials_fn(|| async { aws_smithy_async::future::never::Never::new().await; Ok(credentials(1000)) diff --git a/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs index e74ceddb00..d4ffc7d116 100644 --- a/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs +++ b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs @@ -5,8 +5,8 @@ //! Maintain a cache of discovered endpoints -use aws_smithy_async::rt::sleep::AsyncSleep; -use aws_smithy_async::time::TimeSource; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; +use aws_smithy_async::time::SharedTimeSource; use aws_smithy_client::erase::boxclone::BoxFuture; use aws_smithy_http::endpoint::{ResolveEndpoint, ResolveEndpointError}; use aws_smithy_types::endpoint::Endpoint; @@ -24,8 +24,8 @@ pub struct ReloadEndpoint { endpoint: Arc>>, error: Arc>>, rx: Receiver<()>, - sleep: Arc, - time: Arc, + sleep: SharedAsyncSleep, + time: SharedTimeSource, } impl Debug for ReloadEndpoint { @@ -106,8 +106,8 @@ impl ExpiringEndpoint { pub(crate) async fn create_cache( loader_fn: impl Fn() -> F + Send + Sync + 'static, - sleep: Arc, - time: Arc, + sleep: SharedAsyncSleep, + time: SharedTimeSource, ) -> Result<(EndpointCache, ReloadEndpoint), ResolveEndpointError> where F: Future> + Send + 'static, @@ -155,9 +155,9 @@ impl EndpointCache { #[cfg(test)] mod test { use crate::endpoint_discovery::create_cache; - use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_async::test_util::controlled_time_and_sleep; - use aws_smithy_async::time::SystemTimeSource; + use aws_smithy_async::time::{SharedTimeSource, SystemTimeSource}; use aws_smithy_types::endpoint::Endpoint; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -178,8 +178,8 @@ mod test { SystemTime::now(), )) }, - Arc::new(TokioSleep::new()), - Arc::new(SystemTimeSource::new()), + SharedAsyncSleep::new(TokioSleep::new()), + SharedTimeSource::new(SystemTimeSource::new()), ) .await .unwrap(); @@ -204,8 +204,8 @@ mod test { )) } }, - Arc::new(TokioSleep::new()), - Arc::new(SystemTimeSource::new()), + SharedAsyncSleep::new(TokioSleep::new()), + SharedTimeSource::new(SystemTimeSource::new()), ) .await .expect("returns an endpoint"); @@ -248,8 +248,8 @@ mod test { )) } }, - Arc::new(sleep.clone()), - Arc::new(time.clone()), + SharedAsyncSleep::new(sleep.clone()), + SharedTimeSource::new(time.clone()), ) .await .expect("first load success"); diff --git a/aws/rust-runtime/aws-types/external-types.toml b/aws/rust-runtime/aws-types/external-types.toml index 6c1d2b5aab..830795ad27 100644 --- a/aws/rust-runtime/aws-types/external-types.toml +++ b/aws/rust-runtime/aws-types/external-types.toml @@ -1,7 +1,7 @@ allowed_external_types = [ "aws_credential_types::cache::CredentialsCache", "aws_credential_types::provider::SharedCredentialsProvider", - "aws_smithy_async::rt::sleep::AsyncSleep", + "aws_smithy_async::rt::sleep::SharedAsyncSleep", "aws_smithy_async::time::TimeSource", "aws_smithy_async::time::SharedTimeSource", "aws_smithy_client::http_connector", diff --git a/aws/rust-runtime/aws-types/src/sdk_config.rs b/aws/rust-runtime/aws-types/src/sdk_config.rs index af60e88f5d..4d0728a985 100644 --- a/aws/rust-runtime/aws-types/src/sdk_config.rs +++ b/aws/rust-runtime/aws-types/src/sdk_config.rs @@ -9,11 +9,9 @@ //! //! This module contains an shared configuration representation that is agnostic from a specific service. -use std::sync::Arc; - use aws_credential_types::cache::CredentialsCache; use aws_credential_types::provider::SharedCredentialsProvider; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_client::http_connector::HttpConnector; use aws_smithy_types::retry::RetryConfig; @@ -55,7 +53,7 @@ pub struct SdkConfig { region: Option, endpoint_url: Option, retry_config: Option, - sleep_impl: Option>, + sleep_impl: Option, time_source: Option, timeout_config: Option, http_connector: Option, @@ -76,7 +74,7 @@ pub struct Builder { region: Option, endpoint_url: Option, retry_config: Option, - sleep_impl: Option>, + sleep_impl: Option, time_source: Option, timeout_config: Option, http_connector: Option, @@ -241,8 +239,7 @@ impl Builder { /// # Examples /// /// ```rust - /// use std::sync::Arc; - /// use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; + /// use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; /// use aws_types::SdkConfig; /// /// ##[derive(Debug)] @@ -254,10 +251,10 @@ impl Builder { /// } /// } /// - /// let sleep_impl = Arc::new(ForeverSleep); + /// let sleep_impl = SharedAsyncSleep::new(ForeverSleep); /// let config = SdkConfig::builder().sleep_impl(sleep_impl).build(); /// ``` - pub fn sleep_impl(mut self, sleep_impl: Arc) -> Self { + pub fn sleep_impl(mut self, sleep_impl: SharedAsyncSleep) -> Self { self.set_sleep_impl(Some(sleep_impl)); self } @@ -270,7 +267,7 @@ impl Builder { /// /// # Examples /// ```rust - /// # use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; + /// # use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; /// # use aws_types::sdk_config::{Builder, SdkConfig}; /// #[derive(Debug)] /// pub struct ForeverSleep; @@ -282,7 +279,7 @@ impl Builder { /// } /// /// fn set_never_ending_sleep_impl(builder: &mut Builder) { - /// let sleep_impl = std::sync::Arc::new(ForeverSleep); + /// let sleep_impl = SharedAsyncSleep::new(ForeverSleep); /// builder.set_sleep_impl(Some(sleep_impl)); /// } /// @@ -290,7 +287,7 @@ impl Builder { /// set_never_ending_sleep_impl(&mut builder); /// let config = builder.build(); /// ``` - pub fn set_sleep_impl(&mut self, sleep_impl: Option>) -> &mut Self { + pub fn set_sleep_impl(&mut self, sleep_impl: Option) -> &mut Self { self.sleep_impl = sleep_impl; self } @@ -558,7 +555,7 @@ impl SdkConfig { #[doc(hidden)] /// Configured sleep implementation - pub fn sleep_impl(&self) -> Option> { + pub fn sleep_impl(&self) -> Option { self.sleep_impl.clone() } @@ -568,13 +565,13 @@ impl SdkConfig { } /// Configured credentials provider - pub fn credentials_provider(&self) -> Option<&SharedCredentialsProvider> { - self.credentials_provider.as_ref() + pub fn credentials_provider(&self) -> Option { + self.credentials_provider.clone() } /// Configured time source - pub fn time_source(&self) -> Option<&SharedTimeSource> { - self.time_source.as_ref() + pub fn time_source(&self) -> Option { + self.time_source.clone() } /// Configured app name diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index eb03212135..6bc42cdbc2 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -46,7 +46,7 @@ class CredentialsProviderDecorator : ClientCodegenDecorator { override fun extraSections(codegenContext: ClientCodegenContext): List = listOf( adhocCustomization { section -> - rust("${section.serviceConfigBuilder}.set_credentials_provider(${section.sdkConfig}.credentials_provider().cloned());") + rust("${section.serviceConfigBuilder}.set_credentials_provider(${section.sdkConfig}.credentials_provider());") }, ) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt index b77b5c9bc7..20457237c9 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt @@ -77,7 +77,7 @@ class GenericSmithySdkConfigSettings : ClientCodegenDecorator { ${section.serviceConfigBuilder}.set_sleep_impl(${section.sdkConfig}.sleep_impl()); ${section.serviceConfigBuilder}.set_http_connector(${section.sdkConfig}.http_connector().cloned()); - ${section.serviceConfigBuilder}.set_time_source(${section.sdkConfig}.time_source().cloned()); + ${section.serviceConfigBuilder}.set_time_source(${section.sdkConfig}.time_source()); """, ) }, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt index 8b8bffe398..ac2e8928e0 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt @@ -77,7 +77,7 @@ class TimestreamDecorator : ClientCodegenDecorator { pub async fn enable_endpoint_discovery(self) -> #{Result}<(Self, #{endpoint_discovery}::ReloadEndpoint), #{ResolveEndpointError}> { let mut new_conf = self.conf().clone(); let sleep = self.conf().sleep_impl().expect("sleep impl must be provided"); - let time = ::std::sync::Arc::new(self.conf().time_source.clone()); + let time = self.conf().time_source.clone(); let (resolver, reloader) = #{endpoint_discovery}::create_cache( move || { let client = self.clone(); diff --git a/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs b/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs index b566c598c7..d1a9b9369e 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/timeouts.rs @@ -3,13 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::sync::Arc; use std::time::Duration; use aws_credential_types::provider::SharedCredentialsProvider; use aws_credential_types::Credentials; use aws_sdk_dynamodb::error::SdkError; -use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; use aws_smithy_client::never::NeverConnector; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; @@ -37,7 +36,7 @@ async fn api_call_timeout_retries() { .build(), ) .retry_config(RetryConfig::standard()) - .sleep_impl(Arc::new(InstantSleep)) + .sleep_impl(SharedAsyncSleep::new(InstantSleep)) .build(); let client = aws_sdk_dynamodb::Client::from_conf(aws_sdk_dynamodb::Config::new(&conf)); let resp = client @@ -70,7 +69,7 @@ async fn no_retries_on_operation_timeout() { .build(), ) .retry_config(RetryConfig::standard()) - .sleep_impl(Arc::new(InstantSleep)) + .sleep_impl(SharedAsyncSleep::new(InstantSleep)) .build(); let client = aws_sdk_dynamodb::Client::from_conf(aws_sdk_dynamodb::Config::new(&conf)); let resp = client diff --git a/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs b/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs index 64f0137b24..0ee5c7a5c3 100644 --- a/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs +++ b/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs @@ -27,7 +27,9 @@ async fn test_clients_from_service_config() { } let config = aws_sdk_s3::Config::builder() - .sleep_impl(std::sync::Arc::new(StubSleep {})) + .sleep_impl(aws_smithy_async::rt::sleep::SharedAsyncSleep::new( + StubSleep {}, + )) .build(); // This will panic due to the lack of an HTTP connector aws_sdk_s3::Client::from_conf(config); diff --git a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs index 7d6e84a90f..db36cfeb92 100644 --- a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs +++ b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs @@ -12,13 +12,12 @@ use aws_sdk_s3::types::{ }; use aws_sdk_s3::{Client, Config}; use aws_smithy_async::assert_elapsed; -use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; use aws_smithy_client::never::NeverConnector; use aws_smithy_http::result::SdkError; use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::timeout::TimeoutConfig; use std::fmt::Debug; -use std::sync::Arc; use std::time::{Duration, Instant}; #[derive(Debug)] @@ -34,7 +33,8 @@ impl AsyncSleep for SmolSleep { #[test] fn test_smol_runtime_timeouts() { - if let Err(err) = smol::block_on(async { timeout_test(Arc::new(SmolSleep)).await }) { + if let Err(err) = smol::block_on(async { timeout_test(SharedAsyncSleep::new(SmolSleep)).await }) + { println!("{err}"); panic!(); } @@ -42,7 +42,7 @@ fn test_smol_runtime_timeouts() { #[test] fn test_smol_runtime_retry() { - if let Err(err) = smol::block_on(async { retry_test(Arc::new(SmolSleep)).await }) { + if let Err(err) = smol::block_on(async { retry_test(SharedAsyncSleep::new(SmolSleep)).await }) { println!("{err}"); panic!(); } @@ -59,9 +59,9 @@ impl AsyncSleep for AsyncStdSleep { #[test] fn test_async_std_runtime_timeouts() { - if let Err(err) = - async_std::task::block_on(async { timeout_test(Arc::new(AsyncStdSleep)).await }) - { + if let Err(err) = async_std::task::block_on(async { + timeout_test(SharedAsyncSleep::new(AsyncStdSleep)).await + }) { println!("{err}"); panic!(); } @@ -69,14 +69,15 @@ fn test_async_std_runtime_timeouts() { #[test] fn test_async_std_runtime_retry() { - if let Err(err) = async_std::task::block_on(async { retry_test(Arc::new(AsyncStdSleep)).await }) + if let Err(err) = + async_std::task::block_on(async { retry_test(SharedAsyncSleep::new(AsyncStdSleep)).await }) { println!("{err}"); panic!(); } } -async fn timeout_test(sleep_impl: Arc) -> Result<(), Box> { +async fn timeout_test(sleep_impl: SharedAsyncSleep) -> Result<(), Box> { let conn = NeverConnector::new(); let region = Region::from_static("us-east-2"); let timeout_config = TimeoutConfig::builder() @@ -130,7 +131,7 @@ async fn timeout_test(sleep_impl: Arc) -> Result<(), Box) -> Result<(), Box> { +async fn retry_test(sleep_impl: SharedAsyncSleep) -> Result<(), Box> { let conn = NeverConnector::new(); let conf = aws_types::SdkConfig::builder() .region(Region::new("us-east-2")) diff --git a/aws/sdk/integration-tests/s3/tests/make-connector-override.rs b/aws/sdk/integration-tests/s3/tests/make-connector-override.rs index fc816b8188..90eb79d70c 100644 --- a/aws/sdk/integration-tests/s3/tests/make-connector-override.rs +++ b/aws/sdk/integration-tests/s3/tests/make-connector-override.rs @@ -5,7 +5,7 @@ use aws_credential_types::provider::SharedCredentialsProvider; use aws_credential_types::Credentials; -use aws_smithy_async::rt::sleep::{AsyncSleep, TokioSleep}; +use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_client::http_connector::{ConnectorSettings, HttpConnector}; use aws_smithy_client::test_connection; @@ -25,7 +25,7 @@ async fn make_connector_fn_test() { let sentinel = Arc::new(AtomicUsize::new(0)); let connector_sentinel = sentinel.clone(); let connector_with_counter = HttpConnector::ConnectorFn(Arc::new( - move |_settings: &ConnectorSettings, _sleep: Option>| { + move |_settings: &ConnectorSettings, _sleep: Option| { connector_sentinel.fetch_add(1, Ordering::Relaxed); Some(test_connection::infallible_connection_fn(|_req| { http::Response::builder().status(200).body("ok!").unwrap() @@ -60,7 +60,7 @@ async fn timeouts_can_be_set_by_service() { let sdk_config = SdkConfig::builder() .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) .region(Region::from_static("us-east-1")) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .timeout_config( TimeoutConfig::builder() .operation_timeout(Duration::from_secs(5)) diff --git a/aws/sdk/integration-tests/s3/tests/reconnects.rs b/aws/sdk/integration-tests/s3/tests/reconnects.rs index 85afcd40a9..91935319fb 100644 --- a/aws/sdk/integration-tests/s3/tests/reconnects.rs +++ b/aws/sdk/integration-tests/s3/tests/reconnects.rs @@ -5,7 +5,7 @@ use aws_credential_types::provider::SharedCredentialsProvider; use aws_credential_types::Credentials; -use aws_smithy_async::rt::sleep::TokioSleep; +use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_client::test_connection::wire_mock::{ check_matches, ReplayedEvent, WireLevelTestConnection, }; @@ -13,7 +13,6 @@ use aws_smithy_client::{ev, match_events}; use aws_smithy_types::retry::{ReconnectMode, RetryConfig}; use aws_types::region::Region; use aws_types::SdkConfig; -use std::sync::Arc; #[tokio::test] /// test that disabling reconnects on retry config disables them for the client @@ -28,7 +27,7 @@ async fn disable_reconnects() { let sdk_config = SdkConfig::builder() .region(Region::from_static("us-east-2")) .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .endpoint_url(mock.endpoint_url()) .http_connector(mock.http_connector()) .retry_config( @@ -68,7 +67,7 @@ async fn reconnect_on_503() { let sdk_config = SdkConfig::builder() .region(Region::from_static("us-east-2")) .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .endpoint_url(mock.endpoint_url()) .http_connector(mock.http_connector()) .retry_config(RetryConfig::standard()) diff --git a/aws/sdk/integration-tests/s3/tests/sleep_impl_use_cases.rs b/aws/sdk/integration-tests/s3/tests/sleep_impl_use_cases.rs index 182214e477..dfa82e9660 100644 --- a/aws/sdk/integration-tests/s3/tests/sleep_impl_use_cases.rs +++ b/aws/sdk/integration-tests/s3/tests/sleep_impl_use_cases.rs @@ -8,7 +8,7 @@ mod with_sdk_config { use aws_config::timeout::TimeoutConfig; use aws_config::SdkConfig; use aws_sdk_s3 as s3; - use std::sync::Arc; + use aws_smithy_async::rt::sleep::SharedAsyncSleep; use std::time::Duration; #[tokio::test] @@ -101,7 +101,9 @@ mod with_sdk_config { .build(), ) .retry_config(RetryConfig::standard().with_max_attempts(2)) - .sleep_impl(Arc::new(aws_smithy_async::rt::sleep::TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new( + aws_smithy_async::rt::sleep::TokioSleep::new(), + )) .build(); assert!(config.timeout_config().unwrap().has_timeouts()); assert!(config.retry_config().unwrap().has_retry()); @@ -114,7 +116,7 @@ mod with_service_config { use aws_config::timeout::TimeoutConfig; use aws_config::SdkConfig; use aws_sdk_s3 as s3; - use std::sync::Arc; + use aws_smithy_async::rt::sleep::SharedAsyncSleep; use std::time::Duration; #[test] @@ -188,7 +190,9 @@ mod with_service_config { .build(), ) .retry_config(RetryConfig::standard().with_max_attempts(2)) - .sleep_impl(Arc::new(aws_smithy_async::rt::sleep::TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new( + aws_smithy_async::rt::sleep::TokioSleep::new(), + )) .build(); let _s3 = s3::Client::new(&config); } diff --git a/aws/sdk/integration-tests/s3/tests/timeouts.rs b/aws/sdk/integration-tests/s3/tests/timeouts.rs index 859c41cbf1..59359ad20b 100644 --- a/aws/sdk/integration-tests/s3/tests/timeouts.rs +++ b/aws/sdk/integration-tests/s3/tests/timeouts.rs @@ -12,13 +12,12 @@ use aws_sdk_s3::types::{ }; use aws_sdk_s3::Client; use aws_smithy_async::assert_elapsed; -use aws_smithy_async::rt::sleep::{default_async_sleep, TokioSleep}; +use aws_smithy_async::rt::sleep::{default_async_sleep, SharedAsyncSleep, TokioSleep}; use aws_smithy_client::never::NeverConnector; use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::timeout::TimeoutConfig; use std::future::Future; use std::net::SocketAddr; -use std::sync::Arc; use std::time::Duration; use tokio::net::TcpListener; use tokio::time::timeout; @@ -34,7 +33,7 @@ async fn test_timeout_service_ends_request_that_never_completes() { .operation_timeout(Duration::from_secs_f32(0.5)) .build(), ) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .build(); let client = Client::new(&sdk_config); diff --git a/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs index a045840907..9d4557b0c3 100644 --- a/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs +++ b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs @@ -6,12 +6,12 @@ use aws_credential_types::provider::SharedCredentialsProvider; use aws_sdk_timestreamquery as query; use aws_sdk_timestreamquery::config::Credentials; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_async::test_util::controlled_time_and_sleep; use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_client::dvr::{MediaType, ReplayingConnection}; use aws_types::region::Region; use aws_types::SdkConfig; -use std::sync::Arc; use std::time::{Duration, UNIX_EPOCH}; #[tokio::test] @@ -24,7 +24,7 @@ async fn do_endpoint_discovery() { let config = SdkConfig::builder() .http_connector(conn.clone()) .region(Region::from_static("us-west-2")) - .sleep_impl(Arc::new(sleep)) + .sleep_impl(SharedAsyncSleep::new(sleep)) .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) .time_source(SharedTimeSource::new(ts.clone())) .build(); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 13cc1c26ba..90a03bd348 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -26,8 +26,8 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust private val timeoutModule = RuntimeType.smithyTypes(runtimeConfig).resolve("timeout") private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( - "AsyncSleep" to sleepModule.resolve("AsyncSleep"), "RetryConfig" to retryConfig.resolve("RetryConfig"), + "SharedAsyncSleep" to sleepModule.resolve("SharedAsyncSleep"), "Sleep" to sleepModule.resolve("Sleep"), "TimeoutConfig" to timeoutModule.resolve("TimeoutConfig"), ) @@ -38,7 +38,7 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust is ServiceConfig.ConfigStruct -> rustTemplate( """ retry_config: Option<#{RetryConfig}>, - sleep_impl: Option>, + sleep_impl: Option<#{SharedAsyncSleep}>, timeout_config: Option<#{TimeoutConfig}>, """, *codegenScope, @@ -52,8 +52,8 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust self.retry_config.as_ref() } - /// Return a cloned Arc containing the async sleep implementation from this config, if any. - pub fn sleep_impl(&self) -> Option> { + /// Return a cloned shared async sleep implementation from this config, if any. + pub fn sleep_impl(&self) -> Option<#{SharedAsyncSleep}> { self.sleep_impl.clone() } @@ -70,7 +70,7 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust rustTemplate( """ retry_config: Option<#{RetryConfig}>, - sleep_impl: Option>, + sleep_impl: Option<#{SharedAsyncSleep}>, timeout_config: Option<#{TimeoutConfig}>, """, *codegenScope, @@ -120,7 +120,7 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust /// ## Examples /// /// ```no_run - /// use $moduleUseName::config::{AsyncSleep, Sleep, Config}; + /// use $moduleUseName::config::{AsyncSleep, Config, SharedAsyncSleep, Sleep}; /// /// ##[derive(Debug)] /// pub struct ForeverSleep; @@ -131,10 +131,10 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust /// } /// } /// - /// let sleep_impl = std::sync::Arc::new(ForeverSleep); + /// let sleep_impl = SharedAsyncSleep::new(ForeverSleep); /// let config = Config::builder().sleep_impl(sleep_impl).build(); /// ``` - pub fn sleep_impl(mut self, sleep_impl: std::sync::Arc) -> Self { + pub fn sleep_impl(mut self, sleep_impl: #{SharedAsyncSleep}) -> Self { self.set_sleep_impl(Some(sleep_impl)); self } @@ -144,7 +144,7 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust /// ## Examples /// /// ```no_run - /// use $moduleUseName::config::{AsyncSleep, Sleep, Builder, Config}; + /// use $moduleUseName::config::{AsyncSleep, Builder, Config, SharedAsyncSleep, Sleep}; /// /// ##[derive(Debug)] /// pub struct ForeverSleep; @@ -156,7 +156,7 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust /// } /// /// fn set_never_ending_sleep_impl(builder: &mut Builder) { - /// let sleep_impl = std::sync::Arc::new(ForeverSleep); + /// let sleep_impl = SharedAsyncSleep::new(ForeverSleep); /// builder.set_sleep_impl(Some(sleep_impl)); /// } /// @@ -164,7 +164,7 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust /// set_never_ending_sleep_impl(&mut builder); /// let config = builder.build(); /// ``` - pub fn set_sleep_impl(&mut self, sleep_impl: Option>) -> &mut Self { + pub fn set_sleep_impl(&mut self, sleep_impl: Option<#{SharedAsyncSleep}>) -> &mut Self { self.sleep_impl = sleep_impl; self } @@ -242,7 +242,7 @@ class ResiliencyReExportCustomization(private val runtimeConfig: RuntimeConfig) rustCrate.withModule(ClientRustModule.Config) { rustTemplate( """ - pub use #{sleep}::{AsyncSleep, Sleep}; + pub use #{sleep}::{AsyncSleep, SharedAsyncSleep, Sleep}; /// Retry configuration /// diff --git a/rust-runtime/aws-smithy-async/src/rt/sleep.rs b/rust-runtime/aws-smithy-async/src/rt/sleep.rs index 4ea9f70ed5..076db95c97 100644 --- a/rust-runtime/aws-smithy-async/src/rt/sleep.rs +++ b/rust-runtime/aws-smithy-async/src/rt/sleep.rs @@ -39,15 +39,44 @@ where } } +/// Wrapper type for sharable `AsyncSleep` +#[derive(Clone, Debug)] +pub struct SharedAsyncSleep(Arc); + +impl SharedAsyncSleep { + /// Create a new `SharedAsyncSleep` from `AsyncSleep` + pub fn new(sleep: impl AsyncSleep + 'static) -> Self { + Self(Arc::new(sleep)) + } +} + +impl AsRef for SharedAsyncSleep { + fn as_ref(&self) -> &(dyn AsyncSleep + 'static) { + self.0.as_ref() + } +} + +impl From> for SharedAsyncSleep { + fn from(sleep: Arc) -> Self { + SharedAsyncSleep(sleep) + } +} + +impl AsyncSleep for SharedAsyncSleep { + fn sleep(&self, duration: Duration) -> Sleep { + self.0.sleep(duration) + } +} + #[cfg(feature = "rt-tokio")] /// Returns a default sleep implementation based on the features enabled -pub fn default_async_sleep() -> Option> { - Some(sleep_tokio()) +pub fn default_async_sleep() -> Option { + Some(SharedAsyncSleep::from(sleep_tokio())) } #[cfg(not(feature = "rt-tokio"))] /// Returns a default sleep implementation based on the features enabled -pub fn default_async_sleep() -> Option> { +pub fn default_async_sleep() -> Option { None } diff --git a/rust-runtime/aws-smithy-client/src/builder.rs b/rust-runtime/aws-smithy-client/src/builder.rs index 063c9a6a47..5602c330f5 100644 --- a/rust-runtime/aws-smithy-client/src/builder.rs +++ b/rust-runtime/aws-smithy-client/src/builder.rs @@ -4,12 +4,11 @@ */ use crate::{bounds, erase, retry, Client}; -use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; +use aws_smithy_async::rt::sleep::{default_async_sleep, SharedAsyncSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; use aws_smithy_types::retry::ReconnectMode; use aws_smithy_types::timeout::{OperationTimeoutConfig, TimeoutConfig}; -use std::sync::Arc; #[derive(Clone, Debug)] struct MaybeRequiresSleep { @@ -37,7 +36,7 @@ pub struct Builder { middleware: M, retry_policy: MaybeRequiresSleep, operation_timeout_config: Option, - sleep_impl: Option>, + sleep_impl: Option, reconnect_mode: Option, } @@ -312,14 +311,14 @@ impl Builder { self } - /// Set the [`AsyncSleep`] function that the [`Client`] will use to create things like timeout futures. - pub fn set_sleep_impl(&mut self, async_sleep: Option>) -> &mut Self { + /// Set [`aws_smithy_async::rt::sleep::SharedAsyncSleep`] that the [`Client`] will use to create things like timeout futures. + pub fn set_sleep_impl(&mut self, async_sleep: Option) -> &mut Self { self.sleep_impl = async_sleep; self } - /// Set the [`AsyncSleep`] function that the [`Client`] will use to create things like timeout futures. - pub fn sleep_impl(mut self, async_sleep: Arc) -> Self { + /// Set [`aws_smithy_async::rt::sleep::SharedAsyncSleep`] that the [`Client`] will use to create things like timeout futures. + pub fn sleep_impl(mut self, async_sleep: SharedAsyncSleep) -> Self { self.set_sleep_impl(Some(async_sleep)); self } @@ -458,7 +457,7 @@ where mod tests { use super::*; use crate::never::NeverConnector; - use aws_smithy_async::rt::sleep::Sleep; + use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; use std::panic::{self, AssertUnwindSafe}; use std::time::Duration; diff --git a/rust-runtime/aws-smithy-client/src/conns.rs b/rust-runtime/aws-smithy-client/src/conns.rs index 21d6f5e80e..d4e25c45aa 100644 --- a/rust-runtime/aws-smithy-client/src/conns.rs +++ b/rust-runtime/aws-smithy-client/src/conns.rs @@ -54,13 +54,12 @@ lazy_static::lazy_static! { mod default_connector { use crate::erase::DynConnector; use crate::http_connector::ConnectorSettings; - use aws_smithy_async::rt::sleep::AsyncSleep; - use std::sync::Arc; + use aws_smithy_async::rt::sleep::SharedAsyncSleep; #[cfg(feature = "rustls")] fn base( settings: &ConnectorSettings, - sleep: Option>, + sleep: Option, ) -> crate::hyper_ext::Builder { let mut hyper = crate::hyper_ext::Adapter::builder().connector_settings(settings.clone()); if let Some(sleep) = sleep { @@ -69,10 +68,10 @@ mod default_connector { hyper } - /// Given `ConnectorSettings` and an `AsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. + /// Given `ConnectorSettings` and an `SharedAsyncSleep`, create a `DynConnector` from defaults depending on what cargo features are activated. pub fn default_connector( settings: &ConnectorSettings, - sleep: Option>, + sleep: Option, ) -> Option { #[cfg(feature = "rustls")] { diff --git a/rust-runtime/aws-smithy-client/src/http_connector.rs b/rust-runtime/aws-smithy-client/src/http_connector.rs index 67db6e6918..37366604ee 100644 --- a/rust-runtime/aws-smithy-client/src/http_connector.rs +++ b/rust-runtime/aws-smithy-client/src/http_connector.rs @@ -7,14 +7,14 @@ //! that enable passing HTTP connectors around. use crate::erase::DynConnector; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_types::timeout::TimeoutConfig; use std::time::Duration; use std::{fmt::Debug, sync::Arc}; /// Type alias for a Connector factory function. pub type MakeConnectorFn = - dyn Fn(&ConnectorSettings, Option>) -> Option + Send + Sync; + dyn Fn(&ConnectorSettings, Option) -> Option + Send + Sync; /// Enum for describing the two "kinds" of HTTP Connectors in smithy-rs. #[derive(Clone)] @@ -47,7 +47,7 @@ impl HttpConnector { pub fn connector( &self, settings: &ConnectorSettings, - sleep: Option>, + sleep: Option, ) -> Option { match self { HttpConnector::Prebuilt(conn) => conn.clone(), diff --git a/rust-runtime/aws-smithy-client/src/hyper_ext.rs b/rust-runtime/aws-smithy-client/src/hyper_ext.rs index 8f0ca40609..be1e5679d2 100644 --- a/rust-runtime/aws-smithy-client/src/hyper_ext.rs +++ b/rust-runtime/aws-smithy-client/src/hyper_ext.rs @@ -81,7 +81,7 @@ use crate::http_connector::ConnectorSettings; use crate::hyper_ext::timeout_middleware::{ConnectTimeout, HttpReadTimeout, HttpTimeoutError}; use crate::never::stream::EmptyStream; use aws_smithy_async::future::timeout::TimedOutError; -use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep}; +use aws_smithy_async::rt::sleep::{default_async_sleep, SharedAsyncSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; @@ -95,8 +95,6 @@ use hyper::client::connect::{ use std::error::Error; use std::fmt::Debug; -use std::sync::Arc; - use crate::erase::boxclone::BoxFuture; use aws_smithy_http::connection::{CaptureSmithyConnection, ConnectionMetadata}; use tokio::io::{AsyncRead, AsyncWrite}; @@ -252,7 +250,7 @@ fn find_source<'a, E: Error + 'static>(err: &'a (dyn Error + 'static)) -> Option #[derive(Default, Debug)] pub struct Builder { connector_settings: Option, - sleep_impl: Option>, + sleep_impl: Option, client_builder: Option, } @@ -288,9 +286,7 @@ impl Builder { let read_timeout = match read_timeout { Some(duration) => HttpReadTimeout::new( base, - sleep_impl - .clone() - .expect("a sleep impl must be provided in order to have a read timeout"), + sleep_impl.expect("a sleep impl must be provided in order to have a read timeout"), duration, ), None => HttpReadTimeout::no_timeout(base), @@ -304,7 +300,7 @@ impl Builder { /// /// Calling this is only necessary for testing or to use something other than /// [`default_async_sleep`]. - pub fn sleep_impl(mut self, sleep_impl: Arc) -> Self { + pub fn sleep_impl(mut self, sleep_impl: SharedAsyncSleep) -> Self { self.sleep_impl = Some(sleep_impl); self } @@ -313,10 +309,7 @@ impl Builder { /// /// Calling this is only necessary for testing or to use something other than /// [`default_async_sleep`]. - pub fn set_sleep_impl( - &mut self, - sleep_impl: Option>, - ) -> &mut Self { + pub fn set_sleep_impl(&mut self, sleep_impl: Option) -> &mut Self { self.sleep_impl = sleep_impl; self } @@ -361,7 +354,6 @@ mod timeout_middleware { use std::fmt::Formatter; use std::future::Future; use std::pin::Pin; - use std::sync::Arc; use std::task::{Context, Poll}; use std::time::Duration; @@ -370,8 +362,8 @@ mod timeout_middleware { use tower::BoxError; use aws_smithy_async::future::timeout::{TimedOutError, Timeout}; - use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_async::rt::sleep::Sleep; + use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; #[derive(Debug)] pub(crate) struct HttpTimeoutError { @@ -405,14 +397,14 @@ mod timeout_middleware { #[derive(Clone, Debug)] pub(super) struct ConnectTimeout { inner: I, - timeout: Option<(Arc, Duration)>, + timeout: Option<(SharedAsyncSleep, Duration)>, } impl ConnectTimeout { /// Create a new `ConnectTimeout` around `inner`. /// /// Typically, `I` will implement [`hyper::client::connect::Connect`]. - pub(crate) fn new(inner: I, sleep: Arc, timeout: Duration) -> Self { + pub(crate) fn new(inner: I, sleep: SharedAsyncSleep, timeout: Duration) -> Self { Self { inner, timeout: Some((sleep, timeout)), @@ -430,14 +422,14 @@ mod timeout_middleware { #[derive(Clone, Debug)] pub(crate) struct HttpReadTimeout { inner: I, - timeout: Option<(Arc, Duration)>, + timeout: Option<(SharedAsyncSleep, Duration)>, } impl HttpReadTimeout { /// Create a new `HttpReadTimeout` around `inner`. /// /// Typically, `I` will implement [`tower::Service>`]. - pub(crate) fn new(inner: I, sleep: Arc, timeout: Duration) -> Self { + pub(crate) fn new(inner: I, sleep: SharedAsyncSleep, timeout: Duration) -> Self { Self { inner, timeout: Some((sleep, timeout)), @@ -565,11 +557,10 @@ mod timeout_middleware { use crate::hyper_ext::Adapter; use crate::never::{NeverConnected, NeverReplies}; use aws_smithy_async::assert_elapsed; - use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::timeout::TimeoutConfig; - use std::sync::Arc; use std::time::Duration; use tower::Service; @@ -591,7 +582,7 @@ mod timeout_middleware { ); let mut hyper = Adapter::builder() .connector_settings(connector_settings) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .build(inner); let now = tokio::time::Instant::now(); tokio::time::pause(); @@ -630,7 +621,7 @@ mod timeout_middleware { ); let mut hyper = Adapter::builder() .connector_settings(connector_settings) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .build(inner); let now = tokio::time::Instant::now(); tokio::time::pause(); diff --git a/rust-runtime/aws-smithy-client/src/lib.rs b/rust-runtime/aws-smithy-client/src/lib.rs index 7d3a6f256e..935dddd2f0 100644 --- a/rust-runtime/aws-smithy-client/src/lib.rs +++ b/rust-runtime/aws-smithy-client/src/lib.rs @@ -51,7 +51,7 @@ pub mod hyper_ext; pub mod static_tests; use crate::poison::PoisonLayer; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_http::operation::Operation; use aws_smithy_http::response::ParseHttpResponse; @@ -62,7 +62,6 @@ use aws_smithy_http_tower::parse_response::ParseResponseLayer; use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::retry::{ProvideErrorKind, ReconnectMode}; use aws_smithy_types::timeout::OperationTimeoutConfig; -use std::sync::Arc; use timeout::ClientTimeoutParams; pub use timeout::TimeoutLayer; use tower::{Service, ServiceBuilder, ServiceExt}; @@ -97,7 +96,7 @@ pub struct Client< retry_policy: RetryPolicy, reconnect_mode: ReconnectMode, operation_timeout_config: OperationTimeoutConfig, - sleep_impl: Option>, + sleep_impl: Option, } impl Client<(), (), ()> { diff --git a/rust-runtime/aws-smithy-client/src/retry.rs b/rust-runtime/aws-smithy-client/src/retry.rs index 2c38807fae..8c2fdc246c 100644 --- a/rust-runtime/aws-smithy-client/src/retry.rs +++ b/rust-runtime/aws-smithy-client/src/retry.rs @@ -19,7 +19,7 @@ use std::time::Duration; use tracing::Instrument; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; use aws_smithy_http::operation::Operation; use aws_smithy_http::retry::ClassifyRetry; @@ -40,7 +40,7 @@ where type Policy; /// Create a new policy mechanism instance. - fn new_request_policy(&self, sleep_impl: Option>) -> Self::Policy; + fn new_request_policy(&self, sleep_impl: Option) -> Self::Policy; } /// Retry Policy Configuration @@ -165,7 +165,7 @@ impl Standard { impl NewRequestPolicy for Standard { type Policy = RetryHandler; - fn new_request_policy(&self, sleep_impl: Option>) -> Self::Policy { + fn new_request_policy(&self, sleep_impl: Option) -> Self::Policy { RetryHandler { local: RequestLocalRetryState::new(), shared: self.shared_state.clone(), @@ -262,7 +262,7 @@ pub struct RetryHandler { local: RequestLocalRetryState, shared: CrossRequestRetryState, config: Config, - sleep_impl: Option>, + sleep_impl: Option, } #[cfg(test)] diff --git a/rust-runtime/aws-smithy-client/src/timeout.rs b/rust-runtime/aws-smithy-client/src/timeout.rs index 640e1c4562..f9a03a41f6 100644 --- a/rust-runtime/aws-smithy-client/src/timeout.rs +++ b/rust-runtime/aws-smithy-client/src/timeout.rs @@ -7,13 +7,12 @@ use crate::SdkError; use aws_smithy_async::future::timeout::Timeout; -use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; use aws_smithy_http::operation::Operation; use aws_smithy_types::timeout::OperationTimeoutConfig; use pin_project_lite::pin_project; use std::future::Future; use std::pin::Pin; -use std::sync::Arc; use std::task::{Context, Poll}; use std::time::Duration; use tower::Layer; @@ -46,7 +45,7 @@ pub struct TimeoutServiceParams { /// The kind of timeouts created from these params kind: &'static str, /// The AsyncSleep impl that will be used to create time-limited futures - async_sleep: Arc, + async_sleep: SharedAsyncSleep, } #[derive(Clone, Debug, Default)] @@ -61,7 +60,7 @@ pub(crate) struct ClientTimeoutParams { impl ClientTimeoutParams { pub(crate) fn new( timeout_config: &OperationTimeoutConfig, - async_sleep: Option>, + async_sleep: Option, ) -> Self { if let Some(async_sleep) = async_sleep { Self { @@ -232,11 +231,10 @@ mod test { use crate::never::NeverService; use crate::{SdkError, TimeoutLayer}; use aws_smithy_async::assert_elapsed; - use aws_smithy_async::rt::sleep::{AsyncSleep, TokioSleep}; + use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::operation::{Operation, Request}; use aws_smithy_types::timeout::TimeoutConfig; - use std::sync::Arc; use std::time::Duration; use tower::{Service, ServiceBuilder, ServiceExt}; @@ -250,7 +248,7 @@ mod test { .operation_timeout(Duration::from_secs_f32(0.25)) .build(), ); - let sleep_impl: Arc = Arc::new(TokioSleep::new()); + let sleep_impl = SharedAsyncSleep::new(TokioSleep::new()); let timeout_service_params = ClientTimeoutParams::new(&timeout_config, Some(sleep_impl)); let mut svc = ServiceBuilder::new() .layer(TimeoutLayer::new(timeout_service_params.operation_timeout)) diff --git a/rust-runtime/aws-smithy-client/tests/e2e_test.rs b/rust-runtime/aws-smithy-client/tests/e2e_test.rs index 0a8594d6b3..f18a084bb5 100644 --- a/rust-runtime/aws-smithy-client/tests/e2e_test.rs +++ b/rust-runtime/aws-smithy-client/tests/e2e_test.rs @@ -5,14 +5,13 @@ mod test_operation; use crate::test_operation::{TestOperationParser, TestRetryClassifier}; -use aws_smithy_async::rt::sleep::TokioSleep; +use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_client::Client; use aws_smithy_http::body::SdkBody; use aws_smithy_http::operation; use aws_smithy_http::operation::Operation; use aws_smithy_http::result::SdkError; -use std::sync::Arc; use std::time::Duration; use tower::layer::util::Identity; @@ -72,7 +71,7 @@ async fn end_to_end_retry_test() { .connector(conn.clone()) .middleware(Identity::new()) .retry_config(retry_config) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .build(); tokio::time::pause(); let initial = tokio::time::Instant::now(); diff --git a/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs b/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs index 475c076b12..3fc0fe133a 100644 --- a/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs +++ b/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs @@ -7,7 +7,7 @@ mod test_operation; -use aws_smithy_async::rt::sleep::TokioSleep; +use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_client::test_connection::wire_mock; use aws_smithy_client::test_connection::wire_mock::{check_matches, RecordedEvent, ReplayedEvent}; use aws_smithy_client::{hyper_ext, Builder}; @@ -21,7 +21,6 @@ use http::Uri; use http_body::combinators::BoxBody; use hyper::client::{Builder as HyperBuilder, HttpConnector}; use std::convert::Infallible; -use std::sync::Arc; use std::time::Duration; use test_operation::{TestOperationParser, TestRetryClassifier}; use tower::layer::util::Identity; @@ -94,7 +93,7 @@ async fn wire_level_test( .operation_attempt_timeout(Duration::from_millis(100)) .build(), )) - .sleep_impl(Arc::new(TokioSleep::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .build(); loop { match client diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 6e041566ea..f60801d026 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -12,7 +12,7 @@ use crate::client::interceptors::context::{Error, Input, Output}; use crate::client::retries::RetryClassifiers; use crate::client::retries::RetryStrategy; use aws_smithy_async::future::now_or_later::NowOrLater; -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_http::body::SdkBody; use aws_smithy_types::config_bag::ConfigBag; @@ -141,8 +141,8 @@ pub trait ConfigBagAccessors { fn request_time(&self) -> Option; fn set_request_time(&mut self, time_source: impl TimeSource + 'static); - fn sleep_impl(&self) -> Option>; - fn set_sleep_impl(&mut self, async_sleep: Option>); + fn sleep_impl(&self) -> Option; + fn set_sleep_impl(&mut self, async_sleep: Option); fn loaded_request_body(&self) -> &LoadedRequestBody; fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody); @@ -271,15 +271,15 @@ impl ConfigBagAccessors for ConfigBag { self.put::(SharedTimeSource::new(request_time)); } - fn sleep_impl(&self) -> Option> { - self.get::>().cloned() + fn sleep_impl(&self) -> Option { + self.get::().cloned() } - fn set_sleep_impl(&mut self, sleep_impl: Option>) { + fn set_sleep_impl(&mut self, sleep_impl: Option) { if let Some(sleep_impl) = sleep_impl { - self.put::>(sleep_impl); + self.put::(sleep_impl); } else { - self.unset::>(); + self.unset::(); } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index f626816dce..34d5d34dd5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -7,6 +7,7 @@ use self::auth::orchestrate_auth; use crate::client::orchestrator::endpoints::orchestrate_endpoint; use crate::client::orchestrator::http::read_body; use crate::client::timeout::{MaybeTimeout, ProvideMaybeTimeoutConfig, TimeoutKind}; +use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream::ByteStream; use aws_smithy_http::result::SdkError; diff --git a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs index b54447194c..2f663b2e60 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs @@ -4,7 +4,7 @@ */ use aws_smithy_async::future::timeout::Timeout; -use aws_smithy_async::rt::sleep::{AsyncSleep, Sleep}; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; use aws_smithy_client::SdkError; use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpResponse}; use aws_smithy_types::config_bag::ConfigBag; @@ -12,7 +12,6 @@ use aws_smithy_types::timeout::TimeoutConfig; use pin_project_lite::pin_project; use std::future::Future; use std::pin::Pin; -use std::sync::Arc; use std::task::{Context, Poll}; use std::time::Duration; @@ -104,7 +103,7 @@ pub(super) enum TimeoutKind { #[derive(Clone, Debug)] pub(super) struct MaybeTimeoutConfig { - sleep_impl: Option>, + sleep_impl: Option, timeout: Option, timeout_kind: TimeoutKind, } @@ -187,7 +186,7 @@ mod tests { #[tokio::test] async fn test_no_timeout() { - let sleep_impl: Arc = Arc::new(TokioSleep::new()); + let sleep_impl = SharedAsyncSleep::new(TokioSleep::new()); let sleep_future = sleep_impl.sleep(Duration::from_millis(250)); let underlying_future = async { sleep_future.await; @@ -211,7 +210,7 @@ mod tests { #[tokio::test] async fn test_operation_timeout() { - let sleep_impl: Arc = Arc::new(TokioSleep::new()); + let sleep_impl = SharedAsyncSleep::new(TokioSleep::new()); let never = Never::new(); let underlying_future = async { never.await; From ccc3474d5ba5b4c736fe8083779eaa175380d8ec Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 2 Jun 2023 17:01:18 -0700 Subject: [PATCH 131/253] Fix S3 presigning in the orchestrator (#2738) ## Motivation and Context This PR fixes S3 presigning in the client orchestrator implementation. A Polly presigning fix will come in a separate PR. This PR also: - Renames `ClientProtocolGenerator` to `OperationGenerator` to better represent what its generating. - Moves `OperationCustomization` into `codegen-client` since it is client specific and unused by the server codegen. - Deletes the `ProtocolGenerator` base class in `codegen-core` since it wasn't adding any value. - Adds the concept of stop points to the orchestrator so that orchestration can be halted before transmitting a request. - Adds `DisableInvocationIdInterceptor`, `DisableRequestInfoInterceptor`, `DisableUserAgentInterceptor`, and `HeaderSerializationSettings` config bag configs to facilitate presigning requirements. - Fixes compilation of all S3 and Polly tests when in orchestrator mode. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-inlineable/src/lib.rs | 10 + .../aws-inlineable/src/presigning.rs | 57 ----- .../src/presigning_interceptors.rs | 103 ++++++++ .../aws-inlineable/src/presigning_service.rs | 61 +++++ .../src/serialization_settings.rs | 1 + .../aws-runtime/src/invocation_id.rs | 17 ++ .../aws-runtime/src/request_info.rs | 17 ++ .../aws-runtime/src/user_agent.rs | 17 ++ .../rustsdk/AwsFluentClientDecorator.kt | 2 +- .../smithy/rustsdk/AwsPresigningDecorator.kt | 95 +++++-- .../amazon/smithy/rustsdk/AwsRuntimeType.kt | 22 +- .../smithy/rustsdk/BaseRequestIdDecorator.kt | 4 +- .../amazon/smithy/rustsdk/CredentialCaches.kt | 4 +- .../rustsdk/HttpRequestChecksumDecorator.kt | 4 +- .../rustsdk/HttpResponseChecksumDecorator.kt | 4 +- .../smithy/rustsdk/InvocationIdDecorator.kt | 10 +- .../amazon/smithy/rustsdk/RegionDecorator.kt | 4 +- .../rustsdk/RetryClassifierDecorator.kt | 28 +- .../RetryInformationHeaderDecorator.kt | 23 +- .../smithy/rustsdk/SigV4AuthDecorator.kt | 19 +- .../smithy/rustsdk/SigV4SigningDecorator.kt | 4 +- .../smithy/rustsdk/UserAgentDecorator.kt | 20 +- .../customize/ServiceSpecificDecorator.kt | 11 +- .../apigateway/ApiGatewayDecorator.kt | 4 +- .../customize/glacier/AccountIdAutofill.kt | 4 +- .../customize/glacier/ApiVersionHeader.kt | 4 +- .../customize/glacier/GlacierDecorator.kt | 37 ++- .../customize/glacier/TreeHashHeader.kt | 4 +- .../customize/route53/Route53Decorator.kt | 4 +- .../rustsdk/customize/s3/S3Decorator.kt | 6 +- .../polly/tests/presigning.rs | 17 +- aws/sdk/integration-tests/s3/Cargo.toml | 1 + .../integration-tests/s3/tests/presigning.rs | 171 +++++++------ .../integration-tests/s3/tests/request_id.rs | 240 +++++++++++------- .../s3/tests/required-query-params.rs | 50 ++-- .../client/smithy/ClientCodegenVisitor.kt | 10 +- .../customizations/ApiKeyAuthDecorator.kt | 4 +- .../customizations/EndpointPrefixGenerator.kt | 4 +- .../customizations/HttpAuthDecorator.kt | 20 +- .../HttpChecksumRequiredGenerator.kt | 4 +- .../HttpVersionListCustomization.kt | 4 +- .../IdempotencyTokenGenerator.kt | 4 +- .../customize/ClientCodegenDecorator.kt | 25 +- .../customize/RequiredCustomizations.kt | 2 +- .../endpoint/EndpointParamsDecorator.kt | 20 +- .../smithy/endpoint/EndpointsDecorator.kt | 4 +- .../generators/OperationCustomization.kt | 183 +++++++++++++ ...ocolGenerator.kt => OperationGenerator.kt} | 142 +++++++---- .../OperationRuntimePluginGenerator.kt | 59 +---- .../smithy/generators/PaginatorGenerator.kt | 19 +- .../client/FluentClientGenerator.kt | 9 +- .../generators/config/TimeSourceConfig.kt | 4 +- .../protocol/MakeOperationGenerator.kt | 4 +- .../protocol/ProtocolParserGenerator.kt | 4 +- .../protocol/ProtocolTestGenerator.kt | 57 +++-- .../protocol/RequestSerializerGenerator.kt | 24 +- .../protocol/ResponseDeserializerGenerator.kt | 4 +- .../smithy/protocols/ClientProtocolLoader.kt | 6 +- .../protocols/HttpBoundProtocolGenerator.kt | 8 +- .../protocol/ProtocolTestGeneratorTest.kt | 13 +- .../codegen/core/rustlang/CargoDependency.kt | 6 + .../customize/OperationCustomization.kt | 101 -------- .../generators/protocol/ProtocolGenerator.kt | 18 -- .../protocol/ServerProtocolGenerator.kt | 7 +- .../ServerHttpBoundProtocolGenerator.kt | 4 +- .../aws-smithy-async/src/test_util.rs | 25 -- rust-runtime/aws-smithy-async/src/time.rs | 25 ++ .../src/client/interceptors/context.rs | 29 +-- .../src/client/orchestrator.rs | 6 +- .../src/client/runtime_plugin.rs | 6 - .../src/client/orchestrator.rs | 222 +++++++++++++--- .../src/client/orchestrator/auth.rs | 4 +- .../src/client/orchestrator/endpoints.rs | 4 +- rust-runtime/inlineable/src/lib.rs | 2 + .../inlineable/src/serialization_settings.rs | 84 ++++++ .../check-aws-sdk-orchestrator-impl | 8 +- 76 files changed, 1445 insertions(+), 822 deletions(-) create mode 100644 aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs create mode 100644 aws/rust-runtime/aws-inlineable/src/presigning_service.rs create mode 120000 aws/rust-runtime/aws-inlineable/src/serialization_settings.rs create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt rename codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/{protocol/ClientProtocolGenerator.kt => OperationGenerator.kt} (58%) delete mode 100644 codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt create mode 100644 rust-runtime/inlineable/src/serialization_settings.rs diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index 465aaac302..5ade5bb680 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -28,6 +28,12 @@ pub mod no_credentials; /// Support types required for adding presigning to an operation in a generated service. pub mod presigning; +/// Presigning tower service +pub mod presigning_service; + +/// Presigning interceptors +pub mod presigning_interceptors; + /// Special logic for extracting request IDs from S3's responses. pub mod s3_request_id; @@ -49,6 +55,10 @@ pub mod http_body_checksum; #[allow(dead_code)] pub mod endpoint_discovery; +// This module is symlinked in from the smithy-rs rust-runtime inlineables so that +// the `presigning_interceptors` module can refer to it. +mod serialization_settings; + // just so docs work #[allow(dead_code)] /// allow docs to work diff --git a/aws/rust-runtime/aws-inlineable/src/presigning.rs b/aws/rust-runtime/aws-inlineable/src/presigning.rs index da0997d591..1bc9f42e6f 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning.rs @@ -215,60 +215,3 @@ impl From for http::request::Builder { builder } } - -/// Tower middleware service for creating presigned requests -#[allow(dead_code)] -pub(crate) mod service { - use super::PresignedRequest; - use aws_smithy_http::operation; - use http::header::USER_AGENT; - use std::future::{ready, Ready}; - use std::marker::PhantomData; - use std::task::{Context, Poll}; - - /// Tower [`Service`](tower::Service) for generated a [`PresignedRequest`] from the AWS middleware. - #[derive(Default, Debug)] - #[non_exhaustive] - pub(crate) struct PresignedRequestService { - _phantom: PhantomData, - } - - // Required because of the derive Clone on MapRequestService. - // Manually implemented to avoid requiring errors to implement Clone. - impl Clone for PresignedRequestService { - fn clone(&self) -> Self { - Self { - _phantom: Default::default(), - } - } - } - - impl PresignedRequestService { - /// Creates a new `PresignedRequestService` - pub(crate) fn new() -> Self { - Self { - _phantom: Default::default(), - } - } - } - - impl tower::Service for PresignedRequestService { - type Response = PresignedRequest; - type Error = E; - type Future = Ready>; - - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: operation::Request) -> Self::Future { - let (mut req, _) = req.into_parts(); - - // Remove user agent headers since the request will not be executed by the AWS Rust SDK. - req.headers_mut().remove(USER_AGENT); - req.headers_mut().remove("X-Amz-User-Agent"); - - ready(Ok(PresignedRequest::new(req.map(|_| ())))) - } - } -} diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs new file mode 100644 index 0000000000..af26912ddd --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs @@ -0,0 +1,103 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +use crate::presigning::PresigningConfig; +use crate::serialization_settings::HeaderSerializationSettings; +use aws_runtime::auth::sigv4::{HttpSignatureType, SigV4OperationSigningConfig}; +use aws_runtime::invocation_id::DisableInvocationIdInterceptor; +use aws_runtime::request_info::DisableRequestInfoInterceptor; +use aws_runtime::user_agent::DisableUserAgentInterceptor; +use aws_smithy_async::time::{SharedTimeSource, StaticTimeSource}; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, BoxError, + Interceptor, InterceptorRegistrar, SharedInterceptor, +}; +use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::config_bag::ConfigBag; + +/// Interceptor that tells the SigV4 signer to add the signature to query params, +/// and sets the request expiration time from the presigning config. +#[derive(Debug)] +pub(crate) struct SigV4PresigningInterceptor { + config: PresigningConfig, +} + +impl SigV4PresigningInterceptor { + pub(crate) fn new(config: PresigningConfig) -> Self { + Self { config } + } +} + +impl Interceptor for SigV4PresigningInterceptor { + fn modify_before_serialization( + &self, + _context: &mut BeforeSerializationInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + cfg.put::( + HeaderSerializationSettings::new() + .omit_default_content_length() + .omit_default_content_type(), + ); + cfg.set_request_time(SharedTimeSource::new(StaticTimeSource::new( + self.config.start_time(), + ))); + Ok(()) + } + + fn modify_before_signing( + &self, + _context: &mut BeforeTransmitInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + if let Some(mut config) = cfg.get::().cloned() { + config.signing_options.expires_in = Some(self.config.expires()); + config.signing_options.signature_type = HttpSignatureType::HttpRequestQueryParams; + config.signing_options.payload_override = + Some(aws_sigv4::http_request::SignableBody::UnsignedPayload); + cfg.put::(config); + Ok(()) + } else { + Err( + "SigV4 presigning requires the SigV4OperationSigningConfig to be in the config bag. \ + This is a bug. Please file an issue.".into(), + ) + } + } +} + +/// Runtime plugin that registers the SigV4PresigningInterceptor. +#[derive(Debug)] +pub(crate) struct SigV4PresigningRuntimePlugin { + interceptor: SharedInterceptor, +} + +impl SigV4PresigningRuntimePlugin { + pub(crate) fn new(config: PresigningConfig) -> Self { + Self { + interceptor: SharedInterceptor::new(SigV4PresigningInterceptor::new(config)), + } + } +} + +impl RuntimePlugin for SigV4PresigningRuntimePlugin { + fn configure( + &self, + cfg: &mut ConfigBag, + interceptors: &mut InterceptorRegistrar, + ) -> Result<(), BoxError> { + // Disable some SDK interceptors that shouldn't run for presigning + cfg.put(DisableInvocationIdInterceptor::new("presigning")); + cfg.put(DisableRequestInfoInterceptor::new("presigning")); + cfg.put(DisableUserAgentInterceptor::new("presigning")); + + // Register the presigning interceptor + interceptors.register(self.interceptor.clone()); + Ok(()) + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_service.rs b/aws/rust-runtime/aws-inlineable/src/presigning_service.rs new file mode 100644 index 0000000000..3dc1e61768 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/presigning_service.rs @@ -0,0 +1,61 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +//! Tower middleware service for creating presigned requests + +use crate::presigning::PresignedRequest; +use aws_smithy_http::operation; +use http::header::USER_AGENT; +use std::future::{ready, Ready}; +use std::marker::PhantomData; +use std::task::{Context, Poll}; + +/// Tower [`Service`](tower::Service) for generated a [`PresignedRequest`] from the AWS middleware. +#[derive(Default, Debug)] +#[non_exhaustive] +pub(crate) struct PresignedRequestService { + _phantom: PhantomData, +} + +// Required because of the derive Clone on MapRequestService. +// Manually implemented to avoid requiring errors to implement Clone. +impl Clone for PresignedRequestService { + fn clone(&self) -> Self { + Self { + _phantom: Default::default(), + } + } +} + +impl PresignedRequestService { + /// Creates a new `PresignedRequestService` + pub(crate) fn new() -> Self { + Self { + _phantom: Default::default(), + } + } +} + +impl tower::Service for PresignedRequestService { + type Response = PresignedRequest; + type Error = E; + type Future = Ready>; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, req: operation::Request) -> Self::Future { + let (mut req, _) = req.into_parts(); + + // Remove user agent headers since the request will not be executed by the AWS Rust SDK. + req.headers_mut().remove(USER_AGENT); + req.headers_mut().remove("X-Amz-User-Agent"); + + ready(Ok(PresignedRequest::new(req.map(|_| ())))) + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/serialization_settings.rs b/aws/rust-runtime/aws-inlineable/src/serialization_settings.rs new file mode 120000 index 0000000000..1df1108b01 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/serialization_settings.rs @@ -0,0 +1 @@ +../../../../rust-runtime/inlineable/src/serialization_settings.rs \ No newline at end of file diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 420cedb73d..2ff073f822 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -18,6 +18,23 @@ pub use test_util::{NoInvocationIdGenerator, PredefinedInvocationIdGenerator}; #[allow(clippy::declare_interior_mutable_const)] // we will never mutate this const AMZ_SDK_INVOCATION_ID: HeaderName = HeaderName::from_static("amz-sdk-invocation-id"); +/// Config marker that disables the invocation ID interceptor. +#[doc(hidden)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct DisableInvocationIdInterceptor { + why: &'static str, +} + +impl DisableInvocationIdInterceptor { + /// Creates a new `DisableInvocationIdInterceptor`. + /// + /// Takes a human readable string for the `Debug` impl to state why it is being disabled. + /// This is to assist with debugging issues with requests. + pub fn new(why: &'static str) -> Self { + Self { why } + } +} + /// A generator for returning new invocation IDs on demand. pub trait InvocationIdGenerator: Debug + Send + Sync { /// Call this function to receive a new [`InvocationId`] or an error explaining why one couldn't diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index 0b41310536..706872dd29 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -20,6 +20,23 @@ use std::time::{Duration, SystemTime}; #[allow(clippy::declare_interior_mutable_const)] // we will never mutate this const AMZ_SDK_REQUEST: HeaderName = HeaderName::from_static("amz-sdk-request"); +/// Config marker that disables the invocation ID interceptor. +#[doc(hidden)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct DisableRequestInfoInterceptor { + why: &'static str, +} + +impl DisableRequestInfoInterceptor { + /// Creates a new `DisableRequestInfoInterceptor`. + /// + /// Takes a human readable string for the `Debug` impl to state why it is being disabled. + /// This is to assist with debugging issues with requests. + pub fn new(why: &'static str) -> Self { + Self { why } + } +} + /// Generates and attaches a request header that communicates request-related metadata. /// Examples include: /// diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 52c6cdb4da..12469fa13d 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -49,6 +49,23 @@ impl From for UserAgentInterceptorError { } } +/// Config marker that disables the user agent interceptor. +#[doc(hidden)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct DisableUserAgentInterceptor { + why: &'static str, +} + +impl DisableUserAgentInterceptor { + /// Creates a new `DisableUserAgentInterceptor`. + /// + /// Takes a human readable string for the `Debug` impl to state why it is being disabled. + /// This is to assist with debugging issues with requests. + pub fn new(why: &'static str) -> Self { + Self { why } + } +} + /// Generates and attaches the AWS SDK's user agent to a HTTP request #[non_exhaustive] #[derive(Debug, Default)] diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index bf5eb6e3d6..bd619ea746 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -64,7 +64,7 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { reexportSmithyClientBuilder = false, generics = generics, customizations = listOf( - AwsPresignedFluentBuilderMethod(runtimeConfig), + AwsPresignedFluentBuilderMethod(codegenContext), AwsFluentClientDocs(codegenContext), ), retryClassifier = AwsRuntimeType.awsHttp(runtimeConfig).resolve("retry::AwsResponseRetryClassifier"), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index fb53f61165..ba304c6de9 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -18,10 +18,13 @@ import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientSection import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientHttpBoundProtocolPayloadGenerator +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs @@ -30,14 +33,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.cloneOperation import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rustsdk.AwsRuntimeType.defaultMiddleware import software.amazon.smithy.rustsdk.traits.PresignableTrait import kotlin.streams.toList @@ -100,12 +99,13 @@ class AwsPresigningDecorator internal constructor( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = - baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateMiddleware) { - it + listOf( - AwsInputPresignedMethod(codegenContext, operation), - ) + ): List { + return if (codegenContext.smithyRuntimeMode.generateMiddleware) { + baseCustomizations + AwsInputPresignedMethod(codegenContext, operation) + } else { + baseCustomizations } + } /** * Adds presignable trait to known presignable operations and creates synthetic presignable shapes for codegen @@ -137,6 +137,7 @@ class AwsPresigningDecorator internal constructor( } } +// TODO(enableNewSmithyRuntime): Delete this class when cleaning up middleware class AwsInputPresignedMethod( private val codegenContext: ClientCodegenContext, private val operationShape: OperationShape, @@ -146,8 +147,8 @@ class AwsInputPresignedMethod( private val codegenScope = ( presigningTypes + listOf( - "PresignedRequestService" to AwsRuntimeType.presigning() - .resolve("service::PresignedRequestService"), + "PresignedRequestService" to AwsRuntimeType.presigningService() + .resolve("PresignedRequestService"), "SdkError" to RuntimeType.sdkError(runtimeConfig), "aws_sigv4" to AwsRuntimeType.awsSigv4(runtimeConfig), "sig_auth" to AwsRuntimeType.awsSigAuth(runtimeConfig), @@ -253,8 +254,9 @@ class AwsInputPresignedMethod( } class AwsPresignedFluentBuilderMethod( - runtimeConfig: RuntimeConfig, + private val codegenContext: ClientCodegenContext, ) : FluentClientCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope = ( presigningTypes + arrayOf( *RuntimeType.preludeScope, @@ -269,24 +271,75 @@ class AwsPresignedFluentBuilderMethod( documentPresignedMethod(hasConfigArg = false) rustBlockTemplate( """ + ##[allow(unused_mut)] pub async fn presigned( - self, + mut self, presigning_config: #{PresigningConfig}, - ) -> #{Result}<#{PresignedRequest}, #{SdkError}<#{OpError}>> + ) -> #{Result}<#{PresignedRequest}, #{SdkError}<#{OpError}, #{RawResponseType}>> """, *codegenScope, "OpError" to section.operationErrorType, + "RawResponseType" to if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + RuntimeType.smithyHttp(runtimeConfig).resolve("operation::Response") + } else { + RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse") + }, ) { - rustTemplate( - """ - let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; - input.presigned(&self.handle.conf, presigning_config).await - """, - *codegenScope, - ) + if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + renderPresignedMethodBodyMiddleware() + } else { + renderPresignedMethodBody(section) + } } } } + + private fun RustWriter.renderPresignedMethodBodyMiddleware() { + rustTemplate( + """ + let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; + input.presigned(&self.handle.conf, presigning_config).await + """, + *codegenScope, + ) + } + + private fun RustWriter.renderPresignedMethodBody(section: FluentClientSection.FluentBuilderImpl) { + rustTemplate( + """ + let runtime_plugins = #{Operation}::register_runtime_plugins( + #{RuntimePlugins}::new() + .with_client_plugin(#{SigV4PresigningRuntimePlugin}::new(presigning_config)), + self.handle.clone(), + self.config_override, + ); + + let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; + let mut context = #{Operation}::orchestrate_with_stop_point(&runtime_plugins, input, #{StopPoint}::BeforeTransmit) + .await + .map_err(|err| { + err.map_service_error(|err| { + #{TypedBox}::<#{OperationError}>::assume_from(err.into()) + .expect("correct error type") + .unwrap() + }) + })?; + let request = context.take_request().expect("request set before transmit"); + Ok(#{PresignedRequest}::new(request.map(|_| ()))) + """, + *codegenScope, + "Operation" to codegenContext.symbolProvider.toSymbol(section.operationShape), + "OperationError" to section.operationErrorType, + "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::RuntimePlugins"), + "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors").resolve("SharedInterceptor"), + "SigV4PresigningRuntimePlugin" to AwsRuntimeType.presigningInterceptor(runtimeConfig) + .resolve("SigV4PresigningRuntimePlugin"), + "StopPoint" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::StopPoint"), + "TypedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypedBox"), + "USER_AGENT" to CargoDependency.Http.toType().resolve("header::USER_AGENT"), + ) + } } interface PresignModelTransform { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt index b0ae6a70eb..272cb35f31 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt @@ -41,13 +41,21 @@ fun RuntimeConfig.awsRoot(): RuntimeCrateLocation { } object AwsRuntimeType { - fun presigning(): RuntimeType = RuntimeType.forInlineDependency( - InlineAwsDependency.forRustFile( - "presigning", - visibility = Visibility.PUBLIC, - CargoDependency.Tower, - ), - ) + fun presigning(): RuntimeType = + RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning", visibility = Visibility.PUBLIC)) + fun presigningInterceptor(runtimeConfig: RuntimeConfig): RuntimeType = + RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "presigning_interceptors", + visibility = Visibility.PUBCRATE, + AwsCargoDependency.awsSigv4(runtimeConfig), + CargoDependency.smithyRuntimeApi(runtimeConfig), + ), + ) + + // TODO(enableNewSmithyRuntime): Delete the `presigning_service.rs` inlineable when cleaning up middleware + fun presigningService(): RuntimeType = + RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning_service", visibility = Visibility.PUBCRATE)) // TODO(enableNewSmithyRuntime): Delete defaultMiddleware and middleware.rs, and remove tower dependency from inlinables, when cleaning up middleware fun RuntimeConfig.defaultMiddleware() = RuntimeType.forInlineDependency( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt index e1f08ebb05..9b8adeddab 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/BaseRequestIdDecorator.kt @@ -9,6 +9,8 @@ import software.amazon.smithy.model.shapes.OperationShape 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.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -18,8 +20,6 @@ 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.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderSection import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt index 5aaaeab950..ae3cde9cc0 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt @@ -8,6 +8,8 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection 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.core.rustlang.Writable @@ -16,8 +18,6 @@ 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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization class CredentialsCacheDecorator : ClientCodegenDecorator { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt index 05af48214a..6cbabac906 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt @@ -9,6 +9,8 @@ import software.amazon.smithy.aws.traits.HttpChecksumTrait import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -16,8 +18,6 @@ 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.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.util.expectMember import software.amazon.smithy.rust.codegen.core.util.getTrait diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt index 5cdfbe073f..4e50c4c156 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt @@ -11,10 +11,10 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection 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.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.expectMember import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt index 220b57169a..2b52c2b33a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.util.letIf @@ -36,13 +37,16 @@ private class InvocationIdRuntimePluginCustomization( ) override fun section(section: ServiceRuntimePluginSection): Writable = writable { - when (section) { - is ServiceRuntimePluginSection.AdditionalConfig -> { + if (section is ServiceRuntimePluginSection.AdditionalConfig) { + val invocationId = AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig).resolve("invocation_id") + rustBlockTemplate( + "if cfg.get::<#{DisableInvocationIdInterceptor}>().is_none()", + "DisableInvocationIdInterceptor" to invocationId.resolve("DisableInvocationIdInterceptor"), + ) { section.registerInterceptor(codegenContext.runtimeConfig, this) { rustTemplate("#{InvocationIdInterceptor}::new()", *codegenScope) } } - else -> emptySection } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt index af0953bdcd..61373f5d24 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt @@ -13,6 +13,8 @@ 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.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection 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.core.rustlang.Writable @@ -23,8 +25,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.extendIf diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt index 9d87e68d1a..8eb68da500 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt @@ -8,16 +8,13 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection 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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection -import software.amazon.smithy.rust.codegen.core.util.letIf class RetryClassifierDecorator : ClientCodegenDecorator { override val name: String = "RetryPolicy" @@ -28,17 +25,10 @@ class RetryClassifierDecorator : ClientCodegenDecorator { operation: OperationShape, baseCustomizations: List, ): List = - baseCustomizations + RetryClassifierFeature(codegenContext.runtimeConfig) - - override fun operationRuntimePluginCustomizations( - codegenContext: ClientCodegenContext, - operation: OperationShape, - baseCustomizations: List, - ): List { - return baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { - it + OperationRetryClassifiersFeature(codegenContext, operation) - } - } + baseCustomizations + RetryClassifierFeature(codegenContext.runtimeConfig) + OperationRetryClassifiersFeature( + codegenContext, + operation, + ) } class RetryClassifierFeature(private val runtimeConfig: RuntimeConfig) : OperationCustomization() { @@ -60,7 +50,7 @@ class RetryClassifierFeature(private val runtimeConfig: RuntimeConfig) : Operati class OperationRetryClassifiersFeature( codegenContext: ClientCodegenContext, operation: OperationShape, -) : OperationRuntimePluginCustomization() { +) : OperationCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) private val smithyRuntime = RuntimeType.smithyRuntime(runtimeConfig) @@ -80,8 +70,8 @@ class OperationRetryClassifiersFeature( "RetryReason" to smithyRuntimeApi.resolve("client::retries::RetryReason"), ) - override fun section(section: OperationRuntimePluginSection) = when (section) { - is OperationRuntimePluginSection.RetryClassifier -> writable { + override fun section(section: OperationSection) = when (section) { + is OperationSection.RetryClassifier -> writable { rustTemplate( """ .with_classifier(#{SmithyErrorClassifier}::<#{OperationError}>::new()) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt index 4f8a342e31..f984608b41 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRunti import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.util.letIf @@ -36,14 +37,22 @@ private class AddRetryInformationHeaderInterceptors(codegenContext: ClientCodege override fun section(section: ServiceRuntimePluginSection): Writable = writable { if (section is ServiceRuntimePluginSection.AdditionalConfig) { - // Track the latency between client and server. - section.registerInterceptor(runtimeConfig, this) { - rust("#T::new()", smithyRuntime.resolve("client::orchestrator::interceptors::ServiceClockSkewInterceptor")) - } + rustBlockTemplate( + "if cfg.get::<#{DisableRequestInfoInterceptor}>().is_none()", + "DisableRequestInfoInterceptor" to awsRuntime.resolve("request_info::DisableRequestInfoInterceptor"), + ) { + // Track the latency between client and server. + section.registerInterceptor(runtimeConfig, this) { + rust( + "#T::new()", + smithyRuntime.resolve("client::orchestrator::interceptors::ServiceClockSkewInterceptor"), + ) + } - // Add request metadata to outgoing requests. Sets a header. - section.registerInterceptor(runtimeConfig, this) { - rust("#T::new()", awsRuntime.resolve("request_info::RequestInfoInterceptor")) + // Add request metadata to outgoing requests. Sets a header. + section.registerInterceptor(runtimeConfig, this) { + rust("#T::new()", awsRuntime.resolve("request_info::RequestInfoInterceptor")) + } } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index dbf6710a4c..d444531870 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -12,8 +12,8 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -38,13 +38,13 @@ class SigV4AuthDecorator : ClientCodegenDecorator { it + listOf(AuthServiceRuntimePluginCustomization(codegenContext)) } - override fun operationRuntimePluginCustomizations( + override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, - baseCustomizations: List, - ): List = + baseCustomizations: List, + ): List = baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { - it + listOf(AuthOperationRuntimePluginCustomization(codegenContext)) + it + listOf(AuthOperationCustomization(codegenContext)) } } @@ -101,8 +101,7 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext: } } -private class AuthOperationRuntimePluginCustomization(private val codegenContext: ClientCodegenContext) : - OperationRuntimePluginCustomization() { +private class AuthOperationCustomization(private val codegenContext: ClientCodegenContext) : OperationCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope by lazy { val runtimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) @@ -120,9 +119,9 @@ private class AuthOperationRuntimePluginCustomization(private val codegenContext } private val serviceIndex = ServiceIndex.of(codegenContext.model) - override fun section(section: OperationRuntimePluginSection): Writable = writable { + override fun section(section: OperationSection): Writable = writable { when (section) { - is OperationRuntimePluginSection.AdditionalConfig -> { + is OperationSection.AdditionalRuntimePluginConfig -> { val authSchemes = serviceIndex.getEffectiveAuthSchemes(codegenContext.serviceShape, section.operationShape) if (authSchemes.containsKey(SigV4Trait.ID)) { val unsignedPayload = section.operationShape.hasTrait() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt index 3c40b518eb..8ca37474c7 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt @@ -15,6 +15,8 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection 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.core.rustlang.Writable @@ -23,8 +25,6 @@ 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.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.extendIf diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index 90183fbf93..c3595d3a32 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -10,20 +10,21 @@ import software.amazon.smithy.model.shapes.OperationShape 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.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig 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.rustBlockTemplate 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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersionCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.expectTrait @@ -104,11 +105,16 @@ class UserAgentDecorator : ClientCodegenDecorator { override fun section(section: ServiceRuntimePluginSection): Writable = writable { if (section is ServiceRuntimePluginSection.AdditionalConfig) { - section.putConfigValue(this) { - rust("#T.clone()", ClientRustModule.Meta.toType().resolve("API_METADATA")) - } - section.registerInterceptor(runtimeConfig, this) { - rust("#T::new()", awsRuntime.resolve("user_agent::UserAgentInterceptor")) + rustBlockTemplate( + "if cfg.get::<#{DisableUserAgentInterceptor}>().is_none()", + "DisableUserAgentInterceptor" to awsRuntime.resolve("user_agent::DisableUserAgentInterceptor"), + ) { + section.putConfigValue(this) { + rust("#T.clone()", ClientRustModule.Meta.toType().resolve("API_METADATA")) + } + section.registerInterceptor(runtimeConfig, this) { + rust("#T::new()", awsRuntime.resolve("user_agent::UserAgentInterceptor")) + } } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index ce4d3f88ae..1a1c1d5788 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -14,14 +14,13 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientProtocolMap import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.ManifestCustomizations @@ -153,12 +152,4 @@ class ServiceSpecificDecorator( listOf().maybeApply(codegenContext.serviceShape) { delegateTo.extraSections(codegenContext) } - - override fun operationRuntimePluginCustomizations( - codegenContext: ClientCodegenContext, - operation: OperationShape, - baseCustomizations: List, - ): List = baseCustomizations.maybeApply(codegenContext.serviceShape) { - delegateTo.operationRuntimePluginCustomizations(codegenContext, operation, baseCustomizations) - } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt index 9d5a8400cb..ad600dac26 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt @@ -8,6 +8,8 @@ package software.amazon.smithy.rustsdk.customize.apigateway import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency @@ -16,8 +18,6 @@ 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.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rustsdk.InlineAwsDependency diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt index 19484b07b7..66b7f1fe49 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt @@ -7,11 +7,11 @@ package software.amazon.smithy.rustsdk.customize.glacier import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.inputShape // TODO(enableNewSmithyRuntime): Delete this file when cleaning up middleware. diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt index 91dece494e..69dafc7672 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt @@ -5,12 +5,12 @@ package software.amazon.smithy.rustsdk.customize.glacier +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.dq // TODO(enableNewSmithyRuntime): Delete this file when cleaning up middleware. diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt index 0110331b48..053665c48c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt @@ -10,8 +10,8 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency @@ -20,7 +20,6 @@ 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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.StructureSection import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait @@ -46,17 +45,20 @@ class GlacierDecorator : ClientCodegenDecorator { override val name: String = "Glacier" override val order: Byte = 0 - // TODO(enableNewSmithyRuntime): Delete the operation customizations when cleaning up middleware override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateMiddleware) { - it + listOfNotNull( - ApiVersionHeader(codegenContext.serviceShape.version), - TreeHashHeader.forOperation(operation, codegenContext.runtimeConfig), - AccountIdAutofill.forOperation(operation, codegenContext.model), - ) + ): List { + return if (codegenContext.smithyRuntimeMode.generateMiddleware) { + baseCustomizations + listOfNotNull( + ApiVersionHeader(codegenContext.serviceShape.version), + TreeHashHeader.forOperation(operation, codegenContext.runtimeConfig), + AccountIdAutofill.forOperation(operation, codegenContext.model), + ) + } else { + baseCustomizations + GlacierOperationInterceptorsCustomization(codegenContext) + } } override fun structureCustomizations( @@ -73,15 +75,6 @@ class GlacierDecorator : ClientCodegenDecorator { baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + listOf(GlacierApiVersionCustomization(codegenContext)) } - - override fun operationRuntimePluginCustomizations( - codegenContext: ClientCodegenContext, - operation: OperationShape, - baseCustomizations: List, - ): List = - baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { - it + listOf(GlacierOperationInterceptorsCustomization(codegenContext)) - } } /** Implements the `GlacierAccountId` trait for inputs that have an `account_id` field */ @@ -127,9 +120,9 @@ private class GlacierApiVersionCustomization(private val codegenContext: ClientC * the `aws-sigv4` module to recalculate the payload hash. */ private class GlacierOperationInterceptorsCustomization(private val codegenContext: ClientCodegenContext) : - OperationRuntimePluginCustomization() { - override fun section(section: OperationRuntimePluginSection): Writable = writable { - if (section is OperationRuntimePluginSection.AdditionalConfig) { + OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + if (section is OperationSection.AdditionalRuntimePluginConfig) { val inputShape = codegenContext.model.expectShape(section.operationShape.inputShape) as StructureShape val inlineModule = inlineModule(codegenContext.runtimeConfig) if (inputShape.inputWithAccountId()) { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt index 7a972b93aa..7f1f5d590e 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt @@ -7,14 +7,14 @@ package software.amazon.smithy.rustsdk.customize.glacier import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency 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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rustsdk.InlineAwsDependency diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt index 007254c479..4f9d6fbf2d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt @@ -14,12 +14,12 @@ import software.amazon.smithy.model.traits.HttpLabelTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection 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.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.letIf diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index 0a2bd05212..d49c2732c7 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -21,7 +21,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientRestXmlFactory import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate @@ -52,8 +52,8 @@ class S3Decorator : ClientCodegenDecorator { override fun protocols( serviceId: ShapeId, - currentProtocols: ProtocolMap, - ): ProtocolMap = currentProtocols + mapOf( + currentProtocols: ProtocolMap, + ): ProtocolMap = currentProtocols + mapOf( RestXmlTrait.ID to ClientRestXmlFactory { protocolConfig -> S3ProtocolOverride(protocolConfig) }, diff --git a/aws/sdk/integration-tests/polly/tests/presigning.rs b/aws/sdk/integration-tests/polly/tests/presigning.rs index bfb8729164..eaf525d7fb 100644 --- a/aws/sdk/integration-tests/polly/tests/presigning.rs +++ b/aws/sdk/integration-tests/polly/tests/presigning.rs @@ -5,35 +5,32 @@ use aws_sdk_polly as polly; use polly::config::{Config, Credentials, Region}; -use polly::operation::synthesize_speech::SynthesizeSpeechInput; use polly::presigning::PresigningConfig; use polly::types::{OutputFormat, VoiceId}; -use std::error::Error; use std::time::{Duration, SystemTime}; #[tokio::test] -async fn test_presigning() -> Result<(), Box> { +async fn test_presigning() { let config = Config::builder() .credentials_provider(Credentials::for_tests()) .region(Region::new("us-east-1")) .build(); + let client = polly::Client::from_conf(config); - let input = SynthesizeSpeechInput::builder() + let presigned = client + .synthesize_speech() .output_format(OutputFormat::Mp3) .text("hello, world") .voice_id(VoiceId::Joanna) - .build()?; - - let presigned = input .presigned( - &config, PresigningConfig::builder() .start_time(SystemTime::UNIX_EPOCH + Duration::from_secs(1234567891)) .expires_in(Duration::from_secs(30)) .build() .unwrap(), ) - .await?; + .await + .expect("success"); let pq = presigned.uri().path_and_query().unwrap(); let path = pq.path(); @@ -59,6 +56,4 @@ async fn test_presigning() -> Result<(), Box> { &query_params ); assert!(presigned.headers().is_empty()); - - Ok(()) } diff --git a/aws/sdk/integration-tests/s3/Cargo.toml b/aws/sdk/integration-tests/s3/Cargo.toml index 4961a1f1f6..b8972c8c17 100644 --- a/aws/sdk/integration-tests/s3/Cargo.toml +++ b/aws/sdk/integration-tests/s3/Cargo.toml @@ -31,6 +31,7 @@ hdrhistogram = "7.5.2" http = "0.2.3" http-body = "0.4.5" hyper = "0.14.25" +pretty_assertions = "1.3" serde_json = "1" smol = "1.2" tempfile = "3" diff --git a/aws/sdk/integration-tests/s3/tests/presigning.rs b/aws/sdk/integration-tests/s3/tests/presigning.rs index 6f8a33d571..05625d3405 100644 --- a/aws/sdk/integration-tests/s3/tests/presigning.rs +++ b/aws/sdk/integration-tests/s3/tests/presigning.rs @@ -4,47 +4,73 @@ */ use aws_sdk_s3 as s3; +use futures_util::future::FutureExt; +use futures_util::Future; use http::header::{CONTENT_LENGTH, CONTENT_TYPE}; use http::{HeaderMap, HeaderValue}; use s3::config::{Credentials, Region}; -use s3::operation::get_object::GetObjectInput; -use s3::operation::head_object::HeadObjectInput; -use s3::operation::put_object::PutObjectInput; -use s3::operation::upload_part::UploadPartInput; +use s3::operation::get_object::builders::GetObjectFluentBuilder; +use s3::operation::head_object::builders::HeadObjectFluentBuilder; +use s3::operation::put_object::builders::PutObjectFluentBuilder; +use s3::operation::upload_part::builders::UploadPartFluentBuilder; use s3::presigning::{PresignedRequest, PresigningConfig}; -use std::error::Error; +use std::pin::Pin; use std::time::{Duration, SystemTime}; +trait TestOperation { + fn presign_for_test( + self, + config: PresigningConfig, + ) -> Pin>>; +} + +macro_rules! rig_operation { + ($fluent_builder:ident) => { + impl TestOperation for $fluent_builder { + fn presign_for_test( + self, + config: PresigningConfig, + ) -> Pin>> { + Box::pin($fluent_builder::presigned(self, config).map(|out| out.expect("success"))) + } + } + }; +} + +rig_operation!(GetObjectFluentBuilder); +rig_operation!(PutObjectFluentBuilder); +rig_operation!(UploadPartFluentBuilder); +rig_operation!(HeadObjectFluentBuilder); + /// Generates a `PresignedRequest` from the given input. /// Assumes that that input has a `presigned` method on it. -macro_rules! presign_input { - ($input:expr) => {{ - let creds = Credentials::for_tests(); - let config = s3::Config::builder() - .credentials_provider(creds) - .region(Region::new("us-east-1")) - .build(); - - let req: PresignedRequest = $input - .presigned( - &config, - PresigningConfig::builder() - .start_time(SystemTime::UNIX_EPOCH + Duration::from_secs(1234567891)) - .expires_in(Duration::from_secs(30)) - .build() - .unwrap(), - ) - .await?; - req - }}; +async fn presign(operation: O) -> PresignedRequest +where + O: FnOnce(s3::Client) -> F, + F: TestOperation, +{ + let creds = Credentials::for_tests(); + let config = s3::Config::builder() + .credentials_provider(creds) + .region(Region::new("us-east-1")) + .build(); + let client = s3::Client::from_conf(config); + + operation(client) + .presign_for_test( + PresigningConfig::builder() + .start_time(SystemTime::UNIX_EPOCH + Duration::from_secs(1234567891)) + .expires_in(Duration::from_secs(30)) + .build() + .unwrap(), + ) + .await } #[tokio::test] -async fn test_presigning() -> Result<(), Box> { - let presigned = presign_input!(GetObjectInput::builder() - .bucket("test-bucket") - .key("test-key") - .build()?); +async fn test_presigning() { + let presigned = + presign(|client| client.get_object().bucket("test-bucket").key("test-key")).await; let pq = presigned.uri().path_and_query().unwrap(); let path = pq.path(); @@ -52,13 +78,13 @@ async fn test_presigning() -> Result<(), Box> { let mut query_params: Vec<&str> = query.split('&').collect(); query_params.sort(); - assert_eq!( + pretty_assertions::assert_eq!( "test-bucket.s3.us-east-1.amazonaws.com", presigned.uri().authority().unwrap() ); assert_eq!("GET", presigned.method().as_str()); assert_eq!("/test-key", path); - assert_eq!( + pretty_assertions::assert_eq!( &[ "X-Amz-Algorithm=AWS4-HMAC-SHA256", "X-Amz-Credential=ANOTREAL%2F20090213%2Fus-east-1%2Fs3%2Faws4_request", @@ -72,18 +98,19 @@ async fn test_presigning() -> Result<(), Box> { &query_params ); assert!(presigned.headers().is_empty()); - - Ok(()) } #[tokio::test] -async fn test_presigning_with_payload_headers() -> Result<(), Box> { - let presigned = presign_input!(PutObjectInput::builder() - .bucket("test-bucket") - .key("test-key") - .content_length(12345) - .content_type("application/x-test") - .build()?); +async fn test_presigning_with_payload_headers() { + let presigned = presign(|client| { + client + .put_object() + .bucket("test-bucket") + .key("test-key") + .content_length(12345) + .content_type("application/x-test") + }) + .await; let pq = presigned.uri().path_and_query().unwrap(); let path = pq.path(); @@ -91,13 +118,13 @@ async fn test_presigning_with_payload_headers() -> Result<(), Box> { let mut query_params: Vec<&str> = query.split('&').collect(); query_params.sort(); - assert_eq!( + pretty_assertions::assert_eq!( "test-bucket.s3.us-east-1.amazonaws.com", presigned.uri().authority().unwrap() ); assert_eq!("PUT", presigned.method().as_str()); assert_eq!("/test-key", path); - assert_eq!( + pretty_assertions::assert_eq!( &[ "X-Amz-Algorithm=AWS4-HMAC-SHA256", "X-Amz-Credential=ANOTREAL%2F20090213%2Fus-east-1%2Fs3%2Faws4_request", @@ -115,49 +142,49 @@ async fn test_presigning_with_payload_headers() -> Result<(), Box> { expected_headers.insert(CONTENT_LENGTH, HeaderValue::from_static("12345")); expected_headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/x-test")); assert_eq!(&expected_headers, presigned.headers()); - - Ok(()) } #[tokio::test] -async fn test_presigned_upload_part() -> Result<(), Box> { - let presigned = presign_input!(UploadPartInput::builder() - .content_length(12345) - .bucket("bucket") - .key("key") - .part_number(0) - .upload_id("upload-id") - .build()?); - assert_eq!( - presigned.uri().to_string(), +async fn test_presigned_upload_part() { + let presigned = presign(|client| { + client + .upload_part() + .content_length(12345) + .bucket("bucket") + .key("key") + .part_number(0) + .upload_id("upload-id") + }) + .await; + pretty_assertions::assert_eq!( "https://bucket.s3.us-east-1.amazonaws.com/key?x-id=UploadPart&partNumber=0&uploadId=upload-id&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ANOTREAL%2F20090213%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20090213T233131Z&X-Amz-Expires=30&X-Amz-SignedHeaders=content-length%3Bhost&X-Amz-Signature=a702867244f0bd1fb4d161e2a062520dcbefae3b9992d2e5366bcd61a60c6ddd&X-Amz-Security-Token=notarealsessiontoken", + presigned.uri().to_string(), ); - Ok(()) } #[tokio::test] -async fn test_presigning_object_lambda() -> Result<(), Box> { - let presigned = presign_input!(GetObjectInput::builder() - .bucket("arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint:my-banner-ap-name") - .key("test2.txt") - .build() - .unwrap()); +async fn test_presigning_object_lambda() { + let presigned = presign(|client| { + client + .get_object() + .bucket("arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint:my-banner-ap-name") + .key("test2.txt") + }) + .await; // since the URI is `my-banner-api-name...` we know EP2 is working properly for presigning - assert_eq!(presigned.uri().to_string(), "https://my-banner-ap-name-123456789012.s3-object-lambda.us-west-2.amazonaws.com/test2.txt?x-id=GetObject&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ANOTREAL%2F20090213%2Fus-west-2%2Fs3-object-lambda%2Faws4_request&X-Amz-Date=20090213T233131Z&X-Amz-Expires=30&X-Amz-SignedHeaders=host&X-Amz-Signature=027976453050b6f9cca7af80a59c05ee572b462e0fc1ef564c59412b903fcdf2&X-Amz-Security-Token=notarealsessiontoken"); - Ok(()) + pretty_assertions::assert_eq!( + "https://my-banner-ap-name-123456789012.s3-object-lambda.us-west-2.amazonaws.com/test2.txt?x-id=GetObject&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ANOTREAL%2F20090213%2Fus-west-2%2Fs3-object-lambda%2Faws4_request&X-Amz-Date=20090213T233131Z&X-Amz-Expires=30&X-Amz-SignedHeaders=host&X-Amz-Signature=027976453050b6f9cca7af80a59c05ee572b462e0fc1ef564c59412b903fcdf2&X-Amz-Security-Token=notarealsessiontoken", + presigned.uri().to_string() + ); } #[tokio::test] -async fn test_presigned_head_object() -> Result<(), Box> { - let presigned = presign_input!(HeadObjectInput::builder() - .bucket("bucket") - .key("key") - .build()?); +async fn test_presigned_head_object() { + let presigned = presign(|client| client.head_object().bucket("bucket").key("key")).await; assert_eq!("HEAD", presigned.method().as_str()); - assert_eq!( - presigned.uri().to_string(), + pretty_assertions::assert_eq!( "https://bucket.s3.us-east-1.amazonaws.com/key?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ANOTREAL%2F20090213%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20090213T233131Z&X-Amz-Expires=30&X-Amz-SignedHeaders=host&X-Amz-Signature=6b97012e70d5ee3528b5591e0e90c0f45e0fa303506f854eff50ff922751a193&X-Amz-Security-Token=notarealsessiontoken", + presigned.uri().to_string(), ); - Ok(()) } diff --git a/aws/sdk/integration-tests/s3/tests/request_id.rs b/aws/sdk/integration-tests/s3/tests/request_id.rs index 67d9523fb5..d81da66b42 100644 --- a/aws/sdk/integration-tests/s3/tests/request_id.rs +++ b/aws/sdk/integration-tests/s3/tests/request_id.rs @@ -3,34 +3,48 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_s3::operation::get_object::{GetObject, GetObjectError}; -use aws_sdk_s3::operation::list_buckets::ListBuckets; +use aws_sdk_s3::operation::get_object::GetObjectError; use aws_sdk_s3::operation::{RequestId, RequestIdExt}; +use aws_sdk_s3::{config::Credentials, config::Region, Client, Config}; +use aws_smithy_client::test_connection::capture_request; use aws_smithy_http::body::SdkBody; -use aws_smithy_http::operation; -use aws_smithy_http::response::ParseHttpResponse; -use bytes::Bytes; -#[test] -fn get_request_id_from_modeled_error() { - let resp = http::Response::builder() - .header("x-amz-request-id", "correct-request-id") - .header("x-amz-id-2", "correct-extended-request-id") - .status(404) - .body( - r#" - - NoSuchKey - The resource you requested does not exist - /mybucket/myfoto.jpg - incorrect-request-id - "#, - ) - .unwrap(); - let err = GetObject::new() - .parse_loaded(&resp.map(Bytes::from)) - .expect_err("status was 404, this is an error"); - assert!(matches!(err, GetObjectError::NoSuchKey(_))); +#[tokio::test] +async fn get_request_id_from_modeled_error() { + let (conn, request) = capture_request(Some( + http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") + .status(404) + .body(SdkBody::from( + r#" + + NoSuchKey + The resource you requested does not exist + /mybucket/myfoto.jpg + incorrect-request-id + "#, + )) + .unwrap(), + )); + let config = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .build(); + let client = Client::from_conf(config); + let err = client + .get_object() + .key("dontcare") + .send() + .await + .expect_err("status was 404, this is an error") + .into_service_error(); + request.expect_request(); + assert!( + matches!(err, GetObjectError::NoSuchKey(_)), + "expected NoSuchKey, got {err:?}", + ); assert_eq!(Some("correct-request-id"), err.request_id()); assert_eq!(Some("correct-request-id"), err.meta().request_id()); assert_eq!( @@ -43,25 +57,38 @@ fn get_request_id_from_modeled_error() { ); } -#[test] -fn get_request_id_from_unmodeled_error() { - let resp = http::Response::builder() - .header("x-amz-request-id", "correct-request-id") - .header("x-amz-id-2", "correct-extended-request-id") - .status(500) - .body( - r#" - - SomeUnmodeledError - Something bad happened - /mybucket/myfoto.jpg - incorrect-request-id - "#, - ) - .unwrap(); - let err = GetObject::new() - .parse_loaded(&resp.map(Bytes::from)) - .expect_err("status 500"); +#[tokio::test] +async fn get_request_id_from_unmodeled_error() { + let (conn, request) = capture_request(Some( + http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") + .status(500) + .body(SdkBody::from( + r#" + + SomeUnmodeledError + Something bad happened + /mybucket/myfoto.jpg + incorrect-request-id + "#, + )) + .unwrap(), + )); + let config = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .build(); + let client = Client::from_conf(config); + let err = client + .get_object() + .key("dontcare") + .send() + .await + .expect_err("status 500") + .into_service_error(); + request.expect_request(); assert!(matches!(err, GetObjectError::Unhandled(_))); assert_eq!(Some("correct-request-id"), err.request_id()); assert_eq!(Some("correct-request-id"), err.meta().request_id()); @@ -75,23 +102,34 @@ fn get_request_id_from_unmodeled_error() { ); } -#[test] -fn get_request_id_from_successful_nonstreaming_response() { - let resp = http::Response::builder() - .header("x-amz-request-id", "correct-request-id") - .header("x-amz-id-2", "correct-extended-request-id") - .status(200) - .body( - r#" - - some-idsome-display-name - - "#, - ) - .unwrap(); - let output = ListBuckets::new() - .parse_loaded(&resp.map(Bytes::from)) +#[tokio::test] +async fn get_request_id_from_successful_nonstreaming_response() { + let (conn, request) = capture_request(Some( + http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") + .status(200) + .body(SdkBody::from( + r#" + + some-idsome-display-name + + "#, + )) + .unwrap(), + )); + let config = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .build(); + let client = Client::from_conf(config); + let output = client + .list_buckets() + .send() + .await .expect("valid successful response"); + request.expect_request(); assert_eq!(Some("correct-request-id"), output.request_id()); assert_eq!( Some("correct-extended-request-id"), @@ -99,18 +137,29 @@ fn get_request_id_from_successful_nonstreaming_response() { ); } -#[test] -fn get_request_id_from_successful_streaming_response() { - let resp = http::Response::builder() - .header("x-amz-request-id", "correct-request-id") - .header("x-amz-id-2", "correct-extended-request-id") - .status(200) - .body(SdkBody::from("some streaming file data")) - .unwrap(); - let mut resp = operation::Response::new(resp); - let output = GetObject::new() - .parse_unloaded(&mut resp) +#[tokio::test] +async fn get_request_id_from_successful_streaming_response() { + let (conn, request) = capture_request(Some( + http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") + .status(200) + .body(SdkBody::from("some streaming file data")) + .unwrap(), + )); + let config = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .build(); + let client = Client::from_conf(config); + let output = client + .get_object() + .key("dontcare") + .send() + .await .expect("valid successful response"); + request.expect_request(); assert_eq!(Some("correct-request-id"), output.request_id()); assert_eq!( Some("correct-extended-request-id"), @@ -119,26 +168,37 @@ fn get_request_id_from_successful_streaming_response() { } // Verify that the conversion from operation error to the top-level service error maintains the request ID -#[test] -fn conversion_to_service_error_maintains_request_id() { - let resp = http::Response::builder() - .header("x-amz-request-id", "correct-request-id") - .header("x-amz-id-2", "correct-extended-request-id") - .status(404) - .body( - r#" - - NoSuchKey - The resource you requested does not exist - /mybucket/myfoto.jpg - incorrect-request-id - "#, - ) - .unwrap(); - let err = GetObject::new() - .parse_loaded(&resp.map(Bytes::from)) +#[tokio::test] +async fn conversion_to_service_error_maintains_request_id() { + let (conn, request) = capture_request(Some( + http::Response::builder() + .header("x-amz-request-id", "correct-request-id") + .header("x-amz-id-2", "correct-extended-request-id") + .status(404) + .body(SdkBody::from( + r#" + + NoSuchKey + The resource you requested does not exist + /mybucket/myfoto.jpg + incorrect-request-id + "#, + )) + .unwrap(), + )); + let config = Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .build(); + let client = Client::from_conf(config); + let err = client + .get_object() + .key("dontcare") + .send() + .await .expect_err("status was 404, this is an error"); - + request.expect_request(); let service_error: aws_sdk_s3::Error = err.into(); assert_eq!(Some("correct-request-id"), service_error.request_id()); assert_eq!( diff --git a/aws/sdk/integration-tests/s3/tests/required-query-params.rs b/aws/sdk/integration-tests/s3/tests/required-query-params.rs index 7cf6238da0..b5fede83a0 100644 --- a/aws/sdk/integration-tests/s3/tests/required-query-params.rs +++ b/aws/sdk/integration-tests/s3/tests/required-query-params.rs @@ -3,48 +3,60 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_sdk_s3::config::Region; -use aws_sdk_s3::operation::abort_multipart_upload::AbortMultipartUploadInput; +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::error::DisplayErrorContext; +use aws_sdk_s3::Client; +use aws_smithy_client::test_connection::capture_request; use aws_smithy_http::operation::error::BuildError; #[tokio::test] async fn test_error_when_required_query_param_is_unset() { - let conf = aws_sdk_s3::Config::builder() + let (conn, _request) = capture_request(None); + let config = aws_sdk_s3::Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) .region(Region::new("us-east-1")) .build(); + let client = Client::from_conf(config); - let err = AbortMultipartUploadInput::builder() + let err = client + .abort_multipart_upload() .bucket("test-bucket") .key("test.txt") - .build() - .unwrap() - .make_operation(&conf) + .send() .await .unwrap_err(); - - assert_eq!( - BuildError::missing_field("upload_id", "cannot be empty or unset").to_string(), - err.to_string(), + let expected = BuildError::missing_field("upload_id", "cannot be empty or unset").to_string(); + let actual = format!("{}", DisplayErrorContext(err)); + assert!( + actual.contains(&expected), + "expected error to contain '{expected}', but was '{actual}'", ) } #[tokio::test] async fn test_error_when_required_query_param_is_set_but_empty() { - let conf = aws_sdk_s3::Config::builder() + let (conn, _request) = capture_request(None); + let config = aws_sdk_s3::Config::builder() + .http_connector(conn) + .credentials_provider(Credentials::for_tests()) .region(Region::new("us-east-1")) .build(); - let err = AbortMultipartUploadInput::builder() + let client = Client::from_conf(config); + + let err = client + .abort_multipart_upload() .bucket("test-bucket") .key("test.txt") .upload_id("") - .build() - .unwrap() - .make_operation(&conf) + .send() .await .unwrap_err(); - assert_eq!( - BuildError::missing_field("upload_id", "cannot be empty or unset").to_string(), - err.to_string(), + let expected = BuildError::missing_field("upload_id", "cannot be empty or unset").to_string(); + let actual = format!("{}", DisplayErrorContext(err)); + assert!( + actual.contains(&expected), + "expected error to contain '{expected}', but was '{actual}'", ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index 157e43128c..99bb528fe9 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -20,10 +20,10 @@ import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.ClientEnumGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.error.OperationErrorGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.DefaultProtocolTestGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientProtocolLoader import software.amazon.smithy.rust.codegen.client.smithy.transformers.AddErrorMessage @@ -71,8 +71,8 @@ class ClientCodegenVisitor( private val fileManifest = context.fileManifest private val model: Model private var codegenContext: ClientCodegenContext - private val protocolGeneratorFactory: ProtocolGeneratorFactory - private val protocolGenerator: ClientProtocolGenerator + private val protocolGeneratorFactory: ProtocolGeneratorFactory + private val operationGenerator: OperationGenerator init { val rustSymbolProviderConfig = RustSymbolProviderConfig( @@ -116,7 +116,7 @@ class ClientCodegenVisitor( codegenContext.settings.codegenConfig, codegenContext.expectModuleDocProvider(), ) - protocolGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) + operationGenerator = protocolGeneratorFactory.buildProtocolGenerator(codegenContext) } /** @@ -303,7 +303,7 @@ class ClientCodegenVisitor( rustCrate.useShapeWriter(operationShape) operationWriter@{ rustCrate.useShapeWriter(operationShape.inputShape(codegenContext.model)) inputWriter@{ // Render the operation shape & serializers input `input.rs` - protocolGenerator.renderOperation( + operationGenerator.renderOperation( this@operationWriter, this@inputWriter, operationShape, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt index 14fe1fd1c7..a224915435 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt @@ -14,6 +14,8 @@ import software.amazon.smithy.model.traits.Trait 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.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection 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.core.rustlang.Writable @@ -24,8 +26,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.letIf // TODO(enableNewSmithyRuntime): Delete this decorator when switching to the orchestrator diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt index 4990539575..2183506ca5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt @@ -9,12 +9,12 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.EndpointTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.generators.EndpointTraitBindings +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection 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.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.orNull class EndpointPrefixGenerator(private val codegenContext: ClientCodegenContext, private val shape: OperationShape) : diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index 2ec542d1ab..dd19433315 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -15,8 +15,8 @@ import software.amazon.smithy.model.traits.HttpDigestAuthTrait 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.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization @@ -107,14 +107,14 @@ class HttpAuthDecorator : ClientCodegenDecorator { } } - override fun operationRuntimePluginCustomizations( + override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, - baseCustomizations: List, - ): List = + baseCustomizations: List, + ): List = HttpAuthSchemes.from(codegenContext).let { authSchemes -> baseCustomizations.letIf(authSchemes.anyEnabled()) { - it + HttpAuthOperationRuntimePluginCustomization(codegenContext) + it + HttpAuthOperationCustomization(codegenContext) } } @@ -199,15 +199,13 @@ private class HttpAuthServiceRuntimePluginCustomization( } } -private class HttpAuthOperationRuntimePluginCustomization( - codegenContext: ClientCodegenContext, -) : OperationRuntimePluginCustomization() { +private class HttpAuthOperationCustomization(codegenContext: ClientCodegenContext) : OperationCustomization() { private val serviceShape = codegenContext.serviceShape private val codegenScope = codegenScope(codegenContext.runtimeConfig) - override fun section(section: OperationRuntimePluginSection): Writable = writable { + override fun section(section: OperationSection): Writable = writable { when (section) { - is OperationRuntimePluginSection.AdditionalConfig -> { + is OperationSection.AdditionalRuntimePluginConfig -> { withBlockTemplate( "let auth_option_resolver = #{StaticAuthOptionResolver}::new(vec![", "]);", diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt index bf275034c6..8e76266079 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt @@ -8,13 +8,13 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.HttpChecksumRequiredTrait +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection 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.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.util.hasStreamingMember import software.amazon.smithy.rust.codegen.core.util.hasTrait diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt index da4ba53222..54ef873a9d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt @@ -7,13 +7,13 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.aws.traits.protocols.AwsProtocolTrait import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection 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.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.isEventStream diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt index 24738036e1..b9e36bd30c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt @@ -7,13 +7,13 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.IdempotencyTokenTrait +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection 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.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.findMemberWithTrait import software.amazon.smithy.rust.codegen.core.util.inputShape diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt index 9fb2147c91..4089c0a648 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt @@ -10,21 +10,20 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.error.ErrorCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolTestGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.smithy.customize.CombinedCoreCodegenDecorator import software.amazon.smithy.rust.codegen.core.smithy.customize.CoreCodegenDecorator -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap import java.util.ServiceLoader import java.util.logging.Logger -typealias ClientProtocolMap = ProtocolMap +typealias ClientProtocolMap = ProtocolMap /** * [ClientCodegenDecorator] allows downstream users to customize code generation. @@ -70,15 +69,6 @@ interface ClientCodegenDecorator : CoreCodegenDecorator { baseCustomizations: List, ): List = baseCustomizations - /** - * Hooks to register additional operation-level runtime plugins at codegen time - */ - fun operationRuntimePluginCustomizations( - codegenContext: ClientCodegenContext, - operation: OperationShape, - baseCustomizations: List, - ): List = baseCustomizations - /** * Hook to override the protocol test generator */ @@ -143,15 +133,6 @@ open class CombinedClientCodegenDecorator(decorators: List, - ): List = - combineCustomizations(baseCustomizations) { decorator, customizations -> - decorator.operationRuntimePluginCustomizations(codegenContext, operation, customizations) - } - override fun protocolTestGenerator( codegenContext: ClientCodegenContext, baseGenerator: ProtocolTestGenerator, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index c6116f7326..307866bfac 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -16,6 +16,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.Intercep import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.TimeSourceOperationCustomization @@ -26,7 +27,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.customizations.AllowLints import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersionCustomization import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyErrorTypes import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyPrimitives -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization val TestUtilFeature = Feature("test-util", false, listOf()) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt index 494b6412f8..c6d7281de7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt @@ -8,8 +8,8 @@ package software.amazon.smithy.rust.codegen.client.smithy.endpoint import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable @@ -26,24 +26,24 @@ class EndpointParamsDecorator : ClientCodegenDecorator { override val name: String get() = "EndpointParamsDecorator" override val order: Byte get() = 0 - override fun operationRuntimePluginCustomizations( + override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, - baseCustomizations: List, - ): List = + baseCustomizations: List, + ): List = baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { - it + listOf(EndpointParametersRuntimePluginCustomization(codegenContext, operation)) + it + listOf(EndpointParametersCustomization(codegenContext, operation)) } } -private class EndpointParametersRuntimePluginCustomization( +private class EndpointParametersCustomization( private val codegenContext: ClientCodegenContext, private val operation: OperationShape, -) : OperationRuntimePluginCustomization() { - override fun section(section: OperationRuntimePluginSection): Writable = writable { +) : OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { val symbolProvider = codegenContext.symbolProvider val operationName = symbolProvider.toSymbol(operation).name - if (section is OperationRuntimePluginSection.AdditionalConfig) { + if (section is OperationSection.AdditionalRuntimePluginConfig) { section.registerInterceptor(codegenContext.runtimeConfig, this) { rust("${operationName}EndpointParamsInterceptor") } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt index 0678a4db40..c6384a3861 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt @@ -20,6 +20,8 @@ import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.Cus import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsGenerator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointTests import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rulesgen.SmithyEndpointsStdLib +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -27,8 +29,6 @@ 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.RustCrate -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.orNull diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt new file mode 100644 index 0000000000..fd4f71ec84 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt @@ -0,0 +1,183 @@ +/* + * 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 + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +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.smithy.RuntimeConfig +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.smithy.customize.Section +import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol + +sealed class OperationSection(name: String) : Section(name) { + abstract val customizations: List + + /** Write custom code into the `impl` block of this operation */ + data class OperationImplBlock(override val customizations: List) : + OperationSection("OperationImplBlock") + + // TODO(enableNewSmithyRuntime): Delete this customization hook when cleaning up middleware + /** Write additional functions inside the Input's impl block */ + @Deprecated("customization for middleware; won't be used in the orchestrator impl") + data class InputImpl( + override val customizations: List, + val operationShape: OperationShape, + val inputShape: StructureShape, + val protocol: Protocol, + ) : OperationSection("InputImpl") + + // TODO(enableNewSmithyRuntime): Delete this customization hook when cleaning up middleware + @Deprecated("customization for middleware; won't be used in the orchestrator impl") + data class MutateInput( + override val customizations: List, + val input: String, + val config: String, + ) : OperationSection("MutateInput") + + // TODO(enableNewSmithyRuntime): Delete this customization hook when cleaning up middleware + /** Write custom code into the block that builds an operation + * + * [request]: Name of the variable holding the `aws_smithy_http::Request` + * [config]: Name of the variable holding the service config. + * + * */ + @Deprecated("customization for middleware; won't be used in the orchestrator impl") + data class MutateRequest( + override val customizations: List, + val request: String, + val config: String, + ) : OperationSection("Feature") + + // TODO(enableNewSmithyRuntime): Delete this customization hook when cleaning up middleware + @Deprecated("customization for middleware; won't be used in the orchestrator impl") + data class FinalizeOperation( + override val customizations: List, + val operation: String, + val config: String, + ) : OperationSection("Finalize") + + data class MutateOutput( + override val customizations: List, + val operationShape: OperationShape, + /** Name of the response headers map (for referring to it in Rust code) */ + val responseHeadersName: String, + + // TODO(enableNewSmithyRuntime): Remove this flag when switching to the orchestrator + /** Whether the property bag exists in this context */ + val propertyBagAvailable: Boolean, + ) : OperationSection("MutateOutput") + + /** + * Allows for adding additional properties to the `extras` field on the + * `aws_smithy_types::error::ErrorMetadata`. + */ + data class PopulateErrorMetadataExtras( + override val customizations: List, + /** Name of the generic error builder (for referring to it in Rust code) */ + val builderName: String, + /** Name of the response status (for referring to it in Rust code) */ + val responseStatusName: String, + /** Name of the response headers map (for referring to it in Rust code) */ + val responseHeadersName: String, + ) : OperationSection("PopulateErrorMetadataExtras") + + /** + * Hook to add custom code right before the response is parsed. + */ + data class BeforeParseResponse( + override val customizations: List, + val responseName: String, + ) : OperationSection("BeforeParseResponse") + + /** + * Hook for adding additional things to config inside operation runtime plugins. + */ + data class AdditionalRuntimePluginConfig( + override val customizations: List, + val configBagName: String, + val interceptorRegistrarName: String, + val operationShape: OperationShape, + ) : OperationSection("AdditionalConfig") { + fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { + val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) + writer.rustTemplate( + """ + $interceptorRegistrarName.register(#{SharedInterceptor}::new(#{interceptor}) as _); + """, + "interceptor" to interceptor, + "SharedInterceptor" to smithyRuntimeApi.resolve("client::interceptors::SharedInterceptor"), + ) + } + } + + /** + * Hook for adding retry classifiers to an operation's `RetryClassifiers` bundle. + * + * Should emit 1+ lines of code that look like the following: + * ```rust + * .with_classifier(AwsErrorCodeClassifier::new()) + * .with_classifier(HttpStatusCodeClassifier::new()) + * ``` + */ + data class RetryClassifier( + override val customizations: List, + val configBagName: String, + val operationShape: OperationShape, + ) : OperationSection("RetryClassifier") + + /** + * Hook for adding supporting types for operation-specific runtime plugins. + * Examples include various operation-specific types (retry classifiers, config bag types, etc.) + */ + data class RuntimePluginSupportingTypes( + override val customizations: List, + val configBagName: String, + val operationShape: OperationShape, + ) : OperationSection("RuntimePluginSupportingTypes") + + /** + * Hook for adding additional runtime plugins to an operation. + */ + data class AdditionalRuntimePlugins( + override val customizations: List, + val operationShape: OperationShape, + ) : OperationSection("AdditionalRuntimePlugins") { + fun addServiceRuntimePlugin(writer: RustWriter, plugin: Writable) { + writer.rustTemplate(".with_service_runtime_plugin(#{plugin})", "plugin" to plugin) + } + + fun addOperationRuntimePlugin(writer: RustWriter, plugin: Writable) { + writer.rustTemplate(".with_operation_runtime_plugin(#{plugin})", "plugin" to plugin) + } + } +} + +abstract class OperationCustomization : NamedCustomization() { + // TODO(enableNewSmithyRuntime): Delete this when cleaning up middleware + @Deprecated("property for middleware; won't be used in the orchestrator impl") + open fun retryType(): RuntimeType? = null + + // TODO(enableNewSmithyRuntime): Delete this when cleaning up middleware + /** + * Does `make_operation` consume the self parameter? + * + * This is required for things like idempotency tokens where the operation can only be sent once + * and an idempotency token will mutate the request. + */ + @Deprecated("property for middleware; won't be used in the orchestrator impl") + open fun consumesSelf(): Boolean = false + + // TODO(enableNewSmithyRuntime): Delete this when cleaning up middleware + /** + * Does `make_operation` mutate the self parameter? + */ + @Deprecated("property for middleware; won't be used in the orchestrator impl") + open fun mutSelf(): Boolean = false +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt similarity index 58% rename from codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt rename to codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 731fa56f05..859126d03f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ClientProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -3,14 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol +package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.shapes.OperationShape 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.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsInterceptorGenerator -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationRuntimePluginGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.RequestSerializerGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ResponseDeserializerGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBoundProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.derive @@ -19,18 +21,17 @@ import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock 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.RuntimeConfig 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.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape -open class ClientProtocolGenerator( +open class OperationGenerator( private val codegenContext: ClientCodegenContext, private val protocol: Protocol, /** @@ -42,16 +43,42 @@ open class ClientProtocolGenerator( private val bodyGenerator: ProtocolPayloadGenerator, // TODO(enableNewSmithyRuntime): Remove the `traitGenerator` private val traitGenerator: HttpBoundProtocolTraitImplGenerator, -) : ProtocolGenerator(codegenContext, protocol) { +) { + companion object { + fun registerDefaultRuntimePluginsFn(runtimeConfig: RuntimeConfig): RuntimeType = + RuntimeType.forInlineFun("register_default_runtime_plugins", ClientRustModule.Operation) { + rustTemplate( + """ + pub(crate) fn register_default_runtime_plugins( + runtime_plugins: #{RuntimePlugins}, + operation: #{Box}, + handle: #{Arc}, + config_override: #{Option}, + ) -> #{RuntimePlugins} { + let mut runtime_plugins = runtime_plugins + .with_client_plugin(crate::config::ServiceRuntimePlugin::new(handle)) + .with_operation_plugin(operation); + if let Some(config_override) = config_override { + runtime_plugins = runtime_plugins.with_operation_plugin(config_override); + } + runtime_plugins + } + """, + *preludeScope, + "Arc" to RuntimeType.Arc, + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::RuntimePlugins"), + ) + } + } + + private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig + private val symbolProvider = codegenContext.symbolProvider /** - * Render all code required for serializing requests and deserializing responses for the operation - * - * This primarily relies on two components: - * 1. [traitGenerator]: Generate implementations of the `ParseHttpResponse` trait for the operations - * 2. [makeOperationGenerator]: Generate the `make_operation()` method which is used to serialize operations - * to HTTP requests + * Render the operation struct and its supporting code. */ fun renderOperation( operationWriter: RustWriter, @@ -78,7 +105,6 @@ open class ClientProtocolGenerator( operationWriter, operationShape, operationCustomizations, - codegenDecorator, ) } @@ -86,7 +112,6 @@ open class ClientProtocolGenerator( operationWriter: RustWriter, operationShape: OperationShape, operationCustomizations: List, - codegenDecorator: ClientCodegenDecorator, ) { val operationName = symbolProvider.toSymbol(operationShape).name @@ -116,57 +141,68 @@ open class ClientProtocolGenerator( "OperationError" to errorType, "OperationOutput" to outputType, "HttpResponse" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse"), - "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), - "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::runtime_plugin::RuntimePlugins"), "SdkError" to RuntimeType.sdkError(runtimeConfig), ) if (codegenContext.smithyRuntimeMode.generateOrchestrator) { - val setupRuntimePluginsFn = - RuntimeType.forInlineFun("setup_runtime_plugins", ClientRustModule.Operation) { - rustTemplate( - """ - pub(crate) fn setup_runtime_plugins( - operation: #{Box}, - handle: #{Arc}, - config_override: #{Option}, - ) -> #{RuntimePlugins} { - let mut runtime_plugins = #{RuntimePlugins}::for_operation(operation) - .with_client_plugin(crate::config::ServiceRuntimePlugin::new(handle)); - if let Some(config_override) = config_override { - runtime_plugins = runtime_plugins.with_operation_plugin(config_override); - } - runtime_plugins - } - """, - *codegenScope, - ) - } rustTemplate( """ - pub(crate) async fn orchestrate( - input: #{Input}, + pub(crate) fn register_runtime_plugins( + runtime_plugins: #{RuntimePlugins}, handle: #{Arc}, config_override: #{Option}, + ) -> #{RuntimePlugins} { + #{register_default_runtime_plugins}( + runtime_plugins, + #{Box}::new(Self::new()) as _, + handle, + config_override + ) + #{additional_runtime_plugins} + } + + pub(crate) async fn orchestrate( + runtime_plugins: &#{RuntimePlugins}, + input: #{Input}, ) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { - let runtime_plugins = #{setup_runtime_plugins}(#{Box}::new(#{Operation}::new()) as _, handle, config_override); - let input = #{TypedBox}::new(input).erase(); - let output = #{invoke}(input, &runtime_plugins) + let map_err = |err: #{SdkError}<#{Error}, #{HttpResponse}>| { + err.map_service_error(|err| { + #{TypedBox}::<#{OperationError}>::assume_from(err.into()) + .expect("correct error type") + .unwrap() + }) + }; + let context = Self::orchestrate_with_stop_point(&runtime_plugins, input, #{StopPoint}::None) .await - .map_err(|err| { - err.map_service_error(|err| { - #{TypedBox}::<#{OperationError}>::assume_from(err.into()) - .expect("correct error type") - .unwrap() - }) - })?; + .map_err(map_err)?; + let output = context.finalize().map_err(map_err)?; #{Ok}(#{TypedBox}::<#{OperationOutput}>::assume_from(output).expect("correct output type").unwrap()) } + + pub(crate) async fn orchestrate_with_stop_point( + runtime_plugins: &#{RuntimePlugins}, + input: #{Input}, + stop_point: #{StopPoint}, + ) -> #{Result}<#{InterceptorContext}, #{SdkError}<#{Error}, #{HttpResponse}>> { + let input = #{TypedBox}::new(input).erase(); + #{invoke_with_stop_point}(input, runtime_plugins, stop_point).await + } """, *codegenScope, + "Error" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::Error"), "TypedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypedBox"), - "invoke" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke"), - "setup_runtime_plugins" to setupRuntimePluginsFn, + "InterceptorContext" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::InterceptorContext"), + "OrchestratorError" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::error::OrchestratorError"), + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugins"), + "StopPoint" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::StopPoint"), + "invoke_with_stop_point" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke_with_stop_point"), + "register_default_runtime_plugins" to registerDefaultRuntimePluginsFn(runtimeConfig), + "additional_runtime_plugins" to writable { + writeCustomizations( + operationCustomizations, + OperationSection.AdditionalRuntimePlugins(operationCustomizations, operationShape), + ) + }, ) } @@ -182,7 +218,7 @@ open class ClientProtocolGenerator( operationWriter, operationShape, operationName, - codegenDecorator.operationRuntimePluginCustomizations(codegenContext, operationShape, emptyList()), + operationCustomizations, ) ResponseDeserializerGenerator(codegenContext, protocol) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index 419c15522b..4d786421f1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -8,62 +8,11 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -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.RuntimeConfig 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.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations -sealed class OperationRuntimePluginSection(name: String) : Section(name) { - /** - * Hook for adding additional things to config inside operation runtime plugins. - */ - data class AdditionalConfig( - val configBagName: String, - val interceptorRegistrarName: String, - val operationShape: OperationShape, - ) : OperationRuntimePluginSection("AdditionalConfig") { - fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { - val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) - writer.rustTemplate( - """ - $interceptorRegistrarName.register(#{SharedInterceptor}::new(#{interceptor}) as _); - """, - "interceptor" to interceptor, - "SharedInterceptor" to smithyRuntimeApi.resolve("client::interceptors::SharedInterceptor"), - ) - } - } - - /** - * Hook for adding retry classifiers to an operation's `RetryClassifiers` bundle. - * - * Should emit 1+ lines of code that look like the following: - * ```rust - * .with_classifier(AwsErrorCodeClassifier::new()) - * .with_classifier(HttpStatusCodeClassifier::new()) - * ``` - */ - data class RetryClassifier( - val configBagName: String, - val operationShape: OperationShape, - ) : OperationRuntimePluginSection("RetryClassifier") - - /** - * Hook for adding supporting types for operation-specific runtime plugins. - * Examples include various operation-specific types (retry classifiers, config bag types, etc.) - */ - data class RuntimePluginSupportingTypes( - val configBagName: String, - val operationShape: OperationShape, - ) : OperationRuntimePluginSection("RuntimePluginSupportingTypes") -} - -typealias OperationRuntimePluginCustomization = NamedCustomization - /** * Generates operation-level runtime plugins */ @@ -89,7 +38,7 @@ class OperationRuntimePluginGenerator( writer: RustWriter, operationShape: OperationShape, operationStructName: String, - customizations: List, + customizations: List, ) { writer.rustTemplate( """ @@ -118,16 +67,16 @@ class OperationRuntimePluginGenerator( "additional_config" to writable { writeCustomizations( customizations, - OperationRuntimePluginSection.AdditionalConfig("cfg", "_interceptors", operationShape), + OperationSection.AdditionalRuntimePluginConfig(customizations, "cfg", "_interceptors", operationShape), ) }, "retry_classifier_customizations" to writable { - writeCustomizations(customizations, OperationRuntimePluginSection.RetryClassifier("cfg", operationShape)) + writeCustomizations(customizations, OperationSection.RetryClassifier(customizations, "cfg", operationShape)) }, "runtime_plugin_supporting_types" to writable { writeCustomizations( customizations, - OperationRuntimePluginSection.RuntimePluginSupportingTypes("cfg", operationShape), + OperationSection.RuntimePluginSupportingTypes(customizations, "cfg", operationShape), ) }, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt index 3ee8af0de5..d83e53f157 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt @@ -165,6 +165,7 @@ class PaginatorGenerator private constructor( // Move individual fields out of self for the borrow checker let builder = self.builder; let handle = self.handle; + #{runtime_plugin_init}; #{fn_stream}::FnStream::new(move |tx| #{Box}::pin(async move { // Build the input for the first time. If required fields are missing, this is where we'll produce an early error. let mut input = match builder.build().map_err(#{SdkError}::construction_failure) { @@ -230,11 +231,27 @@ class PaginatorGenerator private constructor( ) } else { rustTemplate( - "#{operation}::orchestrate(input.clone(), handle.clone(), None).await", + "#{operation}::orchestrate(&runtime_plugins, input.clone()).await", *codegenScope, ) } }, + "runtime_plugin_init" to writable { + if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + rustTemplate( + """ + let runtime_plugins = #{operation}::register_runtime_plugins( + #{RuntimePlugins}::new(), + handle, + None + ); + """, + *codegenScope, + "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::RuntimePlugins"), + ) + } + }, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 5461979470..801bb1da1d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -457,6 +457,8 @@ class FluentClientGenerator( "Operation" to operationSymbol, "OperationError" to errorType, "OperationOutput" to outputType, + "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::RuntimePlugins"), "SendResult" to ClientRustModule.Client.customize.toType() .resolve("internal::SendResult"), "SdkError" to RuntimeType.sdkError(runtimeConfig), @@ -466,7 +468,12 @@ class FluentClientGenerator( ##[doc(hidden)] pub async fn send_orchestrator(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; - #{Operation}::orchestrate(input, self.handle, self.config_override).await + let runtime_plugins = #{Operation}::register_runtime_plugins( + #{RuntimePlugins}::new(), + self.handle, + self.config_override + ); + #{Operation}::orchestrate(&runtime_plugins, input).await } ##[doc(hidden)] diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt index 89fff2034d..ea5285aa70 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt @@ -6,13 +6,13 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.config import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.rust 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.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection fun timeSourceCustomization(codegenContext: ClientCodegenContext) = standardConfigParam( ConfigParam( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt index ec806de95b..127c67584f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt @@ -9,6 +9,8 @@ import software.amazon.smithy.aws.traits.ServiceTrait import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.http.RequestBindingGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientAdditionalPayloadContext import software.amazon.smithy.rust.codegen.core.rustlang.Attribute @@ -22,8 +24,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext 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.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt index ec89dbc6ca..b3cf511549 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt @@ -10,6 +10,8 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.http.ResponseBindingGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -23,8 +25,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock 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.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.BuilderGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index 04919a5709..5ce3c2359a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -333,25 +333,44 @@ class DefaultProtocolTestGenerator( """, RuntimeType.sdkBody(runtimeConfig = codegenContext.runtimeConfig), ) - write( - "let mut op_response = #T::new(http_response);", - RuntimeType.operationModule(codegenContext.runtimeConfig).resolve("Response"), - ) - rustTemplate( - """ - use #{parse_http_response}; - let parser = #{op}::new(); - let parsed = parser.parse_unloaded(&mut op_response); - let parsed = parsed.unwrap_or_else(|| { - let (http_response, _) = op_response.into_parts(); - let http_response = http_response.map(|body|#{copy_from_slice}(body.bytes().unwrap())); - <#{op} as #{parse_http_response}>::parse_loaded(&parser, &http_response) - }); - """, - "op" to operationSymbol, - "copy_from_slice" to RuntimeType.Bytes.resolve("copy_from_slice"), - "parse_http_response" to RuntimeType.parseHttpResponse(codegenContext.runtimeConfig), - ) + if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + rust( + "let mut op_response = #T::new(http_response);", + RuntimeType.operationModule(codegenContext.runtimeConfig).resolve("Response"), + ) + rustTemplate( + """ + use #{parse_http_response}; + let parser = #{op}::new(); + let parsed = parser.parse_unloaded(&mut op_response); + let parsed = parsed.unwrap_or_else(|| { + let (http_response, _) = op_response.into_parts(); + let http_response = http_response.map(|body|#{copy_from_slice}(body.bytes().unwrap())); + <#{op} as #{parse_http_response}>::parse_loaded(&parser, &http_response) + }); + """, + "op" to operationSymbol, + "copy_from_slice" to RuntimeType.Bytes.resolve("copy_from_slice"), + "parse_http_response" to RuntimeType.parseHttpResponse(codegenContext.runtimeConfig), + ) + } else { + rustTemplate( + """ + use #{ResponseDeserializer}; + let de = #{OperationDeserializer}; + let parsed = de.deserialize_streaming(&mut http_response); + let parsed = parsed.unwrap_or_else(|| { + let http_response = http_response.map(|body|#{copy_from_slice}(body.bytes().unwrap())); + de.deserialize_nonstreaming(&http_response) + }); + """, + "OperationDeserializer" to codegenContext.symbolProvider.moduleForShape(operationShape).toType() + .resolve("${operationSymbol.name}ResponseDeserializer"), + "copy_from_slice" to RuntimeType.Bytes.resolve("copy_from_slice"), + "ResponseDeserializer" to CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig).toType() + .resolve("client::orchestrator::ResponseDeserializer"), + ) + } if (expectedShape.hasTrait()) { val errorSymbol = codegenContext.symbolProvider.symbolForOperationError(operationShape) val errorVariant = codegenContext.symbolProvider.toSymbol(expectedShape).name diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt index e9391e1d7a..f3b53c3606 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.generators.http.RequestBindingGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientAdditionalPayloadContext import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -39,17 +40,22 @@ class RequestSerializerGenerator( val smithyTypes = CargoDependency.smithyTypes(codegenContext.runtimeConfig).toType() arrayOf( "BoxError" to orchestrator.resolve("BoxError"), + "config" to ClientRustModule.Config, "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), + "header_util" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("header"), + "http" to RuntimeType.Http, "HttpRequest" to orchestrator.resolve("HttpRequest"), "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, "Input" to interceptorContext.resolve("Input"), + "operation" to RuntimeType.operationModule(codegenContext.runtimeConfig), "RequestSerializer" to orchestrator.resolve("RequestSerializer"), "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), + "HeaderSerializationSettings" to RuntimeType.forInlineDependency( + InlineDependency.serializationSettings( + codegenContext.runtimeConfig, + ), + ).resolve("HeaderSerializationSettings"), "TypedBox" to smithyTypes.resolve("type_erasure::TypedBox"), - "config" to ClientRustModule.Config, - "header_util" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("header"), - "http" to RuntimeType.Http, - "operation" to RuntimeType.operationModule(codegenContext.runtimeConfig), ) } @@ -65,6 +71,7 @@ class RequestSerializerGenerator( ##[allow(unused_mut, clippy::let_and_return, clippy::needless_borrow, clippy::useless_conversion)] fn serialize_input(&self, input: #{Input}, _cfg: &mut #{ConfigBag}) -> Result<#{HttpRequest}, #{BoxError}> { let input = #{TypedBox}::<#{ConcreteInput}>::assume_from(input).expect("correct type").unwrap(); + let _header_serialization_settings = _cfg.get::<#{HeaderSerializationSettings}>().cloned().unwrap_or_default(); let mut request_builder = { #{create_http_request} }; @@ -101,7 +108,8 @@ class RequestSerializerGenerator( rustTemplate( """ if let Some(content_length) = body.content_length() { - request_builder = #{header_util}::set_request_header_if_absent(request_builder, #{http}::header::CONTENT_LENGTH, content_length); + let content_length = content_length.to_string(); + request_builder = _header_serialization_settings.set_default_header(request_builder, #{http}::header::CONTENT_LENGTH, &content_length); } """, *codegenScope, @@ -127,17 +135,17 @@ class RequestSerializerGenerator( httpBindingGenerator.renderUpdateHttpBuilder(this) val contentType = httpBindingResolver.requestContentType(operationShape) - rust("let mut builder = update_http_builder(&input, #T::new())?;", RuntimeType.HttpRequestBuilder) + rustTemplate("let mut builder = update_http_builder(&input, #{HttpRequestBuilder}::new())?;", *codegenScope) if (contentType != null) { rustTemplate( - "builder = #{header_util}::set_request_header_if_absent(builder, #{http}::header::CONTENT_TYPE, ${contentType.dq()});", + "builder = _header_serialization_settings.set_default_header(builder, #{http}::header::CONTENT_TYPE, ${contentType.dq()});", *codegenScope, ) } for (header in protocol.additionalRequestHeaders(operationShape)) { rustTemplate( """ - builder = #{header_util}::set_request_header_if_absent( + builder = _header_serialization_settings.set_default_header( builder, #{http}::header::HeaderName::from_static(${header.first.dq()}), ${header.second.dq()} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index 968e335916..938c26c60b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -7,13 +7,13 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctions diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/ClientProtocolLoader.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/ClientProtocolLoader.kt index a194dc98a2..7c949c92a2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/ClientProtocolLoader.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/ClientProtocolLoader.kt @@ -14,7 +14,7 @@ import software.amazon.smithy.aws.traits.protocols.RestJson1Trait import software.amazon.smithy.aws.traits.protocols.RestXmlTrait import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.smithy.protocols.AwsJson @@ -30,8 +30,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestXml import software.amazon.smithy.rust.codegen.core.util.hasTrait -class ClientProtocolLoader(supportedProtocols: ProtocolMap) : - ProtocolLoader(supportedProtocols) { +class ClientProtocolLoader(supportedProtocols: ProtocolMap) : + ProtocolLoader(supportedProtocols) { companion object { val DefaultProtocols = mapOf( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index 6c30515ca7..44961039c8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -8,8 +8,10 @@ package software.amazon.smithy.rust.codegen.client.smithy.protocols import software.amazon.smithy.codegen.core.Symbol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.SensitiveIndex -import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ClientProtocolGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.ProtocolParserGenerator import software.amazon.smithy.rust.codegen.core.rustlang.Attribute @@ -21,8 +23,6 @@ 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.RuntimeType.Companion.preludeScope -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationSection import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.AdditionalPayloadContext @@ -38,7 +38,7 @@ class HttpBoundProtocolGenerator( codegenContext: ClientCodegenContext, protocol: Protocol, bodyGenerator: ProtocolPayloadGenerator = ClientHttpBoundProtocolPayloadGenerator(codegenContext, protocol), -) : ClientProtocolGenerator( +) : OperationGenerator( codegenContext, protocol, MakeOperationGenerator( diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt index 36f6089169..c80ed9ba04 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt @@ -13,6 +13,8 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBoundProtocolTraitImplGenerator import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -21,7 +23,6 @@ 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.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.AdditionalPayloadContext import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport @@ -103,7 +104,7 @@ private class TestProtocolGenerator( httpRequestBuilder: String, body: String, correctResponse: String, -) : ClientProtocolGenerator( +) : OperationGenerator( codegenContext, protocol, TestProtocolMakeOperationGenerator(codegenContext, protocol, body, httpRequestBuilder), @@ -115,10 +116,10 @@ private class TestProtocolFactory( private val httpRequestBuilder: String, private val body: String, private val correctResponse: String, -) : ProtocolGeneratorFactory { +) : ProtocolGeneratorFactory { override fun protocol(codegenContext: ClientCodegenContext): Protocol = RestJson(codegenContext) - override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): ClientProtocolGenerator { + override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): OperationGenerator { return TestProtocolGenerator( codegenContext, protocol(codegenContext), @@ -235,8 +236,8 @@ class ProtocolTestGeneratorTest { override fun classpathDiscoverable(): Boolean = false override fun protocols( serviceId: ShapeId, - currentProtocols: ProtocolMap, - ): ProtocolMap = + currentProtocols: ProtocolMap, + ): ProtocolMap = // Intentionally replace the builtin implementation of RestJson1 with our fake protocol mapOf(RestJson1Trait.ID to TestProtocolFactory(httpRequestBuilder, body, correctResponse)) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index 6976236d85..d501349057 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -115,6 +115,12 @@ class InlineDependency( fun unwrappedXmlErrors(runtimeConfig: RuntimeConfig): InlineDependency = forInlineableRustFile("rest_xml_unwrapped_errors", CargoDependency.smithyXml(runtimeConfig)) + fun serializationSettings(runtimeConfig: RuntimeConfig): InlineDependency = forInlineableRustFile( + "serialization_settings", + CargoDependency.Http, + CargoDependency.smithyHttp(runtimeConfig), + ) + fun constrained(): InlineDependency = InlineDependency.forRustFile(ConstrainedModule, "/inlineable/src/constrained.rs") } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt deleted file mode 100644 index 4c77ee748d..0000000000 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/OperationCustomization.kt +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -package software.amazon.smithy.rust.codegen.core.smithy.customize - -import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol - -sealed class OperationSection(name: String) : Section(name) { - abstract val customizations: List - - /** Write custom code into the `impl` block of this operation */ - data class OperationImplBlock(override val customizations: List) : - OperationSection("OperationImplBlock") - - /** Write additional functions inside the Input's impl block */ - data class InputImpl( - override val customizations: List, - val operationShape: OperationShape, - val inputShape: StructureShape, - val protocol: Protocol, - ) : OperationSection("InputImpl") - - data class MutateInput( - override val customizations: List, - val input: String, - val config: String, - ) : OperationSection("MutateInput") - - /** Write custom code into the block that builds an operation - * - * [request]: Name of the variable holding the `aws_smithy_http::Request` - * [config]: Name of the variable holding the service config. - * - * */ - data class MutateRequest( - override val customizations: List, - val request: String, - val config: String, - ) : OperationSection("Feature") - - data class FinalizeOperation( - override val customizations: List, - val operation: String, - val config: String, - ) : OperationSection("Finalize") - - data class MutateOutput( - override val customizations: List, - val operationShape: OperationShape, - /** Name of the response headers map (for referring to it in Rust code) */ - val responseHeadersName: String, - - // TODO(enableNewSmithyRuntime): Remove this flag when switching to the orchestrator - /** Whether the property bag exists in this context */ - val propertyBagAvailable: Boolean, - ) : OperationSection("MutateOutput") - - /** - * Allows for adding additional properties to the `extras` field on the - * `aws_smithy_types::error::ErrorMetadata`. - */ - data class PopulateErrorMetadataExtras( - override val customizations: List, - /** Name of the generic error builder (for referring to it in Rust code) */ - val builderName: String, - /** Name of the response status (for referring to it in Rust code) */ - val responseStatusName: String, - /** Name of the response headers map (for referring to it in Rust code) */ - val responseHeadersName: String, - ) : OperationSection("PopulateErrorMetadataExtras") - - /** - * Hook to add custom code right before the response is parsed. - */ - data class BeforeParseResponse( - override val customizations: List, - val responseName: String, - ) : OperationSection("BeforeParseResponse") -} - -abstract class OperationCustomization : NamedCustomization() { - open fun retryType(): RuntimeType? = null - - /** - * Does `make_operation` consume the self parameter? - * - * This is required for things like idempotency tokens where the operation can only be sent once - * and an idempotency token will mutate the request. - */ - open fun consumesSelf(): Boolean = false - - /** - * Does `make_operation` mutate the self parameter? - */ - open fun mutSelf(): Boolean = false -} diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt index f48a99d856..b884a249bf 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/protocol/ProtocolGenerator.kt @@ -7,8 +7,6 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators.protocol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol /** Allows for additional context to be given to the payload generator from where it is being called */ interface AdditionalPayloadContext @@ -55,19 +53,3 @@ interface ProtocolPayloadGenerator { additionalPayloadContext: AdditionalPayloadContext = object : AdditionalPayloadContext {}, ) } - -/** - * Class providing scaffolding for HTTP based protocols that must build an HTTP request (headers / URL) and a body. - */ -abstract class ProtocolGenerator( - codegenContext: CodegenContext, - /** - * `Protocol` contains all protocol specific information. Each smithy protocol, e.g. RestJson, RestXml, etc. will - * have their own implementation of the protocol interface which defines how an input shape becomes and http::Request - * and an output shape is build from an `http::Response`. - */ - private val protocol: Protocol, -) { - protected val symbolProvider = codegenContext.symbolProvider - protected val model = codegenContext.model -} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolGenerator.kt index 3605889297..419529f23a 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolGenerator.kt @@ -7,15 +7,12 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators.protocol import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolGenerator import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerHttpBoundProtocolTraitImplGenerator open class ServerProtocolGenerator( - codegenContext: CodegenContext, val protocol: ServerProtocol, private val traitGenerator: ServerHttpBoundProtocolTraitImplGenerator, -) : ProtocolGenerator(codegenContext, protocol) { +) { /** * The server implementation uses this method to generate implementations of the `from_request` and `into_response` * traits for operation input and output shapes, respectively. @@ -24,6 +21,6 @@ open class ServerProtocolGenerator( operationWriter: RustWriter, operationShape: OperationShape, ) { - traitGenerator.generateTraitImpls(operationWriter, operationShape, emptyList()) + traitGenerator.generateTraitImpls(operationWriter, operationShape) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index 89accafd74..a88036b0ed 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -45,7 +45,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget 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.smithy.customize.OperationCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpBindingCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.http.HttpMessageType @@ -106,7 +105,6 @@ class ServerHttpBoundProtocolGenerator( customizations: List = listOf(), additionalHttpBindingCustomizations: List = listOf(), ) : ServerProtocolGenerator( - codegenContext, protocol, ServerHttpBoundProtocolTraitImplGenerator(codegenContext, protocol, customizations, additionalHttpBindingCustomizations), ) { @@ -185,7 +183,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( "Tracing" to RuntimeType.Tracing, ) - fun generateTraitImpls(operationWriter: RustWriter, operationShape: OperationShape, customizations: List) { + fun generateTraitImpls(operationWriter: RustWriter, operationShape: OperationShape) { val inputSymbol = symbolProvider.toSymbol(operationShape.inputShape(model)) val outputSymbol = symbolProvider.toSymbol(operationShape.outputShape(model)) diff --git a/rust-runtime/aws-smithy-async/src/test_util.rs b/rust-runtime/aws-smithy-async/src/test_util.rs index 7d3b4f78ce..365db3b504 100644 --- a/rust-runtime/aws-smithy-async/src/test_util.rs +++ b/rust-runtime/aws-smithy-async/src/test_util.rs @@ -194,37 +194,12 @@ pub fn controlled_time_and_sleep( (ManualTimeSource { start_time, log }, sleep, gate) } -#[derive(Debug)] -/// Time source that always returns the same time -pub struct StaticTimeSource { - time: SystemTime, -} - -impl StaticTimeSource { - /// Creates a new static time source that always returns the same time - pub fn new(time: SystemTime) -> Self { - Self { time } - } -} - -impl TimeSource for StaticTimeSource { - fn now(&self) -> SystemTime { - self.time - } -} - impl TimeSource for SystemTime { fn now(&self) -> SystemTime { *self } } -impl From for SharedTimeSource { - fn from(value: StaticTimeSource) -> Self { - SharedTimeSource::new(value) - } -} - impl From for SharedTimeSource { fn from(value: SystemTime) -> Self { SharedTimeSource::new(value) diff --git a/rust-runtime/aws-smithy-async/src/time.rs b/rust-runtime/aws-smithy-async/src/time.rs index d2e52c9a87..2abe332c88 100644 --- a/rust-runtime/aws-smithy-async/src/time.rs +++ b/rust-runtime/aws-smithy-async/src/time.rs @@ -38,6 +38,31 @@ impl Default for SharedTimeSource { } } +/// Time source that always returns the same time +#[derive(Debug)] +pub struct StaticTimeSource { + time: SystemTime, +} + +impl StaticTimeSource { + /// Creates a new static time source that always returns the same time + pub fn new(time: SystemTime) -> Self { + Self { time } + } +} + +impl TimeSource for StaticTimeSource { + fn now(&self) -> SystemTime { + self.time + } +} + +impl From for SharedTimeSource { + fn from(value: StaticTimeSource) -> Self { + SharedTimeSource::new(value) + } +} + #[derive(Debug, Clone)] /// Time source structure used inside SDK /// diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 9b58b816db..89d0d4e8cd 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -37,7 +37,7 @@ use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; use phase::Phase; use std::{fmt, mem}; -use tracing::{error, trace}; +use tracing::{debug, error, trace}; pub type Input = TypeErasedBox; pub type Output = TypeErasedBox; @@ -149,10 +149,8 @@ where } /// Takes ownership of the request. - pub fn take_request(&mut self) -> Request { - self.request - .take() - .expect("take request once during 'transmit'") + pub fn take_request(&mut self) -> Option { + self.request.take() } /// Set the response for the operation being invoked. @@ -177,7 +175,7 @@ where /// Returns the deserialized output or error. pub fn output_or_error(&self) -> Option>> { - self.output_or_error.as_ref().map(|res| res.as_ref()) + self.output_or_error.as_ref().map(Result::as_ref) } /// Returns the mutable reference to the deserialized output or error. @@ -188,7 +186,7 @@ where /// Advance to the Serialization phase. #[doc(hidden)] pub fn enter_serialization_phase(&mut self) { - trace!("entering \'serialization\' phase"); + debug!("entering \'serialization\' phase"); debug_assert!( self.phase.is_before_serialization(), "called enter_serialization_phase but phase is not before 'serialization'" @@ -199,7 +197,7 @@ where /// Advance to the BeforeTransmit phase. #[doc(hidden)] pub fn enter_before_transmit_phase(&mut self) { - trace!("entering \'before transmit\' phase"); + debug!("entering \'before transmit\' phase"); debug_assert!( self.phase.is_serialization(), "called enter_before_transmit_phase but phase is not 'serialization'" @@ -212,17 +210,14 @@ where self.request.is_some(), "request must be set before calling enter_before_transmit_phase" ); - self.request_checkpoint = try_clone( - self.request() - .expect("request is set before calling enter_before_transmit_phase"), - ); + self.request_checkpoint = try_clone(self.request().expect("checked above")); self.phase = Phase::BeforeTransmit; } /// Advance to the Transmit phase. #[doc(hidden)] pub fn enter_transmit_phase(&mut self) { - trace!("entering \'transmit\' phase"); + debug!("entering \'transmit\' phase"); debug_assert!( self.phase.is_before_transmit(), "called enter_transmit_phase but phase is not before transmit" @@ -233,7 +228,7 @@ where /// Advance to the BeforeDeserialization phase. #[doc(hidden)] pub fn enter_before_deserialization_phase(&mut self) { - trace!("entering \'before deserialization\' phase"); + debug!("entering \'before deserialization\' phase"); debug_assert!( self.phase.is_transmit(), "called enter_before_deserialization_phase but phase is not 'transmit'" @@ -252,7 +247,7 @@ where /// Advance to the Deserialization phase. #[doc(hidden)] pub fn enter_deserialization_phase(&mut self) { - trace!("entering \'deserialization\' phase"); + debug!("entering \'deserialization\' phase"); debug_assert!( self.phase.is_before_deserialization(), "called enter_deserialization_phase but phase is not 'before deserialization'" @@ -263,7 +258,7 @@ where /// Advance to the AfterDeserialization phase. #[doc(hidden)] pub fn enter_after_deserialization_phase(&mut self) { - trace!("entering \'after deserialization\' phase"); + debug!("entering \'after deserialization\' phase"); debug_assert!( self.phase.is_deserialization(), "called enter_after_deserialization_phase but phase is not 'deserialization'" @@ -480,7 +475,7 @@ mod tests { ); context.enter_transmit_phase(); - let request = context.take_request(); + let request = context.take_request().unwrap(); assert_eq!( "request-modified-after-signing", request.headers().get("test").unwrap() diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index f60801d026..2a82860593 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -3,9 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -/// Errors that can occur while running the orchestrator. -mod error; - use crate::client::auth::{AuthOptionResolver, AuthOptionResolverParams, HttpAuthSchemes}; use crate::client::identity::IdentityResolvers; use crate::client::interceptors::context::{Error, Input, Output}; @@ -24,6 +21,9 @@ use std::future::Future as StdFuture; use std::pin::Pin; use std::sync::Arc; +/// Errors that can occur while running the orchestrator. +mod error; + pub use error::OrchestratorError; pub type HttpRequest = http::Request; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index 3c6802d05a..80c708a5a5 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -39,12 +39,6 @@ impl RuntimePlugins { Default::default() } - pub fn for_operation(operation: Box) -> Self { - let mut plugins = Self::new(); - plugins.operation_plugins.push(operation); - plugins - } - pub fn with_client_plugin( mut self, plugin: impl RuntimePlugin + Send + Sync + 'static, diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 34d5d34dd5..0c6fab72bf 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -21,7 +21,7 @@ use aws_smithy_runtime_api::client::retries::ShouldAttempt; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; use aws_smithy_types::config_bag::ConfigBag; use std::mem; -use tracing::{debug, debug_span, instrument, trace, Instrument}; +use tracing::{debug, debug_span, instrument, Instrument}; mod auth; /// Defines types that implement a trait for endpoint resolution @@ -31,7 +31,7 @@ pub mod interceptors; macro_rules! halt { ([$ctx:ident] => $err:expr) => {{ - trace!("encountered orchestrator error, continuing"); + debug!("encountered orchestrator error; halting"); $ctx.fail($err.into()); return; }}; @@ -49,17 +49,38 @@ macro_rules! halt_on_err { macro_rules! continue_on_err { ([$ctx:ident] => $expr:expr) => { if let Err(err) = $expr { - trace!("encountered orchestrator error, continuing"); + debug!("encountered orchestrator error; continuing"); $ctx.fail(err.into()); } }; } -#[tracing::instrument(skip_all)] pub async fn invoke( input: Input, runtime_plugins: &RuntimePlugins, ) -> Result> { + invoke_with_stop_point(input, runtime_plugins, StopPoint::None) + .await? + .finalize() +} + +/// Allows for returning early at different points during orchestration. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum StopPoint { + /// Don't stop orchestration early + None, + + /// Stop the orchestrator before transmitting the request + BeforeTransmit, +} + +#[tracing::instrument(skip_all, name = "invoke")] +pub async fn invoke_with_stop_point( + input: Input, + runtime_plugins: &RuntimePlugins, + stop_point: StopPoint, +) -> Result> { let mut cfg = ConfigBag::base(); let cfg = &mut cfg; @@ -74,10 +95,10 @@ pub async fn invoke( // If running the pre-execution interceptors failed, then we skip running the op and run the // final interceptors instead. if !ctx.is_failed() { - try_op(&mut ctx, cfg, &interceptors).await; + try_op(&mut ctx, cfg, &interceptors, stop_point).await; } finally_op(&mut ctx, cfg, &interceptors).await; - ctx.finalize() + Ok(ctx) } .maybe_timeout_with_config(operation_timeout_config) .await @@ -103,7 +124,12 @@ fn apply_configuration( } #[instrument(skip_all)] -async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: &Interceptors) { +async fn try_op( + ctx: &mut InterceptorContext, + cfg: &mut ConfigBag, + interceptors: &Interceptors, + stop_point: StopPoint, +) { // Before serialization halt_on_err!([ctx] => interceptors.read_before_serialization(ctx, cfg)); halt_on_err!([ctx] => interceptors.modify_before_serialization(ctx, cfg)); @@ -120,10 +146,10 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: // Load the request body into memory if configured to do so if let LoadedRequestBody::Requested = cfg.loaded_request_body() { let mut body = SdkBody::taken(); - let req = ctx.request_mut().expect("request exists"); - mem::swap(&mut body, req.body_mut()); + mem::swap(&mut body, ctx.request_mut().expect("set above").body_mut()); let loaded_body = halt_on_err!([ctx] => ByteStream::new(body).collect().await).into_bytes(); - *req.body_mut() = SdkBody::from(loaded_body.clone()); + *ctx.request_mut().as_mut().expect("set above").body_mut() = + SdkBody::from(loaded_body.clone()); cfg.set_loaded_request_body(LoadedRequestBody::Loaded(loaded_body)); } @@ -140,10 +166,10 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: .unwrap_or(Ok(ShouldAttempt::Yes)); match should_attempt { // Yes, let's make a request - Ok(ShouldAttempt::Yes) => trace!("retry strategy has OKed initial request"), + Ok(ShouldAttempt::Yes) => debug!("retry strategy has OK'd initial request"), // No, this request shouldn't be sent Ok(ShouldAttempt::No) => { - let err: BoxError = "The retry strategy indicates that an initial request shouldn't be made, but it didn't specify why.".into(); + let err: BoxError = "the retry strategy indicates that an initial request shouldn't be made, but it didn't specify why".into(); halt!([ctx] => err); } // No, we shouldn't make a request because... @@ -157,22 +183,18 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: // the request in the case of retry attempts. ctx.save_checkpoint(); for i in 0usize.. { - trace!("beginning attempt #{i}"); + debug!("beginning attempt #{i}"); // Break from the loop if we can't rewind the request's state. This will always succeed the // first time, but will fail on subsequent iterations if the request body wasn't retryable. - match ctx.rewind(cfg) { - r @ RewindResult::Impossible => { - debug!("{r}"); - break; - } - r @ RewindResult::Occurred => debug!("{r}"), - r @ RewindResult::Unnecessary => debug!("{r}"), + if let RewindResult::Impossible = ctx.rewind(cfg) { + debug!("request cannot be retried since the request body cannot be cloned"); + break; } // Track which attempt we're currently on. cfg.put::(i.into()); let attempt_timeout_config = cfg.maybe_timeout_config(TimeoutKind::OperationAttempt); let maybe_timeout = async { - try_attempt(ctx, cfg, interceptors).await; + try_attempt(ctx, cfg, interceptors, stop_point).await; finally_attempt(ctx, cfg, interceptors).await; Result::<_, SdkError>::Ok(()) } @@ -196,12 +218,12 @@ async fn try_op(ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: ShouldAttempt::Yes => continue, // No, this request shouldn't be retried ShouldAttempt::No => { - trace!("this error is not retryable, exiting attempt loop"); + debug!("this error is not retryable, exiting attempt loop"); break; } ShouldAttempt::YesAfterDelay(delay) => { let sleep_impl = halt_on_err!([ctx] => cfg.sleep_impl().ok_or(OrchestratorError::other( - "The retry strategy requested a delay before sending the next request, but no 'async sleep' implementation was set." + "the retry strategy requested a delay before sending the next request, but no 'async sleep' implementation was set" ))); sleep_impl.sleep(delay).await; continue; @@ -215,6 +237,7 @@ async fn try_attempt( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, interceptors: &Interceptors, + stop_point: StopPoint, ) { halt_on_err!([ctx] => interceptors.read_before_attempt(ctx, cfg)); halt_on_err!([ctx] => orchestrate_endpoint(ctx, cfg)); @@ -227,11 +250,16 @@ async fn try_attempt( halt_on_err!([ctx] => interceptors.modify_before_transmit(ctx, cfg)); halt_on_err!([ctx] => interceptors.read_before_transmit(ctx, cfg)); + // Return early if a stop point is set for before transmit + if let StopPoint::BeforeTransmit = stop_point { + return; + } + // The connection consumes the request but we need to keep a copy of it // within the interceptor context, so we clone it here. ctx.enter_transmit_phase(); let call_result = halt_on_err!([ctx] => { - let request = ctx.take_request(); + let request = ctx.take_request().expect("set during serialization"); cfg.connection().call(request).await }); ctx.set_response(call_result); @@ -243,20 +271,18 @@ async fn try_attempt( ctx.enter_deserialization_phase(); let output_or_error = async { - let response = ctx - .response_mut() - .ok_or("No response was present in the InterceptorContext")?; + let response = ctx.response_mut().expect("set during transmit"); let response_deserializer = cfg.response_deserializer(); match response_deserializer.deserialize_streaming(response) { - Some(output_or_error) => Ok(output_or_error), + Some(output_or_error) => output_or_error, None => read_body(response) .instrument(debug_span!("read_body")) .await - .map(|_| response_deserializer.deserialize_nonstreaming(response)), + .map_err(OrchestratorError::other) + .and_then(|_| response_deserializer.deserialize_nonstreaming(response)), } } - .await - .expect("how should I insert this into the context?"); + .await; ctx.set_output_or_error(output_or_error); ctx.enter_after_deserialization_phase(); @@ -289,6 +315,7 @@ mod tests { use crate::client::orchestrator::endpoints::{ StaticUriEndpointResolver, StaticUriEndpointResolverParams, }; + use crate::client::orchestrator::{invoke_with_stop_point, StopPoint}; use crate::client::retries::strategy::NeverRetryStrategy; use crate::client::runtime_plugin::anonymous_auth::AnonymousAuthRuntimePlugin; use crate::client::test_util::{ @@ -312,8 +339,10 @@ mod tests { use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, OrchestratorError}; use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin, RuntimePlugins}; use aws_smithy_types::config_bag::ConfigBag; - use aws_smithy_types::type_erasure::TypeErasedBox; + use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use http::StatusCode; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; use tracing_test::traced_test; fn new_request_serializer() -> CannedRequestSerializer { @@ -930,4 +959,133 @@ mod tests { expected ); } + + #[tokio::test] + async fn test_stop_points() { + let runtime_plugins = || { + RuntimePlugins::new() + .with_operation_plugin(TestOperationRuntimePlugin) + .with_operation_plugin(AnonymousAuthRuntimePlugin) + }; + + // StopPoint::None should result in a response getting set since orchestration doesn't stop + let context = invoke_with_stop_point( + TypedBox::new(()).erase(), + &runtime_plugins(), + StopPoint::None, + ) + .await + .expect("success"); + assert!(context.response().is_some()); + + // StopPoint::BeforeTransmit will exit right before sending the request, so there should be no response + let context = invoke_with_stop_point( + TypedBox::new(()).erase(), + &runtime_plugins(), + StopPoint::BeforeTransmit, + ) + .await + .expect("success"); + assert!(context.response().is_none()); + } + + /// The "finally" interceptors should run upon error when the StopPoint is set to BeforeTransmit + #[tokio::test] + async fn test_stop_points_error_handling() { + #[derive(Debug, Default)] + struct Inner { + modify_before_retry_loop_called: AtomicBool, + modify_before_completion_called: AtomicBool, + read_after_execution_called: AtomicBool, + } + #[derive(Clone, Debug, Default)] + struct TestInterceptor { + inner: Arc, + } + + impl Interceptor for TestInterceptor { + fn modify_before_retry_loop( + &self, + _context: &mut BeforeTransmitInterceptorContextMut<'_>, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + self.inner + .modify_before_retry_loop_called + .store(true, Ordering::Relaxed); + Err("test error".into()) + } + + fn modify_before_completion( + &self, + _context: &mut FinalizerInterceptorContextMut<'_>, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + self.inner + .modify_before_completion_called + .store(true, Ordering::Relaxed); + Ok(()) + } + + fn read_after_execution( + &self, + _context: &FinalizerInterceptorContextRef<'_>, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + self.inner + .read_after_execution_called + .store(true, Ordering::Relaxed); + Ok(()) + } + } + + #[derive(Debug)] + struct TestInterceptorRuntimePlugin { + interceptor: TestInterceptor, + } + impl RuntimePlugin for TestInterceptorRuntimePlugin { + fn configure( + &self, + cfg: &mut ConfigBag, + interceptors: &mut InterceptorRegistrar, + ) -> Result<(), BoxError> { + cfg.put(self.interceptor.clone()); + + interceptors.register(SharedInterceptor::new(self.interceptor.clone())); + Ok(()) + } + } + + let interceptor = TestInterceptor::default(); + let runtime_plugins = || { + RuntimePlugins::new() + .with_operation_plugin(TestOperationRuntimePlugin) + .with_operation_plugin(AnonymousAuthRuntimePlugin) + .with_operation_plugin(TestInterceptorRuntimePlugin { + interceptor: interceptor.clone(), + }) + }; + + // StopPoint::BeforeTransmit will exit right before sending the request, so there should be no response + let context = invoke_with_stop_point( + TypedBox::new(()).erase(), + &runtime_plugins(), + StopPoint::BeforeTransmit, + ) + .await + .expect("success"); + assert!(context.response().is_none()); + + assert!(interceptor + .inner + .modify_before_retry_loop_called + .load(Ordering::Relaxed)); + assert!(interceptor + .inner + .modify_before_completion_called + .load(Ordering::Relaxed)); + assert!(interceptor + .inner + .read_after_execution_called + .load(Ordering::Relaxed)); + } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 37b21f731a..005828aa67 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -85,9 +85,7 @@ pub(super) async fn orchestrate_auth( extract_endpoint_auth_scheme_config(endpoint, scheme_id)?; let identity = identity_resolver.resolve_identity(cfg).await?; - let request = ctx - .request_mut() - .expect("request is present before orchestrate_auth is called"); + let request = ctx.request_mut().expect("set during serialization"); request_signer.sign_request( request, &identity, diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index e78b3beac5..0d16f18704 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -93,9 +93,7 @@ pub(super) fn orchestrate_endpoint( ) -> Result<(), BoxError> { let params = cfg.endpoint_resolver_params(); let endpoint_prefix = cfg.get::(); - let request = ctx - .request_mut() - .expect("request is present before orchestrate_endpoint is called"); + let request = ctx.request_mut().expect("set during serialization"); let endpoint_resolver = cfg.endpoint_resolver(); let endpoint = endpoint_resolver.resolve_endpoint(params)?; diff --git a/rust-runtime/inlineable/src/lib.rs b/rust-runtime/inlineable/src/lib.rs index e53b81db7d..c6786d504e 100644 --- a/rust-runtime/inlineable/src/lib.rs +++ b/rust-runtime/inlineable/src/lib.rs @@ -17,6 +17,8 @@ mod json_errors; mod rest_xml_unwrapped_errors; #[allow(unused)] mod rest_xml_wrapped_errors; +#[allow(unused)] +mod serialization_settings; #[allow(unused)] mod endpoint_lib; diff --git a/rust-runtime/inlineable/src/serialization_settings.rs b/rust-runtime/inlineable/src/serialization_settings.rs new file mode 100644 index 0000000000..0bb85b98c9 --- /dev/null +++ b/rust-runtime/inlineable/src/serialization_settings.rs @@ -0,0 +1,84 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +use aws_smithy_http::header::set_request_header_if_absent; +use http::header::{HeaderName, CONTENT_LENGTH, CONTENT_TYPE}; + +/// Configuration for how default protocol headers are serialized +#[derive(Clone, Debug, Default)] +pub(crate) struct HeaderSerializationSettings { + omit_default_content_length: bool, + omit_default_content_type: bool, +} + +impl HeaderSerializationSettings { + /// Creates new [`HeaderSerializationSettings`] + pub(crate) fn new() -> Self { + Default::default() + } + + /// Omit the default `Content-Length` header during serialization + pub(crate) fn omit_default_content_length(self) -> Self { + Self { + omit_default_content_length: true, + ..self + } + } + + /// Omit the default `Content-Type` header during serialization + pub(crate) fn omit_default_content_type(self) -> Self { + Self { + omit_default_content_type: true, + ..self + } + } + + /// Returns true if the given default header name should be serialized + fn include_header(&self, header: &HeaderName) -> bool { + (!self.omit_default_content_length || header != CONTENT_LENGTH) + && (!self.omit_default_content_type || header != CONTENT_TYPE) + } + + /// Sets a default header on the given request builder if it should be serialized + pub(crate) fn set_default_header( + &self, + mut request: http::request::Builder, + header_name: HeaderName, + value: &str, + ) -> http::request::Builder { + if self.include_header(&header_name) { + request = set_request_header_if_absent(request, header_name, value); + } + request + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_include_header() { + let settings = HeaderSerializationSettings::default(); + assert!(settings.include_header(&CONTENT_LENGTH)); + assert!(settings.include_header(&CONTENT_TYPE)); + + let settings = HeaderSerializationSettings::default().omit_default_content_length(); + assert!(!settings.include_header(&CONTENT_LENGTH)); + assert!(settings.include_header(&CONTENT_TYPE)); + + let settings = HeaderSerializationSettings::default().omit_default_content_type(); + assert!(settings.include_header(&CONTENT_LENGTH)); + assert!(!settings.include_header(&CONTENT_TYPE)); + + let settings = HeaderSerializationSettings::default() + .omit_default_content_type() + .omit_default_content_length(); + assert!(!settings.include_header(&CONTENT_LENGTH)); + assert!(!settings.include_header(&CONTENT_TYPE)); + } +} diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index 41b0eafff9..7bb15ba663 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -12,17 +12,13 @@ C_RESET='\033[0m' set -eu cd smithy-rs -# TODO(enableNewSmithyRuntime): Move these into `services_that_compile` as more progress is made -services_that_fail_compile=(\ - "s3"\ - "polly"\ -) - # TODO(enableNewSmithyRuntime): Move these into `services_that_pass_tests` as more progress is made services_that_compile=(\ "aws-config"\ "dynamodb"\ + "polly"\ "route53"\ + "s3"\ "sts"\ ) From 18aad5c66aec0316d880f1ac41b8f9d44b71350c Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 5 Jun 2023 11:27:15 -0700 Subject: [PATCH 132/253] Fix bug in `update-sdk-next.yml` (#2746) ## Motivation and Context The workflow is failing to update the aws-sdk-rust/next branch due to the `tests/` folder in the repository root now. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .github/workflows/update-sdk-next.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-sdk-next.yml b/.github/workflows/update-sdk-next.yml index 542abdde00..728f645169 100644 --- a/.github/workflows/update-sdk-next.yml +++ b/.github/workflows/update-sdk-next.yml @@ -47,7 +47,7 @@ jobs: git checkout origin/main -b next # Delete the old SDK - rm -rf sdk examples + rm -rf sdk examples tests rm -f versions.toml Cargo.toml index.md # Copy in the new SDK From 74a7204123555ff68c841b42cbe273e08cef1c16 Mon Sep 17 00:00:00 2001 From: Thomas Cameron Date: Tue, 6 Jun 2023 04:25:27 +0900 Subject: [PATCH 133/253] Add serde support to date time (#2646) ## Motivation and Context This is a child PR of https://github.com/awslabs/smithy-rs/pull/2616 The changes that this PR introduces is same as the ones that were merged to `unstable-serde-support` branch before. Initially, we tried to make commit to unstable-serde-support branch and merge changes one by one in small PRs. However, in order to make it up to date with the main branch, we would need to go through a large PR of over 700 files. Thus, I decided to create individual PRs that commits directly to `main` branch. ## Description - Implements `serde` support to `DateTime` ## Testing - Test checks whether the serialized/de-serialized data matches with the expected value ## Checklist NA ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- rust-runtime/aws-smithy-types/Cargo.toml | 7 + .../aws-smithy-types/src/date_time/de.rs | 140 ++++++++++++++++++ .../aws-smithy-types/src/date_time/mod.rs | 9 +- .../aws-smithy-types/src/date_time/ser.rs | 85 +++++++++++ 4 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 rust-runtime/aws-smithy-types/src/date_time/de.rs create mode 100644 rust-runtime/aws-smithy-types/src/date_time/ser.rs diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index aa2462d6d0..60af72da0f 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -9,6 +9,8 @@ repository = "https://github.com/awslabs/smithy-rs" [features] test-util = [] +serde-serialize = [] +serde-deserialize = [] [dependencies] itoa = "1.0.0" @@ -25,6 +27,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" criterion = "0.4" rand = "0.8.4" +ciborium = { version = "0.2.1" } [package.metadata.docs.rs] all-features = true @@ -35,3 +38,7 @@ rustdoc-args = ["--cfg", "docsrs"] [[bench]] name = "base64" harness = false + +[target."cfg(aws_sdk_unstable)".dependencies.serde] +version = "1" +features = ["derive"] diff --git a/rust-runtime/aws-smithy-types/src/date_time/de.rs b/rust-runtime/aws-smithy-types/src/date_time/de.rs new file mode 100644 index 0000000000..c87a32e0f5 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/date_time/de.rs @@ -0,0 +1,140 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::*; +use serde::de::{Error, Visitor}; +use serde::Deserialize; + +struct DateTimeVisitor; + +struct NonHumanReadableDateTimeVisitor; + +impl<'de> Visitor<'de> for DateTimeVisitor { + type Value = DateTime; + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("expected RFC-3339 Date Time") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match DateTime::from_str(v, Format::DateTime) { + Ok(e) => Ok(e), + Err(e) => Err(Error::custom(e)), + } + } +} + +impl<'de> Visitor<'de> for NonHumanReadableDateTimeVisitor { + type Value = DateTime; + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("DateTime type expects a tuple of i64 and u32 when deserializing from non human readable format like CBOR or AVRO, i.e. (i64, u32)") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + match seq.size_hint() { + Some(2) | None => match (seq.next_element()?, seq.next_element()?) { + (Some(seconds), Some(subsecond_nanos)) => Ok(DateTime { + seconds, + subsecond_nanos, + }), + _ => return Err(Error::custom("datatype mismatch")), + }, + _ => Err(Error::custom("Size mismatch")), + } + } +} + +impl<'de> Deserialize<'de> for DateTime { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + if deserializer.is_human_readable() { + deserializer.deserialize_str(DateTimeVisitor) + } else { + deserializer.deserialize_tuple(2, NonHumanReadableDateTimeVisitor) + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + /// check for human redable format + #[test] + fn de_human_readable_datetime() { + use serde::{Deserialize, Serialize}; + + let datetime = DateTime::from_secs(1576540098); + #[derive(Serialize, Deserialize, PartialEq)] + struct Test { + datetime: DateTime, + } + let datetime_json = r#"{"datetime":"2019-12-16T23:48:18Z"}"#; + let test = serde_json::from_str::(&datetime_json).ok(); + assert!(test == Some(Test { datetime })); + } + + /// check for non-human redable format + #[test] + fn de_not_human_readable_datetime() { + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(1576540098i64.into()), + ciborium::value::Value::Integer(0u32.into()), + ]); + let mut buf = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let cbor_dt: DateTime = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap(); + assert_eq!( + cbor_dt, + DateTime { + seconds: 1576540098i64, + subsecond_nanos: 0 + } + ); + }; + + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(0i64.into()), + ciborium::value::Value::Integer(0u32.into()), + ]); + let mut buf = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let cbor_dt: DateTime = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap(); + assert_eq!( + cbor_dt, + DateTime { + seconds: 0, + subsecond_nanos: 0 + } + ); + }; + + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(i64::MAX.into()), + ciborium::value::Value::Integer(u32::MAX.into()), + ]); + let mut buf = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let cbor_dt: DateTime = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap(); + assert_eq!( + cbor_dt, + DateTime { + seconds: i64::MAX, + subsecond_nanos: u32::MAX + } + ); + }; + } +} diff --git a/rust-runtime/aws-smithy-types/src/date_time/mod.rs b/rust-runtime/aws-smithy-types/src/date_time/mod.rs index 84ac29799b..8dcdc53067 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/mod.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs @@ -17,7 +17,12 @@ use std::time::Duration; use std::time::SystemTime; use std::time::UNIX_EPOCH; +#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))] +mod de; mod format; +#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))] +mod ser; + pub use self::format::DateTimeFormatError; pub use self::format::DateTimeParseError; @@ -51,8 +56,8 @@ const NANOS_PER_SECOND_U32: u32 = 1_000_000_000; /// [`time`](https://crates.io/crates/time) or [`chrono`](https://crates.io/crates/chrono). #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub struct DateTime { - seconds: i64, - subsecond_nanos: u32, + pub(crate) seconds: i64, + pub(crate) subsecond_nanos: u32, } /* ANCHOR_END: date_time */ diff --git a/rust-runtime/aws-smithy-types/src/date_time/ser.rs b/rust-runtime/aws-smithy-types/src/date_time/ser.rs new file mode 100644 index 0000000000..5ea5f55dcd --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/date_time/ser.rs @@ -0,0 +1,85 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use super::*; +use serde::ser::SerializeTuple; + +impl serde::Serialize for DateTime { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if serializer.is_human_readable() { + match self.fmt(Format::DateTime) { + Ok(val) => serializer.serialize_str(&val), + Err(e) => Err(serde::ser::Error::custom(e)), + } + } else { + let mut tup_ser = serializer.serialize_tuple(2)?; + tup_ser.serialize_element(&self.seconds)?; + tup_ser.serialize_element(&self.subsecond_nanos)?; + tup_ser.end() + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + /// check for human redable format + #[test] + fn ser_human_readable_datetime() { + use serde::{Deserialize, Serialize}; + + let datetime = DateTime::from_secs(1576540098); + #[derive(Serialize, Deserialize, PartialEq)] + struct Test { + datetime: DateTime, + } + let datetime_json = r#"{"datetime":"2019-12-16T23:48:18Z"}"#; + assert!(serde_json::to_string(&Test { datetime }).ok() == Some(datetime_json.to_string())); + } + + /// check for non-human redable format + #[test] + fn ser_not_human_readable_datetime() { + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(1576540098i64.into()), + ciborium::value::Value::Integer(0u32.into()), + ]); + let mut buf = vec![]; + let mut buf2 = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let _ = ciborium::ser::into_writer(&cbor, &mut buf2); + assert_eq!(buf, buf2); + }; + + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(0i64.into()), + ciborium::value::Value::Integer(0u32.into()), + ]); + let mut buf = vec![]; + let mut buf2 = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let _ = ciborium::ser::into_writer(&cbor, &mut buf2); + assert_eq!(buf, buf2); + }; + + { + let cbor = ciborium::value::Value::Array(vec![ + ciborium::value::Value::Integer(i64::MAX.into()), + ciborium::value::Value::Integer(u32::MAX.into()), + ]); + let mut buf = vec![]; + let mut buf2 = vec![]; + let _ = ciborium::ser::into_writer(&cbor, &mut buf); + let _ = ciborium::ser::into_writer(&cbor, &mut buf2); + assert_eq!(buf, buf2); + }; + } +} From 77395c3582f31e4bc073d8bd9fce615c6b9dc313 Mon Sep 17 00:00:00 2001 From: Thomas Cameron Date: Wed, 7 Jun 2023 01:16:56 +0900 Subject: [PATCH 134/253] Add serde support to Blob type (#2647) ## Motivation and Context This is a child PR of https://github.com/awslabs/smithy-rs/pull/2616 The changes that this PR introduces is same as the ones that were merged to `unstable-serde-support` branch before. Initially, we tried to make commit to unstable-serde-support branch and merge changes one by one in small PRs. However, in order to make it up to date with the main branch, we would need to go through a large PR of over 700 files. Thus, I decided to create individual PRs that commits directly to `main` branch. ## Description - Implements `serde` support to `Blob` ## Testing - Test checks whether the serialized/de-serialized data matches with the expected value ## Checklist NA ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti --- rust-runtime/aws-smithy-types/Cargo.toml | 5 +- rust-runtime/aws-smithy-types/additional-ci | 4 +- rust-runtime/aws-smithy-types/src/blob.rs | 125 ++++++++++++++++++++ 3 files changed, 132 insertions(+), 2 deletions(-) diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index 60af72da0f..9b61280f8f 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "aws-smithy-types" version = "0.0.0-smithy-rs-head" -authors = ["AWS Rust SDK Team ", "Russell Cohen "] +authors = [ + "AWS Rust SDK Team ", + "Russell Cohen ", +] description = "Types for smithy-rs codegen." edition = "2021" license = "Apache-2.0" diff --git a/rust-runtime/aws-smithy-types/additional-ci b/rust-runtime/aws-smithy-types/additional-ci index c1fd7ce406..2d9305d510 100755 --- a/rust-runtime/aws-smithy-types/additional-ci +++ b/rust-runtime/aws-smithy-types/additional-ci @@ -5,8 +5,10 @@ # # This script contains additional CI checks to run for this specific package - set -e echo "### Checking for duplicate dependency versions in the normal dependency graph with all features enabled" cargo tree -d --edges normal --all-features + +echo "### Checking whether the features are properly feature-gated" +! cargo tree -e no-dev | grep serde diff --git a/rust-runtime/aws-smithy-types/src/blob.rs b/rust-runtime/aws-smithy-types/src/blob.rs index bdd335c492..5365b91249 100644 --- a/rust-runtime/aws-smithy-types/src/blob.rs +++ b/rust-runtime/aws-smithy-types/src/blob.rs @@ -30,3 +30,128 @@ impl AsRef<[u8]> for Blob { &self.inner } } + +#[cfg(all(aws_sdk_unstable, feature = "serde-serialize"))] +mod serde_serialize { + use super::*; + use crate::base64; + use serde::Serialize; + + impl Serialize for Blob { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if serializer.is_human_readable() { + serializer.serialize_str(&crate::base64::encode(&self.inner)) + } else { + serializer.serialize_bytes(&self.inner) + } + } + } +} + +#[cfg(all(aws_sdk_unstable, feature = "serde-deserialize"))] +mod serde_deserialize { + use super::*; + use crate::base64; + use serde::{de::Visitor, Deserialize}; + + struct HumanReadableBlobVisitor; + impl<'de> Visitor<'de> for HumanReadableBlobVisitor { + type Value = Blob; + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("expected base64 encoded string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match base64::decode(v) { + Ok(inner) => Ok(Blob { inner }), + Err(e) => Err(E::custom(e)), + } + } + } + + struct NotHumanReadableBlobVisitor; + impl<'de> Visitor<'de> for NotHumanReadableBlobVisitor { + type Value = Blob; + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("expected bytes") + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: serde::de::Error, + { + Ok(Blob { inner: v }) + } + } + + impl<'de> Deserialize<'de> for Blob { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + if deserializer.is_human_readable() { + deserializer.deserialize_str(HumanReadableBlobVisitor) + } else { + deserializer.deserialize_byte_buf(NotHumanReadableBlobVisitor) + } + } + } +} + +#[cfg(test)] +#[cfg(all( + aws_sdk_unstable, + feature = "serde-serialize", + feature = "serde-deserialize" +))] +mod test_serde { + use crate::Blob; + use serde::{Deserialize, Serialize}; + use std::collections::HashMap; + + #[derive(Deserialize, Serialize, Debug, PartialEq)] + struct ForTest { + blob: Blob, + } + + #[test] + fn human_readable_blob() { + let aws_in_base64 = r#"{"blob":"QVdT"}"#; + let for_test = ForTest { + blob: Blob { + inner: vec![b'A', b'W', b'S'], + }, + }; + assert_eq!(for_test, serde_json::from_str(aws_in_base64).unwrap()); + assert_eq!(serde_json::to_string(&for_test).unwrap(), aws_in_base64); + } + + #[test] + fn not_human_readable_blob() { + use std::ffi::CString; + + let for_test = ForTest { + blob: Blob { + inner: vec![b'A', b'W', b'S'], + }, + }; + let mut buf = vec![]; + let res = ciborium::ser::into_writer(&for_test, &mut buf); + assert!(res.is_ok()); + + // checks whether the bytes are deserialized properly + let n: HashMap = + ciborium::de::from_reader(std::io::Cursor::new(buf.clone())).unwrap(); + assert!(n.get("blob").is_some()); + assert!(n.get("blob") == CString::new([65, 87, 83]).ok().as_ref()); + + let de: ForTest = ciborium::de::from_reader(std::io::Cursor::new(buf)).unwrap(); + assert_eq!(for_test, de); + } +} From 3285c43b42b65a347718d133745a5c6268f6fc13 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 6 Jun 2023 14:58:30 -0400 Subject: [PATCH 135/253] Runtime config refactors (#2748) ## Motivation and Context Part of refactoring config bag to work with the new RuntimePlugin API ## Description - change config bag to a vector instead of a linked list ## Testing - existing UTs ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-smithy-types/src/config_bag.rs | 355 ++++++++++-------- 1 file changed, 204 insertions(+), 151 deletions(-) diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index f307d927b8..6a7f3797d7 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -5,7 +5,7 @@ //! Layered Configuration Bag Structure //! -//! [`config_bag::ConfigBag`] and [`config_bag::FrozenConfigBag`] are the two representations of a layered configuration structure +//! [`config_bag::ConfigBag`] represents the layered configuration structure //! with the following properties: //! 1. A new layer of configuration may be applied onto an existing configuration structure without modifying it or taking ownership. //! 2. No lifetime shenanigans to deal with @@ -18,8 +18,9 @@ use std::borrow::Cow; use std::fmt::{Debug, Formatter}; use std::iter::Rev; use std::marker::PhantomData; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use std::slice; +use std::slice::Iter; use std::sync::Arc; /// Layered Configuration Structure @@ -28,7 +29,7 @@ use std::sync::Arc; #[must_use] pub struct ConfigBag { head: Layer, - tail: Option, + tail: Vec, } impl Debug for ConfigBag { @@ -45,15 +46,15 @@ impl Debug for ConfigBag { } } -/// Layered Configuration Structure +/// [`FrozenLayer`] is the "locked" form of [`Layer`]. /// -/// [`FrozenConfigBag`] is the "locked" form of the bag. +/// [`ConfigBag`] contains a ordered collection of [`FrozenLayer`] #[derive(Clone, Debug)] #[must_use] -pub struct FrozenConfigBag(Arc); +pub struct FrozenLayer(Arc); -impl Deref for FrozenConfigBag { - type Target = ConfigBag; +impl Deref for FrozenLayer { + type Target = Layer; fn deref(&self) -> &Self::Target { &self.0 @@ -77,7 +78,7 @@ impl Default for Value { } /// A named layer comprising a config bag -struct Layer { +pub struct Layer { name: Cow<'static, str>, props: TypeIdMap, } @@ -192,102 +193,55 @@ impl Debug for Layer { } impl Layer { - /// Inserts `value` into the layer - fn put(&mut self, value: T::StoredType) -> &mut Self { + /// Inserts `value` into the layer directly + fn put_directly(&mut self, value: T::StoredType) -> &mut Self { self.props .insert(TypeId::of::(), TypeErasedBox::new(value)); self } - /// Retrieves the value of type `T` from this layer if exists - fn get(&self) -> Option<&T::StoredType> { - self.props - .get(&TypeId::of::()) - .map(|t| t.downcast_ref().expect("typechecked")) - } - - /// Returns a mutable reference to `T` if it is stored in this layer - fn get_mut(&mut self) -> Option<&mut T::StoredType> { - self.props - .get_mut(&TypeId::of::()) - .map(|t| t.downcast_mut().expect("typechecked")) + pub fn empty(&self) -> bool { + self.props.is_empty() } - /// Returns a mutable reference to `T` if it is stored in this layer, otherwise returns the - /// [`Default`] implementation of `T` - fn get_mut_or_default(&mut self) -> &mut T::StoredType - where - T::StoredType: Default, - { - self.props - .entry(TypeId::of::()) - .or_insert_with(|| TypeErasedBox::new(T::StoredType::default())) - .downcast_mut() - .expect("typechecked") + pub fn freeze(self) -> FrozenLayer { + self.into() } -} - -fn no_op(_: &mut ConfigBag) {} -impl FrozenConfigBag { - /// Attempts to convert this bag directly into a [`ConfigBag`] if no other references exist - /// - /// This allows modifying the top layer of the bag. [`Self::add_layer`] may be - /// used to add a new layer to the bag. - pub fn try_modify(self) -> Option { - Arc::try_unwrap(self.0).ok() + /// Create a new Layer with a given name + pub fn new(name: impl Into>) -> Self { + let name = name.into(); + Self { + name, + props: Default::default(), + } } - /// Add a new layer to the config bag - /// - /// This is equivalent to calling [`Self::with_fn`] with a no-op function - /// - /// # Examples - /// ``` - /// use aws_smithy_types::config_bag::ConfigBag; - /// fn add_more_config(bag: &mut ConfigBag) { /* ... */ } - /// let bag = ConfigBag::base().with_fn("first layer", |_| { /* add a property */ }); - /// let mut bag = bag.add_layer("second layer"); - /// add_more_config(&mut bag); - /// let bag = bag.freeze(); - /// ``` - pub fn add_layer(&self, name: impl Into>) -> ConfigBag { - self.with_fn(name, no_op) + /// Load a storable item from the bag + pub fn load(&self) -> ::ReturnedType<'_> { + T::Storer::merge_iter(ItemIter { + inner: BagIter { + head: Some(self), + tail: [].iter().rev(), + }, + t: Default::default(), + }) } - /// Add more items to the config bag - pub fn with_fn( - &self, - name: impl Into>, - next: impl Fn(&mut ConfigBag), - ) -> ConfigBag { - let new_layer = Layer { - name: name.into(), - props: Default::default(), - }; - let mut bag = ConfigBag { - head: new_layer, - tail: Some(self.clone()), - }; - next(&mut bag); - bag + /// Remove `T` from this bag + pub fn unset(&mut self) -> &mut Self { + self.put_directly::>(Value::ExplicitlyUnset(type_name::())); + self } -} -impl ConfigBag { - /// Create a new config bag "base". + /// Insert `value` into the bag /// - /// Configuration may then be "layered" onto the base by calling - /// [`ConfigBag::store_put`], [`ConfigBag::store_or_unset`], [`ConfigBag::store_append`]. Layers - /// of configuration may then be "frozen" (made immutable) by calling [`ConfigBag::freeze`]. - pub fn base() -> Self { - ConfigBag { - head: Layer { - name: Cow::Borrowed("base"), - props: Default::default(), - }, - tail: None, - } + /// NOTE: This method exists for legacy reasons to allow storing values that are not `Storeable` + /// + /// The implementation assumes that the type is [`StoreReplace`]. + pub fn put(&mut self, value: T) -> &mut Self { + self.put_directly::>(Value::Set(value)); + self } /// Stores `item` of type `T` into the config bag, overriding a previous value of the same type @@ -295,7 +249,7 @@ impl ConfigBag { where T: Storable>, { - self.head.put::>(Value::Set(item)); + self.put_directly::>(Value::Set(item)); self } @@ -309,7 +263,7 @@ impl ConfigBag { Some(item) => Value::Set(item), None => Value::ExplicitlyUnset(type_name::()), }; - self.head.put::>(item); + self.put_directly::>(item); self } @@ -335,13 +289,96 @@ impl ConfigBag { where T: Storable>, { - match self.head.get_mut_or_default::>() { + match self.get_mut_or_default::>() { Value::Set(list) => list.push(item), v @ Value::ExplicitlyUnset(_) => *v = Value::Set(vec![item]), } self } + /// Retrieves the value of type `T` from this layer if exists + fn get(&self) -> Option<&T::StoredType> { + self.props + .get(&TypeId::of::()) + .map(|t| t.downcast_ref().expect("typechecked")) + } + + /// Returns a mutable reference to `T` if it is stored in this layer + fn get_mut(&mut self) -> Option<&mut T::StoredType> { + self.props + .get_mut(&TypeId::of::()) + .map(|t| t.downcast_mut().expect("typechecked")) + } + + /// Returns a mutable reference to `T` if it is stored in this layer, otherwise returns the + /// [`Default`] implementation of `T` + fn get_mut_or_default(&mut self) -> &mut T::StoredType + where + T::StoredType: Default, + { + self.props + .entry(TypeId::of::()) + .or_insert_with(|| TypeErasedBox::new(T::StoredType::default())) + .downcast_mut() + .expect("typechecked") + } +} + +impl FrozenLayer { + /// Attempts to convert this bag directly into a [`ConfigBag`] if no other references exist + /// + /// This allows modifying the top layer of the bag. [`Self::add_layer`] may be + /// used to add a new layer to the bag. + pub fn try_modify(self) -> Option { + Arc::try_unwrap(self.0).ok() + } +} + +// TODO(refactor of configbag): consider removing these Deref impls—they exist to keep existing code compiling +impl Deref for ConfigBag { + type Target = Layer; + + fn deref(&self) -> &Self::Target { + &self.head + } +} + +impl DerefMut for ConfigBag { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.head + } +} + +impl ConfigBag { + /// Create a new config bag "base". + /// + /// Configuration may then be "layered" onto the base by calling + /// [`ConfigBag::store_put`], [`ConfigBag::store_or_unset`], [`ConfigBag::store_append`]. Layers + /// of configuration may then be "frozen" (made immutable) by calling [`ConfigBag::freeze`]. + pub fn base() -> Self { + ConfigBag { + head: Layer { + name: Cow::Borrowed("base"), + props: Default::default(), + }, + tail: vec![], + } + } + + pub fn push_layer(&mut self, layer: &FrozenLayer) -> &mut Self { + if !self.head.empty() { + self.freeze_head(); + } + self.tail.push(layer.clone()); + self + } + + fn freeze_head(&mut self) { + let new_head = Layer::new("scratch"); + let old_head = std::mem::replace(&mut self.head, new_head); + self.tail.push(old_head.freeze()); + } + /// Clears the value of type `T` from the config bag /// /// This internally marks the item of type `T` as cleared as opposed to wiping it out from the @@ -351,7 +388,7 @@ impl ConfigBag { T: Storable>, { self.head - .put::>(Value::ExplicitlyUnset(type_name::())); + .put_directly::>(Value::ExplicitlyUnset(type_name::())); } /// Load a value (or values) of type `T` depending on how `T` implements [`Storable`] @@ -374,7 +411,7 @@ impl ConfigBag { // alive (even in a returned branch) and then call `store_put`. So: drop the borrow immediately // store, the value, then pull it right back if matches!(self.head.get_mut::>(), None) { - let new_item = match self.tail.as_deref().and_then(|b| b.load::()) { + let new_item = match self.tail.iter().find_map(|b| b.load::()) { Some(item) => item.clone(), None => return None, }; @@ -429,35 +466,15 @@ impl ConfigBag { self.get_mut().unwrap() } - /// Insert `value` into the bag - pub fn put(&mut self, value: T) -> &mut Self { - self.head.put::>(Value::Set(value)); - self - } - - /// Remove `T` from this bag - pub fn unset(&mut self) -> &mut Self { - self.head - .put::>(Value::ExplicitlyUnset(type_name::())); - self - } - - /// Freeze this layer by wrapping it in an `Arc` - /// - /// This prevents further items from being added to this layer, but additional layers can be - /// added to the bag. - pub fn freeze(self) -> FrozenConfigBag { - self.into() - } - /// Add another layer to this configuration bag /// /// Hint: If you want to re-use this layer, call `freeze` first. /// ``` - /// use aws_smithy_types::config_bag::ConfigBag; + /// /* + /// use aws_smithy_types::config_bag::{ConfigBag, Layer}; /// let bag = ConfigBag::base(); - /// let first_layer = bag.with_fn("a", |b: &mut ConfigBag| { b.put("a"); }).freeze(); - /// let second_layer = first_layer.with_fn("other", |b: &mut ConfigBag| { b.put(1i32); }); + /// let first_layer = bag.with_fn("a", |b: &mut Layer| { b.put("a"); }); + /// let second_layer = first_layer.with_fn("other", |b: &mut Layer| { b.put(1i32); }); /// // The number is only in the second layer /// assert_eq!(first_layer.get::(), None); /// assert_eq!(second_layer.get::(), Some(&1)); @@ -465,14 +482,26 @@ impl ConfigBag { /// // The string is in both layers /// assert_eq!(first_layer.get::<&'static str>(), Some(&"a")); /// assert_eq!(second_layer.get::<&'static str>(), Some(&"a")); + /// */ /// ``` - pub fn with_fn(self, name: &'static str, next: impl Fn(&mut ConfigBag)) -> ConfigBag { - self.freeze().with_fn(name, next) + pub fn with_fn( + self, + name: impl Into>, + next: impl Fn(&mut Layer), + ) -> ConfigBag { + let mut new_layer = Layer::new(name); + next(&mut new_layer); + let ConfigBag { head, mut tail } = self; + tail.push(head.freeze()); + ConfigBag { + head: new_layer, + tail, + } } /// Add a new layer with `name` after freezing the top layer so far pub fn add_layer(self, name: impl Into>) -> ConfigBag { - self.freeze().add_layer(name) + self.with_fn(name, |_| {}) } /// Return a value (or values) of type `T` depending on how it has been stored in a `ConfigBag` @@ -488,7 +517,10 @@ impl ConfigBag { } fn layers(&self) -> BagIter<'_> { - BagIter { bag: Some(self) } + BagIter { + head: Some(&self.head), + tail: self.tail.iter().rev(), + } } } @@ -520,31 +552,32 @@ where /// Iterator over the layers of a config bag struct BagIter<'a> { - bag: Option<&'a ConfigBag>, + head: Option<&'a Layer>, + tail: Rev>, } impl<'a> Iterator for BagIter<'a> { type Item = &'a Layer; fn next(&mut self) -> Option { - let next = self.bag.map(|b| &b.head); - if let Some(bag) = &mut self.bag { - self.bag = bag.tail.as_deref(); + if let Some(head) = self.head.take() { + Some(head) + } else { + self.tail.next().map(|t| t.deref()) } - next } } -impl From for FrozenConfigBag { - fn from(bag: ConfigBag) -> Self { - FrozenConfigBag(Arc::new(bag)) +impl From for FrozenLayer { + fn from(layer: Layer) -> Self { + FrozenLayer(Arc::new(layer)) } } #[cfg(test)] mod test { use super::ConfigBag; - use crate::config_bag::{Storable, StoreAppend, StoreReplace}; + use crate::config_bag::{Layer, Storable, StoreAppend, StoreReplace}; #[test] fn layered_property_bag() { @@ -552,11 +585,11 @@ mod test { struct Prop1; #[derive(Debug)] struct Prop2; - let layer_a = |bag: &mut ConfigBag| { + let layer_a = |bag: &mut Layer| { bag.put(Prop1); }; - let layer_b = |bag: &mut ConfigBag| { + let layer_b = |bag: &mut Layer| { bag.put(Prop2); }; @@ -572,16 +605,14 @@ mod test { #[derive(Debug)] struct Prop4; - let layer_c = |bag: &mut ConfigBag| { + let layer_c = |bag: &mut Layer| { bag.put(Prop4); bag.unset::(); }; - let base_bag = base_bag.freeze(); let final_bag = base_bag.with_fn("c", layer_c); assert!(final_bag.get::().is_some()); - assert!(base_bag.get::().is_none()); assert!(final_bag.get::().is_some()); assert!(final_bag.get::().is_some()); // we unset prop3 @@ -594,7 +625,7 @@ mod test { let bag = ConfigBag::base(); #[derive(Debug)] struct Region(&'static str); - let bag = bag.with_fn("service config", |layer: &mut ConfigBag| { + let bag = bag.with_fn("service config", |layer: &mut Layer| { layer.put(Region("asdf")); }); @@ -602,15 +633,13 @@ mod test { #[derive(Debug)] struct SigningName(&'static str); - let bag = bag.freeze(); - let operation_config = bag.with_fn("operation", |layer: &mut ConfigBag| { + let operation_config = bag.with_fn("operation", |layer: &mut Layer| { layer.put(SigningName("s3")); }); - assert!(bag.get::().is_none()); assert_eq!(operation_config.get::().unwrap().0, "s3"); - let mut open_bag = operation_config.with_fn("my_custom_info", |_bag: &mut ConfigBag| {}); + let mut open_bag = operation_config.with_fn("my_custom_info", |_bag: &mut Layer| {}); open_bag.put("foo"); assert_eq!(open_bag.layers().count(), 4); @@ -669,6 +698,34 @@ mod test { ); } + #[test] + fn adding_layers() { + let mut layer_1 = Layer::new("layer1"); + + let mut layer_2 = Layer::new("layer2"); + + #[derive(Clone, Debug, PartialEq, Eq, Default)] + struct Foo(usize); + impl Storable for Foo { + type Storer = StoreReplace; + } + + layer_1.store_put(Foo(0)); + layer_2.store_put(Foo(1)); + + let layer_1 = layer_1.freeze(); + let layer_2 = layer_2.freeze(); + + let mut bag_1 = ConfigBag::base(); + let mut bag_2 = ConfigBag::base(); + bag_1.push_layer(&layer_1).push_layer(&layer_2); + bag_2.push_layer(&layer_2).push_layer(&layer_1); + + // bags have same layers but in different orders + assert_eq!(bag_1.load::(), Some(&Foo(1))); + assert_eq!(bag_2.load::(), Some(&Foo(0))); + } + #[test] fn get_mut_or_else() { #[derive(Clone, Debug, PartialEq, Eq, Default)] @@ -683,22 +740,18 @@ mod test { bag.get_mut_or_default::().0 += 1; assert_eq!(bag.get::(), Some(&Foo(1))); - let bag = bag.freeze(); - let old_ref = bag.load::().unwrap(); assert_eq!(old_ref, &Foo(1)); // there is one in the bag, so it can be returned - let mut next = bag.add_layer("next"); - next.get_mut::().unwrap().0 += 1; - let new_ref = next.load::().unwrap(); + //let mut next = bag.add_layer("next"); + bag.get_mut::().unwrap().0 += 1; + let new_ref = bag.load::().unwrap(); assert_eq!(new_ref, &Foo(2)); - // no funny business - assert_eq!(old_ref, &Foo(1)); - next.unset::(); + bag.unset::(); // if it was unset, we can't clone the current one, that would be wrong - assert_eq!(next.get_mut::(), None); - assert_eq!(next.get_mut_or_default::(), &Foo(0)); + assert_eq!(bag.get_mut::(), None); + assert_eq!(bag.get_mut_or_default::(), &Foo(0)); } } From 29a900e74d1037306821fecd8295dbc1ca735dc8 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Wed, 7 Jun 2023 10:01:16 +0100 Subject: [PATCH 136/253] Add CI to the book (#2027) ## Motivation and Context Closes https://github.com/awslabs/smithy-rs/issues/2004 ## Description Run `mdbook test` over the `design` folder. ## TODO - [x] Ignore the RFC sections using `ignore` tag on the code blocks. - [ ] Fix the remaining examples. - [x] Ensure local `rust-runtime` dependencies are being used. --------- Signed-off-by: Daniele Ahmed Co-authored-by: 82marbag <69267416+82marbag@users.noreply.github.com> --- .github/workflows/ci.yml | 2 + ci.mk | 4 + design/book.toml | 5 +- design/src/SUMMARY.md | 1 - design/src/client/identity_and_auth.md | 8 +- design/src/client/orchestrator.md | 6 +- ...a_low-level_feature_that_relies_on_HTTP.md | 16 +- design/src/overview.md | 4 +- design/src/rfcs/rfc0001_shared_config.md | 14 +- design/src/rfcs/rfc0002_http_versions.md | 16 +- design/src/rfcs/rfc0003_presigning_api.md | 6 +- design/src/rfcs/rfc0004_retry_behavior.md | 10 +- design/src/rfcs/rfc0008_paginators.md | 10 +- design/src/rfcs/rfc0010_waiters.md | 12 +- design/src/rfcs/rfc0013_body_callback_apis.md | 8 +- design/src/rfcs/rfc0014_timeout_config.md | 6 +- .../rfc0015_using_features_responsibly.md | 2 +- .../rfcs/rfc0016_flexible_checksum_support.md | 20 +- .../rfc0017_customizable_client_operations.md | 12 +- design/src/rfcs/rfc0018_logging_sensitive.md | 18 +- .../src/rfcs/rfc0019_event_streams_errors.md | 10 +- design/src/rfcs/rfc0020_service_builder.md | 58 ++--- ...rfc0022_error_context_and_compatibility.md | 16 +- design/src/rfcs/rfc0023_refine_builder.md | 60 ++--- design/src/rfcs/rfc0024_request_id.md | 8 +- design/src/rfcs/rfc0025_constraint_traits.md | 10 +- .../rfcs/rfc0026_client_crate_organization.md | 4 +- design/src/rfcs/rfc0027_endpoints_20.md | 16 +- ...fc0028_sdk_credential_cache_type_safety.md | 22 +- .../rfcs/rfc0029_new_home_for_cred_types.md | 10 +- ...c0030_serialization_and_deserialization.md | 10 +- ...oviding_fallback_credentials_on_timeout.md | 22 +- .../rfc0032_better_constraint_violations.md | 30 +-- .../rfc0033_improve_sdk_request_id_access.md | 8 +- .../src/rfcs/rfc0034_smithy_orchestrator.md | 20 +- design/src/server/anatomy.md | 56 ++--- design/src/server/code_generation.md | 98 ++++---- design/src/server/from_parts.md | 10 +- design/src/server/instrumentation.md | 6 +- design/src/server/middleware.md | 37 ++- design/src/server/overview.md | 1 - design/src/server/pokemon_service.md | 236 ------------------ design/src/smithy/aggregate_shapes.md | 6 +- design/src/smithy/backwards-compat.md | 10 +- design/src/smithy/endpoint.md | 6 +- design/src/smithy/simple_shapes.md | 2 +- design/src/transport/operation.md | 4 +- tools/ci-build/Dockerfile | 13 + tools/ci-scripts/check-book | 14 ++ 49 files changed, 396 insertions(+), 587 deletions(-) delete mode 100644 design/src/server/pokemon_service.md create mode 100755 tools/ci-scripts/check-book diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3ce1bd51c..fa47463eab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -111,6 +111,8 @@ jobs: runner: ubuntu-latest - action: check-style-and-lints runner: ubuntu-latest + - action: check-book + runner: ubuntu-latest - action: check-tools runner: smithy_ubuntu-latest_8-core - action: check-deterministic-codegen diff --git a/ci.mk b/ci.mk index ecf6a3363c..a7022355e6 100644 --- a/ci.mk +++ b/ci.mk @@ -52,6 +52,10 @@ check-aws-sdk-smoketest-unit-tests: generate-aws-sdk-smoketest check-aws-sdk-standalone-integration-tests: generate-aws-sdk-smoketest $(CI_ACTION) $@ $(ARGS) +.PHONY: check-book +check-book: check-rust-runtimes + $(CI_ACTION) $@ $(ARGS) + .PHONY: check-client-codegen-integration-tests check-client-codegen-integration-tests: $(CI_ACTION) $@ $(ARGS) diff --git a/design/book.toml b/design/book.toml index f8cfdd021f..3c6fffdaf5 100644 --- a/design/book.toml +++ b/design/book.toml @@ -1,9 +1,12 @@ [book] +title = "Smithy Rust" authors = ["Russell Cohen", "aws-sdk-rust@amazon.com"] language = "en" multilingual = false src = "src" -title = "AWS Rust SDK Design" + +[rust] +edition = "2021" [preprocessor.mermaid] command = "mdbook-mermaid" diff --git a/design/src/SUMMARY.md b/design/src/SUMMARY.md index 6b3510a643..ea3bcb65c9 100644 --- a/design/src/SUMMARY.md +++ b/design/src/SUMMARY.md @@ -24,7 +24,6 @@ - [Accessing Un-modelled Data](./server/from_parts.md) - [The Anatomy of a Service](./server/anatomy.md) - [Generating Common Service Code](./server/code_generation.md) - - [Generating the Pokémon Service](./server/pokemon_service.md) - [RFCs](./rfcs/overview.md) - [RFC-0001: Sharing configuration between multiple clients](./rfcs/rfc0001_shared_config.md) diff --git a/design/src/client/identity_and_auth.md b/design/src/client/identity_and_auth.md index e6cfb2cdac..87aa8b9c66 100644 --- a/design/src/client/identity_and_auth.md +++ b/design/src/client/identity_and_auth.md @@ -85,7 +85,7 @@ unmaintainable levels if each configurable implementation in it was made generic These traits look like this: -```rust +```rust,ignore #[derive(Clone, Debug)] pub struct HttpAuthOption { scheme_id: &'static str, @@ -123,7 +123,7 @@ will need to understand what the concrete data type underlying that identity is. uses a `Arc` to represent the actual identity data so that generics are not needed in the traits: -```rust +```rust,ignore #[derive(Clone, Debug)] pub struct Identity { data: Arc, @@ -136,7 +136,7 @@ rather than `Box`. This also reduces the allocations required. The signer implem will use downcasting to access the identity data types they understand. For example, with AWS SigV4, it might look like the following: -```rust +```rust,ignore fn sign_request( &self, request: &mut HttpRequest, @@ -162,7 +162,7 @@ to verify that that type is that trait is lost at compile time (a `std::any::Typ about the concrete type). In an ideal world, it would be possible to extract the expiration like this: -```rust +```rust,ignore pub trait ExpiringIdentity { fn expiration(&self) -> SystemTime; } diff --git a/design/src/client/orchestrator.md b/design/src/client/orchestrator.md index b773d5b13b..d23088dcc5 100644 --- a/design/src/client/orchestrator.md +++ b/design/src/client/orchestrator.md @@ -90,7 +90,7 @@ In designing the orchestrator, we sought to solve the problems we had with the o *The type signatures for the old client and its `call` method:* -```rust +```rust,ignore impl Client where C: bounds::SmithyConnector, @@ -136,7 +136,7 @@ where *The type signature for the new `orchestrate` method:* -```rust +```rust,ignore pub async fn orchestrate( input: Input, runtime_plugins: &RuntimePlugins, @@ -153,7 +153,7 @@ I'm glad you asked. Generally, when you need traits, but you aren't willing to u So, what are `Input` and `Output`? They're our own special flavor of a boxed trait object. -```rust +```rust,ignore pub type Input = TypeErasedBox; pub type Output = TypeErasedBox; pub type Error = TypeErasedBox; diff --git a/design/src/contributing/writing_and_debugging_a_low-level_feature_that_relies_on_HTTP.md b/design/src/contributing/writing_and_debugging_a_low-level_feature_that_relies_on_HTTP.md index 8a15f29f4c..3e9fa6a767 100644 --- a/design/src/contributing/writing_and_debugging_a_low-level_feature_that_relies_on_HTTP.md +++ b/design/src/contributing/writing_and_debugging_a_low-level_feature_that_relies_on_HTTP.md @@ -25,7 +25,7 @@ checksum and attaching it either as a header or a trailer.) Here's [an example from the QLDB SDK of creating a body] from inputs and inserting it into the request to be sent: -```rust +```rust,ignore let body = aws_smithy_http::body::SdkBody::from( crate::operation_ser::serialize_operation_crate_operation_send_command(&self)?, ); @@ -52,7 +52,7 @@ body until you've sent the request. Any metadata that needs to be calculated by trailers. Additionally, some metadata, like `Content-Length`, can't be sent as a trailer at all. [MDN maintains a helpful list] of metadata that can only be sent as a header. -```rust +```rust,ignore // When trailers are set, we must send an AWS-specific header that lists them named `x-amz-trailer`. // For example, when sending a SHA256 checksum as a trailer, // we have to send an `x-amz-trailer` header telling the service to watch out for it: @@ -73,7 +73,7 @@ a request body for `aws-chunked` requires us to know the length of each chunk we have to prefix each chunk with its size in bytes, represented by one or more [hexadecimal] digits. To close the body, we send a final chunk with a zero. For example, the body "Hello world" would look like this when encoded: -``` +```text B\r\n Hello world\r\n 0\r\n @@ -120,7 +120,7 @@ When using `aws-chunked` encoding, the trailers have to be appended to the body relying on the `poll_trailers` method. The working `http_body::Body` implementation of an `aws-chunked` encoded body looked like this: -```rust +```rust,ignore impl Body for AwsChunkedBody { type Data = Bytes; type Error = aws_smithy_http::body::Error; @@ -222,14 +222,14 @@ been read. be visible when printing with the `Debug` impl. Case in point was an error I was getting because of the `is_end_stream` issue. When `Debug` printed, the error looked like this: - ``` + ```rust,ignore DispatchFailure(ConnectorError { err: hyper::Error(User(Body), hyper::Error(BodyWriteAborted)), kind: User }) ``` That wasn't too helpful for me on its own. I looked into the `hyper` source code and found that the `Display` impl contained a helpful message, so I matched into the error and printed the `hyper::Error` with the `Display` impl: - ``` + ```markdown user body write aborted: early end, expected 2 more bytes' ``` @@ -239,7 +239,7 @@ been read. being sent out by the SDK as I was working on it. The Rust SDK supports setting endpoints for request. This is often used to send requests to something like [LocalStack], but I used it to send request to `localhost` instead: - ```rust + ```rust,ignore #[tokio::test] async fn test_checksum_on_streaming_request_against_s3() { let sdk_config = aws_config::from_env() @@ -262,7 +262,7 @@ been read. The echo server was based off of an [axum] example and looked like this: - ```rust + ```rust,ignore use axum::{ body::{Body, Bytes}, http::{request::Parts, Request, StatusCode}, diff --git a/design/src/overview.md b/design/src/overview.md index 975532c614..6ae40d7358 100644 --- a/design/src/overview.md +++ b/design/src/overview.md @@ -13,7 +13,7 @@ The design builds on the learnings, ideas, hard work, and GitHub issues of the 1 The Rust SDK is "modular" meaning that each AWS service is its own crate. Each crate provides two layers to access the service: 1. The "fluent" API. For most use cases, a high level API that ties together connection management and serialization will be the quickest path to success. -```rust +```rust,ignore #[tokio::main] async fn main() { let client = dynamodb::Client::from_env(); @@ -27,7 +27,7 @@ async fn main() { 2. The "low-level" API: It is also possible for customers to assemble the pieces themselves. This offers more control over operation construction & dispatch semantics: -```rust +```rust,ignore #[tokio::main] async fn main() { let conf = dynamodb::Config::builder().build(); diff --git a/design/src/rfcs/rfc0001_shared_config.md b/design/src/rfcs/rfc0001_shared_config.md index bd2a04ebbe..b0abd77500 100644 --- a/design/src/rfcs/rfc0001_shared_config.md +++ b/design/src/rfcs/rfc0001_shared_config.md @@ -37,7 +37,7 @@ tokio = { version = "1", features = ["full"] } Let's write a small example project to list tables: -```rust +```rust,ignore use aws_sdk_dynamodb as dynamodb; #[tokio::main] @@ -58,7 +58,7 @@ Next, we'll explore some other ways to configure the SDK. Perhaps you want to ov environment with your region. In this case, we'll want more control over how we load config, using `aws_config::from_env()` directly: -```rust +```rust,ignore use aws_sdk_dynamodb as dynamodb; #[tokio::main] @@ -88,7 +88,7 @@ tokio = { version = "1", features = ["full"] } Then, we can use the shared configuration to build both service clients. The region override will apply to both clients: -```rust +```rust,ignore use aws_sdk_dynamodb as dynamodb; use aws_sdk_polly as polly; @@ -135,7 +135,7 @@ To do this, implement the `ProvideCredentials` trait. > NOTE: `aws_types::Credentials` already implements `ProvideCredentials`. If you want to use the SDK with static credentials, you're already done! -```rust +```rust,ignore use aws_types::credentials::{ProvideCredentials, provide_credentials::future, Result}; struct MyCustomProvider; @@ -160,7 +160,7 @@ impl ProvideCredentials for MyCustomProvider { After writing your custom provider, you'll use it in when constructing the configuration: -```rust +```rust,ignore #[tokio::main] async fn main() { let config = aws_config::from_env().credentials_provider(MyCustomProvider).load().await; @@ -192,7 +192,7 @@ however, they won't have any default resolvers built in. Each AWS config will im This RFC proposes adding region and credentials providers support to the shared config. A future RFC will propose integration with HTTP settings, HTTPs connectors, and async sleep. -```rust +```rust,ignore struct Config { // private fields ... @@ -239,7 +239,7 @@ uses `Config` that is incompatible, they will get confusing compiler errors. An example of a problematic set of dependent versions: -``` +```markdown ┌─────────────────┐ ┌───────────────┐ │ aws-types = 0.1 │ │aws-types= 0.2 │ └─────────────────┘ └───────────────┘ diff --git a/design/src/rfcs/rfc0002_http_versions.md b/design/src/rfcs/rfc0002_http_versions.md index 3c8b87ab6e..1acfe48f39 100644 --- a/design/src/rfcs/rfc0002_http_versions.md +++ b/design/src/rfcs/rfc0002_http_versions.md @@ -45,7 +45,7 @@ around the underlying connector. When constructing operation builders, this hand given to the new builder instances so that their `send()` calls can initiate a request. The generated fluent client code ends up looking like this: -```rust +```rust,ignore struct Handle { client: aws_smithy_client::Client, conf: crate::Config, @@ -59,7 +59,7 @@ pub struct Client { Functions are generated per operation on the fluent client to gain access to the individual operation builders. For example: -```rust +```rust,ignore pub fn assume_role(&self) -> fluent_builders::AssumeRole { fluent_builders::AssumeRole::new(self.handle.clone()) } @@ -68,7 +68,7 @@ pub fn assume_role(&self) -> fluent_builders::AssumeRole { The fluent operation builders ultimately implement `send()`, which chooses the one and only Smithy client out of the handle to make the request with: -```rust +```rust,ignore pub struct AssumeRole { handle: std::sync::Arc>, inner: crate::input::assume_role_input::Builder, @@ -86,7 +86,7 @@ impl AssumeRole where ...{ Smithy clients are constructed from a connector, as shown: -```rust +```rust,ignore let connector = Builder::new() .https() .middleware(...) @@ -117,7 +117,7 @@ so that alternate HTTP implementations can be used, or so that a fake implementa To accomplish this, `SharedConfig` will have a `make_connector` member. A customer would configure it as such: -```rust +```rust,ignore let config = some_shared_config_loader() .with_http_settings(my_http_settings) .with_make_connector(|reqs: &MakeConnectorRequirements| { @@ -170,7 +170,7 @@ Smithy client. This cache needs to be adjusted to: To accomplish this, the `Handle` will hold a cache that is optimized for many reads and few writes: -```rust +```rust,ignore #[derive(Debug, Hash, Eq, PartialEq)] struct ConnectorKey { http_settings: HttpSettings, @@ -194,7 +194,7 @@ For cases where it is not, the custom connector type can host its own `dyn Trait The `HttpRequirements` struct will hold `HttpSettings` as copy-on-write so that it can be used for cache lookup without having to clone `HttpSettings`: -```rust +```rust,ignore struct HttpRequirements<'a> { http_settings: Cow<'a, HttpSettings>, http_version: HttpVersion, @@ -223,7 +223,7 @@ HTTP setting overrides are implemented. This doc is not attempting to solve that In the fluent client, this will look as follows: -```rust +```rust,ignore impl AssumeRole where ... { pub async fn send(self) -> Result> where ... { let input = self.create_input()?; diff --git a/design/src/rfcs/rfc0003_presigning_api.md b/design/src/rfcs/rfc0003_presigning_api.md index e8c6cef4bc..c75230751c 100644 --- a/design/src/rfcs/rfc0003_presigning_api.md +++ b/design/src/rfcs/rfc0003_presigning_api.md @@ -37,7 +37,7 @@ default to `SystemTime::now()`. Construction `PresigningConfig` can be done with a builder, but a `PresigningConfig::expires_in` convenience function will be provided to bypass the builder for the most frequent use-case. -```rust +```rust,ignore #[non_exhaustive] #[derive(Debug, Clone)] pub struct PresigningConfig { @@ -85,7 +85,7 @@ The generated fluent builders for operations that support presigning will have a in addition to `send()` that will return a presigned URL rather than sending the request. For S3's GetObject, the usage of this will look as follows: -```rust +```rust,ignore let config = aws_config::load_config_from_environment().await; let client = s3::Client::new(&config); let presigning_config = PresigningConfig::expires_in(Duration::from_secs(86400)); @@ -123,7 +123,7 @@ Even though generating a presigned URL through the fluent client doesn't necessi it will be clearer that this is the case by allowing the creation of presigned URLs directly from an input. This would look as follows: -```rust +```rust,ignore let config = aws_config::load_config_from_environment().await; let presigning_config = PresigningConfig::expires_in(Duration::from_secs(86400)); let presigned: PresignedRequest = GetObjectInput::builder() diff --git a/design/src/rfcs/rfc0004_retry_behavior.md b/design/src/rfcs/rfc0004_retry_behavior.md index ee3672ca33..3da28afdc7 100644 --- a/design/src/rfcs/rfc0004_retry_behavior.md +++ b/design/src/rfcs/rfc0004_retry_behavior.md @@ -39,7 +39,7 @@ _The default number of retries is 3 as specified in the [AWS SDKs and Tools Refe Here's an example app that logs your AWS user's identity -```rust +```rust,ignore use aws_sdk_sts as sts; #[tokio::main] @@ -66,7 +66,7 @@ cargo run Here's an example app that creates a shared config with custom retry behavior and then logs your AWS user's identity -```rust +```rust,ignore use aws_sdk_sts as sts; use aws_types::retry_config::StandardRetryConfig; @@ -86,7 +86,7 @@ async fn main() -> Result<(), sts::Error> { Here's an example app that creates a service-specific config with custom retry behavior and then logs your AWS user's identity -```rust +```rust,ignore use aws_sdk_sts as sts; use aws_types::retry_config::StandardRetryConfig; @@ -107,7 +107,7 @@ async fn main() -> Result<(), sts::Error> { Here's an example app that creates a shared config that disables retries and then logs your AWS user's identity -```rust +```rust,ignore use aws_sdk_sts as sts; use aws_types::config::Config; @@ -125,7 +125,7 @@ async fn main() -> Result<(), sts::Error> { Retries can also be disabled by explicitly passing the `RetryConfig::NoRetries` enum variant to the `retry_config` builder method: -```rust +```rust,ignore use aws_sdk_sts as sts; use aws_types::retry_config::RetryConfig; diff --git a/design/src/rfcs/rfc0008_paginators.md b/design/src/rfcs/rfc0008_paginators.md index a387af4b09..fc0a0c8b6e 100644 --- a/design/src/rfcs/rfc0008_paginators.md +++ b/design/src/rfcs/rfc0008_paginators.md @@ -23,7 +23,7 @@ original input, but with the field marked `inputToken` to the value of `outputTo Usage example: -```rust +```rust,ignore let paginator = client .list_tables() .paginate() @@ -41,7 +41,7 @@ Paginators are lazy and only retrieve pages when polled by a client. Paginators will be generated into the `paginator` module of service crates. Currently, paginators are _not_ feature gated, but this could be considered in the future. A `paginator` struct captures 2 pieces of data: -```rust +```rust,ignore // dynamodb/src/paginator.rs struct ListTablesPaginator { // holds the low-level client and configuration @@ -55,7 +55,7 @@ struct ListTablesPaginator { In addition to the basic usage example above, when `pageSize` is modeled, customers can specify the page size during pagination: -```rust +```rust,ignore let mut tables = vec![]; let mut pages = client .list_tables() @@ -75,7 +75,7 @@ enables demand driven execution of a closure. A rendezvous channel is used which When modeled by Smithy, `page_size` which automatically sets the appropriate page_size parameter and `items()` which returns an automatically flattened paginator are also generated. **Note**: `page_size` directly sets the modeled parameter on the internal builder. This means that a value set for page size will override any previously set value for that field. -```rust +```rust,ignore // Generated paginator for ListTables impl ListTablesPaginator { @@ -166,7 +166,7 @@ regressions in the generated code which would break users. The `builders` generated by ergonomic clients will gain the following method, if they represent an operation that implements the `Paginated` trait: -```rust +```rust,ignore /// Create a paginator for this request /// /// Paginators are used by calling [`send().await`](crate::paginator::ListTablesPaginator::send) which returns a [`Stream`](tokio_stream::Stream). diff --git a/design/src/rfcs/rfc0010_waiters.md b/design/src/rfcs/rfc0010_waiters.md index f738f0f357..3d0f80b291 100644 --- a/design/src/rfcs/rfc0010_waiters.md +++ b/design/src/rfcs/rfc0010_waiters.md @@ -10,7 +10,7 @@ than building out an entire polling mechanism manually. At the highest level, a waiter is a simple polling loop (pseudo-Rust): -```rust +```rust,ignore // Track state that contains the number of attempts made and the previous delay let mut state = initial_state(); @@ -74,7 +74,7 @@ Waiter API To invoke a waiter, customers will only need to invoke a single function on the AWS Client. For example, if waiting for a S3 bucket to exist, it would look like the following: -```rust +```rust,ignore // Request bucket creation client.create_bucket() .bucket_name("my-bucket") @@ -93,7 +93,7 @@ that will start the polling and return a future. To avoid name conflicts with other API methods, the waiter functions can be added to the client via trait: -```rust +```rust,ignore pub trait WaitUntilBucketExists { fn wait_until_bucket_exists(&self) -> crate::waiter::bucket_exists::Builder; } @@ -107,7 +107,7 @@ Waiter Implementation A waiter trait implementation will merely return a fluent builder: -```rust +```rust,ignore impl WaitUntilBucketExists for Client { fn wait_until_bucket_exists(&self) -> crate::waiter::bucket_exists::Builder { crate::waiter::bucket_exists::Builder::new() @@ -117,7 +117,7 @@ impl WaitUntilBucketExists for Client { This builder will have a short `send()` function to kick off the actual waiter implementation: -```rust +```rust,ignore impl Builder { // ... existing fluent builder codegen can be reused to create all the setters and constructor @@ -134,7 +134,7 @@ This wait function needs to, in a loop similar to the pseudo-code in the beginni convert the given input into an operation, replace the default response classifier on it with a no-retry classifier, and then determine what to do next based on that classification: -```rust +```rust,ignore pub async fn wait( handle: Arc, retry::Standard>>, input: HeadBucketInput, diff --git a/design/src/rfcs/rfc0013_body_callback_apis.md b/design/src/rfcs/rfc0013_body_callback_apis.md index b6b8e93e60..ea9b024c1d 100644 --- a/design/src/rfcs/rfc0013_body_callback_apis.md +++ b/design/src/rfcs/rfc0013_body_callback_apis.md @@ -9,7 +9,7 @@ Adding a callback API to `ByteStream` and `SdkBody` will enable developers using *Note that comments starting with '//' are not necessarily going to be included in the actual implementation and are intended as clarifying comments for the purposes of this RFC.* -```rust +```rust,ignore // in aws_smithy_http::callbacks... /// A callback that, when inserted into a request body, will be called for corresponding lifecycle events. @@ -41,7 +41,7 @@ The changes we need to make to `ByteStream`: *(The current version of `ByteStream` and `Inner` can be seen [here][ByteStream impls].)* -```rust +```rust,ignore // in `aws_smithy_http::byte_stream`... // We add a new method to `ByteStream` for inserting callbacks @@ -68,7 +68,7 @@ The changes we need to make to `SdkBody`: *(The current version of `SdkBody` can be seen [here][SdkBody impls].)* -```rust +```rust,ignore // In aws_smithy_http::body... #[pin_project] @@ -243,7 +243,7 @@ What follows is a simplified example of how this API could be used to introduce the checksum of some data and then returns the checksum of that data when `trailers` is called. This is fine because it's being used to calculate the checksum of a streaming body for a request. -```rust +```rust,ignore #[derive(Default)] struct Crc32cChecksumCallback { state: Option, diff --git a/design/src/rfcs/rfc0014_timeout_config.md b/design/src/rfcs/rfc0014_timeout_config.md index d4295f379f..52b1496ec0 100644 --- a/design/src/rfcs/rfc0014_timeout_config.md +++ b/design/src/rfcs/rfc0014_timeout_config.md @@ -99,7 +99,7 @@ Timeouts are achieved by racing a future against a `tokio::time::Sleep` future. _View [AwsMiddleware] in GitHub_ -```rust +```rust,ignore #[derive(Debug, Default)] #[non_exhaustive] pub struct AwsMiddleware; @@ -127,7 +127,7 @@ The above code is only included for context. This RFC doesn't define any timeout _View [aws_smithy_client::Client::call_raw] in GitHub_ -```rust +```rust,ignore impl Client where C: bounds::SmithyConnector, @@ -175,7 +175,7 @@ The **HTTP Request Timeout For A Single Attempt** and **HTTP Request Timeout For The resulting code would look like this: -```rust +```rust,ignore impl Client where C: bounds::SmithyConnector, diff --git a/design/src/rfcs/rfc0015_using_features_responsibly.md b/design/src/rfcs/rfc0015_using_features_responsibly.md index 2b018c64f4..336b25ca80 100644 --- a/design/src/rfcs/rfc0015_using_features_responsibly.md +++ b/design/src/rfcs/rfc0015_using_features_responsibly.md @@ -94,7 +94,7 @@ Conditionally compiling code when a feature is **not** activated can make it har One case where using `not` is acceptable is when providing a fallback when no features are set: -```rust +```rust,ignore #[cfg(feature = "rt-tokio")] pub fn default_async_sleep() -> Option> { Some(sleep_tokio()) diff --git a/design/src/rfcs/rfc0016_flexible_checksum_support.md b/design/src/rfcs/rfc0016_flexible_checksum_support.md index aa0851c619..ca906057aa 100644 --- a/design/src/rfcs/rfc0016_flexible_checksum_support.md +++ b/design/src/rfcs/rfc0016_flexible_checksum_support.md @@ -25,7 +25,7 @@ TLDR; This refactor of aws-smithy-checksums: - **Adds `fn checksum_header_name_to_checksum_algorithm`:** a function that's used in generated code when creating a checksum-validating response body. - **Add new checksum-related "body wrapping" HTTP body types**: These are defined in the `body` module and will be shown later in this RFC. -```rust +```rust,ignore // In aws-smithy-checksums/src/lib.rs //! Checksum calculation and verification callbacks @@ -489,7 +489,7 @@ When creating a checksum-validated request with an in-memory request body, we ca We will accomplish this by wrapping the `SdkBody` that requires validation within a `ChecksumBody`. Afterwards, we'll need to wrap the `ChecksumBody` in yet another layer which we'll discuss in the [`AwsChunkedBody` and `AwsChunkedBodyOptions`](#awschunkedbody-and-awschunkedbodyoptions) section. -```rust +```rust,ignore // In aws-smithy-checksums/src/body.rs use crate::{new_checksum, Checksum}; @@ -654,7 +654,7 @@ impl http_body::Body for ChecksumBody { Users may request checksum validation for response bodies. That capability is provided by `ChecksumValidatedBody`, which will calculate a checksum as the response body is being read. Once all data has been read, the calculated checksum is compared to a precalculated checksum set during body creation. If the checksums don't match, then the body will emit an error. -```rust +```rust,ignore // In aws-smithy-checksums/src/body.rs /// A response body that will calculate a checksum as it is read. If all data is read and the /// calculated checksum doesn't match a precalculated checksum, this body will emit an @@ -838,7 +838,7 @@ _**NOTES:**_ This encoding scheme is performed by `AwsChunkedBody` and configured with `AwsChunkedBodyOptions`. -```rust +```rust,ignore // In aws-http/src/content_encoding.rs use aws_smithy_checksums::body::ChecksumBody; use aws_smithy_http::body::SdkBody; @@ -1264,7 +1264,7 @@ Setting `STREAMING-UNSIGNED-PAYLOAD-TRAILER` tells the signer that we're sending We can achieve this by: - Adding a new variant to `SignableBody`: - ```rust + ```rust,ignore /// A signable HTTP request body #[derive(Debug, Clone, Eq, PartialEq)] #[non_exhaustive] @@ -1279,7 +1279,7 @@ We can achieve this by: } ``` - Updating the `CanonicalRequest::payload_hash` method to include the new `SignableBody` variant: - ```rust + ```rust,ignore fn payload_hash<'b>(body: &'b SignableBody<'b>) -> Cow<'b, str> { // Payload hash computation // @@ -1300,7 +1300,7 @@ We can achieve this by: } ``` - *(in generated code)* Inserting the `SignableBody` into the request property bag when making a checksum-verified streaming request: - ```rust + ```rust,ignore if self.checksum_algorithm.is_some() { request .properties_mut() @@ -1315,7 +1315,7 @@ It's possible to send `aws-chunked` requests where each chunk is signed individu In order to avoid writing lots of Rust in Kotlin, I have implemented request and response building functions as inlineables: - Building checksum-validated requests with in-memory request bodies: - ```rust + ```rust,ignore // In aws/rust-runtime/aws-inlineable/src/streaming_body_with_checksum.rs /// Given a `&mut http::request::Request`, and checksum algorithm name, calculate a checksum and /// then modify the request to include the checksum as a header. @@ -1344,7 +1344,7 @@ In order to avoid writing lots of Rust in Kotlin, I have implemented request and } ``` - Building checksum-validated requests with streaming request bodies: - ```rust + ```rust,ignore /// Given an `http::request::Builder`, `SdkBody`, and a checksum algorithm name, return a /// `Request` with checksum trailers where the content is `aws-chunked` encoded. pub fn build_checksum_validated_request_with_streaming_body( @@ -1395,7 +1395,7 @@ In order to avoid writing lots of Rust in Kotlin, I have implemented request and } ``` - Building checksum-validated responses: - ```rust + ```rust,ignore /// Given a `Response`, checksum algorithm name, and pre-calculated checksum, return a /// `Response` where the body will processed with the checksum algorithm and checked /// against the pre-calculated checksum. diff --git a/design/src/rfcs/rfc0017_customizable_client_operations.md b/design/src/rfcs/rfc0017_customizable_client_operations.md index e91f4d625b..260635b799 100644 --- a/design/src/rfcs/rfc0017_customizable_client_operations.md +++ b/design/src/rfcs/rfc0017_customizable_client_operations.md @@ -10,7 +10,7 @@ the SDK has no easy way to accomplish this. At time of writing, the lower level client has to be used to create an operation, and then the HTTP request augmented on that operation type. For example: -```rust +```rust,ignore let input = SomeOperationInput::builder().some_value(5).build()?; let operation = { @@ -52,7 +52,7 @@ The code generated fluent builders returned by the fluent client should have a m similar to `send`, but that returns a customizable request. The customer experience should look as follows: -```rust +```rust,ignore let response = client.some_operation() .some_value(5) .customize() @@ -69,7 +69,7 @@ let response = client.some_operation() This new async `customize` method would return the following: -```rust +```rust,ignore pub struct CustomizableOperation { handle: Arc, operation: Operation, @@ -137,7 +137,7 @@ HTTP request. The `CustomizableOperation` type will then mirror these functions so that the experience can look as follows: -```rust +```rust,ignore let mut operation = client.some_operation() .some_value(5) .customize() @@ -167,7 +167,7 @@ Alternatively, the name `build` could be used, but this increases the odds that customers won't realize that they can call `send` directly, and then call a longer `build`/`send` chain when customization isn't needed: -```rust +```rust,ignore client.some_operation() .some_value() .build() // Oops, didn't need to do this @@ -177,7 +177,7 @@ client.some_operation() vs. -```rust +```rust,ignore client.some_operation() .some_value() .send() diff --git a/design/src/rfcs/rfc0018_logging_sensitive.md b/design/src/rfcs/rfc0018_logging_sensitive.md index 4ae6a27dd3..9598c823c4 100644 --- a/design/src/rfcs/rfc0018_logging_sensitive.md +++ b/design/src/rfcs/rfc0018_logging_sensitive.md @@ -46,7 +46,7 @@ Each of these configurable parts must therefore be logged cautiously. It would be unfeasible to forbid the logging of sensitive data all together using the type system. With the current API, the customer will always have an opportunity to log a request containing sensitive data before it enters the `Service>` that we provide to them. -```rust +```rust,ignore // The API provides us with a `Service>` let app: Router = OperationRegistryBuilder::default().build().expect("unable to build operation registry").into(); @@ -88,7 +88,7 @@ Developers might want to observe sensitive data for debugging purposes. It shoul To prevent excessive branches such as -```rust +```rust,ignore if cfg!(feature = "unredacted-logging") { debug!(%data, "logging here"); } else { @@ -98,7 +98,7 @@ if cfg!(feature = "unredacted-logging") { the following wrapper should be provided from a runtime crate: -```rust +```rust,ignore pub struct Sensitive(T); impl Debug for Sensitive @@ -130,7 +130,7 @@ where In which case the branch above becomes -```rust +```rust,ignore debug!(sensitive_data = %Sensitive(data)); ``` @@ -168,7 +168,7 @@ structure Stocked { should generate the following -```rust +```rust,ignore // NOTE: This code is intended to show behavior - it does not compile pub struct InventoryLogging { @@ -226,7 +226,7 @@ This logging middleware should be applied outside of the [OperationHandler](http An easy position to apply the logging middleware is illustrated below in the form of `Logging{Operation}::new`: -```rust +```rust,ignore let empty_operation = LoggingEmptyOperation::new(operation(registry.empty_operation)); let get_pokemon_species = LoggingPokemonSpecies::new(operation(registry.get_pokemon_species)); let get_server_statistics = LoggingServerStatistics::new(operation(registry.get_server_statistics)); @@ -273,7 +273,7 @@ Request extensions can be used to adjoin data to a Request as it passes through These can be used to provide data to middleware interested in logging potentially sensitive data. -```rust +```rust,ignore struct Sensitivity { /* Data concerning which parts of the request are sensitive */ } @@ -301,7 +301,7 @@ impl Service> for Middleware { A middleware layer must be code generated (much in the same way as the logging middleware) which is dedicated to inserting the `Sensitivity` struct into the extensions of each incoming request. -```rust +```rust,ignore impl Service> for SensitivityInserter where S: Service> @@ -333,7 +333,7 @@ where It is possible that sensitivity is a parameter passed to middleware during construction. This is similar in nature to [Use Request Extensions](#use-request-extensions) except that the `Sensitivity` is passed to middleware during construction. -```rust +```rust,ignore struct Middleware { inner: S, sensitivity: Sensitivity diff --git a/design/src/rfcs/rfc0019_event_streams_errors.md b/design/src/rfcs/rfc0019_event_streams_errors.md index 35274276f0..83311d902e 100644 --- a/design/src/rfcs/rfc0019_event_streams_errors.md +++ b/design/src/rfcs/rfc0019_event_streams_errors.md @@ -16,7 +16,7 @@ The user experience if this RFC is implemented ---------------------------------------------- In the current version of smithy-rs, customers who want to use errors in event streams need to use them as so: -```rust +```rust,ignore stream! { yield Ok(EventStreamUnion::ErrorVariant ...) } @@ -27,7 +27,7 @@ it does not signal termination and thus does not complete the stream. This RFC proposes to make changes to: * terminate the stream upon receiving a modeled error * change the API so that customers will write their business logic in a more Rust-like experience: -```rust +```rust,ignore stream! { yield Err(EventStreamUnionError::ErrorKind ...) } @@ -130,7 +130,7 @@ Wherever irrelevant, documentation and other lines are stripped out from the cod The error in `AttemptCapturingPokemonEvent` is modeled as follows. On the client, -```rust +```rust,ignore pub struct AttemptCapturingPokemonEventError { pub kind: AttemptCapturingPokemonEventErrorKind, pub(crate) meta: aws_smithy_types::Error, @@ -142,7 +142,7 @@ pub enum AttemptCapturingPokemonEventErrorKind { ``` On the server, -```rust +```rust,ignore pub enum AttemptCapturingPokemonEventError { MasterBallUnsuccessful(crate::error::MasterBallUnsuccessful), } @@ -160,7 +160,7 @@ On the other side, the `Receiver<>` needs to terminate the stream upon [receivin A terminated stream has [no more data](https://github.com/awslabs/smithy-rs/blob/8f7e03ff8a84236955a65dba3d21c4bdbf17a9f4/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs#L38) and will always be a [bug](https://github.com/awslabs/smithy-rs/blob/8f7e03ff8a84236955a65dba3d21c4bdbf17a9f4/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs#L54) to use it. An example of how errors can be used on clients, extracted from [this test](https://github.com/awslabs/smithy-rs/blob/8f7e03ff8a84236955a65dba3d21c4bdbf17a9f4/rust-runtime/aws-smithy-http-server/examples/pokemon_service/tests/simple_integration_test.rs#L100): -```rust +```rust,ignore yield Err(AttemptCapturingPokemonEventError::new( AttemptCapturingPokemonEventErrorKind::MasterBallUnsuccessful(MasterBallUnsuccessful::builder().build()), Default::default() diff --git a/design/src/rfcs/rfc0020_service_builder.md b/design/src/rfcs/rfc0020_service_builder.md index 35bf97f1f9..c6e18b9c10 100644 --- a/design/src/rfcs/rfc0020_service_builder.md +++ b/design/src/rfcs/rfc0020_service_builder.md @@ -45,7 +45,7 @@ We have purposely omitted details from the model that are unimportant to describ Here is a quick example of what a customer might write when using the service builder: -```rust +```rust,ignore async fn handler0(input: Operation0Input) -> Operation0Output { todo!() } @@ -71,7 +71,7 @@ During the survey we touch on the major mechanisms used to achieve this API. A core concept in the service builder is the `Handler` trait: -```rust +```rust,ignore pub trait Handler { async fn call(self, req: http::Request) -> http::Response; } @@ -81,7 +81,7 @@ Its purpose is to provide an even interface over closures of the form `FnOnce({O We generate `Handler` implementations for said closures in [ServerOperationHandlerGenerator.kt](https://github.com/awslabs/smithy-rs/blob/458eeb63b95e6e1e26de0858457adbc0b39cbe4e/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt): -```rust +```rust,ignore impl Handler<(), Operation0Input> for Fun where Fun: FnOnce(Operation0Input) -> Fut, @@ -122,7 +122,7 @@ The `request.extensions().get::()` present in the `Fun: FnOnce(Operation0Inpu To convert the closures described above into a `Service` an `OperationHandler` is used: -```rust +```rust,ignore pub struct OperationHandler { handler: H, } @@ -151,7 +151,7 @@ The service builder we provide to the customer is the `OperationRegistryBuilder` Currently, the reference model would generate the following `OperationRegistryBuilder` and `OperationRegistry`: -```rust +```rust,ignore pub struct OperationRegistryBuilder { operation1: Option, operation2: Option, @@ -165,7 +165,7 @@ pub struct OperationRegistry { The `OperationRegistryBuilder` includes a setter per operation, and a fallible `build` method: -```rust +```rust,ignore impl OperationRegistryBuilder { pub fn operation0(mut self, value: Op0) -> Self { self.operation0 = Some(value); @@ -188,7 +188,7 @@ impl OperationRegistryBuilder { The `OperationRegistry` does not include any methods of its own, however it does enjoy a `From for Router` implementation: -```rust +```rust,ignore impl From> for Router where Op0: Handler, @@ -223,7 +223,7 @@ where The [aws_smithy_http::routing::Router](https://github.com/awslabs/smithy-rs/blob/458eeb63b95e6e1e26de0858457adbc0b39cbe4e/rust-runtime/aws-smithy-http-server/src/routing/mod.rs#L58-L60) provides the protocol aware routing of requests to their target , it exists as -```rust +```rust,ignore pub struct Route { service: Box>, } @@ -242,7 +242,7 @@ pub struct Router { and enjoys the following `Service` implementation: -```rust +```rust,ignore impl Service for Router { type Response = http::Response; @@ -268,7 +268,7 @@ impl Service for Router Along side the protocol specific constructors, `Router` includes a `layer` method. This provides a way for the customer to apply a `tower::Layer` to all routes. For every protocol, `Router::layer` has the approximately the same behavior: -```rust +```rust,ignore let new_routes = old_routes .into_iter() // Apply the layer @@ -299,7 +299,7 @@ To identify where the implementations should differ we should classify in what w In `axum` there is a notion of [Extractor](https://docs.rs/axum/latest/axum/extract/index.html), which allows the customer to easily define a decomposition of an incoming `http::Request` by specifying the arguments to the handlers. For example, -```rust +```rust,ignore async fn request(Json(payload): Json, Query(params): Query>, headers: HeaderMap) { todo!() } @@ -350,7 +350,7 @@ The Smithy model not only specifies the `http::Request` decomposition and `http: This is in contrast to `axum`, where the user specifies the routing by use of various combinators included on the `axum::Router`, applied to other `tower::Service`s. In an `axum` application one might encounter the following code: -```rust +```rust,ignore let user_routes = Router::new().route("/:id", /* service */); let team_routes = Router::new().route("/", /* service */); @@ -366,7 +366,7 @@ Note that, in `axum` handlers are eagerly converted to a `tower::Service` (via ` Introducing state to handlers in `axum` is done in the same way as `smithy-rs`, described briefly in [Handlers](#handlers) - a layer is used to insert state into incoming `http::Request`s and the `Handler` implementation pops it out of the type map layer. In `axum`, if a customer wanted to scope state to all routes within `/users/` they are able to do the following: -```rust +```rust,ignore async fn handler(Extension(state): Extension) -> /* Return Type */ {} let api_routes = Router::new() @@ -390,7 +390,7 @@ As described in [Builder](#builder), the customer is required to perform two con As described in [Builder](#builder), the `OperationRegistryBuilder::build` method is fallible - it yields a runtime error when one of the handlers has not been set. -```rust +```rust,ignore pub fn build( self, ) -> Result, OperationRegistryBuilderError> { @@ -403,7 +403,7 @@ As described in [Builder](#builder), the `OperationRegistryBuilder::build` metho We can do away with fallibility if we allow for on `Op0`, `Op1` to switch types during build and remove the `Option` from around the fields. The `OperationRegistryBuilder` then becomes -```rust +```rust,ignore struct OperationRegistryBuilder { operation_0: Op0, operation_1: Op1 @@ -444,19 +444,19 @@ The customer will now get a compile time error rather than a runtime error when To construct a `Router`, the customer must either give a type ascription -```rust +```rust,ignore let app: Router = /* Service builder */.into(); ``` or be explicit about the `Router` namespace -```rust +```rust,ignore let app = Router::from(/* Service builder */); ``` If we switch from a `From for Router` to a `build` method on `OperationRegistry` the customer may simply -```rust +```rust,ignore let app = /* Service builder */.build(); ``` @@ -478,7 +478,7 @@ Throughout this section we purposely ignore the existence of handlers accepting It's possible to make progress with a small changeset, by requiring the customer eagerly uses `OperationHandler::new` rather than it being applied internally within `From for Router` (see [Handlers](#handlers)). The setter would then become: -```rust +```rust,ignore pub struct OperationRegistryBuilder { operation1: Option, operation2: Option @@ -494,7 +494,7 @@ impl OperationRegistryBuilder { The API usage would then become -```rust +```rust,ignore async fn handler0(input: Operation0Input) -> Operation0Output { todo!() } @@ -514,7 +514,7 @@ Note that this requires that the `OperationRegistryBuilder` stores services, rat It is still possible to retain the original API which accepts `Handler` by introducing the following setters: -```rust +```rust,ignore impl OperationRegistryBuilder { fn operation0_handler(self, handler: H) -> OperationRegistryBuilder, Op2> { OperationRegistryBuilder { @@ -535,7 +535,7 @@ This does not solve (3), as the customer is not able to provide a `tower::Servic In order to achieve all three we model operations as middleware: -```rust +```rust,ignore pub struct Operation0 { inner: S, } @@ -572,7 +572,7 @@ A consequence of this is that the user `Operation0` must have two constructors: A brief example of how this might look: -```rust +```rust,ignore use tower::util::{ServiceFn, service_fn}; impl Operation0 { @@ -594,7 +594,7 @@ impl Operation0> { The API usage then becomes: -```rust +```rust,ignore async fn handler(input: Operation0Input) -> Operation0Output { todo!() } @@ -615,7 +615,7 @@ While [Attempt B](#approach-b-operations-as-middleware) solves all three problem Any solution which provides an `{Operation}` structure and wishes it to be accepted by multiple service builders must deal with this problem. We currently build one library per service and hence have duplicate structures when [service closures](https://awslabs.github.io/smithy/1.0/spec/core/model.html#service-closure) overlap. This means we wouldn't run into this problem today, but it would be a future obstruction if we wanted to reduce the amount of generated code. -```rust +```rust,ignore use tower::layer::util::{Stack, Identity}; use tower::util::{ServiceFn, service_fn}; @@ -709,7 +709,7 @@ Currently the `Router` stores `Box`, allows us to write: -```rust +```rust,ignore impl Router { fn layer(self, layer: &L) -> Router where @@ -775,7 +775,7 @@ This is compatible with [Protocol specific Routers](#protocol-specific-routers), With both of these changes the API would take the form: -```rust +```rust,ignore let service_0: Service0 = Service0::builder() /* use the setters */ .build() @@ -785,7 +785,7 @@ let service_0: Service0 = Service0::builder() With [Remove two-step build procedure](#remove-two-step-build-procedure), [Switch `From for Router` to a `OperationRegistry::build` method](#switch-fromoperationregistry-for-router-to-an-operationregistrybuild-method), and [Statically check for missing Handlers](#statically-check-for-missing-handlers) we obtain the following API: -```rust +```rust,ignore let service_0: Service0 = Service0::builder() /* use the setters */ .build(); @@ -795,7 +795,7 @@ let service_0: Service0 = Service0::builder() A combination of all the proposed transformations results in the following API: -```rust +```rust,ignore struct Context { /* fields */ } diff --git a/design/src/rfcs/rfc0022_error_context_and_compatibility.md b/design/src/rfcs/rfc0022_error_context_and_compatibility.md index cf03b2ed8d..c8791dbfaa 100644 --- a/design/src/rfcs/rfc0022_error_context_and_compatibility.md +++ b/design/src/rfcs/rfc0022_error_context_and_compatibility.md @@ -28,7 +28,7 @@ that this RFC will attempt to solve, and calls out what was done well, and what ### Case study: `InvalidFullUriError` To start, let's examine `InvalidFullUriError` (doc comments omitted): -```rust +```rust,ignore #[derive(Debug)] #[non_exhaustive] pub enum InvalidFullUriError { @@ -89,7 +89,7 @@ However, there are also a number of things that could be improved: Next, let's look at a much simpler error. The `ProfileParseError` is focused purely on the parsing logic for the SDK config file: -```rust +```rust,ignore #[derive(Debug, Clone)] pub struct ProfileParseError { location: Location, @@ -128,7 +128,7 @@ What could be improved: The SDK currently generates errors such as the following (from S3): -```rust +```rust,ignore #[non_exhaustive] pub enum Error { BucketAlreadyExists(BucketAlreadyExists), @@ -232,7 +232,7 @@ all errors in the public API for the Rust runtime crates and generated client cr Actionable errors are represented as enums. If an error variant has an error source or additional contextual information, it must use a separate context struct that is referenced via tuple in the enum. For example: -```rust +```rust,ignore // Good: new error types can be added in the future #[non_exhaustive] pub enum Error { @@ -296,7 +296,7 @@ adding a new error variant's source at a later date. The error `Display` implementation _must not_ include the source in its output: -```rust +```rust,ignore // Good impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -326,7 +326,7 @@ impl fmt::Display for Error { Informative errors must be represented as structs. If error messaging changes based on an underlying cause, then a private error kind enum can be used internally for this purpose. For example: -```rust +```rust,ignore #[derive(Debug)] pub struct InformativeError { some_additional_info: u32, @@ -356,13 +356,13 @@ In code where errors are logged rather than returned to the customer, the full e must be displayed. This will be made easy by placing a `DisplayErrorContext` struct in `aws-smithy-types` that is used as a wrapper to get the better error formatting: -```rust +```rust,ignore tracing::warn!(err = %DisplayErrorContext(err), "some message"); ``` This might be implemented as follows: -```rust +```rust,ignore #[derive(Debug)] pub struct DisplayErrorContext(pub E); diff --git a/design/src/rfcs/rfc0023_refine_builder.md b/design/src/rfcs/rfc0023_refine_builder.md index d93cd6bf77..1c0b6f9786 100644 --- a/design/src/rfcs/rfc0023_refine_builder.md +++ b/design/src/rfcs/rfc0023_refine_builder.md @@ -40,7 +40,7 @@ Constraints: Let's start by reviewing the API proposed in [RFC 20]. We will use the [Pokemon service] as our driving example throughout the RFC. This is what the startup code looks like: -```rust +```rust,ignore #[tokio::main] pub async fn main() { // [...] @@ -115,7 +115,7 @@ Given the above, we think that the impact of a runtime error is low enough to be Moving from a compile-time error to a runtime error does not require extensive refactoring. The definition of `PokemonServiceBuilder` goes from: -```rust +```rust,ignore pub struct PokemonServiceBuilder< Op1, Op2, @@ -145,7 +145,7 @@ pub struct PokemonServiceBuilder< to: -```rust +```rust,ignore pub struct PokemonServiceBuilder< Op1, Op2, @@ -176,7 +176,7 @@ pub struct PokemonServiceBuilder< All operation fields are now `Option`-wrapped. We introduce a new `MissingOperationsError` error to hold the names of the missing operations and their respective setter methods: -```rust +```rust,ignore #[derive(Debug)] pub struct MissingOperationsError { service_name: &'static str, @@ -208,7 +208,7 @@ We can also provide actionable suggestions: Rust beginners should be able to eas Let's take a second look at the (updated) definition of `PokemonServiceBuilder`: -```rust +```rust,ignore pub struct PokemonServiceBuilder< Op1, Op2, @@ -255,7 +255,7 @@ Let's consider a toy example: if a `check_database` flag is set to `true`, we wa The "obvious" solution would look somewhat like this: -```rust +```rust,ignore let check_database: bool = /* */; let app = if check_database { app.check_health(check_health) @@ -303,7 +303,7 @@ The developer has three options to move forward: I can't easily see a way to accomplish 1) using the current API. Pursuing 2) is straight-forward with a single conditional: -```rust +```rust,ignore let check_database: bool = /* */; let app = if check_database { app.check_health(check_health).build() @@ -314,7 +314,7 @@ let app = if check_database { It becomes more cumbersome when we have more than a single conditional: -```rust +```rust,ignore let check_database: bool = /* */; let include_cpu_statics: bool = /* */; match (check_database, include_cpu_statics) { @@ -339,7 +339,7 @@ match (check_database, include_cpu_statics) { A lot of repetition compared to the code for the "obvious" approach: -```rust +```rust,ignore let check_database: bool = /* */; let include_cpu_statics: bool = /* */; let app = if check_database { @@ -367,7 +367,7 @@ The service builder must be one of the arguments if we want to register handlers A first sketch: -```rust +```rust,ignore fn partial_setup(builder: PokemonServiceBuilder) -> PokemonServiceBuilder { /* */ } @@ -395,7 +395,7 @@ note: struct defined here, with at least 6 generic parameters: `Op1`, `Op2`, `Op We could try to nudge the compiler into inferring them: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder<_, _, _, _, _, _>, ) -> PokemonServiceBuilder<_, _, _, _, _, _> { @@ -421,7 +421,7 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures We must type it all out: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder { @@ -432,7 +432,7 @@ fn partial_setup( That compiles, at last. Let's try to register an operation handler now: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder { @@ -468,7 +468,7 @@ Can we get rid of them? Yes! Let's look at one possible approach: -```rust +```rust,ignore pub struct PokemonServiceBuilder { check_health: Option>, do_nothing: Option>, @@ -483,7 +483,7 @@ pub struct PokemonServiceBuilder { We no longer store the raw handlers inside `PokemonServiceBuilder`. We eagerly upgrade the operation handlers to a `Route` instance when they are registered with the builder. -```rust +```rust,ignore impl PokemonServiceBuilder { pub fn get_pokemon_species(mut self, handler: Handler) -> Self /* Complex trait bounds */ @@ -500,7 +500,7 @@ impl PokemonServiceBuilder { The existing API performs the upgrade when `build` is called, forcing `PokemonServiceBuilder` to store the raw handlers and keep two generic parameters around (`OpX` and `ExtsX`) for each operation. The proposed API requires plugins to be specified upfront, when creating an instance of the builder. They cannot be modified after a `PokemonServiceBuilder` instance has been built: -```rust +```rust,ignore impl PokemonService<()> { /// Constructs a builder for [`PokemonService`]. pub fn builder(plugin: Plugin) -> PokemonServiceBuilder { @@ -526,7 +526,7 @@ We have seen how cumbersome it is to break the startup logic into different func The new design prohibits the following invocation style: -```rust +```rust,ignore let plugin = ColorPlugin::new(); PokemonService::builder(plugin) // [...] @@ -548,7 +548,7 @@ There are no technical obstacles preventing us from implementing this API, but I We can provide developers with other mechanisms to register plugins for a single operation or a subset of operations without introducing ambiguity. For attaching additional plugins to a single operation, we could introduce a blanket `Pluggable` implementation for all operations in `aws-smithy-http-server`: -```rust +```rust,ignore impl Pluggable for Operation where Pl: Plugin { type Output = Operation; @@ -561,7 +561,7 @@ impl Pluggable for Operation where Pl: Plugin { @@ -631,7 +631,7 @@ The above leaves us with two unconstrained type parameters, `Operation` and `Ext Going back to the branching example: -```rust +```rust,ignore let check_database: bool = /* */; let builder = if check_database { builder.check_health(check_health) @@ -643,7 +643,7 @@ let app = builder.build(); In approach 1), we could leverage the `.boxed()` method to convert the actual `OpX` type into a `Box`, thus ensuring that both branches return the same type: -```rust +```rust,ignore let check_database: bool = /* */; let builder = if check_database { builder.check_health_operation(Operation::from_handler(check_health).boxed()) @@ -655,7 +655,7 @@ let app = builder.build(); The same cannot be done when conditionally registering a route, because on the `else` branch we cannot convert `MissingOperation` into a `Box` since `MissingOperation` doesn't implement `Upgradable` - the pillar on which we built all our compile-time safety story. -```rust +```rust,ignore // This won't compile! let builder = if check_database { builder.check_health_operation(Operation::from_handler(check_health).boxed()) @@ -666,7 +666,7 @@ let builder = if check_database { In approach 2), we can erase the whole builder in both branches when they both register a route: -```rust +```rust,ignore let check_database: bool = /* */; let boxed_builder = if check_database { builder.check_health(check_health).erase() @@ -682,7 +682,7 @@ but, like in approach 1), we will still get a type mismatch error if one of the Developers would still have to spell out all generic parameters when writing a function that takes in a builder as a parameter: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder { @@ -693,7 +693,7 @@ fn partial_setup( Writing the signature after having modified the builder becomes easier though. In approach 1), they can explicitly change the touched operation parameters to the boxed variant: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder< @@ -706,7 +706,7 @@ fn partial_setup( It becomes trickier in approach 2), since to retain compile-time safety on the builder we expect `erase` to map `MissingOperation` into `MissingOperation`. Therefore, we can't write something like this: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder, Route, Route, Route, Route, Route> { @@ -716,7 +716,7 @@ fn partial_setup( The compiler would reject it since it can't guarantee that all other operations can be erased to a `Route`. This is likely to require something along the lines of: -```rust +```rust,ignore fn partial_setup( builder: PokemonServiceBuilder, ) -> PokemonServiceBuilder<::Erased, ::Erased, ::Erased, ::Erased, ::Erased, ::Erased> @@ -742,7 +742,7 @@ We believe that the ergonomics advantages of the proposal advanced by this RFC o The `Pluggable` trait was an interesting development out of [RFC 20]: it allows you to attach methods to a service builder using an extension trait. -```rust +```rust,ignore /// An extension to service builders to add the `print()` function. pub trait PrintExt: aws_smithy_http_server::plugin::Pluggable { /// Causes all operations to print the operation name when called. @@ -760,7 +760,7 @@ pub trait PrintExt: aws_smithy_http_server::plugin::Pluggable { This pattern needs to be revisited if we want to move forward with this RFC, since new plugins cannot be registered after the builder has been instantiated. My recommendation would be to implement `Pluggable` for `PluginStack`, providing the same pattern ahead of the creation of the builder: -```rust +```rust,ignore // Currently you'd have to go for `PluginStack::new(IdentityPlugin, IdentityPlugin)`, // but that can be smoothed out even if this RFC isn't approved. let plugin_stack = PluginStack::default() diff --git a/design/src/rfcs/rfc0024_request_id.md b/design/src/rfcs/rfc0024_request_id.md index a10d5e92e7..5c06d10619 100644 --- a/design/src/rfcs/rfc0024_request_id.md +++ b/design/src/rfcs/rfc0024_request_id.md @@ -43,13 +43,13 @@ To aid customers already relying on clients' request IDs, there will be two type 1. Implementing `FromParts` for `Extension` gives customers the ability to write their handlers: -```rust +```rust,ignore pub async fn handler( input: input::Input, request_id: Extension, ) -> ... ``` -```rust +```rust,ignore pub async fn handler( input: input::Input, request_id: Extension, @@ -71,7 +71,7 @@ For privacy reasons, any format that provides service details should be avoided. The proposed format is to use UUID, version 4. A `Service` that inserts a RequestId in the extensions will be implemented as follows: -```rust +```rust,ignore impl Service> for ServerRequestIdProvider where S: Service>, @@ -96,7 +96,7 @@ For client request IDs, the process will be, in order: * Otherwise, None `Option` is used to distinguish whether a client had provided an ID or not. -```rust +```rust,ignore impl Service> for ClientRequestIdProvider where S: Service>, diff --git a/design/src/rfcs/rfc0025_constraint_traits.md b/design/src/rfcs/rfc0025_constraint_traits.md index a60b24e574..e3aaa32319 100644 --- a/design/src/rfcs/rfc0025_constraint_traits.md +++ b/design/src/rfcs/rfc0025_constraint_traits.md @@ -114,7 +114,7 @@ values and perform the _validation_ at request deserialization, we can wrapper [tuple struct] that _parses_ the string's value and is "tight" in the set of values it can accept: -```rust +```rust,ignore pub struct NiceString(String); impl TryFrom for NiceString { @@ -145,7 +145,7 @@ the [`?` operator for error propagation]. Each constrained struct will have a related `std::error::Error` enum type to signal the _first_ parsing failure, with one enum variant per applied constraint trait: -```rust +```rust,ignore pub mod nice_string { pub enum ConstraintViolation { /// Validation error holding the number of Unicode code points found, when a value between `1` and @@ -161,7 +161,7 @@ pub mod nice_string { `#[derive(Debug)]`, unless the shape also has the [`sensitive` trait], in which case we will just print the name of the struct: -```rust +```rust,ignore impl std::fmt::Debug for ConstraintViolation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut formatter = f.debug_struct("ConstraintViolation"); @@ -207,7 +207,7 @@ string Language the code the client generates when deserializing a string from a JSON document into the `Language` enum is (excerpt): -```rust +```rust,ignore ... match key.to_unescaped()?.as_ref() { "language" => { @@ -229,7 +229,7 @@ match key.to_unescaped()?.as_ref() { Note how the `String` gets converted to the enum via `Language::from()`. -```rust +```rust,ignore impl std::convert::From<&str> for Language { fn from(s: &str) -> Self { match s { diff --git a/design/src/rfcs/rfc0026_client_crate_organization.md b/design/src/rfcs/rfc0026_client_crate_organization.md index 672eed876d..5611d0ebfb 100644 --- a/design/src/rfcs/rfc0026_client_crate_organization.md +++ b/design/src/rfcs/rfc0026_client_crate_organization.md @@ -163,7 +163,7 @@ or that are required for the most frequent config changes (such as setting crede or changing the region/endpoint). Previously, the following were exported in root: -``` +```text . ├── AppName ├── Client @@ -182,7 +182,7 @@ need to be at the top-level, and will be moved into `crate::config`. `ErrorExt` `crate::error`, but `Error` will stay in the crate root so that customers that alias the SDK crate can easily reference it in their `Result`s: -```rust +```rust,ignore use aws_sdk_s3 as s3; fn some_function(/* ... */) -> Result<(), s3::Error> { diff --git a/design/src/rfcs/rfc0027_endpoints_20.md b/design/src/rfcs/rfc0027_endpoints_20.md index b5428c1ed1..90739d347b 100644 --- a/design/src/rfcs/rfc0027_endpoints_20.md +++ b/design/src/rfcs/rfc0027_endpoints_20.md @@ -96,7 +96,7 @@ global endpoint provider that can override different services. There is a single is shared across all services. However, this isn't the case for `Endpoints 2.0` where the trait actually has a generic parameter: -```rust +```rust,ignore pub trait ResolveEndpoint: Send + Sync { fn resolve_endpoint(&self, params: &T) -> Result; } @@ -129,7 +129,7 @@ This RFC proposes making the following changes: **Example: overriding the endpoint URI globally** -```rust +```rust,ignore async fn main() { let sdk_conf = aws_config::from_env().endpoint_url("http://localhost:8123").load().await; let dynamo = aws_sdk_dynamodb::Client::new(&sdk_conf); @@ -139,7 +139,7 @@ async fn main() { **Example: overriding the endpoint resolver for a service** -```rust +```rust,ignore /// Resolve to Localhost when an environment variable is set struct CustomDdbResolver; @@ -191,7 +191,7 @@ gated with documentation warning about stability. #### The Endpoint Struct -```rust +```rust,ignore // module: `aws_smithy_types::endpoint` // potential optimization to reduce / remove allocations for keys which are almost always static // this can also just be `String` @@ -221,7 +221,7 @@ pub struct Endpoint { To perform produce an `Endpoint` struct we have a generic `ResolveEndpoint` trait which will be both generic in terms of parameters and being "smithy-generic: -```rust +```rust,ignore // module: `smithy_types::endpoint` or `aws_smithy_client`?? pub trait ResolveEndpoint: Send + Sync { /// Resolves an `Endpoint` for `Params` @@ -242,7 +242,7 @@ the parameters are set, *not* how they are generated. **Example `Params` struct for S3:** -```rust +```rust,ignore #[non_exhaustive] #[derive(std::clone::Clone, std::cmp::PartialEq, std::fmt::Debug)] /// Configuration parameters for resolving the correct endpoint @@ -289,7 +289,7 @@ When an endpoint ruleset is present, Smithy will code generate an endpoint resol resolver **MUST** be a struct so that it can store/cache computations (such as a partition resolver that has compiled regexes). -```rust +```rust,ignore pub struct DefaultEndpointResolver { partition_resolver: PartitionResolver } @@ -539,7 +539,7 @@ An alternative design that could provide more flexibility is a context-aware end give context about the endpoint being returned. This would, for example, allow a customer to say explicitly "don't modify this endpoint": -```rust +```rust,ignore enum ContextualEndpoint { /// Just the URI please. Pass it into the default endpoint resolver as a baseline Uri { uri: Uri, immutable: bool }, diff --git a/design/src/rfcs/rfc0028_sdk_credential_cache_type_safety.md b/design/src/rfcs/rfc0028_sdk_credential_cache_type_safety.md index d16b24e226..719f2ef9fd 100644 --- a/design/src/rfcs/rfc0028_sdk_credential_cache_type_safety.md +++ b/design/src/rfcs/rfc0028_sdk_credential_cache_type_safety.md @@ -33,7 +33,7 @@ and wrapping the given (or default) credentials provider. The `CredentialsCache` would look as follows: -```rust +```rust,ignore enum Inner { Lazy(LazyConfig), // Eager doesn't exist today, so this is purely for illustration @@ -89,7 +89,7 @@ the `impl ProvideCredentials + 'static`. A sealed trait could be added to facili Customers that don't care about credential caching can configure credential providers without needing to think about it: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials_provider(ImdsCredentialsProvider::builder().build()) .load() @@ -99,7 +99,7 @@ let sdk_config = aws_config::from_env() However, if they want to customize the caching, they can do so without modifying the credentials provider at all (in case they want to use the default): -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials_cache(CredentialsCache::default_eager()) .load() @@ -141,7 +141,7 @@ without any caching logic, although this wouldn't be recommended and this provid in `aws-config`. Example configuration: -```rust +```rust,ignore // Compiles let sdk_config = aws_config::from_env() .credentials( @@ -162,7 +162,7 @@ let sdk_config = aws_config::from_env() Another method could be added to `ConfigLoader` that makes it easier to use the default cache: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials_with_default_cache(ImdsCredentialsProvider::new()) .load() @@ -187,7 +187,7 @@ This alternative is similar to alternative A, except that the cache trait is dis that it's more apparent when mistakenly implementing the wrong trait for a custom credentials provider. A `CacheCredentials` trait would be added that looks as follows: -```rust +```rust,ignore pub trait CacheCredentials: Send + Sync + Debug { async fn cached(&self, now: SystemTime) -> Result; } @@ -216,7 +216,7 @@ but at the same time, doesn't make it impossible to add custom caching later. The idea is that there would be a struct called `CredentialsCache` that specifies the desired caching approach for a given credentials provider: -```rust +```rust,ignore pub struct LazyCache { credentials_provider: Arc, // ... @@ -280,7 +280,7 @@ than `impl ProvideCredentials + 'static`. A sealed trait could be added to facil Configuration would look as follows: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials(CredentialsCache::default_lazy(ImdsCredentialsProvider::builder().build())) .load() @@ -293,7 +293,7 @@ a use case, then a `CredentialsCache::NoCache` variant could be made. Like alternative A, a convenience method can be added to make using the default cache easier: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials_with_default_cache(ImdsCredentialsProvider::builder().build()) .load() @@ -302,7 +302,7 @@ let sdk_config = aws_config::from_env() In the future if custom caching is added, it would look as follows: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials( CredentialsCache::custom(ImdsCredentialsProvider::builder().build(), MyCache::new()) @@ -316,7 +316,7 @@ from the config are needed to construct the cache (such as `sleep_impl`). Thus, setter would merely save off the `CredentialsCache` instance, and then when `load` is called, the complete `SharedCredentialsProvider` would be constructed: -```rust +```rust,ignore pub async fn load(self) -> SdkConfig { // ... let credentials_provider = self.credentials_cache.create_cache(sleep_impl); diff --git a/design/src/rfcs/rfc0029_new_home_for_cred_types.md b/design/src/rfcs/rfc0029_new_home_for_cred_types.md index 734ef7408c..e864bd7d5b 100644 --- a/design/src/rfcs/rfc0029_new_home_for_cred_types.md +++ b/design/src/rfcs/rfc0029_new_home_for_cred_types.md @@ -23,7 +23,7 @@ Problems -------- Here is how our attempt to implement the selected design in the preceding RFC can lead to an obstacle. Consider this code snippet we are planning to support: -```rust +```rust,ignore let sdk_config = aws_config::from_env() .credentials_cache(CredentialsCache::lazy()) .load() @@ -34,7 +34,7 @@ let client = aws_sdk_s3::Client::new(&sdk_config); A `CredentialsCache` created by `CredentialsCache::lazy()` above will internally go through three crates before the variable `client` has been created: 1. `aws-config`: after it has been passed to `aws_config::ConfigLoader::credentials_cache` -```rust +```rust,ignore // in lib.rs impl ConfigLoader { @@ -47,7 +47,7 @@ impl ConfigLoader { } ``` 2. `aws-types`: after `aws_config::ConfigLoader::load` has passed it to `aws_types::sdk_config::Builder::credentials_cache` -```rust +```rust,ignore // in sdk_config.rs impl Builder { @@ -60,7 +60,7 @@ impl Builder { } ``` 3. `aws-sdk-s3`: after `aws_sdk_s3::Client::new` has been called with the variable `sdk_config` -```rust +```rust,ignore // in client.rs impl Client { @@ -72,7 +72,7 @@ impl Client { } ``` calls -```rust +```rust,ignore // in config.rs impl From<&aws_types::sdk_config::SdkConfig> for Builder { diff --git a/design/src/rfcs/rfc0030_serialization_and_deserialization.md b/design/src/rfcs/rfc0030_serialization_and_deserialization.md index 1e4c6c3bf1..46ad1a4cb6 100644 --- a/design/src/rfcs/rfc0030_serialization_and_deserialization.md +++ b/design/src/rfcs/rfc0030_serialization_and_deserialization.md @@ -170,7 +170,7 @@ Serde can distinguish each variant without a tag as each variant's content is di Builder types and non Builder types implement `Serialize` and `Deserialize` with derive macro. Example: -```rust +```rust,ignore #[cfg_attr( all(aws-sdk-unstable, feature = "serialize"), derive(serde::Serialize) @@ -220,7 +220,7 @@ Here is an example of data types affected by this decision: We considered serializing them as bytes, however, it could take some time for a stream to reach the end, and the resulting serialized data may be too big for itself to fit into the ram. Here is an example snippet. -```rust +```rust,ignore #[allow(missing_docs)] #[cfg_attr( all(aws-sdk-unstable, feature = "serde-serialize"), @@ -272,7 +272,7 @@ SDK does not have a method that allows users to use deserialized `Input`. Thus, we add a new method `fn set_fields` to `Client` types. This method accepts inputs and replaces all parameters that `Client` has with the new one. -```rust +```rust,ignore pub fn set_fields(mut self, input_type: path::to::input_type) -> path::to::input_type { self.inner = input_type; self @@ -310,7 +310,7 @@ To clarify, this is the same approach we took on `Data Type to skip` section. Either way, we will mention this on the generated docs to avoid surprising users. e.g. -```rust +```rust,ignore #[derive(serde::Serialize, serde::Deserialize)] struct OutputV1 { string_field: Option @@ -389,7 +389,7 @@ We believe that features implemented as part of this RFC do not produce a mislea # Appendix ## [Use Case Examples](UseCaseExamples) -```rust +```rust,ignore use aws_sdk_dynamodb::{Client, Error}; async fn example(read_builder: bool) -> Result<(), Error> { diff --git a/design/src/rfcs/rfc0031_providing_fallback_credentials_on_timeout.md b/design/src/rfcs/rfc0031_providing_fallback_credentials_on_timeout.md index 98ccfad3d9..f6e76e87a8 100644 --- a/design/src/rfcs/rfc0031_providing_fallback_credentials_on_timeout.md +++ b/design/src/rfcs/rfc0031_providing_fallback_credentials_on_timeout.md @@ -27,14 +27,14 @@ We have mentioned static stability. Supporting it calls for the following functi - REQ 1: Once a credentials provider has served credentials, it should continue serving them in the event of a timeout (whether internal or external) while obtaining refreshed credentials. Today, we have the following trait method to obtain credentials: -```rust +```rust,ignore fn provide_credentials<'a>(&'a self) -> future::ProvideCredentials<'a> where Self: 'a, ``` This method returns a future, which can be raced against a timeout future as demonstrated by the following code snippet from `LazyCredentialsCache`: -```rust +```rust,ignore let timeout_future = self.sleeper.sleep(self.load_timeout); // by default self.load_timeout is 5 seconds. // --snip-- let future = Timeout::new(provider.provide_credentials(), timeout_future); @@ -76,7 +76,7 @@ Proposal To address the problem in the previous section, we propose to add a new method to the `ProvideCredentials` trait called `fallback_on_interrupt`. This method allows credentials providers to have a fallback mechanism on an external timeout and to serve credentials to users if needed. There are two options as to how it is implemented, either as a synchronous primitive or as an asynchronous primitive. #### Option A: Synchronous primitive -```rust +```rust,ignore pub trait ProvideCredentials: Send + Sync + std::fmt::Debug { // --snip-- @@ -90,7 +90,7 @@ pub trait ProvideCredentials: Send + Sync + std::fmt::Debug { - :-1: It may turn into a blocking operation if it takes longer than it should. #### Option B: Asynchronous primitive -```rust +```rust,ignore mod future { // --snip-- @@ -117,7 +117,7 @@ pub trait ProvideCredentials: Send + Sync + std::fmt::Debug { Option A cannot be reversible in the future if we are to support the use case for asynchronously retrieving the fallback credentials, whereas option B allows us to continue supporting both ready and pending futures when retrieving the fallback credentials. However, `fallback_on_interrupt` is supposed to return credentials that have been set aside in case `provide_credentials` is timed out. To express that intent, we choose option A and document that users should NOT go fetch new credentials in `fallback_on_interrupt`. The user experience for the code snippet in question will look like this once this proposal is implemented: -```rust +```rust,ignore let timeout_future = self.sleeper.sleep(self.load_timeout); // by default self.load_timeout is 5 seconds. // --snip-- let future = Timeout::new(provider.provide_credentials(), timeout_future); @@ -146,7 +146,7 @@ Almost all credentials providers do not have to implement their own `fallback_on Considering the two cases we analyzed above, implementing `CredentialsProviderChain::fallback_on_interrupt` is not so straightforward. Keeping track of whose turn in the chain it is to call `provide_credentials` when an external timeout has occurred is a challenging task. Even if we figured it out, that would still not satisfy `Case 2` above, because it was provider 1 that was actively running when the external timeout kicked in, but the chain should return credentials from provider 2, not from provider 1. With that in mind, consider instead the following approach: -```rust +```rust,ignore impl ProvideCredentials for CredentialsProviderChain { // --snip-- @@ -174,7 +174,7 @@ Alternative In this section, we will describe an alternative approach that we ended up dismissing as unworkable. Instead of `fallback_on_interrupt`, we considered the following method to be added to the `ProvideCredentials` trait: -```rust +```rust,ignore pub trait ProvideCredentials: Send + Sync + std::fmt::Debug { // --snip-- @@ -201,7 +201,7 @@ pub trait ProvideCredentials: Send + Sync + std::fmt::Debug { } ``` `provide_credentials_with_timeout` encapsulated the timeout race and allowed users to specify how long the external timeout for `provide_credentials` would be. The code snippet from `LazyCredentialsCache` then looked like -```rust +```rust,ignore let sleeper = Arc::clone(&self.sleeper); let load_timeout = self.load_timeout; // by default self.load_timeout is 5 seconds. // --snip-- @@ -217,7 +217,7 @@ let result = cache // --snip-- ``` However, implementing `CredentialsProviderChain::provide_credentials_with_timeout` quickly ran into the following problem: -```rust +```rust,ignore impl ProvideCredentials for CredentialsProviderChain { // --snip-- @@ -289,7 +289,7 @@ This a case where `CredentialsProviderChain::fallback_on_interrupt` requires the

The outermost chain is a `CredentialsProviderChain` and follows the precedence policy for `fallback_on_interrupt`. It contains a sub-chain that, in turn, contains provider 1 and provider 2. This sub-chain implements its own `fallback_on_interrupt` to realize the recency policy for fallback credentials found in provider 1 and provider 2. Conceptually, we have -``` +```rust,ignore pub struct FallbackRecencyChain { provider_chain: CredentialsProviderChain, } @@ -310,7 +310,7 @@ impl ProvideCredentials for FallbackRecencyChain { } ``` We can then compose the entire chain like so: -``` +```rust,ignore let provider_1 = /* ... */ let provider_2 = /* ... */ let provider_3 = /* ... */ diff --git a/design/src/rfcs/rfc0032_better_constraint_violations.md b/design/src/rfcs/rfc0032_better_constraint_violations.md index d8648df316..6b44d27835 100644 --- a/design/src/rfcs/rfc0032_better_constraint_violations.md +++ b/design/src/rfcs/rfc0032_better_constraint_violations.md @@ -67,7 +67,7 @@ A constrained type has a fallible constructor by virtue of it implementing the [`TryFrom`] trait. The error type this constructor may yield is known as a **constraint violation**: -```rust +```rust,ignore impl TryFrom for ConstrainedType { type Error = ConstraintViolation; @@ -90,7 +90,7 @@ structure A { Yields: -```rust +```rust,ignore /// See [`A`](crate::model::A). pub mod a { #[derive(std::cmp::PartialEq, std::fmt::Debug)] @@ -107,7 +107,7 @@ variant. Constraint violations can occur in application code: -```rust +```rust,ignore use my_server_sdk::model let res = model::a::Builder::default().build(); // We forgot to set `member`. @@ -148,7 +148,7 @@ string LengthString This produces: -```rust +```rust,ignore pub struct LengthMap( pub(crate) std::collections::HashMap, ); @@ -206,7 +206,7 @@ the server framework uses upon deserialization. Observe how `LengthMapOfLengthStringsUnconstrained` is _fully unconstrained_ and how the `try_from` constructor can yield `ConstraintViolation::Value`. -```rust +```rust,ignore pub(crate) mod length_map_of_length_strings_unconstrained { #[derive(Debug, Clone)] pub(crate) struct LengthMapOfLengthStringsUnconstrained( @@ -260,7 +260,7 @@ violation type into two types, which this RFC proposes: 2. one for use by user application code, with `pub` visibility, named `ConstraintViolation`. -```rust +```rust,ignore pub mod length_map { pub enum ConstraintViolation { Length(usize), @@ -362,7 +362,7 @@ string LengthPatternString Yields: -```rust +```rust,ignore pub struct LengthPatternString(pub(crate) std::string::String); impl LengthPatternString { @@ -446,7 +446,7 @@ Let's consider a `ConstraintViolations` type (note the plural) that represents a collection of constraint violations that can occur _within user application code_. Roughly: -```rust +```rust,ignore pub ConstraintViolations(pub(crate) Vec); impl IntoIterator for ConstraintViolations { ... } @@ -558,7 +558,7 @@ string LengthString The corresponding `ConstraintViolationException` Rust type for the `LengthMap` shape is: -```rust +```rust,ignore pub mod length_map { pub enum ConstraintViolation { Length(usize), @@ -575,7 +575,7 @@ pub mod length_map { `ConstraintViolationExceptions` is just a container over this type: -```rust +```rust,ignore pub ConstraintViolationExceptions(pub(crate) Vec); impl IntoIterator for ConstraintViolationExceptions { ... } @@ -604,8 +604,8 @@ string LengthPatternString This would yield: -```rust -pub ConstraintViolations(pub(crate) Vec); +```rust,ignore +pub struct ConstraintViolations(pub(crate) Vec); impl IntoIterator for ConstraintViolations { ... } @@ -656,7 +656,7 @@ string LengthPatternString This would yield, as per the first substitution: -```rust +```rust,ignore pub mod length_pattern_string { pub struct ConstraintViolations { pub length: Option, @@ -693,7 +693,7 @@ map LengthMap { This gives us: -```rust +```rust,ignore pub mod length_map { pub struct ConstraintViolations { pub length: Option, @@ -752,7 +752,7 @@ structure A { And this time let's feature _both_ the resulting `ConstraintViolationExceptions` and `ConstraintViolations` types: -```rust +```rust,ignore pub mod a { pub struct ConstraintViolationExceptions { // All fields must be `Option`, despite the members being `@required`, diff --git a/design/src/rfcs/rfc0033_improve_sdk_request_id_access.md b/design/src/rfcs/rfc0033_improve_sdk_request_id_access.md index 3e78f4070c..9ec673248c 100644 --- a/design/src/rfcs/rfc0033_improve_sdk_request_id_access.md +++ b/design/src/rfcs/rfc0033_improve_sdk_request_id_access.md @@ -156,7 +156,7 @@ Example Interactions ### Generic Handling Case -```rust +```rust,ignore // A re-export of the RequestId trait use aws_sdk_service::primitives::RequestId; @@ -170,7 +170,7 @@ my_request_id_logging_fn(&result); ### Success Case -```rust +```rust,ignore use aws_sdk_service::primitives::RequestId; let output = client.some_operation().send().await?; @@ -179,7 +179,7 @@ println!("request ID: {:?}", output.request_id()); ### Error Case with `SdkError` -```rust +```rust,ignore use aws_sdk_service::primitives::RequestId; match client.some_operation().send().await { @@ -192,7 +192,7 @@ match client.some_operation().send().await { ### Error Case with operation error -```rust +```rust,ignore use aws_sdk_service::primitives::RequestId; match client.some_operation().send().await { diff --git a/design/src/rfcs/rfc0034_smithy_orchestrator.md b/design/src/rfcs/rfc0034_smithy_orchestrator.md index 84d5952253..075a83e33a 100644 --- a/design/src/rfcs/rfc0034_smithy_orchestrator.md +++ b/design/src/rfcs/rfc0034_smithy_orchestrator.md @@ -51,7 +51,7 @@ When a smithy client communicates with a smithy service, messages are handled by For many users, the changes described by this RFC will be invisible. Making a request with an orchestrator-based SDK client looks very similar to the way requests were made pre-RFC: -```rust +```rust,ignore let sdk_config = aws_config::load_from_env().await; let client = aws_sdk_s3::Client::new(&sdk_config); let res = client.get_object() @@ -111,7 +111,7 @@ Interceptors are similar to middlewares, in that they are functions that can rea As mentioned above, interceptors may read/write a context object that is shared between all interceptors: -```rust +```rust,ignore pub struct InterceptorContext { // a.k.a. the input message modeled_request: ModReq, @@ -134,7 +134,7 @@ The optional request and response types in the interceptor context can only be a Imagine we have some sort of request signer. This signer doesn't refer to any orchestrator types. All it needs is a `HeaderMap` along with two strings, and will return a signature in string form. -```rust +```rust,ignore struct Signer; impl Signer { @@ -147,7 +147,7 @@ impl Signer { Now imagine things from the orchestrator's point of view. It requires something that implements an `AuthOrchestrator` which will be responsible for resolving the correct auth scheme, identity, and signer for an operation, as well as signing the request -```rust +```rust,ignore pub trait AuthOrchestrator: Send + Sync + Debug { fn auth_request(&self, req: &mut Req, cfg: &ConfigBag) -> Result<(), BoxError>; } @@ -171,7 +171,7 @@ fn invoke() { The specific implementation of the `AuthOrchestrator` is what brings these two things together: -```rust +```rust,ignore struct Sigv4AuthOrchestrator; impl AuthOrchestrator for Sigv4AuthOrchestrator { @@ -201,7 +201,7 @@ This intermediate code should be free from as much logic as possible. Whenever p > > *See [typemap](https://docs.rs/typemap), [type-map](https://docs.rs/crate/type-map), [http::Extensions](https://docs.rs/http/latest/http/struct.Extensions.html), and [actix_http::Extensions](https://docs.rs/actix-http/latest/actix_http/struct.Extensions.html) for examples.* -```rust +```rust,ignore let conf: ConfigBag = aws_config::from_env() // Configuration can be common to all smithy clients .with(RetryConfig::builder().disable_retries().build()) @@ -229,7 +229,7 @@ Setting configuration that will not be used wastes memory and can make debugging Configuration has precedence. Configuration set on an operation will override configuration set on a client, and configuration set on a client will override default configuration. However, configuration with a higher precedence can also augment configuration with a lower precedence. For example: -```rust +```rust,ignore let conf: ConfigBag = aws_config::from_env() .with( SomeConfig::builder() @@ -269,7 +269,7 @@ Config values are wrapped in a special enum called `Value` with three variants: Builders are defined like this: -```rust +```rust,ignore struct SomeBuilder { value: Value, } @@ -316,7 +316,7 @@ Configuration is resolved in a fixed manner by reading the "lowest level" of con *I've omitted some of the error conversion to shorten this example and make it easier to understand. The real version will be messier.* -```rust +```rust,ignore /// `In`: The input message e.g. `ListObjectsRequest` /// `Req`: The transport request message e.g. `http::Request` /// `Res`: The transport response message e.g. `http::Response` @@ -452,7 +452,7 @@ async fn make_an_attempt( At various points in the execution of `invoke`, trait objects are fetched from the `ConfigBag`. These are preliminary definitions of those traits: -```rust +```rust,ignore pub trait TraceProbe: Send + Sync + Debug { fn dispatch_events(&self, cfg: &ConfigBag) -> BoxFallibleFut<()>; } diff --git a/design/src/server/anatomy.md b/design/src/server/anatomy.md index 51f7b68f2d..9c65c87bab 100644 --- a/design/src/server/anatomy.md +++ b/design/src/server/anatomy.md @@ -41,7 +41,7 @@ service PokemonService { Smithy Rust will use this model to produce the following API: -```rust +```rust,ignore // A handler for the `GetPokemonSpecies` operation (the `PokemonSpecies` resource). async fn get_pokemon_species(input: GetPokemonSpeciesInput) -> Result { /* implementation */ @@ -67,7 +67,7 @@ A [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.htm We represent this in Rust using the [`OperationShape`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L8-L22) trait: -```rust +```rust,ignore pub trait OperationShape { /// The name of the operation. const NAME: &'static str; @@ -97,7 +97,7 @@ operation GetPokemonSpecies { the following implementation is generated -```rust +```rust,ignore /// Retrieve information about a Pokémon species. pub struct GetPokemonSpecies; @@ -118,7 +118,7 @@ The following nomenclature will aid us in our survey. We describe a `tower::Serv In contrast to the marker ZSTs above, the [`Operation`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/mod.rs#L192-L198) structure holds the actual runtime behavior of an operation, which is specified, during construction, by the customer. -```rust +```rust,ignore /// A Smithy operation, represented by a [`Service`](tower::Service) `S` and a [`Layer`](tower::Layer) `L`. /// /// The `L` is held and applied lazily during [`Upgradable::upgrade`]. @@ -130,7 +130,7 @@ pub struct Operation { The `S` here is a model service, this is specified during construction of the `Operation`. The constructors exist on the marker ZSTs as an extension trait to `OperationShape`, namely [`OperationShapeExt`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L24-L45): -```rust +```rust,ignore /// An extension trait over [`OperationShape`]. pub trait OperationShapeExt: OperationShape { /// Creates a new [`Operation`] for well-formed [`Handler`]s. @@ -162,7 +162,7 @@ The [`Handler`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693 The `from_handler` constructor is used in the following way: -```rust +```rust,ignore async fn get_pokemon_service(input: GetPokemonServiceInput) -> Result { /* Handler logic */ } @@ -172,7 +172,7 @@ let operation = GetPokemonService::from_handler(get_pokemon_service); Alternatively, `from_service` constructor: -```rust +```rust,ignore struct Svc { /* ... */ } @@ -192,7 +192,7 @@ To summarize, the `S`, in `Operation`, is a _model service_ constructed fr Now, what about the `L` in `Operation`? The `L` is a [`tower::Layer`](https://docs.rs/tower/latest/tower/layer/trait.Layer.html), or colloquially "middleware", that is applied to a _HTTP service_. Note that this means that `L` is _not_ applied directly to `S`. We can append to `L` using the `Operation::layer` method: -```rust +```rust,ignore impl Operation { /// Applies a [`Layer`] to the operation _after_ it has been upgraded via [`Operation::upgrade`]. pub fn layer(self, layer: NewL) -> Operation> { @@ -208,7 +208,7 @@ where [`tower::layer::util::Stack`](https://docs.rs/tower/latest/tower/layer/uti A typical use of this might be: -```rust +```rust,ignore let operation = GetPokemonSpecies::from_handler(handler).layer(RequestBodyLimitLayer::new(500)); ``` @@ -220,7 +220,7 @@ As mentioned, `L` is applied _after_ the `Operation` has been "upgraded" t A [Smithy protocol](https://awslabs.github.io/smithy/2.0/spec/protocol-traits.html#serialization-and-protocol-traits) specifies the serialization/deserialization scheme - how a HTTP request is transformed into a modelled input and a modelled output to a HTTP response. The is formalized using the [`FromRequest`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/request.rs#L156-L164) and [`IntoResponse`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/response.rs#L40-L44) traits: -```rust +```rust,ignore /// Provides a protocol aware extraction from a [`Request`]. This consumes the /// [`Request`], in contrast to [`FromParts`]. pub trait FromRequest: Sized { @@ -240,7 +240,7 @@ pub trait IntoResponse { Note that both traits are parameterized by `Protocol`. These [protocols](https://awslabs.github.io/smithy/2.0/aws/protocols/index.html) exist as ZST marker structs: -```rust +```rust,ignore /// [AWS REST JSON 1.0 Protocol](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restjson1-protocol.html). pub struct RestJson1; @@ -274,7 +274,7 @@ stateDiagram-v2 This is formalized by the [`Upgrade`](https://github.com/awslabs/smithy-rs/blob/9a6de1f533f8743dbbc3fa6ad974d104c8b841f4/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L74-L82) HTTP service. The `tower::Service` implementation is approximately: -```rust +```rust,ignore impl Service for Upgrade where // `Op` is used to specify the operation shape @@ -321,7 +321,7 @@ Note that the `S` and `L` are specified by logic written, in Rust, by the custom The procedure of taking a struct and transforming it into a HTTP service is formalized by the [`Upgradable`](https://github.com/awslabs/smithy-rs/blob/9a6de1f533f8743dbbc3fa6ad974d104c8b841f4/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L220-L225) trait: -```rust +```rust,ignore /// An interface to convert a representation of a Smithy operation into a [`Route`]. pub trait Upgradable { /// Upgrade the representation of a Smithy operation to a [`Route`]. @@ -333,7 +333,7 @@ Why do we need a trait for this? Why not simply write an `upgrade` method on `Op Below we give an example of a ZST which can be provided to the builder, which also satisfies `Upgradable` and returns a `MissingFailure` `tower::Service`. This `MissingFailure` service simply returns a status code 500. -```rust +```rust,ignore /// A marker struct indicating an [`Operation`] has not been set in a builder. /// /// This _does_ implement [`Upgradable`] but produces a [`Service`] which always returns an internal failure message. @@ -358,7 +358,7 @@ Different protocols supported by Smithy enjoy different routing mechanisms, for Despite their differences, all routing mechanisms satisfy a common interface. This is formalized using the `Router` trait: -```rust +```rust,ignore /// An interface for retrieving an inner [`Service`] given a [`http::Request`]. pub trait Router { type Service; @@ -373,7 +373,7 @@ which provides the ability to determine an inner HTTP service from a collection Types which implement the `Router` trait are converted to a HTTP service via the `RoutingService` struct: -```rust +```rust,ignore /// A [`Service`] using a [`Router`] `R` to redirect messages to specific routes. /// /// The `Protocol` parameter is used to determine the serialization of errors. @@ -432,7 +432,7 @@ You can create an instance of a service builder by calling either `builder_witho > Plugins? What plugins? Don't worry, they'll be covered in a [dedicated section](#plugins) later on! -```rust +```rust,ignore /// The service builder for [`PokemonService`]. /// /// Constructed via [`PokemonService::builder`]. @@ -449,7 +449,7 @@ pub struct PokemonServiceBuilder { The builder has two setter methods for each [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) in the [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service): -```rust +```rust,ignore /// Sets the [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) operation. /// /// This should be an async function satisfying the [`Handler`](aws_smithy_http_server::operation::Handler) trait. @@ -499,7 +499,7 @@ Both builder methods take care of: The final outcome, an instance of `PokemonService`, looks roughly like this: -```rust +```rust,ignore /// The Pokémon Service allows you to retrieve information about Pokémon species. #[derive(Clone)] pub struct PokemonService { @@ -560,7 +560,7 @@ After the build is finalized: Although this provides a reasonably "complete" API, it can be cumbersome in some use cases. Suppose a customer wants to log the operation name when a request is routed to said operation. Writing a `Layer`, `NameLogger`, to log an operation name is simple, however with the current API the customer is forced to do the following -```rust +```rust,ignore let get_pokemon_species = GetPokemonSpecies::from_handler(/* handler */).layer(NameLogger::new("GetPokemonSpecies")); let get_storage = GetStorage::from_handler(/* handler */).layer(NameLogger::new("GetStorage")); let do_nothing = DoNothing::from_handler(/* handler */).layer(NameLogger::new("DoNothing")); @@ -569,7 +569,7 @@ let do_nothing = DoNothing::from_handler(/* handler */).layer(NameLogger::new("D Note that `PokemonService::layer` cannot be used here because it applies a _single_ layer uniformly across all `Route`s stored in the `Router`. -```rust +```rust,ignore impl PokemonService { /// Applies a [`Layer`](tower::Layer) uniformly to all routes. pub fn layer(self, layer: &L) -> PokemonService @@ -587,7 +587,7 @@ The plugin system solves the general problem of modifying `Operation` prio The central trait is [`Plugin`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/plugin.rs#L31-L41): -```rust +```rust,ignore /// A mapping from one [`Operation`] to another. Used to modify the behavior of /// [`Upgradable`](crate::operation::Upgradable) and therefore the resulting service builder. /// @@ -603,7 +603,7 @@ pub trait Plugin { The `Upgradable::upgrade` method on `Operation`, previously presented in [Upgrading a Model Service](#upgrading-a-model-service), is more accurately: -```rust +```rust,ignore /// Takes the [`Operation`](Operation), applies [`Plugin`], then applies [`UpgradeLayer`] to /// the modified `S`, then finally applies the modified `L`. /// @@ -656,7 +656,7 @@ This constraint is in place to ensure that all handlers are upgraded using the s You might find yourself wanting to apply _multiple_ plugins to your service. This can be accommodated via [`PluginPipeline`]. -```rust +```rust,ignore use aws_smithy_http_server::plugin::PluginPipeline; # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; @@ -670,7 +670,7 @@ In the example above, `LoggingPlugin` would run first, while `MetricsPlugin` is If you are vending a plugin, you can leverage `PluginPipeline` as an extension point: you can add custom methods to it using an extension trait. For example: -```rust +```rust,ignore use aws_smithy_http_server::plugin::{PluginPipeline, PluginStack}; # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; @@ -695,7 +695,7 @@ let pipeline = PluginPipeline::new() An additional omitted detail is that we provide an "escape hatch" allowing `Handler`s and `OperationService`s to accept data that isn't modelled. In addition to accepting `Op::Input` they can accept additional arguments which implement the [`FromParts`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/request.rs#L114-L121) trait: -```rust +```rust,ignore use http::request::Parts; /// Provides a protocol aware extraction from a [`Request`]. This borrows the @@ -711,7 +711,7 @@ pub trait FromParts: Sized { This differs from `FromRequest` trait, introduced in [Serialization and Deserialization](#serialization-and-deserialization), as it's synchronous and has non-consuming access to [`Parts`](https://docs.rs/http/0.2.8/http/request/struct.Parts.html), rather than the entire [Request](https://docs.rs/http/0.2.8/http/request/struct.Request.html). -```rust +```rust,ignore pub struct Parts { pub method: Method, pub uri: Uri, @@ -724,7 +724,7 @@ pub struct Parts { This is commonly used to access types stored within [`Extensions`](https://docs.rs/http/0.2.8/http/struct.Extensions.html) which have been inserted by a middleware. An `Extension` struct implements `FromParts` to support this use case: -```rust +```rust,ignore /// Generic extension type stored in and extracted from [request extensions]. /// /// This is commonly used to share state across handlers. diff --git a/design/src/server/code_generation.md b/design/src/server/code_generation.md index b04008cb46..f65c08d2f1 100644 --- a/design/src/server/code_generation.md +++ b/design/src/server/code_generation.md @@ -1,6 +1,5 @@ # Generating Common Service Code -How a service is constructed and how to plug in new business logic is described in [Pokémon Service][1]. This document introduces the project and how code is being generated. It is written for developers who want to start contributing to `smithy-rs`. ## Folder structure @@ -12,37 +11,37 @@ The project is divided in: - `/codegen-server`: server code generation. Depends on `codegen-core` - `/aws`: the AWS Rust SDK, it deals with AWS services specifically. The folder structure reflects the project's, with the `rust-runtime` and the `codegen` - `/rust-runtime`: the generated client and server crates may depend on crates in this folder. Crates here are not code generated. The only crate that is not published is `inlineable`, -which contains common functions used by other crates, [copied into][2] the source crate +which contains common functions used by other crates, [copied into][1] the source crate Crates in `/rust-runtime` (informally referred to as "runtime crates") are added to a crate's dependency only when used. -For example, if a model uses event streams, the generated crates will depend on [`aws-smithy-eventstream`][3]. +For example, if a model uses event streams, the generated crates will depend on [`aws-smithy-eventstream`][2]. ## Generating code -`smithy-rs`'s entry points are Smithy code-generation plugins, and is not a command. One entry point is in [RustCodegenPlugin::execute][4] and -inherits from `SmithyBuildPlugin` in [smithy-build][5]. Code generation is in Kotlin and shared common, non-Rust specific code with the [`smithy` Java repository][6]. They plug into the [Smithy gradle][7] plugin, which is a gradle plugin. +`smithy-rs`'s entry points are Smithy code-generation plugins, and is not a command. One entry point is in [RustCodegenPlugin::execute][3] and +inherits from `SmithyBuildPlugin` in [smithy-build][4]. Code generation is in Kotlin and shared common, non-Rust specific code with the [`smithy` Java repository][5]. They plug into the [Smithy gradle][6] plugin, which is a gradle plugin. The comment at the beginning of `execute` describes what a `Decorator` is and uses the following terms: - Context: contains the model being generated, projection and settings for the build -- Decorator: (also referred to as customizations) customizes how code is being generated. AWS services are required to sign with the SigV4 protocol, and [a decorator][8] adds Rust code to sign requests and responses. +- Decorator: (also referred to as customizations) customizes how code is being generated. AWS services are required to sign with the SigV4 protocol, and [a decorator][7] adds Rust code to sign requests and responses. Decorators are applied in reverse order of being added and have a priority order. - Writer: creates files and adds content; it supports templating, using `#` for substitutions - Location: the file where a symbol will be written to -The only task of a `RustCodegenPlugin` is to construct a `CodegenVisitor` and call its [execute()][9] method. +The only task of a `RustCodegenPlugin` is to construct a `CodegenVisitor` and call its [execute()][8] method. -`CodegenVisitor::execute()` is given a `Context` and decorators, and calls a [CodegenVisitor][10]. +`CodegenVisitor::execute()` is given a `Context` and decorators, and calls a [CodegenVisitor][9]. CodegenVisitor, RustCodegenPlugin, and wherever there are different implementations between client and server, such as in generating error types, have corresponding server versions. Objects used throughout code generation are: -- Symbol: a node in a graph, an abstraction that represents the qualified name of a type; symbols reference and depend on other symbols, and have some common properties among languages (such as a namespace or a definition file). For Rust, we add properties to include more metadata about a symbol, such as its [type][11] -- [RustType][12]: `Option`, `HashMap`, ... along with their namespaces of origin such as `std::collections` -- [RuntimeType][13]: the information to locate a type, plus the crates it depends on -- [ShapeId][14]: an immutable object that identifies a `Shape` +- Symbol: a node in a graph, an abstraction that represents the qualified name of a type; symbols reference and depend on other symbols, and have some common properties among languages (such as a namespace or a definition file). For Rust, we add properties to include more metadata about a symbol, such as its [type][10] +- [RustType][11]: `Option`, `HashMap`, ... along with their namespaces of origin such as `std::collections` +- [RuntimeType][12]: the information to locate a type, plus the crates it depends on +- [ShapeId][13]: an immutable object that identifies a `Shape` Useful conversions are: @@ -51,9 +50,9 @@ SymbolProvider.toSymbol(shape) ``` where `SymbolProvider` constructs symbols for shapes. Some symbols require to create other symbols and types; -[event streams][15] and [other streaming shapes][16] are an example. -Symbol providers are all [applied][17] in order; if a shape uses a reserved keyword in Rust, its name is converted to a new name by a [symbol provider][18], -and all other providers will work with this [new][19] symbol. +[event streams][14] and [other streaming shapes][15] are an example. +Symbol providers are all [applied][16] in order; if a shape uses a reserved keyword in Rust, its name is converted to a new name by a [symbol provider][17], +and all other providers will work with this [new][18] symbol. ```kotlin Model.expectShape(shapeId) @@ -61,42 +60,41 @@ Model.expectShape(shapeId) Each model has a `shapeId` to `shape` map; this method returns the shape associated with this shapeId. -Some objects implement a `transform` [method][20] that only change the input model, so that code generation will work on that new model. This is used to, for example, add a trait to a shape. +Some objects implement a `transform` [method][19] that only change the input model, so that code generation will work on that new model. This is used to, for example, add a trait to a shape. -`CodegenVisitor` is a `ShapeVisitor`. For all services in the input model, shapes are [converted into Rust][21]; -[here][22] is how a service is constructed, -[here][23] a structure and so on. +`CodegenVisitor` is a `ShapeVisitor`. For all services in the input model, shapes are [converted into Rust][20]; +[here][21] is how a service is constructed, +[here][22] a structure and so on. -Code generation flows from writer to files and entities are (mostly) generated only on a [need-by-need basis][24]. -The complete result is a [Rust crate][25], -in which all dependencies are written into their modules and `lib.rs` is generated ([here][26]). -`execute()` ends by running [cargo fmt][27], +Code generation flows from writer to files and entities are (mostly) generated only on a [need-by-need basis][23]. +The complete result is a [Rust crate][24], +in which all dependencies are written into their modules and `lib.rs` is generated ([here][25]). +`execute()` ends by running [cargo fmt][26], to avoid having to format correctly Rust in `Writer`s and to be sure the generated code follows the styling rules. -[1]: ./pokemon_service.md -[2]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt#L95-L95 -[3]: https://docs.rs/aws-smithy-eventstream -[4]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt#L34 -[5]: https://github.com/awslabs/smithy/tree/main/smithy-build -[6]: https://github.com/awslabs/smithy -[7]: https://awslabs.github.io/smithy/1.0/guides/building-models/gradle-plugin.html -[8]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt#L45 -[9]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L115-L115 -[10]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L44 -[11]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt#L363-L363 -[12]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt#L25-L25 -[13]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt#L113-L113 -[14]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#shape-id -[15]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProvider.kt#L65-L65 -[16]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/StreamingTraitSymbolProvider.kt#L26-L26 -[17]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt#L62-L62 -[18]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustReservedWords.kt#L26-L26 -[19]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProvider.kt#L38-L38 -[20]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/OperationNormalizer.kt#L52-L52 -[21]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L119-L119 -[22]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L150-L150 -[23]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L172-L172 -[24]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L119-L126 -[25]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L42-L42 -[26]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L96-L107 -[27]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L133-L133 +[1]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/CargoDependency.kt#L95-L95 +[2]: https://docs.rs/aws-smithy-eventstream +[3]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt#L34 +[4]: https://github.com/awslabs/smithy/tree/main/smithy-build +[5]: https://github.com/awslabs/smithy +[6]: https://awslabs.github.io/smithy/1.0/guides/building-models/gradle-plugin.html +[7]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt#L45 +[8]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L115-L115 +[9]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L44 +[10]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/SymbolVisitor.kt#L363-L363 +[11]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustTypes.kt#L25-L25 +[12]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RuntimeTypes.kt#L113-L113 +[13]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#shape-id +[14]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProvider.kt#L65-L65 +[15]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/StreamingTraitSymbolProvider.kt#L26-L26 +[16]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/RustCodegenPlugin.kt#L62-L62 +[17]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/rustlang/RustReservedWords.kt#L26-L26 +[18]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/EventStreamSymbolProvider.kt#L38-L38 +[19]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/transformers/OperationNormalizer.kt#L52-L52 +[20]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L119-L119 +[21]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L150-L150 +[22]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L172-L172 +[23]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L119-L126 +[24]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L42-L42 +[25]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenDelegator.kt#L96-L107 +[26]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/CodegenVisitor.kt#L133-L133 diff --git a/design/src/server/from_parts.md b/design/src/server/from_parts.md index a03386cc90..98b5365ac4 100644 --- a/design/src/server/from_parts.md +++ b/design/src/server/from_parts.md @@ -6,7 +6,7 @@ But what if we, the customer, want to access data in the handler which is _not_ -```rust +```rust,ignore /// Provides a protocol aware extraction from a [`Request`]. This borrows the /// [`Parts`], in contrast to [`FromRequest`]. pub trait FromParts: Sized { @@ -22,7 +22,7 @@ Here [`Parts`](https://docs.rs/http/latest/http/request/struct.Parts.html) is th A prolific example of a `FromParts` implementation is `Extension`: -```rust +```rust,ignore /// Generic extension type stored in and extracted from [request extensions]. /// /// This is commonly used to share state across handlers. @@ -61,7 +61,7 @@ where This allows the service builder to accept the following handler -```rust +```rust,ignore async fn handler(input: ModelInput, extension: Extension) -> ModelOutput { /* ... */ } @@ -71,7 +71,7 @@ where `ModelInput` and `ModelOutput` are specified by the Smithy Operation and ` Up to 32 structures implementing `FromParts` can be provided to the handler with the constraint that they _must_ be provided _after_ the `ModelInput`: -```rust +```rust,ignore async fn handler(input: ModelInput, ext1: Extension, ext2: Extension, other: Other /* : FromParts */, /* ... */) -> ModelOutput { /* ... */ } @@ -81,7 +81,7 @@ Note that the `parts.extensions.remove::()` in `Extensions::from_parts` will The `FromParts` trait is public so customers have the ability specify their own implementations: -```rust +```rust,ignore struct CustomerDefined { /* ... */ } diff --git a/design/src/server/instrumentation.md b/design/src/server/instrumentation.md index d946e51b0e..b24ba6775c 100644 --- a/design/src/server/instrumentation.md +++ b/design/src/server/instrumentation.md @@ -19,7 +19,7 @@ RUST_LOG=aws_smithy_http_server=warn,aws_smithy_http_server_python=error and -```rust +```rust,ignore,ignore let filter = filter::Targets::new().with_target("aws_smithy_http_server", Level::DEBUG); ``` @@ -55,7 +55,7 @@ Smithy provides an out-the-box middleware which: This is enabled via the `instrument` method provided by the `aws_smithy_http_server::instrumentation::InstrumentExt` trait. -```rust +```rust,ignore use aws_smithy_http_server::instrumentation::InstrumentExt; let plugins = PluginPipeline::new().instrument(); @@ -71,7 +71,7 @@ let app = PokemonService::builder_with_plugins(plugins) The Pokémon service example, located at `/examples/pokemon-service`, sets up a `tracing` `Subscriber` as follows: -```rust +```rust,ignore,ignore /// Setup `tracing::subscriber` to read the log level from RUST_LOG environment variable. pub fn setup_tracing() { let format = tracing_subscriber::fmt::layer().pretty(); diff --git a/design/src/server/middleware.md b/design/src/server/middleware.md index 45334adf9e..eb8d46c9ae 100644 --- a/design/src/server/middleware.md +++ b/design/src/server/middleware.md @@ -49,7 +49,7 @@ The `Service` trait can be thought of as an asynchronous function from a request Middleware in `tower` typically conforms to the following pattern, a `Service` implementation of the form -```rust +```rust,ignore pub struct NewService { inner: S, /* auxillary data */ @@ -58,7 +58,7 @@ pub struct NewService { and a complementary -```rust +```rust,ignore pub struct NewLayer { /* auxiliary data */ } @@ -133,7 +133,7 @@ where `UpgradeLayer` is the `Layer` converting Smithy model structures to HTTP s The output of the Smithy service builder provides the user with a `Service` implementation. A `Layer` can be applied around the entire `Service`. -```rust +```rust,ignore // This is a HTTP `Service`. let app /* : PokemonService> */ = PokemonService::builder_without_plugins() .get_pokemon_species(/* handler */) @@ -151,7 +151,7 @@ let app = timeout_layer.layer(app); A _single_ layer can be applied to _all_ routes inside the `Router`. This exists as a method on the output of the service builder. -```rust +```rust,ignore // Construct `TraceLayer`. let trace_layer = TraceLayer::new_for_http(Duration::from_secs(3)); @@ -169,7 +169,7 @@ Note that requests pass through this middleware immediately _after_ routing succ A "HTTP layer" can be applied to specific operations. -```rust +```rust,ignore // Construct `TraceLayer`. let trace_layer = TraceLayer::new_for_http(Duration::from_secs(3)); @@ -189,8 +189,18 @@ This middleware transforms the operations HTTP requests and responses. A "model layer" can be applied to specific operations. ```rust +# extern crate tower; +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# use tower::{util::service_fn, Layer}; +# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, PokemonService, input::*, output::*, error::*}; +# use aws_smithy_http_server::operation::OperationShapeExt; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +# struct BufferLayer; +# impl BufferLayer { pub fn new(size: usize) -> Self { Self } } +# impl Layer for BufferLayer { type Service = S; fn layer(&self, svc: S) -> Self::Service { svc } } // A handler `Service`. -let handler_svc = service_fn(/* handler */); +let handler_svc = service_fn(handler); // Construct `BufferLayer`. let buffer_layer = BufferLayer::new(3); @@ -203,7 +213,10 @@ let layered_handler = GetPokemonSpecies::from_service(handler_svc); let app /* : PokemonService> */ = PokemonService::builder_without_plugins() .get_pokemon_species_operation(layered_handler) /* ... */ - .build(); + .build() + # ;Result::<(), ()>::Ok(()) + .unwrap(); +# let app: Result, _> = app; ``` In contrast to [position C](#c-operation-specific-http-middleware), this middleware transforms the operations modelled inputs to modelled outputs. @@ -214,7 +227,7 @@ Suppose we want to apply a different `Layer` to every operation. In this case, p Consider the following middleware: -```rust +```rust,ignore /// A [`Service`] that adds a print log. #[derive(Clone, Debug)] pub struct PrintService { @@ -261,7 +274,7 @@ The plugin system provides a way to construct then apply `Layer`s in position [C An example of a `PrintPlugin` which applies a layer printing the operation name: -```rust +```rust,ignore /// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations. #[derive(Debug)] pub struct PrintPlugin; @@ -281,7 +294,7 @@ where An alternative example which applies a layer for a given protocol: -```rust +```rust,ignore /// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations. #[derive(Debug)] pub struct PrintPlugin; @@ -309,7 +322,7 @@ impl Plugin for PrintPlugin You can provide a custom method to add your plugin to a `PluginPipeline` via an extension trait: -```rust +```rust,ignore /// This provides a [`print`](PrintExt::print) method on [`PluginPipeline`]. pub trait PrintExt { /// Causes all operations to print the operation name when called. @@ -327,7 +340,7 @@ impl PrintExt for PluginPipeline { - capture_pokemon_operation: Op0, - empty_operation: Op1, - get_pokemon_species: Op2, - get_server_statistics: Op3, - get_storage: Op4, - health_check_operation: Op5, - _phantom: std::marker::PhantomData<(B, In0, In1, In2, In3, In4, In5)>, -} -``` - -The builder is constructed by a `OperationRegistryBuilder`; if an operation is not passed to the builder, it will return an error. - -```rust -let app: Router = OperationRegistryBuilder::default() - .get_pokemon_species(get_pokemon_species) - .get_storage(get_storage) - .get_server_statistics(get_server_statistics) - .capture_pokemon_operation(capture_pokemon) - .empty_operation(empty_operation) - .health_check_operation(health_check_operation) - .build() - .expect("Unable to build operation registry") - .into(); -``` - -Each of these operations is a function that can take any of these signatures. - -1. If the operation is not fallible and does not share any state: - - ```rust - pub async fn health_check_operation(_input: input::HealthCheckOperationInput) -> output::HealthCheckOperationOutput {...} - ``` - -2. If the operation is fallible and does not share any state: - - ```rust - pub async fn capture_pokemon( - mut input: input::CapturePokemonOperationInput, - ) -> Result {...} - ``` - -3. If the operation is not fallible and shares some state: - - ```rust - pub async fn get_server_statistics( - _input: input::GetServerStatisticsInput, - state: Extension>, - ) -> output::GetServerStatisticsOutput {...} - ``` - -4. If the operation is fallible and shares some state: - - ```rust - pub async fn get_storage( - input: input::GetStorageInput, - _state: Extension>, - ) -> Result {...} - ``` - -All of these are operations which implementors define; they are the business logic of the application. The rest is code generated. - -The `OperationRegistry` builds into a `Router` (`let app: Router = OperationRegistryBuilder...build().into()`). -The implementation is code generated [here][5]. - -```rust -impl - std::convert::From< - OperationRegistry, - > for aws_smithy_http_server::routing::Router -where - B: Send + 'static, - Op0: crate::server_operation_handler_trait::Handler< - B, - In0, - crate::input::CapturePokemonOperationInput, - >, - In0: 'static + Send, - ... for all Op, In -{ - fn from( - registry: OperationRegistry, - ) -> Self {...} -} -``` - -For each operation, it registers a route; the specifics depend on the [protocol][6]. -The PokemonService uses [restJson1][7] as its protocol, an operation like the `HealthCheckOperation` will be rendered as: - -```rust -let capture_pokemon_operation_request_spec = aws_smithy_http_server::routing::request_spec::RequestSpec::new( - http::Method::POST, - aws_smithy_http_server::routing::request_spec::UriSpec::new( - aws_smithy_http_server::routing::request_spec::PathAndQuerySpec::new( - aws_smithy_http_server::routing::request_spec::PathSpec::from_vector_unchecked(vec![ - aws_smithy_http_server::routing::request_spec::PathSegment::Literal(String::from("capture-pokemon-event")), - aws_smithy_http_server::routing::request_spec::PathSegment::Label, - ]), - aws_smithy_http_server::routing::request_spec::QuerySpec::from_vector_unchecked(vec![]))),); -``` - -because the URI is `/capture-pokemon-event/{region}`, with method `POST` and `region` a `Label` (then passed to the operation with its `CapturePokemonOperationInput` input struct). - -Finally, it creates a RestJSON `Router`, because that is the service's protocol. -You will have noticed, each operation is implemented as a `pub async fn`. Each operation is wrapped into an `OperationHandler`, generated [here][8]. -`OperationHandler` implements tower's `Service` [trait][9]. Implementing `Service` means that -the business logic is written as protocol-agnostic and clients request a service by calling into them, similar to an RPC call. - -```rust -aws_smithy_http_server::routing::Router::new_rest_json_router(vec![ - { - let svc = crate::server_operation_handler_trait::operation( - registry.capture_pokemon_operation, - ); -``` - -At this level, logging might be prohibited by the [`@sensitive`][10] trait. If there are no `@sensitive` shapes, the generated code looks like: - -```rust -let request_fmt = aws_smithy_http_server::instrumentation::sensitivity::RequestFmt::new(); -let response_fmt = aws_smithy_http_server::instrumentation::sensitivity::ResponseFmt::new(); -let svc = aws_smithy_http_server::instrumentation::InstrumentOperation::new( - svc, - "capture_pokemon_operation", -) -.request_fmt(request_fmt) -.response_fmt(response_fmt); -``` - -Accessing the Pokédex is modeled as a restricted operation: a passcode is needed by the Pokémon trainer. -To not log the passcode, the code will be generated [here][11] as: - -```rust -let request_fmt = aws_smithy_http_server::instrumentation::sensitivity::RequestFmt::new() - .header(|name: &http::header::HeaderName| { - #[allow(unused_variables)] - let name = name.as_str(); - let name_match = matches!(name, "passcode"); - let key_suffix = None; - let value = name_match; - aws_smithy_http_server::instrumentation::sensitivity::headers::HeaderMarker { - value, - key_suffix, - } - }) - .label(|index: usize| matches!(index, 1)); -let response_fmt = aws_smithy_http_server::instrumentation::sensitivity::ResponseFmt::new(); -``` - -Each route is a pair, [`BoxCloneService`][12] wrapping the service operation (the implementation) and -the information to consume the service operation. - -```rust -(tower::util::BoxCloneService::new(svc), capture_pokemon_operation_request_spec) -``` - -Now the `Router` is built. `Router` is not code generated, it instead lives in the [`aws-smithy-http-server`][13] crate. -We write Rust code in the runtime to: - -- Aid development of the project -- Have service-specific code that the majority of services share in the runtime - In Kotlin we generate code that is service-specific. - -A `Router` is a [`tower::Service`][9] that routes requests to the implemented services; hence it [implements][14] `Service` -like the other operations. - -The `Router` [implements][15] -the `tower::Layer` trait. Middleware are added as layers. The Pokémon example uses them: - -```rust -let shared_state = Arc::new(State::default()); -let app = app.layer( - ServiceBuilder::new() - .layer(TraceLayer::new_for_http()) - .layer(AddExtensionLayer::new(shared_state)), -); -``` - -The service is run by a [Hyper server][16]: - -```rust -hyper::Server::bind(&bind).serve(app.into_make_service()); -``` - -Generation of objects common to services, such as shapes, is described in [Code Generation][17]. - -[1]: https://github.com/awslabs/smithy-rs/tree/db48039065bec890ef387385773b37154b555b14 -[2]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen-server-test/model/pokemon.smithy -[3]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/rust-runtime/aws-smithy-http-server/examples/pokemon-service/src/main.rs#L34 -[4]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt#L1 -[5]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationRegistryGenerator.kt#L285 -[6]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/Protocol.kt#L81 -[7]: https://awslabs.github.io/smithy/1.0/spec/aws/aws-restjson1-protocol.html -[8]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationHandlerGenerator.kt#L30 -[9]: https://docs.rs/tower-service/latest/tower_service/trait.Service.html -[10]: https://awslabs.github.io/smithy/1.0/spec/core/documentation-traits.html#sensitive-trait -[11]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerHttpSensitivityGenerator.kt#L58 -[12]: https://docs.rs/tower/latest/tower/util/struct.BoxCloneService.html -[13]: https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/ -[14]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/rust-runtime/aws-smithy-http-server/src/routing/mod.rs#L302 -[15]: https://github.com/awslabs/smithy-rs/blob/db48039065bec890ef387385773b37154b555b14/rust-runtime/aws-smithy-http-server/src/routing/mod.rs#L146 -[16]: https://docs.rs/hyper/latest/hyper/server/struct.Server.html -[17]: ./code_generation.md diff --git a/design/src/smithy/aggregate_shapes.md b/design/src/smithy/aggregate_shapes.md index 627ea875b3..0dafac77f9 100644 --- a/design/src/smithy/aggregate_shapes.md +++ b/design/src/smithy/aggregate_shapes.md @@ -28,7 +28,7 @@ Smithy `structure` becomes a `struct` in Rust. Backwards compatibility & usabili 2. All structs are marked `#[non_exhaustive]` 3. All structs derive `Debug` & `PartialEq`. Structs **do not** derive `Eq` because a `float` member may be added in the future. 4. Struct fields are public. Public struct fields allow for [split borrows](https://doc.rust-lang.org/nomicon/borrow-splitting.html). When working with output objects this significantly improves ergonomics, especially with optional fields. - ```rust + ```rust,ignore let out = dynamo::ListTablesOutput::new(); out.some_field.unwrap(); // <- partial move, impossible with an accessor ``` @@ -52,7 +52,7 @@ long ReadIOs long WriteIOs ``` **Rust Output**: -```rust +```rust,ignore ///

Contains I/O usage metrics for a command that was invoked.

#[non_exhaustive] #[derive(std::clone::Clone, std::cmp::PartialEq)] @@ -151,7 +151,7 @@ list BoolList { } ``` **Rust**: -```rust +```rust,ignore #[non_exhaustive] #[derive(std::clone::Clone, std::cmp::PartialEq, std::fmt::Debug)] pub enum AttributeValue { diff --git a/design/src/smithy/backwards-compat.md b/design/src/smithy/backwards-compat.md index d39c071d6b..85a3ea5fd6 100644 --- a/design/src/smithy/backwards-compat.md +++ b/design/src/smithy/backwards-compat.md @@ -62,7 +62,7 @@ Specifically, [`#[non_exhaustive]`](https://doc.rust-lang.org/reference/attribut following patterns: 1. Direct structure instantiation: - ```rust + ```rust,ignore # fn foo() { let ip_addr = IpAddress { addr: "192.168.1.1" }; # } @@ -72,20 +72,20 @@ following patterns: our structures while maintaining backwards compatibility, all structures expose a builder, accessible at `SomeStruct::Builder`: - ```rust + ```rust,ignore # fn foo() { let ip_addr = IpAddress::builder().addr("192.168.1.1").build(); # } ``` 2. Structure destructuring: - ```rust + ```rust,ignore # fn foo() { let IpAddress { addr } = some_ip_addr(); # } ``` This will also fail to compile if a new member is added, however, by adding `#[non_exhaustive]`, the `..` multifield wildcard MUST be added to support new fields being added in the future: - ```rust + ```rust,ignore # fn foo() { let IpAddress { addr, .. } = some_ip_addr(); # } @@ -110,7 +110,7 @@ because new fields cannot be added to union variants, the union variants themsel to be `#[non_exhaustive]`. To support new variants from services, each union contains an `Unknown` variant. By marking `Unknown` as non_exhaustive, we prevent customers from instantiating it directly. -```rust +```rust,ignore #[non_exhaustive] #[derive(std::clone::Clone, std::cmp::PartialEq, std::fmt::Debug)] pub enum AttributeValue { diff --git a/design/src/smithy/endpoint.md b/design/src/smithy/endpoint.md index 699f170209..bef52bdfeb 100644 --- a/design/src/smithy/endpoint.md +++ b/design/src/smithy/endpoint.md @@ -4,7 +4,7 @@ The core codegen generates HTTP requests that do not contain an authority, scheme or post. These properties must be set later based on configuration. Existing AWS services have a number of requirements that increase the complexity: 1. Endpoints must support manual configuration by end users: -```rust +```rust,ignore let config = dynamodb::Config::builder() .endpoint(StaticEndpoint::for_uri("http://localhost:8000")) ``` @@ -14,7 +14,7 @@ When a user specifies a custom endpoint URI, _typically_ they will want to avoid 2. Endpoints must support being customized on a per-operation basis by the endpoint trait. This will prefix the base endpoint, potentially driven by fields of the operation. [Docs](https://awslabs.github.io/smithy/1.0/spec/core/endpoint-traits.html#endpoint-trait) 3. Endpoints must support being customized by [endpoint discovery](https://awslabs.github.io/smithy/1.0/spec/aws/aws-core.html#client-endpoint-discovery). A request, customized by a predefined set of fields from the input operation is dispatched to a specific URI. That operation returns the endpoint that should be used. Endpoints must be cached by a cache key containing: -``` +```markdown (access_key_id, [all input fields], operation) ``` Endpoints retrieved in this way specify a TTL. @@ -29,7 +29,7 @@ Configuration objects for services _must_ contain an `Endpoint`. This endpoint m During operation construction (see [Operation Construction](../transport/operation.md#operation-construction)) an `EndpointPrefix` may be set on the property bag. The eventual endpoint middleware will search for this in the property bag and (depending on the URI mutability) utilize this prefix when setting the endpoint. In the case of endpoint discovery, we envision a different pattern: -```rust +```rust,ignore // EndpointClient manages the endpoint cache let (tx, rx) = dynamodb::EndpointClient::new(); let client = aws_hyper::Client::new(); diff --git a/design/src/smithy/simple_shapes.md b/design/src/smithy/simple_shapes.md index 171196d0d7..9e2c99b434 100644 --- a/design/src/smithy/simple_shapes.md +++ b/design/src/smithy/simple_shapes.md @@ -51,7 +51,7 @@ Current models represent strings as `String`. Smithy defines the concept of "Document Types": > [Documents represent] protocol-agnostic open content that is accessed like JSON data. Open content is useful for modeling unstructured data that has no schema, data that can't be modeled using rigid types, or data that has a schema that evolves outside of the purview of a model. The serialization format of a document is an implementation detail of a protocol and MUST NOT have any effect on the types exposed by tooling to represent a document value. -```rust +```rust,ignore {{#include ../../../rust-runtime/aws-smithy-types/src/lib.rs:document}} ``` diff --git a/design/src/transport/operation.md b/design/src/transport/operation.md index 332e607657..9ca8a4dfc9 100644 --- a/design/src/transport/operation.md +++ b/design/src/transport/operation.md @@ -16,7 +16,7 @@ This section details the flow of a request through the SDK until a response is r A customer interacts with the SDK builders to construct an input. The `build()` method on an input returns an `Operation`. This codifies the base HTTP request & all the configuration and middleware layers required to modify and dispatch the request. -```rust +```rust,ignore pub struct Operation { request: Request, response_handler: H, @@ -37,7 +37,7 @@ By using a property bag, we can define the `Operation` in Smithy core. AWS speci In order to construct an operation, the generated code injects appropriate middleware & configuration via the configuration property bag. It does this by reading the configuration properties out of the service config, copying them as necessary, and loading them into the `Request`: -```rust +```rust,ignore // This is approximately the generated code, I've cleaned a few things up for readability. pub fn build(self, config: &dynamodb::config::Config) -> Operation { let op = BatchExecuteStatement::new(BatchExecuteStatementInput { diff --git a/tools/ci-build/Dockerfile b/tools/ci-build/Dockerfile index f5ed4869d9..045805f479 100644 --- a/tools/ci-build/Dockerfile +++ b/tools/ci-build/Dockerfile @@ -142,6 +142,17 @@ FROM install_rust AS cargo_semver_checks ARG cargo_semver_checks_version=0.20.0 ARG rust_nightly_version RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-semver-checks --locked --version ${cargo_semver_checks_version} + +FROM install_rust AS cargo_mdbook +ARG cargo_mdbook_version=0.4.30 +ARG rust_nightly_version +RUN cargo +${rust_nightly_version} -Z sparse-registry install mdbook --locked --version ${cargo_mdbook_version} + +FROM install_rust AS cargo_mdbook_mermaid +ARG cargo_mdbook_mermaid_version=0.12.6 +ARG rust_nightly_version +RUN cargo +${rust_nightly_version} -Z sparse-registry install mdbook-mermaid --locked --version ${cargo_mdbook_mermaid_version} + # # Final image # @@ -179,6 +190,8 @@ COPY --chown=build:build --from=wasmtime /opt/wasmtime /opt/cargo/bin/wasmtime COPY --chown=build:build --from=cargo_wasi /opt/cargo/bin/cargo-wasi /opt/cargo/bin/cargo-wasi COPY --chown=build:build --from=install_rust /opt/rustup /opt/rustup COPY --chown=build:build --from=cargo_semver_checks /opt/cargo/bin/cargo-semver-checks /opt/cargo/bin/cargo-semver-checks +COPY --chown=build:build --from=cargo_mdbook /opt/cargo/bin/mdbook /opt/cargo/bin/mdbook +COPY --chown=build:build --from=cargo_mdbook_mermaid /opt/cargo/bin/mdbook-mermaid /opt/cargo/bin/mdbook-mermaid COPY --chown=build:build --from=musl_toolchain /usr/local/musl/ /usr/local/musl/ ENV PATH=$PATH:/usr/local/musl/bin/ ENV PATH=/opt/cargo/bin:$PATH \ diff --git a/tools/ci-scripts/check-book b/tools/ci-scripts/check-book new file mode 100755 index 0000000000..e26aae552b --- /dev/null +++ b/tools/ci-scripts/check-book @@ -0,0 +1,14 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +set -eux + +cd smithy-rs/examples +make +cargo b + +cd .. +mdbook test design -L target/debug/deps From bdc771ca583a2bd2ef892719ca3417028c983ad8 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Wed, 7 Jun 2023 12:44:14 +0100 Subject: [PATCH 137/253] Add `mdbook` to the `sanity-test` script (#2751) ## Motivation and Context https://github.com/awslabs/smithy-rs/pull/2027#discussion_r1219881622 --- tools/ci-build/sanity-test | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/ci-build/sanity-test b/tools/ci-build/sanity-test index 933d36c9a3..329bc6558a 100755 --- a/tools/ci-build/sanity-test +++ b/tools/ci-build/sanity-test @@ -18,3 +18,4 @@ rustc +"${RUST_NIGHTLY_VERSION}" --version rustc --version sdk-versioner --version cargo semver-checks --version +mdbook --version From 334891d98fa77633fd2f26357717320ede270698 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 7 Jun 2023 09:31:39 -0400 Subject: [PATCH 138/253] Remove Debug bound from struct definition (#2749) ## Description the debug bound on the struct definition is redundant and is viral, making the code harder to use ## Testing code compiles ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../src/client/interceptors/context.rs | 11 +++-------- .../src/client/interceptors/context/wrappers.rs | 16 ++++------------ .../src/client/orchestrator/error.rs | 2 +- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 89d0d4e8cd..03cb367556 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -36,6 +36,7 @@ use aws_smithy_http::result::SdkError; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; use phase::Phase; +use std::fmt::Debug; use std::{fmt, mem}; use tracing::{debug, error, trace}; @@ -53,10 +54,7 @@ type Response = HttpResponse; /// context in the [`Phase::BeforeSerialization`] phase won't have a `request` yet since the input hasn't been /// serialized at that point. But once it gets into the [`Phase::BeforeTransmit`] phase, the `request` will be set. #[derive(Debug)] -pub struct InterceptorContext -where - E: fmt::Debug, -{ +pub struct InterceptorContext { pub(crate) input: Option, pub(crate) output_or_error: Option>>, pub(crate) request: Option, @@ -81,10 +79,7 @@ impl InterceptorContext { } } -impl InterceptorContext -where - E: fmt::Debug, -{ +impl InterceptorContext { /// Decomposes the context into its constituent parts. #[doc(hidden)] #[allow(clippy::type_complexity)] diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs index a72df5bea8..7754478a2d 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs @@ -85,8 +85,7 @@ macro_rules! declare_known_method { macro_rules! declare_wrapper { (($ref_struct_name:ident $mut_struct_name:ident)$($tt:tt)+) => { - pub struct $ref_struct_name<'a, I = Input, O = Output, E = Error> - where E: Debug { + pub struct $ref_struct_name<'a, I = Input, O = Output, E = Error> { inner: &'a InterceptorContext, } @@ -101,8 +100,7 @@ macro_rules! declare_wrapper { declare_ref_wrapper_methods!($($tt)+); } - pub struct $mut_struct_name<'a, I = Input, O = Output, E = Error> - where E: Debug { + pub struct $mut_struct_name<'a, I = Input, O = Output, E = Error> { inner: &'a mut InterceptorContext, } @@ -170,10 +168,7 @@ declare_wrapper!( // time. Consider updating the macros to support these last two if you're looking for a challenge. // - Zelda -pub struct FinalizerInterceptorContextRef<'a, I = Input, O = Output, E = Error> -where - E: Debug, -{ +pub struct FinalizerInterceptorContextRef<'a, I = Input, O = Output, E = Error> { inner: &'a InterceptorContext, } @@ -203,10 +198,7 @@ impl<'a, I, O, E: Debug> FinalizerInterceptorContextRef<'a, I, O, E> { } } -pub struct FinalizerInterceptorContextMut<'a, I = Input, O = Output, E = Error> -where - E: Debug, -{ +pub struct FinalizerInterceptorContextMut<'a, I = Input, O = Output, E = Error> { inner: &'a mut InterceptorContext, } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs index 56e6b9f4cc..152e442013 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs @@ -13,7 +13,7 @@ use std::fmt::Debug; #[derive(Debug)] #[non_exhaustive] -pub enum OrchestratorError { +pub enum OrchestratorError { /// An error occurred within an interceptor. Interceptor { err: InterceptorError }, /// An error returned by a service. From a6c3aba5eb01a35305995c46eeb46ceda3cad124 Mon Sep 17 00:00:00 2001 From: Thomas Cameron Date: Thu, 8 Jun 2023 00:16:09 +0900 Subject: [PATCH 139/253] RFC30: Compile time benchmark (#2617) ## Motivation and Context This PR implements ci-script for bench marking compile time. Results are turned into a markdown file and compile time is normalized to a value relative to the compile time of S3's sdk on dev profile without any features. Benchmark is triggered for every PR. I considered using AWS batch, however, I decided not to move forward with for following reasons - I do not have access to your AWS account, thus making the debugging extremely difficult - Fork's github action will always fail if you block access ## Testing NA ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --------- Co-authored-by: Zelda Hessler --- .github/workflows/pull-request-bot.yml | 39 ++++++++++++++++++++++++- tools/ci-scripts/compiletime-benchmark | 33 +++++++++++++++++++++ tools/compiletime-benchmark/format.py | 40 ++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100755 tools/ci-scripts/compiletime-benchmark create mode 100644 tools/compiletime-benchmark/format.py diff --git a/.github/workflows/pull-request-bot.yml b/.github/workflows/pull-request-bot.yml index aafeaf773c..e07a79fe92 100644 --- a/.github/workflows/pull-request-bot.yml +++ b/.github/workflows/pull-request-bot.yml @@ -135,10 +135,45 @@ jobs: run: | aws s3 cp target/doc "s3://${S3_BUCKET_NAME}/docs/${{ inputs.head_revision }}" --recursive + compiletime-benchmark: + runs-on: ubuntu-latest + name: Run Compiletime Benchmark + permissions: + id-token: write + contents: read + pull-requests: write + outputs: + compiletime-benchmark: ${{ steps.compiletime-benchmark.outputs.compiletime-benchmark }} + steps: + - uses: actions/checkout@v3 + - uses: actions/cache@v3 + name: Gradle Cache + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: | + ${{ runner.os }}-gradle- + # JDK is needed to generate code + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: corretto + java-package: jdk + java-version: ${{ env.java_version }} + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.rust_version }} + - name: run benchmark + id: run-compiletime-benchmark + run: bash tools/ci-scripts/compiletime-benchmark && cat /tmp/compiletime-benchmark.md >> "$GITHUB_OUTPUT" + post-bot-comment: needs: - generate-diff - generate-doc-preview + - compiletime-benchmark runs-on: ubuntu-latest name: Post bot comment permissions: @@ -164,6 +199,8 @@ jobs: issue_number: ${{ inputs.issue_number }}, owner: context.repo.owner, repo: context.repo.repo, - body: '${{ steps.bot-messages.outputs.codegen-diff }}\n\n' + + body: '${{ steps.compiletime-benchmark.outputs.compiletime-benchmark }}\n\n' + + '${{ steps.bot-messages.outputs.codegen-diff }}\n\n' + '${{ needs.generate-doc-preview.outputs.bot-message }}\n\n' + }) diff --git a/tools/ci-scripts/compiletime-benchmark b/tools/ci-scripts/compiletime-benchmark new file mode 100755 index 0000000000..3b0a18cc7f --- /dev/null +++ b/tools/ci-scripts/compiletime-benchmark @@ -0,0 +1,33 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# +set -eux + +export TIMEFORMAT=%R + +function compile() { + cd $1 && + export CARGORUSTFLAG="" && + cargo build &>/dev/null && cargo clean && # this is for downloading crates + \time --format="%e seconds" bash -c "cargo build &> /dev/null" && cargo clean && + \time --format="%e seconds" bash -c "cargo build --release &> /dev/null" && cargo clean && + export CARGORUSTFLAG="--cfg aws_sdk_unstable" && + \time --format="%e seconds" bash -c "cargo build --all-features &>/dev/null" && cargo clean && + \time --format="%e seconds" bash -c "cargo build --all-features --release &>/dev/null" && cargo clean +} + +./gradlew :aws:sdk:assemble +DIR=$PWD +for variable in $(dir "aws/sdk/build/aws-sdk/sdk"); do + if [[ $variable != *"aws-"* ]]; then + echo "START" &>>/tmp/compiletime-benchmark.txt + echo "$variable" &>>/tmp/compiletime-benchmark.txt + compile "$DIR/aws/sdk/build/aws-sdk/sdk/$variable" &>>/tmp/compiletime-benchmark.txt + echo "END" &>>/tmp/compiletime-benchmark.txt + fi +done + +cd $DIR +python3 tools/compiletime-benchmark/format.py diff --git a/tools/compiletime-benchmark/format.py b/tools/compiletime-benchmark/format.py new file mode 100644 index 0000000000..f74e06bd11 --- /dev/null +++ b/tools/compiletime-benchmark/format.py @@ -0,0 +1,40 @@ +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +import itertools + + +def main(): + f = open("/tmp/compiletime-benchmark.txt", "r").read() + iter = map(lambda x: x.split("END"), f.split("START")) + iter = itertools.chain.from_iterable(iter) + markdown = """ + | sdk name | dev | release | dev all features | release all features | + | -------- | --- | ------- | ---------------- | -------------------- | + """ + idx = 0 + for i in iter: + idx += 1 + print("============") + print(idx) + print(i) + print("============") + + lines = i.splitlines() + if len(lines) > 1: + continue + + sdk_name = lines[0] + row = "\n|" + sdk_name + \ + "|".join(map(lambda x: float(x), lines[1:])) + "|" + + markdown += row + + print(markdown) + with open("/tmp/compiletime-benchmark.md", "w") as f: + f.write(markdown) + f.flush() + +main() From ccbc9a1a91288e9a89469354ef6fa033df2b1d65 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Thu, 8 Jun 2023 16:09:26 +0100 Subject: [PATCH 140/253] Revert "RFC30: Compile time benchmark" (#2756) ## Motivation and Context There seems to be some problem which causes this CI job to fail. https://github.com/awslabs/smithy-rs/actions/runs/5209247728/jobs/9398874499 Reverting smithy-rs#2617 until we have a clearer picture of why and how to fix. --- .github/workflows/pull-request-bot.yml | 39 +------------------------ tools/ci-scripts/compiletime-benchmark | 33 --------------------- tools/compiletime-benchmark/format.py | 40 -------------------------- 3 files changed, 1 insertion(+), 111 deletions(-) delete mode 100755 tools/ci-scripts/compiletime-benchmark delete mode 100644 tools/compiletime-benchmark/format.py diff --git a/.github/workflows/pull-request-bot.yml b/.github/workflows/pull-request-bot.yml index e07a79fe92..aafeaf773c 100644 --- a/.github/workflows/pull-request-bot.yml +++ b/.github/workflows/pull-request-bot.yml @@ -135,45 +135,10 @@ jobs: run: | aws s3 cp target/doc "s3://${S3_BUCKET_NAME}/docs/${{ inputs.head_revision }}" --recursive - compiletime-benchmark: - runs-on: ubuntu-latest - name: Run Compiletime Benchmark - permissions: - id-token: write - contents: read - pull-requests: write - outputs: - compiletime-benchmark: ${{ steps.compiletime-benchmark.outputs.compiletime-benchmark }} - steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 - name: Gradle Cache - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-gradle- - # JDK is needed to generate code - - name: Set up JDK - uses: actions/setup-java@v3 - with: - distribution: corretto - java-package: jdk - java-version: ${{ env.java_version }} - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ env.rust_version }} - - name: run benchmark - id: run-compiletime-benchmark - run: bash tools/ci-scripts/compiletime-benchmark && cat /tmp/compiletime-benchmark.md >> "$GITHUB_OUTPUT" - post-bot-comment: needs: - generate-diff - generate-doc-preview - - compiletime-benchmark runs-on: ubuntu-latest name: Post bot comment permissions: @@ -199,8 +164,6 @@ jobs: issue_number: ${{ inputs.issue_number }}, owner: context.repo.owner, repo: context.repo.repo, - body: '${{ steps.compiletime-benchmark.outputs.compiletime-benchmark }}\n\n' + - '${{ steps.bot-messages.outputs.codegen-diff }}\n\n' + + body: '${{ steps.bot-messages.outputs.codegen-diff }}\n\n' + '${{ needs.generate-doc-preview.outputs.bot-message }}\n\n' - }) diff --git a/tools/ci-scripts/compiletime-benchmark b/tools/ci-scripts/compiletime-benchmark deleted file mode 100755 index 3b0a18cc7f..0000000000 --- a/tools/ci-scripts/compiletime-benchmark +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 -# -set -eux - -export TIMEFORMAT=%R - -function compile() { - cd $1 && - export CARGORUSTFLAG="" && - cargo build &>/dev/null && cargo clean && # this is for downloading crates - \time --format="%e seconds" bash -c "cargo build &> /dev/null" && cargo clean && - \time --format="%e seconds" bash -c "cargo build --release &> /dev/null" && cargo clean && - export CARGORUSTFLAG="--cfg aws_sdk_unstable" && - \time --format="%e seconds" bash -c "cargo build --all-features &>/dev/null" && cargo clean && - \time --format="%e seconds" bash -c "cargo build --all-features --release &>/dev/null" && cargo clean -} - -./gradlew :aws:sdk:assemble -DIR=$PWD -for variable in $(dir "aws/sdk/build/aws-sdk/sdk"); do - if [[ $variable != *"aws-"* ]]; then - echo "START" &>>/tmp/compiletime-benchmark.txt - echo "$variable" &>>/tmp/compiletime-benchmark.txt - compile "$DIR/aws/sdk/build/aws-sdk/sdk/$variable" &>>/tmp/compiletime-benchmark.txt - echo "END" &>>/tmp/compiletime-benchmark.txt - fi -done - -cd $DIR -python3 tools/compiletime-benchmark/format.py diff --git a/tools/compiletime-benchmark/format.py b/tools/compiletime-benchmark/format.py deleted file mode 100644 index f74e06bd11..0000000000 --- a/tools/compiletime-benchmark/format.py +++ /dev/null @@ -1,40 +0,0 @@ -# -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 -# - -import itertools - - -def main(): - f = open("/tmp/compiletime-benchmark.txt", "r").read() - iter = map(lambda x: x.split("END"), f.split("START")) - iter = itertools.chain.from_iterable(iter) - markdown = """ - | sdk name | dev | release | dev all features | release all features | - | -------- | --- | ------- | ---------------- | -------------------- | - """ - idx = 0 - for i in iter: - idx += 1 - print("============") - print(idx) - print(i) - print("============") - - lines = i.splitlines() - if len(lines) > 1: - continue - - sdk_name = lines[0] - row = "\n|" + sdk_name + \ - "|".join(map(lambda x: float(x), lines[1:])) + "|" - - markdown += row - - print(markdown) - with open("/tmp/compiletime-benchmark.md", "w") as f: - f.write(markdown) - f.flush() - -main() From dabbfaa8e395907ff1e68b271f3d16069abac8b4 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 8 Jun 2023 13:31:52 -0400 Subject: [PATCH 141/253] Add the ability to disable interceptors via the config bag (#2757) This replaces the existing custom interceptor disable logic with shared logic to allow generally disabling any public interceptors from Runtime plugins. The previous behavior was very brittle because it relied heavily on runtime plugin execution order. ## Motivation and Context - simplify presigning behavior - generalize logic to disable interceptors ## Description Create `disable_interceptor` struct, which, when inserted into the configuration bag can disable an interceptor via the `Interceptors` execution interface. ## Testing - ` (cd aws/sdk/build/aws-sdk/sdk/s3 && cargo test --test presigning)` ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --- .../src/presigning_interceptors.rs | 17 +- .../aws-runtime/src/invocation_id.rs | 17 -- .../aws-runtime/src/request_info.rs | 17 -- .../aws-runtime/src/user_agent.rs | 17 -- .../smithy/rustsdk/InvocationIdDecorator.kt | 11 +- .../RetryInformationHeaderDecorator.kt | 26 +- .../smithy/rustsdk/UserAgentDecorator.kt | 16 +- design/src/client/orchestrator.md | 2 +- .../src/client/interceptors.rs | 237 ++++++++++++++---- 9 files changed, 209 insertions(+), 151 deletions(-) diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs index af26912ddd..18b94723a2 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs @@ -8,13 +8,14 @@ use crate::presigning::PresigningConfig; use crate::serialization_settings::HeaderSerializationSettings; use aws_runtime::auth::sigv4::{HttpSignatureType, SigV4OperationSigningConfig}; -use aws_runtime::invocation_id::DisableInvocationIdInterceptor; -use aws_runtime::request_info::DisableRequestInfoInterceptor; -use aws_runtime::user_agent::DisableUserAgentInterceptor; +use aws_runtime::invocation_id::InvocationIdInterceptor; +use aws_runtime::request_info::RequestInfoInterceptor; +use aws_runtime::user_agent::UserAgentInterceptor; use aws_smithy_async::time::{SharedTimeSource, StaticTimeSource}; use aws_smithy_runtime_api::client::interceptors::{ - BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, BoxError, - Interceptor, InterceptorRegistrar, SharedInterceptor, + disable_interceptor, BeforeSerializationInterceptorContextMut, + BeforeTransmitInterceptorContextMut, BoxError, Interceptor, InterceptorRegistrar, + SharedInterceptor, }; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; @@ -92,9 +93,9 @@ impl RuntimePlugin for SigV4PresigningRuntimePlugin { interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { // Disable some SDK interceptors that shouldn't run for presigning - cfg.put(DisableInvocationIdInterceptor::new("presigning")); - cfg.put(DisableRequestInfoInterceptor::new("presigning")); - cfg.put(DisableUserAgentInterceptor::new("presigning")); + cfg.put(disable_interceptor::("presigning")); + cfg.put(disable_interceptor::("presigning")); + cfg.put(disable_interceptor::("presigning")); // Register the presigning interceptor interceptors.register(self.interceptor.clone()); diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 2ff073f822..420cedb73d 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -18,23 +18,6 @@ pub use test_util::{NoInvocationIdGenerator, PredefinedInvocationIdGenerator}; #[allow(clippy::declare_interior_mutable_const)] // we will never mutate this const AMZ_SDK_INVOCATION_ID: HeaderName = HeaderName::from_static("amz-sdk-invocation-id"); -/// Config marker that disables the invocation ID interceptor. -#[doc(hidden)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct DisableInvocationIdInterceptor { - why: &'static str, -} - -impl DisableInvocationIdInterceptor { - /// Creates a new `DisableInvocationIdInterceptor`. - /// - /// Takes a human readable string for the `Debug` impl to state why it is being disabled. - /// This is to assist with debugging issues with requests. - pub fn new(why: &'static str) -> Self { - Self { why } - } -} - /// A generator for returning new invocation IDs on demand. pub trait InvocationIdGenerator: Debug + Send + Sync { /// Call this function to receive a new [`InvocationId`] or an error explaining why one couldn't diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index 706872dd29..0b41310536 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -20,23 +20,6 @@ use std::time::{Duration, SystemTime}; #[allow(clippy::declare_interior_mutable_const)] // we will never mutate this const AMZ_SDK_REQUEST: HeaderName = HeaderName::from_static("amz-sdk-request"); -/// Config marker that disables the invocation ID interceptor. -#[doc(hidden)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct DisableRequestInfoInterceptor { - why: &'static str, -} - -impl DisableRequestInfoInterceptor { - /// Creates a new `DisableRequestInfoInterceptor`. - /// - /// Takes a human readable string for the `Debug` impl to state why it is being disabled. - /// This is to assist with debugging issues with requests. - pub fn new(why: &'static str) -> Self { - Self { why } - } -} - /// Generates and attaches a request header that communicates request-related metadata. /// Examples include: /// diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 12469fa13d..52c6cdb4da 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -49,23 +49,6 @@ impl From for UserAgentInterceptorError { } } -/// Config marker that disables the user agent interceptor. -#[doc(hidden)] -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct DisableUserAgentInterceptor { - why: &'static str, -} - -impl DisableUserAgentInterceptor { - /// Creates a new `DisableUserAgentInterceptor`. - /// - /// Takes a human readable string for the `Debug` impl to state why it is being disabled. - /// This is to assist with debugging issues with requests. - pub fn new(why: &'static str) -> Self { - Self { why } - } -} - /// Generates and attaches the AWS SDK's user agent to a HTTP request #[non_exhaustive] #[derive(Debug, Default)] diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt index 2b52c2b33a..e26ddbec76 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt @@ -10,7 +10,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.util.letIf @@ -38,14 +37,8 @@ private class InvocationIdRuntimePluginCustomization( override fun section(section: ServiceRuntimePluginSection): Writable = writable { if (section is ServiceRuntimePluginSection.AdditionalConfig) { - val invocationId = AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig).resolve("invocation_id") - rustBlockTemplate( - "if cfg.get::<#{DisableInvocationIdInterceptor}>().is_none()", - "DisableInvocationIdInterceptor" to invocationId.resolve("DisableInvocationIdInterceptor"), - ) { - section.registerInterceptor(codegenContext.runtimeConfig, this) { - rustTemplate("#{InvocationIdInterceptor}::new()", *codegenScope) - } + section.registerInterceptor(codegenContext.runtimeConfig, this) { + rustTemplate("#{InvocationIdInterceptor}::new()", *codegenScope) } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt index f984608b41..69cf7f9b15 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt @@ -11,7 +11,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRunti import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.util.letIf @@ -37,22 +36,17 @@ private class AddRetryInformationHeaderInterceptors(codegenContext: ClientCodege override fun section(section: ServiceRuntimePluginSection): Writable = writable { if (section is ServiceRuntimePluginSection.AdditionalConfig) { - rustBlockTemplate( - "if cfg.get::<#{DisableRequestInfoInterceptor}>().is_none()", - "DisableRequestInfoInterceptor" to awsRuntime.resolve("request_info::DisableRequestInfoInterceptor"), - ) { - // Track the latency between client and server. - section.registerInterceptor(runtimeConfig, this) { - rust( - "#T::new()", - smithyRuntime.resolve("client::orchestrator::interceptors::ServiceClockSkewInterceptor"), - ) - } + // Track the latency between client and server. + section.registerInterceptor(runtimeConfig, this) { + rust( + "#T::new()", + smithyRuntime.resolve("client::orchestrator::interceptors::ServiceClockSkewInterceptor"), + ) + } - // Add request metadata to outgoing requests. Sets a header. - section.registerInterceptor(runtimeConfig, this) { - rust("#T::new()", awsRuntime.resolve("request_info::RequestInfoInterceptor")) - } + // Add request metadata to outgoing requests. Sets a header. + section.registerInterceptor(runtimeConfig, this) { + rust("#T::new()", awsRuntime.resolve("request_info::RequestInfoInterceptor")) } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index c3595d3a32..7ead51d18b 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -18,7 +18,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.config.Confi import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig 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.rustBlockTemplate 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.RuntimeConfig @@ -105,16 +104,11 @@ class UserAgentDecorator : ClientCodegenDecorator { override fun section(section: ServiceRuntimePluginSection): Writable = writable { if (section is ServiceRuntimePluginSection.AdditionalConfig) { - rustBlockTemplate( - "if cfg.get::<#{DisableUserAgentInterceptor}>().is_none()", - "DisableUserAgentInterceptor" to awsRuntime.resolve("user_agent::DisableUserAgentInterceptor"), - ) { - section.putConfigValue(this) { - rust("#T.clone()", ClientRustModule.Meta.toType().resolve("API_METADATA")) - } - section.registerInterceptor(runtimeConfig, this) { - rust("#T::new()", awsRuntime.resolve("user_agent::UserAgentInterceptor")) - } + section.putConfigValue(this) { + rust("#T.clone()", ClientRustModule.Meta.toType().resolve("API_METADATA")) + } + section.registerInterceptor(runtimeConfig, this) { + rust("#T::new()", awsRuntime.resolve("user_agent::UserAgentInterceptor")) } } } diff --git a/design/src/client/orchestrator.md b/design/src/client/orchestrator.md index d23088dcc5..d0330905bd 100644 --- a/design/src/client/orchestrator.md +++ b/design/src/client/orchestrator.md @@ -52,7 +52,7 @@ The orchestrator's work is divided into four phases: *NOTE: If an interceptor fails, then the other interceptors for that lifecycle event are still run. All resulting errors are collected and emitted together.* 0. **Building the `ConfigBag` and mounting interceptors**. - - *This phase is infallible.* + - *This phase is fallible.* - An interceptor context is created. This will hold request and response objects, making them available to interceptors. - All runtime plugins set at the client-level are run. These plugins can set config and mount interceptors. Any _"read before execution"_ interceptors that have been set get run. - All runtime plugins set at the operation-level are run. These plugins can also set config and mount interceptors. Any new _"read before execution"_ interceptors that have been set get run. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index 12722aef6c..fe1f7e535d 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -22,6 +22,8 @@ pub use context::{ }; use context::{Error, Input, Output}; pub use error::{BoxError, InterceptorError}; +use std::fmt::{Debug, Formatter}; +use std::marker::PhantomData; use std::ops::Deref; use std::sync::Arc; @@ -578,38 +580,66 @@ pub trait Interceptor: std::fmt::Debug { } /// Interceptor wrapper that may be shared -#[derive(Debug, Clone)] -pub struct SharedInterceptor(Arc); +#[derive(Clone)] +pub struct SharedInterceptor { + interceptor: Arc, + check_enabled: Arc bool + Send + Sync>, +} + +impl Debug for SharedInterceptor { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SharedInterceptor") + .field("interceptor", &self.interceptor) + .finish() + } +} impl SharedInterceptor { /// Create a new `SharedInterceptor` from `Interceptor` - pub fn new(interceptor: impl Interceptor + Send + Sync + 'static) -> Self { - Self(Arc::new(interceptor)) + pub fn new(interceptor: T) -> Self { + Self { + interceptor: Arc::new(interceptor), + check_enabled: Arc::new(|conf: &ConfigBag| { + conf.get::>().is_none() + }), + } + } + + fn enabled(&self, conf: &ConfigBag) -> bool { + (self.check_enabled)(conf) } } -impl AsRef for SharedInterceptor { - fn as_ref(&self) -> &(dyn Interceptor + 'static) { - self.0.as_ref() +/// A interceptor wrapper to conditionally enable the interceptor based on [`DisableInterceptor`] +struct ConditionallyEnabledInterceptor<'a>(&'a SharedInterceptor); +impl ConditionallyEnabledInterceptor<'_> { + fn if_enabled(&self, cfg: &ConfigBag) -> Option<&dyn Interceptor> { + if self.0.enabled(cfg) { + Some(self.0.as_ref()) + } else { + None + } } } -impl From> for SharedInterceptor { - fn from(interceptor: Arc) -> Self { - SharedInterceptor(interceptor) +impl AsRef for SharedInterceptor { + fn as_ref(&self) -> &(dyn Interceptor + 'static) { + self.interceptor.as_ref() } } impl Deref for SharedInterceptor { type Target = Arc; fn deref(&self) -> &Self::Target { - &self.0 + &self.interceptor } } /// Collection of [`SharedInterceptor`] that allows for only registration #[derive(Debug, Clone, Default)] -pub struct InterceptorRegistrar(Vec); +pub struct InterceptorRegistrar { + interceptors: Vec, +} impl InterceptorRegistrar { /// Register an interceptor with this `InterceptorRegistrar`. @@ -617,7 +647,7 @@ impl InterceptorRegistrar { /// When this `InterceptorRegistrar` is passed to an orchestrator, the orchestrator will run the /// registered interceptor for all the "hooks" that it implements. pub fn register(&mut self, interceptor: SharedInterceptor) { - self.0.push(interceptor); + self.interceptors.push(interceptor); } } @@ -645,11 +675,13 @@ macro_rules! interceptor_impl_fn { let mut result: Result<(), BoxError> = Ok(()); let mut ctx = ctx.into(); for interceptor in self.interceptors() { - if let Err(new_error) = interceptor.$interceptor(&mut ctx, cfg) { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.$interceptor(&mut ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); } - result = Err(new_error); } } result.map_err(InterceptorError::$interceptor) @@ -664,11 +696,13 @@ macro_rules! interceptor_impl_fn { let mut result: Result<(), BoxError> = Ok(()); let ctx = ctx.into(); for interceptor in self.interceptors() { - if let Err(new_error) = interceptor.$interceptor(&ctx, cfg) { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.$interceptor(&ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); } - result = Err(new_error); } } result.map_err(InterceptorError::$interceptor) @@ -676,18 +710,47 @@ macro_rules! interceptor_impl_fn { }; } +/// Generalized interceptor disabling interface +/// +/// RuntimePlugins can disable interceptors by inserting [`DisableInterceptor`](DisableInterceptor) into the config bag +#[must_use] +#[derive(Debug)] +pub struct DisableInterceptor { + _t: PhantomData, + #[allow(unused)] + cause: &'static str, +} + +/// Disable an interceptor with a given cause +pub fn disable_interceptor(cause: &'static str) -> DisableInterceptor { + DisableInterceptor { + _t: PhantomData::default(), + cause, + } +} + impl Interceptors { pub fn new() -> Self { Self::default() } - fn interceptors(&self) -> impl Iterator { - // Since interceptors can modify the interceptor list (since its in the config bag), copy the list ahead of time. - // This should be cheap since the interceptors inside the list are Arcs. + fn interceptors(&self) -> impl Iterator> { + self.client_interceptors() + .chain(self.operation_interceptors()) + } + + fn client_interceptors(&self) -> impl Iterator> { self.client_interceptors - .0 + .interceptors + .iter() + .map(ConditionallyEnabledInterceptor) + } + + fn operation_interceptors(&self) -> impl Iterator> { + self.operation_interceptors + .interceptors .iter() - .chain(self.operation_interceptors.0.iter()) + .map(ConditionallyEnabledInterceptor) } pub fn client_interceptors_mut(&mut self) -> &mut InterceptorRegistrar { @@ -705,12 +768,14 @@ impl Interceptors { ) -> Result<(), InterceptorError> { let mut result: Result<(), BoxError> = Ok(()); let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); - for interceptor in self.client_interceptors.0.iter() { - if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + for interceptor in self.client_interceptors() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); } - result = Err(new_error); } } result.map_err(InterceptorError::read_before_execution) @@ -723,12 +788,14 @@ impl Interceptors { ) -> Result<(), InterceptorError> { let mut result: Result<(), BoxError> = Ok(()); let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); - for interceptor in self.operation_interceptors.0.iter() { - if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + for interceptor in self.operation_interceptors() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); } - result = Err(new_error); } } result.map_err(InterceptorError::read_before_execution) @@ -757,11 +824,14 @@ impl Interceptors { let mut result: Result<(), BoxError> = Ok(()); let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); for interceptor in self.interceptors() { - if let Err(new_error) = interceptor.modify_before_attempt_completion(&mut ctx, cfg) { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.modify_before_attempt_completion(&mut ctx, cfg) + { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); } - result = Err(new_error); } } result.map_err(InterceptorError::modify_before_attempt_completion) @@ -775,11 +845,13 @@ impl Interceptors { let mut result: Result<(), BoxError> = Ok(()); let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); for interceptor in self.interceptors() { - if let Err(new_error) = interceptor.read_after_attempt(&ctx, cfg) { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.read_after_attempt(&ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); } - result = Err(new_error); } } result.map_err(InterceptorError::read_after_attempt) @@ -793,11 +865,13 @@ impl Interceptors { let mut result: Result<(), BoxError> = Ok(()); let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); for interceptor in self.interceptors() { - if let Err(new_error) = interceptor.modify_before_completion(&mut ctx, cfg) { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.modify_before_completion(&mut ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); } - result = Err(new_error); } } result.map_err(InterceptorError::modify_before_completion) @@ -811,11 +885,13 @@ impl Interceptors { let mut result: Result<(), BoxError> = Ok(()); let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); for interceptor in self.interceptors() { - if let Err(new_error) = interceptor.read_after_execution(&ctx, cfg) { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.read_after_execution(&ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); } - result = Err(new_error); } } result.map_err(InterceptorError::read_after_execution) @@ -824,7 +900,12 @@ impl Interceptors { #[cfg(test)] mod tests { - use crate::client::interceptors::{Interceptor, InterceptorRegistrar, SharedInterceptor}; + use crate::client::interceptors::context::Input; + use crate::client::interceptors::{ + disable_interceptor, BeforeTransmitInterceptorContextRef, BoxError, Interceptor, + InterceptorContext, InterceptorRegistrar, Interceptors, SharedInterceptor, + }; + use aws_smithy_types::config_bag::ConfigBag; #[derive(Debug)] struct TestInterceptor; @@ -834,7 +915,7 @@ mod tests { fn register_interceptor() { let mut registrar = InterceptorRegistrar::default(); registrar.register(SharedInterceptor::new(TestInterceptor)); - assert_eq!(1, registrar.0.len()); + assert_eq!(1, registrar.interceptors.len()); } #[test] @@ -843,6 +924,52 @@ mod tests { let number_of_interceptors = 3; let interceptors = vec![SharedInterceptor::new(TestInterceptor); number_of_interceptors]; registrar.extend(interceptors); - assert_eq!(number_of_interceptors, registrar.0.len()); + assert_eq!(number_of_interceptors, registrar.interceptors.len()); + } + + #[test] + fn test_disable_interceptors() { + #[derive(Debug)] + struct PanicInterceptor; + impl Interceptor for PanicInterceptor { + fn read_before_transmit( + &self, + _context: &BeforeTransmitInterceptorContextRef<'_>, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + Err("boom".into()) + } + } + let mut interceptors = Interceptors::new(); + let interceptors_vec = vec![ + SharedInterceptor::new(PanicInterceptor), + SharedInterceptor::new(TestInterceptor), + ]; + interceptors + .client_interceptors_mut() + .extend(interceptors_vec); + let mut cfg = ConfigBag::base(); + assert_eq!( + interceptors + .interceptors() + .filter(|i| i.if_enabled(&cfg).is_some()) + .count(), + 2 + ); + interceptors + .read_before_transmit(&mut InterceptorContext::new(Input::new(5)), &mut cfg) + .expect_err("interceptor returns error"); + cfg.put(disable_interceptor::("test")); + assert_eq!( + interceptors + .interceptors() + .filter(|i| i.if_enabled(&cfg).is_some()) + .count(), + 1 + ); + // shouldn't error because interceptors won't run + interceptors + .read_before_transmit(&mut InterceptorContext::new(Input::new(5)), &mut cfg) + .expect("interceptor is now disabled"); } } From 5eb0927514de3c3bdf5017bba523a36fce21fc62 Mon Sep 17 00:00:00 2001 From: 82marbag <69267416+82marbag@users.noreply.github.com> Date: Fri, 9 Jun 2023 14:33:40 +0100 Subject: [PATCH 142/253] OperationShape::NAME as ShapeId (#2678) (#2717) See https://github.com/awslabs/smithy-rs/issues/2634 ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --------- Signed-off-by: Daniele Ahmed Co-authored-by: Harry Barber <106155934+hlbarber@users.noreply.github.com> --- CHANGELOG.next.toml | 29 +++++++ .../generators/ServerOperationGenerator.kt | 4 +- .../generators/ServerServiceGenerator.kt | 7 +- examples/pokemon-service/src/plugin.rs | 11 +-- .../aws-smithy-http-server/src/extension.rs | 80 ++++++------------- .../src/instrumentation/layer.rs | 14 ++-- .../src/instrumentation/mod.rs | 4 +- .../src/instrumentation/plugin.rs | 3 +- .../src/instrumentation/service.rs | 18 +++-- .../aws-smithy-http-server/src/lib.rs | 1 + .../src/operation/mod.rs | 6 +- .../src/operation/shape.rs | 3 +- .../src/plugin/closure.rs | 29 ++++--- .../src/plugin/filter.rs | 26 +++--- .../aws-smithy-http-server/src/plugin/mod.rs | 25 +++--- .../src/plugin/pipeline.rs | 11 +-- .../aws-smithy-http-server/src/shape_id.rs | 59 ++++++++++++++ 17 files changed, 205 insertions(+), 125 deletions(-) create mode 100644 rust-runtime/aws-smithy-http-server/src/shape_id.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index c31d53f166..a23ab7455c 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -133,3 +133,32 @@ message = "A newtype wrapper `SharedAsyncSleep` has been introduced and occurren references = ["smithy-rs#2742"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "ysaito1001" + +[[smithy-rs]] +message = """`ShapeId` is the new structure used to represent a shape, with its absolute name, namespace and name. +`OperationExtension`'s members are replaced by the `ShapeId` and operations' names are now replced by a `ShapeId`. + +Before you had an operation and an absolute name as its `NAME` member. You could apply a plugin only to some selected operation: + +``` +filter_by_operation_name(plugin, |name| name != Op::NAME); +``` + +Your new filter selects on an operation's absolute name, namespace or name. + +``` +filter_by_operation_id(plugin, |id| id.name() != Op::NAME.name()); +``` + +The above filter is applied to an operation's name, the one you use to specify the operation in the Smithy model. + +You can filter all operations in a namespace or absolute name: + +``` +filter_by_operation_id(plugin, |id| id.namespace() != "namespace"); +filter_by_operation_id(plugin, |id| id.absolute() != "namespace#name"); +``` +""" +author = "82marbag" +references = ["smithy-rs#2678"] +meta = { "breaking" = true, "tada" = false, "bug" = false } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt index 451aa65a85..a8ce8cbf06 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt @@ -13,6 +13,7 @@ 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.CodegenContext +import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency @@ -49,12 +50,13 @@ class ServerOperationGenerator( val requestFmt = generator.requestFmt() val responseFmt = generator.responseFmt() + val operationIdAbsolute = operationId.toString().replace("#", "##") writer.rustTemplate( """ pub struct $operationName; impl #{SmithyHttpServer}::operation::OperationShape for $operationName { - const NAME: &'static str = "${operationId.toString().replace("#", "##")}"; + const NAME: #{SmithyHttpServer}::shape_id::ShapeId = #{SmithyHttpServer}::shape_id::ShapeId::new(${operationIdAbsolute.dq()}, ${operationId.namespace.dq()}, ${operationId.name.dq()}); type Input = crate::input::${operationName}Input; type Output = crate::output::${operationName}Output; diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt index 160f9fd685..d78a7aa903 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt @@ -478,13 +478,13 @@ class ServerServiceGenerator( } private fun missingOperationsError(): Writable = writable { - rust( + rustTemplate( """ /// The error encountered when calling the [`$builderName::build`] method if one or more operation handlers are not /// specified. ##[derive(Debug)] pub struct MissingOperationsError { - operation_names2setter_methods: std::collections::HashMap<&'static str, &'static str>, + operation_names2setter_methods: std::collections::HashMap<#{SmithyHttpServer}::shape_id::ShapeId, &'static str>, } impl std::fmt::Display for MissingOperationsError { @@ -495,7 +495,7 @@ class ServerServiceGenerator( We are missing handlers for the following operations:\n", )?; for operation_name in self.operation_names2setter_methods.keys() { - writeln!(f, "- {}", operation_name)?; + writeln!(f, "- {}", operation_name.absolute())?; } writeln!(f, "\nUse the dedicated methods on `$builderName` to register the missing handlers:")?; @@ -508,6 +508,7 @@ class ServerServiceGenerator( impl std::error::Error for MissingOperationsError {} """, + *codegenScope, ) } diff --git a/examples/pokemon-service/src/plugin.rs b/examples/pokemon-service/src/plugin.rs index 8ce0e50d09..ea6ee09d91 100644 --- a/examples/pokemon-service/src/plugin.rs +++ b/examples/pokemon-service/src/plugin.rs @@ -8,6 +8,7 @@ use aws_smithy_http_server::{ operation::{Operation, OperationShape}, plugin::{Plugin, PluginPipeline, PluginStack}, + shape_id::ShapeId, }; use tower::{layer::util::Stack, Layer, Service}; @@ -17,7 +18,7 @@ use std::task::{Context, Poll}; #[derive(Clone, Debug)] pub struct PrintService { inner: S, - name: &'static str, + id: ShapeId, } impl Service for PrintService @@ -33,7 +34,7 @@ where } fn call(&mut self, req: R) -> Self::Future { - println!("Hi {}", self.name); + println!("Hi {}", self.id.absolute()); self.inner.call(req) } } @@ -41,7 +42,7 @@ where /// A [`Layer`] which constructs the [`PrintService`]. #[derive(Debug)] pub struct PrintLayer { - name: &'static str, + id: ShapeId, } impl Layer for PrintLayer { type Service = PrintService; @@ -49,7 +50,7 @@ impl Layer for PrintLayer { fn layer(&self, service: S) -> Self::Service { PrintService { inner: service, - name: self.name, + id: self.id.clone(), } } } @@ -66,7 +67,7 @@ where type Layer = Stack; fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { name: Op::NAME }) + input.layer(PrintLayer { id: Op::NAME }) } } diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index 2a8b664cdd..371df01529 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -19,15 +19,18 @@ //! //! [extensions]: https://docs.rs/http/latest/http/struct.Extensions.html -use std::{fmt, future::Future, ops::Deref, pin::Pin, task::Context, task::Poll}; +use std::hash::Hash; +use std::{fmt, fmt::Debug, future::Future, ops::Deref, pin::Pin, task::Context, task::Poll}; +use crate::extension; use futures_util::ready; use futures_util::TryFuture; use thiserror::Error; use tower::{layer::util::Stack, Layer, Service}; use crate::operation::{Operation, OperationShape}; -use crate::plugin::{plugin_from_operation_name_fn, OperationNameFn, Plugin, PluginPipeline, PluginStack}; +use crate::plugin::{plugin_from_operation_id_fn, OperationIdFn, Plugin, PluginPipeline, PluginStack}; +use crate::shape_id::ShapeId; pub use crate::request::extension::{Extension, MissingExtension}; @@ -35,13 +38,8 @@ pub use crate::request::extension::{Extension, MissingExtension}; /// This extension type is inserted, via the [`OperationExtensionPlugin`], whenever it has been correctly determined /// that the request should be routed to a particular operation. The operation handler might not even get invoked /// because the request fails to deserialize into the modeled operation input. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct OperationExtension { - absolute: &'static str, - - namespace: &'static str, - name: &'static str, -} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct OperationExtension(pub ShapeId); /// An error occurred when parsing an absolute operation shape ID. #[derive(Debug, Clone, Error, PartialEq, Eq)] @@ -51,36 +49,6 @@ pub enum ParseError { MissingNamespace, } -#[allow(deprecated)] -impl OperationExtension { - /// Creates a new [`OperationExtension`] from the absolute shape ID. - pub fn new(absolute_operation_id: &'static str) -> Result { - let (namespace, name) = absolute_operation_id - .rsplit_once('#') - .ok_or(ParseError::MissingNamespace)?; - Ok(Self { - absolute: absolute_operation_id, - namespace, - name, - }) - } - - /// Returns the Smithy model namespace. - pub fn namespace(&self) -> &'static str { - self.namespace - } - - /// Returns the Smithy operation name. - pub fn name(&self) -> &'static str { - self.name - } - - /// Returns the absolute operation shape ID. - pub fn absolute(&self) -> &'static str { - self.absolute - } -} - pin_project_lite::pin_project! { /// The [`Service::Future`] of [`OperationExtensionService`] - inserts an [`OperationExtension`] into the /// [`http::Response]`. @@ -154,7 +122,7 @@ impl Layer for OperationExtensionLayer { } /// A [`Plugin`] which applies [`OperationExtensionLayer`] to every operation. -pub struct OperationExtensionPlugin(OperationNameFn OperationExtensionLayer>); +pub struct OperationExtensionPlugin(OperationIdFn OperationExtensionLayer>); impl fmt::Debug for OperationExtensionPlugin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -170,7 +138,7 @@ where type Layer = Stack; fn map(&self, input: Operation) -> Operation { - OperationExtensionLayer> as Plugin>::map(&self.0, input) + OperationExtensionLayer> as Plugin>::map(&self.0, input) } } @@ -184,9 +152,8 @@ pub trait OperationExtensionExt

{ impl

OperationExtensionExt

for PluginPipeline

{ fn insert_operation_extension(self) -> PluginPipeline> { - let plugin = OperationExtensionPlugin(plugin_from_operation_name_fn(|name| { - let operation_extension = OperationExtension::new(name).expect("Operation name is malformed, this should never happen. Please file an issue against https://github.com/awslabs/smithy-rs"); - OperationExtensionLayer(operation_extension) + let plugin = OperationExtensionPlugin(plugin_from_operation_id_fn(|shape_id| { + OperationExtensionLayer(extension::OperationExtension(shape_id)) })); self.push(plugin) } @@ -243,28 +210,27 @@ mod tests { #[test] fn ext_accept() { let value = "com.amazonaws.ebs#CompleteSnapshot"; - let ext = OperationExtension::new(value).unwrap(); + let ext = ShapeId::new( + "com.amazonaws.ebs#CompleteSnapshot", + "com.amazonaws.ebs", + "CompleteSnapshot", + ); assert_eq!(ext.absolute(), value); assert_eq!(ext.namespace(), "com.amazonaws.ebs"); assert_eq!(ext.name(), "CompleteSnapshot"); } - #[test] - fn ext_reject() { - let value = "CompleteSnapshot"; - assert_eq!( - OperationExtension::new(value).unwrap_err(), - ParseError::MissingNamespace - ) - } - #[tokio::test] async fn plugin() { struct DummyOp; impl OperationShape for DummyOp { - const NAME: &'static str = "com.amazonaws.ebs#CompleteSnapshot"; + const NAME: ShapeId = ShapeId::new( + "com.amazonaws.ebs#CompleteSnapshot", + "com.amazonaws.ebs", + "CompleteSnapshot", + ); type Input = (); type Output = (); @@ -283,8 +249,8 @@ mod tests { // Check for `OperationExtension`. let response = svc.oneshot(http::Request::new(())).await.unwrap(); - let expected = OperationExtension::new(DummyOp::NAME).unwrap(); + let expected = DummyOp::NAME; let actual = response.extensions().get::().unwrap(); - assert_eq!(*actual, expected); + assert_eq!(actual.0, expected); } } diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs index 956fd6b24d..c070d1297e 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs @@ -5,21 +5,23 @@ use tower::Layer; +use crate::shape_id::ShapeId; + use super::{InstrumentOperation, MakeIdentity}; /// A [`Layer`] used to apply [`InstrumentOperation`]. #[derive(Debug)] pub struct InstrumentLayer { - operation_name: &'static str, + operation_id: ShapeId, make_request: RequestMakeFmt, make_response: ResponseMakeFmt, } impl InstrumentLayer { /// Constructs a new [`InstrumentLayer`] with no data redacted. - pub fn new(operation_name: &'static str) -> Self { + pub fn new(operation_id: ShapeId) -> Self { Self { - operation_name, + operation_id, make_request: MakeIdentity, make_response: MakeIdentity, } @@ -32,7 +34,7 @@ impl InstrumentLayer(self, make_request: R) -> InstrumentLayer { InstrumentLayer { - operation_name: self.operation_name, + operation_id: self.operation_id, make_request, make_response: self.make_response, } @@ -43,7 +45,7 @@ impl InstrumentLayer(self, make_response: R) -> InstrumentLayer { InstrumentLayer { - operation_name: self.operation_name, + operation_id: self.operation_id, make_request: self.make_request, make_response, } @@ -58,7 +60,7 @@ where type Service = InstrumentOperation; fn layer(&self, service: S) -> Self::Service { - InstrumentOperation::new(service, self.operation_name) + InstrumentOperation::new(service, self.operation_id.clone()) .request_fmt(self.make_request.clone()) .response_fmt(self.make_response.clone()) } diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs index 72fd2af2e6..2519e10137 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs @@ -13,6 +13,7 @@ //! ``` //! # use std::convert::Infallible; //! # use aws_smithy_http_server::instrumentation::{*, sensitivity::{*, headers::*, uri::*}}; +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # use http::{Request, Response}; //! # use tower::{util::service_fn, Service}; //! # async fn service(request: Request<()>) -> Result, Infallible> { @@ -20,6 +21,7 @@ //! # } //! # async fn example() { //! # let service = service_fn(service); +//! # const NAME: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); //! let request = Request::get("http://localhost/a/b/c/d?bar=hidden") //! .header("header-name-a", "hidden") //! .body(()) @@ -47,7 +49,7 @@ //! } //! }) //! .status_code(); -//! let mut service = InstrumentOperation::new(service, "foo-operation") +//! let mut service = InstrumentOperation::new(service, NAME) //! .request_fmt(request_fmt) //! .response_fmt(response_fmt); //! diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs index ce77218603..7da5e2fbeb 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs @@ -26,7 +26,8 @@ where type Layer = Stack>; fn map(&self, operation: Operation) -> Operation { - let layer = InstrumentLayer::new(Op::NAME) + let operation_id = Op::NAME; + let layer = InstrumentLayer::new(operation_id) .request_fmt(Op::request_fmt()) .response_fmt(Op::response_fmt()); operation.layer(layer) diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs index 1047167ffb..76410cfa65 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs @@ -16,6 +16,8 @@ use http::{HeaderMap, Request, Response, StatusCode, Uri}; use tower::Service; use tracing::{debug, debug_span, instrument::Instrumented, Instrument}; +use crate::shape_id::ShapeId; + use super::{MakeDebug, MakeDisplay, MakeIdentity}; pin_project_lite::pin_project! { @@ -87,15 +89,17 @@ where /// /// ``` /// # use aws_smithy_http_server::instrumentation::{sensitivity::{*, uri::*, headers::*}, *}; +/// # use aws_smithy_http_server::shape_id::ShapeId; /// # use tower::{Service, service_fn}; /// # use http::{Request, Response}; /// # async fn f(request: Request<()>) -> Result, ()> { Ok(Response::new(())) } /// # let mut svc = service_fn(f); +/// # const NAME: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); /// let request_fmt = RequestFmt::new() /// .label(|index| index == 1, None) /// .query(|_| QueryMarker { key: false, value: true }); /// let response_fmt = ResponseFmt::new().status_code(); -/// let mut svc = InstrumentOperation::new(svc, "foo-operation") +/// let mut svc = InstrumentOperation::new(svc, NAME) /// .request_fmt(request_fmt) /// .response_fmt(response_fmt); /// # svc.call(Request::new(())); @@ -103,17 +107,17 @@ where #[derive(Debug, Clone)] pub struct InstrumentOperation { inner: S, - operation_name: &'static str, + operation_id: ShapeId, make_request: RequestMakeFmt, make_response: ResponseMakeFmt, } impl InstrumentOperation { /// Constructs a new [`InstrumentOperation`] with no data redacted. - pub fn new(inner: S, operation_name: &'static str) -> Self { + pub fn new(inner: S, operation_id: ShapeId) -> Self { Self { inner, - operation_name, + operation_id, make_request: MakeIdentity, make_response: MakeIdentity, } @@ -127,7 +131,7 @@ impl InstrumentOperation(self, make_request: R) -> InstrumentOperation { InstrumentOperation { inner: self.inner, - operation_name: self.operation_name, + operation_id: self.operation_id, make_request, make_response: self.make_response, } @@ -139,7 +143,7 @@ impl InstrumentOperation(self, make_response: R) -> InstrumentOperation { InstrumentOperation { inner: self.inner, - operation_name: self.operation_name, + operation_id: self.operation_id, make_request: self.make_request, make_response, } @@ -170,7 +174,7 @@ where let span = { let headers = self.make_request.make_debug(request.headers()); let uri = self.make_request.make_display(request.uri()); - debug_span!("request", operation = %self.operation_name, method = %request.method(), %uri, ?headers) + debug_span!("request", operation = %self.operation_id.absolute(), method = %request.method(), %uri, ?headers) }; InstrumentedFuture { diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 6031c53e2a..c6d1175c37 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -28,6 +28,7 @@ pub mod response; pub mod routing; #[doc(hidden)] pub mod runtime_error; +pub mod shape_id; #[doc(inline)] pub(crate) use self::error::Error; diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index 91b6c36f88..b9935b86d5 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -24,6 +24,7 @@ //! is identified with the implementation //! //! ```rust,no_run +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # use aws_smithy_http_server::operation::OperationShape; //! # pub struct CartIdentifier; //! # pub struct ShoppingCart; @@ -31,7 +32,7 @@ //! pub struct GetShopping; //! //! impl OperationShape for GetShopping { -//! const NAME: &'static str = "GetShopping"; +//! const NAME: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); //! //! type Input = CartIdentifier; //! type Output = ShoppingCart; @@ -102,12 +103,13 @@ //! # use std::task::{Poll, Context}; //! # use aws_smithy_http_server::operation::*; //! # use tower::Service; +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # pub struct CartIdentifier; //! # pub struct ShoppingCart; //! # pub enum GetShoppingError {} //! # pub struct GetShopping; //! # impl OperationShape for GetShopping { -//! # const NAME: &'static str = "GetShopping"; +//! # const NAME: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); //! # //! # type Input = CartIdentifier; //! # type Output = ShoppingCart; diff --git a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs index 18414fc30c..72f712f2c0 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs @@ -4,13 +4,14 @@ */ use super::{Handler, IntoService, Normalize, Operation, OperationService}; +use crate::shape_id::ShapeId; /// Models the [Smithy Operation shape]. /// /// [Smithy Operation shape]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#operation pub trait OperationShape { /// The name of the operation. - const NAME: &'static str; + const NAME: ShapeId; /// The operation input. type Input; diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs index 75685c97b9..f1f5951e8a 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs @@ -6,56 +6,59 @@ use tower::layer::util::Stack; use crate::operation::{Operation, OperationShape}; +use crate::shape_id::ShapeId; use super::Plugin; -/// An adapter to convert a `Fn(&'static str) -> Layer` closure into a [`Plugin`]. See [`plugin_from_operation_name_fn`] for more details. -pub struct OperationNameFn { +/// An adapter to convert a `Fn(ShapeId) -> Layer` closure into a [`Plugin`]. See [`plugin_from_operation_id_fn`] for more details. +pub struct OperationIdFn { f: F, } -impl Plugin for OperationNameFn +impl Plugin for OperationIdFn where - F: Fn(&'static str) -> NewLayer, + F: Fn(ShapeId) -> NewLayer, Op: OperationShape, { type Service = S; type Layer = Stack; fn map(&self, input: Operation) -> Operation { - input.layer((self.f)(Op::NAME)) + let operation_id = Op::NAME; + input.layer((self.f)(operation_id)) } } -/// Constructs a [`Plugin`] using a closure over the operation name `F: Fn(&'static str) -> L` where `L` is a HTTP +/// Constructs a [`Plugin`] using a closure over the operation name `F: Fn(ShapeId) -> L` where `L` is a HTTP /// [`Layer`](tower::Layer). /// /// # Example /// /// ```rust -/// use aws_smithy_http_server::plugin::plugin_from_operation_name_fn; +/// use aws_smithy_http_server::plugin::plugin_from_operation_id_fn; +/// use aws_smithy_http_server::shape_id::ShapeId; /// use tower::layer::layer_fn; /// /// // A `Service` which prints the operation name before calling `S`. /// struct PrintService { -/// operation_name: &'static str, +/// operation_name: ShapeId, /// inner: S /// } /// /// // A `Layer` applying `PrintService`. /// struct PrintLayer { -/// operation_name: &'static str +/// operation_name: ShapeId /// } /// /// // Defines a closure taking the operation name to `PrintLayer`. /// let f = |operation_name| PrintLayer { operation_name }; /// /// // This plugin applies the `PrintService` middleware around every operation. -/// let plugin = plugin_from_operation_name_fn(f); +/// let plugin = plugin_from_operation_id_fn(f); /// ``` -pub fn plugin_from_operation_name_fn(f: F) -> OperationNameFn +pub fn plugin_from_operation_id_fn(f: F) -> OperationIdFn where - F: Fn(&'static str) -> L, + F: Fn(ShapeId) -> L, { - OperationNameFn { f } + OperationIdFn { f } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs index 814398b089..a9108a0747 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs @@ -6,14 +6,15 @@ use super::{either::Either, IdentityPlugin}; use crate::operation::{Operation, OperationShape}; +use crate::shape_id::ShapeId; use super::Plugin; /// Filters the application of an inner [`Plugin`] using a predicate over the /// [`OperationShape::NAME`](crate::operation::OperationShape). /// -/// See [`filter_by_operation_name`] for more details. -pub struct FilterByOperationName { +/// See [`filter_by_operation_id`] for more details. +pub struct FilterByOperationId { inner: Inner, predicate: F, } @@ -24,35 +25,36 @@ pub struct FilterByOperationName { /// # Example /// /// ```rust -/// use aws_smithy_http_server::plugin::filter_by_operation_name; +/// use aws_smithy_http_server::plugin::filter_by_operation_id; +/// use aws_smithy_http_server::shape_id::ShapeId; /// # use aws_smithy_http_server::{plugin::Plugin, operation::{Operation, OperationShape}}; /// # struct Pl; /// # struct CheckHealth; -/// # impl OperationShape for CheckHealth { const NAME: &'static str = ""; type Input = (); type Output = (); type Error = (); } +/// # impl OperationShape for CheckHealth { const NAME: ShapeId = ShapeId::new("ns#CheckHealth", "ns", "CheckHealth"); type Input = (); type Output = (); type Error = (); } /// # impl Plugin<(), CheckHealth, (), ()> for Pl { type Service = (); type Layer = (); fn map(&self, input: Operation<(), ()>) -> Operation<(), ()> { input }} /// # let plugin = Pl; /// # let operation = Operation { inner: (), layer: () }; /// // Prevents `plugin` from being applied to the `CheckHealth` operation. -/// let filtered_plugin = filter_by_operation_name(plugin, |name| name != CheckHealth::NAME); +/// let filtered_plugin = filter_by_operation_id(plugin, |id| id.name() != CheckHealth::NAME.name()); /// let new_operation = filtered_plugin.map(operation); /// ``` -pub fn filter_by_operation_name(plugins: Inner, predicate: F) -> FilterByOperationName +pub fn filter_by_operation_id(plugins: Inner, predicate: F) -> FilterByOperationId where - F: Fn(&str) -> bool, + F: Fn(ShapeId) -> bool, { - FilterByOperationName::new(plugins, predicate) + FilterByOperationId::new(plugins, predicate) } -impl FilterByOperationName { - /// Creates a new [`FilterByOperationName`]. +impl FilterByOperationId { + /// Creates a new [`FilterByOperationId`]. fn new(inner: Inner, predicate: F) -> Self { Self { inner, predicate } } } -impl Plugin for FilterByOperationName +impl Plugin for FilterByOperationId where - F: Fn(&str) -> bool, + F: Fn(ShapeId) -> bool, Inner: Plugin, Op: OperationShape, { diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs index ab3c385720..b6acaa038a 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -12,27 +12,29 @@ //! //! ``` //! # use aws_smithy_http_server::plugin::*; +//! # use aws_smithy_http_server::shape_id::ShapeId; //! # let layer = (); //! # struct GetPokemonSpecies; -//! # impl GetPokemonSpecies { const NAME: &'static str = ""; }; +//! # impl GetPokemonSpecies { const NAME: ShapeId = ShapeId::new("namespace#name", "namespace", "name"); }; //! // Create a `Plugin` from a HTTP `Layer` //! let plugin = HttpLayer(layer); //! //! // Only apply the layer to operations with name "GetPokemonSpecies" -//! let plugin = filter_by_operation_name(plugin, |name| name == GetPokemonSpecies::NAME); +//! let plugin = filter_by_operation_id(plugin, |id| id.name() == GetPokemonSpecies::NAME.name()); //! ``` //! //! # Construct a [`Plugin`] from a closure that takes as input the operation name //! //! ``` //! # use aws_smithy_http_server::plugin::*; +//! # use aws_smithy_http_server::shape_id::ShapeId; //! // A `tower::Layer` which requires the operation name //! struct PrintLayer { -//! name: &'static str +//! name: ShapeId, //! } //! //! // Create a `Plugin` using `PrintLayer` -//! let plugin = plugin_from_operation_name_fn(|name| PrintLayer { name }); +//! let plugin = plugin_from_operation_id_fn(|name| PrintLayer { name }); //! ``` //! //! # Combine [`Plugin`]s @@ -55,6 +57,7 @@ //! use aws_smithy_http_server::{ //! operation::{Operation, OperationShape}, //! plugin::{Plugin, PluginPipeline, PluginStack}, +//! shape_id::ShapeId, //! }; //! # use tower::{layer::util::Stack, Layer, Service}; //! # use std::task::{Context, Poll}; @@ -63,7 +66,7 @@ //! #[derive(Clone, Debug)] //! pub struct PrintService { //! inner: S, -//! name: &'static str, +//! id: ShapeId, //! } //! //! impl Service for PrintService @@ -79,7 +82,7 @@ //! } //! //! fn call(&mut self, req: R) -> Self::Future { -//! println!("Hi {}", self.name); +//! println!("Hi {}", self.id.absolute()); //! self.inner.call(req) //! } //! } @@ -87,7 +90,7 @@ //! /// A [`Layer`] which constructs the [`PrintService`]. //! #[derive(Debug)] //! pub struct PrintLayer { -//! name: &'static str, +//! id: ShapeId, //! } //! impl Layer for PrintLayer { //! type Service = PrintService; @@ -95,7 +98,7 @@ //! fn layer(&self, service: S) -> Self::Service { //! PrintService { //! inner: service, -//! name: self.name, +//! id: self.id.clone(), //! } //! } //! } @@ -112,7 +115,7 @@ //! type Layer = Stack; //! //! fn map(&self, input: Operation) -> Operation { -//! input.layer(PrintLayer { name: Op::NAME }) +//! input.layer(PrintLayer { id: Op::NAME }) //! } //! } //! ``` @@ -129,9 +132,9 @@ mod stack; use crate::operation::Operation; -pub use closure::{plugin_from_operation_name_fn, OperationNameFn}; +pub use closure::{plugin_from_operation_id_fn, OperationIdFn}; pub use either::Either; -pub use filter::{filter_by_operation_name, FilterByOperationName}; +pub use filter::{filter_by_operation_id, FilterByOperationId}; pub use identity::IdentityPlugin; pub use layer::HttpLayer; pub use pipeline::PluginPipeline; diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs index 2a956922e4..d38ecfd782 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs @@ -35,19 +35,20 @@ use super::HttpLayer; /// /// `PluginPipeline` is itself a [`Plugin`]: you can apply any transformation that expects a /// [`Plugin`] to an entire pipeline. In this case, we want to use -/// [`filter_by_operation_name`](crate::plugin::filter_by_operation_name) to limit the scope of +/// [`filter_by_operation_name`](crate::plugin::filter_by_operation_id) to limit the scope of /// the logging and metrics plugins to the `CheckHealth` operation: /// /// ```rust -/// use aws_smithy_http_server::plugin::{filter_by_operation_name, PluginPipeline}; +/// use aws_smithy_http_server::plugin::{filter_by_operation_id, PluginPipeline}; /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; +/// use aws_smithy_http_server::shape_id::ShapeId; /// # struct CheckHealth; -/// # impl CheckHealth { const NAME: &'static str = "MyName"; } +/// # impl CheckHealth { const NAME: ShapeId = ShapeId::new("namespace#MyName", "namespace", "MyName"); } /// /// // The logging and metrics plugins will only be applied to the `CheckHealth` operation. -/// let operation_specific_pipeline = filter_by_operation_name( +/// let operation_specific_pipeline = filter_by_operation_id( /// PluginPipeline::new() /// .push(LoggingPlugin) /// .push(MetricsPlugin), @@ -156,7 +157,7 @@ impl

PluginPipeline

{ /// { /// // [...] /// fn map(&self, input: Operation) -> Operation { - /// input.layer(PrintLayer { name: Op::NAME }) + /// input.layer(PrintLayer { id: Op::NAME }) /// } /// } /// ``` diff --git a/rust-runtime/aws-smithy-http-server/src/shape_id.rs b/rust-runtime/aws-smithy-http-server/src/shape_id.rs new file mode 100644 index 0000000000..1cb13f0921 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/shape_id.rs @@ -0,0 +1,59 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Extension types. +//! +//! Shape ID is a type that describes a Smithy shape. +//! +//! # Example +//! +//! In the following model: +//! ```smithy +//! namespace smithy.example +//! +//! operation CheckHealth {} +//! ``` +//! +//! - `absolute` is `"smithy.example#CheckHealth"` +//! - `namespace` is `"smithy.example"` +//! - `name` is `"CheckHealth"` + +pub use crate::request::extension::{Extension, MissingExtension}; + +/// Shape ID for a modelled Smithy shape. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ShapeId { + absolute: &'static str, + + namespace: &'static str, + name: &'static str, +} + +impl ShapeId { + /// Constructs a new [`ShapeId`]. This is used by the code-generator which preserves the invariants of the Shape ID format. + #[doc(hidden)] + pub const fn new(absolute: &'static str, namespace: &'static str, name: &'static str) -> Self { + Self { + absolute, + namespace, + name, + } + } + + /// Returns the Smithy operation namespace. + pub fn namespace(&self) -> &'static str { + self.namespace + } + + /// Returns the Smithy operation name. + pub fn name(&self) -> &'static str { + self.name + } + + /// Returns the absolute operation shape ID. + pub fn absolute(&self) -> &'static str { + self.absolute + } +} From ec45767a4183db21a671ca43e5e8dc15910deeec Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 9 Jun 2023 10:55:42 -0500 Subject: [PATCH 143/253] Avoid exposing `Arc`ed `ResolveEndpoint` in public API (#2758) ## Motivation and Context Hides `Arc` from public API. ## Description This PR replaces the occurrences of `Arc` with `SharedEndpointResolver` to not expose bare `Arc`s in the public API. ## Testing - [x] Passed tests in CI ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ --------- Co-authored-by: Yuki Saito --- CHANGELOG.next.toml | 6 ++++++ .../timestream/TimestreamDecorator.kt | 5 ++++- .../endpoint/EndpointConfigCustomization.kt | 19 ++++++++++++------- .../smithy/endpoint/EndpointsDecorator.kt | 2 ++ .../codegen/client/smithy/endpoint/Util.kt | 1 + .../ServiceRuntimePluginGenerator.kt | 3 +-- 6 files changed, 26 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index a23ab7455c..918e5140bc 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -162,3 +162,9 @@ filter_by_operation_id(plugin, |id| id.absolute() != "namespace#name"); author = "82marbag" references = ["smithy-rs#2678"] meta = { "breaking" = true, "tada" = false, "bug" = false } + +[[smithy-rs]] +message = "The occurrences of `Arc` have now been replaced with `SharedEndpointResolver` in public APIs." +references = ["smithy-rs#2758"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } +author = "ysaito1001" diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt index ac2e8928e0..695110e590 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt @@ -45,6 +45,7 @@ class TimestreamDecorator : ClientCodegenDecorator { }, ) } + override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { val endpointDiscovery = InlineAwsDependency.forRustFile( "endpoint_discovery", @@ -87,7 +88,7 @@ class TimestreamDecorator : ClientCodegenDecorator { time ) .await?; - new_conf.endpoint_resolver = ::std::sync::Arc::new(resolver); + new_conf.endpoint_resolver = #{SharedEndpointResolver}::new(resolver); Ok((Self::from_conf(new_conf), reloader)) } } @@ -96,6 +97,8 @@ class TimestreamDecorator : ClientCodegenDecorator { "endpoint_discovery" to endpointDiscovery.toType(), "SystemTime" to RuntimeType.std.resolve("time::SystemTime"), "Duration" to RuntimeType.std.resolve("time::Duration"), + "SharedEndpointResolver" to RuntimeType.smithyHttp(codegenContext.runtimeConfig) + .resolve("endpoint::SharedEndpointResolver"), "SystemTimeSource" to RuntimeType.smithyAsync(codegenContext.runtimeConfig) .resolve("time::SystemTimeSource"), *Types(codegenContext.runtimeConfig).toArray(), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index 2d742bbbec..6d74f1a7dc 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -13,6 +13,7 @@ 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.RuntimeType.Companion.preludeScope /** * Customization which injects an Endpoints 2.0 Endpoint Resolver into the service config struct @@ -28,14 +29,17 @@ internal class EndpointConfigCustomization( override fun section(section: ServiceConfig): Writable { return writable { + val sharedEndpointResolver = "#{SharedEndpointResolver}<#{Params}>" val resolverTrait = "#{SmithyResolver}<#{Params}>" val codegenScope = arrayOf( + *preludeScope, + "SharedEndpointResolver" to types.sharedEndpointResolver, "SmithyResolver" to types.resolveEndpoint, "Params" to typesGenerator.paramsStruct(), ) when (section) { is ServiceConfig.ConfigStruct -> rustTemplate( - "pub (crate) endpoint_resolver: std::sync::Arc,", + "pub (crate) endpoint_resolver: $sharedEndpointResolver,", *codegenScope, ) @@ -43,7 +47,7 @@ internal class EndpointConfigCustomization( rustTemplate( """ /// Returns the endpoint resolver. - pub fn endpoint_resolver(&self) -> std::sync::Arc { + pub fn endpoint_resolver(&self) -> $sharedEndpointResolver { self.endpoint_resolver.clone() } """, @@ -52,7 +56,7 @@ internal class EndpointConfigCustomization( is ServiceConfig.BuilderStruct -> rustTemplate( - "endpoint_resolver: Option>,", + "endpoint_resolver: #{Option}<$sharedEndpointResolver>,", *codegenScope, ) @@ -99,7 +103,7 @@ internal class EndpointConfigCustomization( /// Sets the endpoint resolver to use when making requests. $defaultResolverDocs pub fn endpoint_resolver(mut self, endpoint_resolver: impl $resolverTrait + 'static) -> Self { - self.endpoint_resolver = Some(std::sync::Arc::new(endpoint_resolver) as _); + self.endpoint_resolver = #{Some}(#{SharedEndpointResolver}::new(endpoint_resolver)); self } @@ -107,7 +111,7 @@ internal class EndpointConfigCustomization( /// /// When unset, the client will used a generated endpoint resolver based on the endpoint resolution /// rules for `$moduleUseName`. - pub fn set_endpoint_resolver(&mut self, endpoint_resolver: Option>) -> &mut Self { + pub fn set_endpoint_resolver(&mut self, endpoint_resolver: #{Option}<$sharedEndpointResolver>) -> &mut Self { self.endpoint_resolver = endpoint_resolver; self } @@ -122,7 +126,7 @@ internal class EndpointConfigCustomization( rustTemplate( """ endpoint_resolver: self.endpoint_resolver.unwrap_or_else(|| - std::sync::Arc::new(#{DefaultResolver}::new()) + #{SharedEndpointResolver}::new(#{DefaultResolver}::new()) ), """, *codegenScope, @@ -150,8 +154,9 @@ internal class EndpointConfigCustomization( // always fail. In the future, this will be changed to an `expect()` rustTemplate( """ - endpoint_resolver: self.endpoint_resolver.unwrap_or_else(||std::sync::Arc::new(#{FailingResolver})), + endpoint_resolver: self.endpoint_resolver.unwrap_or_else(||#{SharedEndpointResolver}::new(#{FailingResolver})), """, + *codegenScope, "FailingResolver" to alwaysFailsResolver, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt index c6384a3861..c2cf1c9fd3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt @@ -169,12 +169,14 @@ class EndpointsDecorator : ClientCodegenDecorator { val codegenScope = arrayOf( *RuntimeType.preludeScope, "Params" to typesGenerator.paramsStruct(), + "ResolveEndpoint" to types.resolveEndpoint, "ResolveEndpointError" to types.resolveEndpointError, ) return when (section) { is OperationSection.MutateInput -> writable { rustTemplate( """ + use #{ResolveEndpoint}; let params_result = #{Params}::builder()#{builderFields:W}.build() .map_err(|err| #{ResolveEndpointError}::from_source("could not construct endpoint parameters", err)); let (endpoint_result, params) = match params_result { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt index df909e9452..bed14f40ae 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/Util.kt @@ -48,6 +48,7 @@ class Types(runtimeConfig: RuntimeConfig) { private val smithyTypesEndpointModule = RuntimeType.smithyTypes(runtimeConfig).resolve("endpoint") val smithyHttpEndpointModule = RuntimeType.smithyHttp(runtimeConfig).resolve("endpoint") val resolveEndpoint = smithyHttpEndpointModule.resolve("ResolveEndpoint") + val sharedEndpointResolver = smithyHttpEndpointModule.resolve("SharedEndpointResolver") val smithyEndpoint = smithyTypesEndpointModule.resolve("Endpoint") val resolveEndpointError = smithyHttpEndpointModule.resolve("ResolveEndpointError") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index e495aeae2c..0d6b924584 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -97,7 +97,6 @@ class ServiceRuntimePluginGenerator( "Params" to endpointTypesGenerator.paramsStruct(), "ResolveEndpoint" to http.resolve("endpoint::ResolveEndpoint"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), - "SharedEndpointResolver" to http.resolve("endpoint::SharedEndpointResolver"), "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "default_connector" to client.resolve("conns::default_connector"), "require_connector" to client.resolve("conns::require_connector"), @@ -134,7 +133,7 @@ class ServiceRuntimePluginGenerator( cfg.set_auth_option_resolver(#{StaticAuthOptionResolver}::new(#{Vec}::new())); let endpoint_resolver = #{DefaultEndpointResolver}::<#{Params}>::new( - #{SharedEndpointResolver}::from(self.handle.conf.endpoint_resolver())); + self.handle.conf.endpoint_resolver()); cfg.set_endpoint_resolver(endpoint_resolver); // TODO(enableNewSmithyRuntime): Use the `store_append` method of ConfigBag to insert classifiers From 11cf41d944a3e3e42e46b93f932bc007a1273fba Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Mon, 12 Jun 2023 17:46:58 +0100 Subject: [PATCH 144/253] Simplify `Plugin` trait (#2740) ## Motivation and Context https://github.com/awslabs/smithy-rs/issues/2444 ## Description - Simplify `Plugin`, make it closer to `Layer`. - Remove `Operation`. --- CHANGELOG.next.toml | 199 +++++- .../codegen/server/smithy/ServerRustModule.kt | 36 +- .../smithy/generators/DocHandlerGenerator.kt | 31 +- .../generators/ServerOperationGenerator.kt | 2 +- .../smithy/generators/ServerRootGenerator.kt | 5 +- .../ServerRuntimeTypesReExportsGenerator.kt | 1 - .../generators/ServerServiceGenerator.kt | 152 ++++- design/src/server/anatomy.md | 639 +++++++++--------- design/src/server/instrumentation.md | 35 +- design/src/server/middleware.md | 224 +++--- .../tests/plugins_execution_order.rs | 25 +- examples/pokemon-service/src/main.rs | 6 +- examples/pokemon-service/src/plugin.rs | 39 +- .../aws-smithy-http-server/src/extension.rs | 63 +- .../src/instrumentation/layer.rs | 67 -- .../src/instrumentation/mod.rs | 6 +- .../src/instrumentation/plugin.rs | 37 +- .../src/instrumentation/service.rs | 4 +- .../src/operation/handler.rs | 4 +- .../src/operation/mod.rs | 87 +-- .../src/operation/operation_service.rs | 4 +- .../src/operation/shape.rs | 27 +- .../src/operation/upgrade.rs | 189 ++---- .../src/plugin/alb_health_check.rs | 4 +- .../src/plugin/closure.rs | 20 +- .../src/plugin/either.rs | 38 +- .../src/plugin/filter.rs | 30 +- .../src/plugin/identity.rs | 11 +- .../src/plugin/layer.rs | 47 +- .../aws-smithy-http-server/src/plugin/mod.rs | 58 +- .../src/plugin/pipeline.rs | 41 +- .../src/plugin/stack.rs | 15 +- 32 files changed, 1091 insertions(+), 1055 deletions(-) delete mode 100644 rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 918e5140bc..fa8f427f1b 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -141,13 +141,13 @@ message = """`ShapeId` is the new structure used to represent a shape, with its Before you had an operation and an absolute name as its `NAME` member. You could apply a plugin only to some selected operation: ``` -filter_by_operation_name(plugin, |name| name != Op::NAME); +filter_by_operation_name(plugin, |name| name != Op::ID); ``` Your new filter selects on an operation's absolute name, namespace or name. ``` -filter_by_operation_id(plugin, |id| id.name() != Op::NAME.name()); +filter_by_operation_id(plugin, |id| id.name() != Op::ID.name()); ``` The above filter is applied to an operation's name, the one you use to specify the operation in the Smithy model. @@ -168,3 +168,198 @@ message = "The occurrences of `Arc` have now been replaced references = ["smithy-rs#2758"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } author = "ysaito1001" + +[[smithy-rs]] +message = """ +The `Plugin` trait has now been simplified and the `Operation` struct has been removed. + +## Simplication of the `Plugin` trait + +Previously, + +```rust +trait Plugin { + type Service; + type Layer; + + fn map(&self, input: Operation) -> Operation; +} +``` + +modified an `Operation`. + +Now, + +```rust +trait Plugin { + type Service; + + fn apply(&self, svc: S) -> Self::Service; +} +``` + +maps a `tower::Service` to a `tower::Service`. This is equivalent to `tower::Layer` with two extra type parameters: `Protocol` and `Operation`. + +The following middleware setup + +```rust +pub struct PrintService { + inner: S, + name: &'static str, +} + +impl Service for PrintService +where + S: Service, +{ + async fn call(&mut self, req: R) -> Self::Future { + println!("Hi {}", self.name); + self.inner.call(req) + } +} + +pub struct PrintLayer { + name: &'static str, +} + +impl Layer for PrintLayer { + type Service = PrintService; + + fn layer(&self, service: S) -> Self::Service { + PrintService { + inner: service, + name: self.name, + } + } +} + +pub struct PrintPlugin; + +impl Plugin for PrintPlugin +where + Op: OperationShape, +{ + type Service = S; + type Layer = Stack; + + fn map(&self, input: Operation) -> Operation { + input.layer(PrintLayer { name: Op::NAME }) + } +} +``` + +now becomes + +```rust +pub struct PrintService { + inner: S, + name: &'static str, +} + +impl Service for PrintService +where + S: Service, +{ + async fn call(&mut self, req: R) -> Self::Future { + println!("Hi {}", self.name); + self.inner.call(req) + } +} + +pub struct PrintPlugin; + +impl Plugin for PrintPlugin +where + Op: OperationShape, +{ + type Service = PrintService; + + fn apply(&self, svc: S) -> Self::Service { + PrintService { inner, name: Op::ID.name() } + } +} +``` + +A single `Plugin` can no longer apply a `tower::Layer` on HTTP requests/responses _and_ modelled structures at the same time (see middleware positions [C](https://awslabs.github.io/smithy-rs/design/server/middleware.html#c-operation-specific-http-middleware) and [D](https://awslabs.github.io/smithy-rs/design/server/middleware.html#d-operation-specific-model-middleware). Instead one `Plugin` must be specified for each and passed to the service builder constructor separately: + +```rust +let app = PokemonService::builder_with_plugins(/* HTTP plugins */, /* model plugins */) + /* setters */ + .build() + .unwrap(); +``` + +The motivation behind this change is to simplify the job of middleware authors, separate concerns, accomodate common cases better, and to improve composition internally. + +Because `Plugin` is now closer to `tower::Layer` we have two canonical converters: + +```rust +use aws_smithy_http_server::plugin::{PluginLayer, LayerPlugin}; + +// Convert from `Layer` to `Plugin` which applies uniformly across all operations +let layer = /* some layer */; +let plugin = PluginLayer(layer); + +// Convert from `Plugin` to `Layer` for some fixed protocol and operation +let plugin = /* some plugin */; +let layer = LayerPlugin::new::(plugin); +``` + +## Remove `Operation` + +The `aws_smithy_http_server::operation::Operation` structure has now been removed. Previously, there existed a `{operation_name}_operation` setter on the service builder, which accepted an `Operation`. This allowed users to + +```rust +let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_service(/* tower::Service */); + +let app = PokemonService::builder_without_plugins() + .get_pokemon_species_operation(operation) + /* other setters */ + .build() + .unwrap(); +``` + +to set an operation with a `tower::Service`, and + +```rust +let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_service(/* tower::Service */).layer(/* layer */); +let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_handler(/* closure */).layer(/* layer */); + +let app = PokemonService::builder_without_plugins() + .get_pokemon_species_operation(operation) + /* other setters */ + .build() + .unwrap(); +``` + +to add a `tower::Layer` (acting on HTTP requests/responses post-routing) to a single operation. + +We have seen little adoption of this API and for this reason we have opted instead to introduce a new setter, accepting a `tower::Service`, on the service builder: + +```rust +let app = PokemonService::builder_without_plugins() + .get_pokemon_species_service(/* tower::Service */) + /* other setters */ + .build() + .unwrap(); +``` + +Applying a `tower::Layer` to a _single_ operation is now done through the `Plugin` API: + +```rust +use aws_smithy_http_server::plugin::{PluginLayer, filter_by_operation_name, IdentityPlugin}; + +let plugin = PluginLayer(/* layer */); +let scoped_plugin = filter_by_operation_name(plugin, |id| id == GetPokemonSpecies::ID); + +let app = PokemonService::builder_with_plugins(scoped_plugin, IdentityPlugin) + .get_pokemon_species(/* handler */) + /* other setters */ + .build() + .unwrap(); +``` + +""" +references = ["smithy-rs#2740"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "hlbarber" diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustModule.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustModule.kt index 15fde9837f..2c2ef75f74 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustModule.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRustModule.kt @@ -27,7 +27,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.module import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticInputTrait import software.amazon.smithy.rust.codegen.core.smithy.traits.SyntheticOutputTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import software.amazon.smithy.rust.codegen.server.smithy.generators.DocHandlerGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.handlerImports @@ -73,49 +72,22 @@ class ServerModuleDocProvider(private val codegenContext: ServerCodegenContext) val operations = index.getContainedOperations(codegenContext.serviceShape).toSortedSet(compareBy { it.id }) val firstOperation = operations.first() ?: return@writable - val firstOperationSymbol = codegenContext.symbolProvider.toSymbol(firstOperation) - val firstOperationName = firstOperationSymbol.name.toPascalCase() val crateName = codegenContext.settings.moduleName.toSnakeCase() rustTemplate( """ /// A collection of types representing each operation defined in the service closure. /// - /// ## Constructing an [`Operation`](#{SmithyHttpServer}::operation::OperationShapeExt) - /// - /// To apply middleware to specific operations the [`Operation`](#{SmithyHttpServer}::operation::Operation) - /// API must be used. - /// - /// Using the [`OperationShapeExt`](#{SmithyHttpServer}::operation::OperationShapeExt) trait - /// implemented on each ZST we can construct an [`Operation`](#{SmithyHttpServer}::operation::Operation) - /// with appropriate constraints given by Smithy. - /// - /// #### Example - /// - /// ```no_run - /// use $crateName::operation_shape::$firstOperationName; - /// use #{SmithyHttpServer}::operation::OperationShapeExt; - /// - #{HandlerImports:W} - /// - #{Handler:W} - /// - /// let operation = $firstOperationName::from_handler(handler) - /// .layer(todo!("Provide a layer implementation")); - /// ``` - /// - /// ## Use as Marker Structs - /// - /// The [plugin system](#{SmithyHttpServer}::plugin) also makes use of these + /// The [plugin system](#{SmithyHttpServer}::plugin) makes use of these /// [zero-sized types](https://doc.rust-lang.org/nomicon/exotic-sizes.html##zero-sized-types-zsts) (ZSTs) to - /// parameterize [`Plugin`](#{SmithyHttpServer}::plugin::Plugin) implementations. The traits, such as - /// [`OperationShape`](#{SmithyHttpServer}::operation::OperationShape) can be used to provide + /// parameterize [`Plugin`](#{SmithyHttpServer}::plugin::Plugin) implementations. Their traits, such as + /// [`OperationShape`](#{SmithyHttpServer}::operation::OperationShape), can be used to provide /// operation specific information to the [`Layer`](#{Tower}::Layer) being applied. """.trimIndent(), "SmithyHttpServer" to ServerCargoDependency.smithyHttpServer(codegenContext.runtimeConfig).toType(), "Tower" to ServerCargoDependency.Tower.toType(), - "Handler" to DocHandlerGenerator(codegenContext, firstOperation, "handler", commentToken = "///")::render, + "Handler" to DocHandlerGenerator(codegenContext, firstOperation, "handler", commentToken = "///").docSignature(), "HandlerImports" to handlerImports(crateName, operations, commentToken = "///"), ) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt index a0dcf07ba9..d67b79b455 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/DocHandlerGenerator.kt @@ -6,10 +6,8 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.CodegenContext import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -55,14 +53,25 @@ class DocHandlerGenerator( } } - fun render(writer: RustWriter) { - // This assumes that the `error` (if applicable) `input`, and `output` modules have been imported by the - // caller and hence are in scope. - writer.rustTemplate( - """ - #{Handler:W} - """, - "Handler" to docSignature(), - ) + /** + * Similarly to `docSignature`, returns the function signature of an operation handler implementation, with the + * difference that we don't ellide the error for use in `tower::service_fn`. + */ + fun docFixedSignature(): Writable { + val errorT = if (operation.errors.isEmpty()) { + "std::convert::Infallible" + } else { + "${ErrorModule.name}::${errorSymbol.name}" + } + + return writable { + rust( + """ + $commentToken async fn $handlerName(input: ${InputModule.name}::${inputSymbol.name}) -> Result<${OutputModule.name}::${outputSymbol.name}, $errorT> { + $commentToken todo!() + $commentToken } + """.trimIndent(), + ) + } } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt index a8ce8cbf06..3a5af6cb50 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerOperationGenerator.kt @@ -56,7 +56,7 @@ class ServerOperationGenerator( pub struct $operationName; impl #{SmithyHttpServer}::operation::OperationShape for $operationName { - const NAME: #{SmithyHttpServer}::shape_id::ShapeId = #{SmithyHttpServer}::shape_id::ShapeId::new(${operationIdAbsolute.dq()}, ${operationId.namespace.dq()}, ${operationId.name.dq()}); + const ID: #{SmithyHttpServer}::shape_id::ShapeId = #{SmithyHttpServer}::shape_id::ShapeId::new(${operationIdAbsolute.dq()}, ${operationId.namespace.dq()}, ${operationId.name.dq()}); type Input = crate::input::${operationName}Input; type Output = crate::output::${operationName}Output; diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt index a8221b9327..c8c1dd8450 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt @@ -53,7 +53,7 @@ open class ServerRootGenerator( val hasErrors = operations.any { it.errors.isNotEmpty() } val handlers: Writable = operations .map { operation -> - DocHandlerGenerator(codegenContext, operation, builderFieldNames[operation]!!, "//!")::render + DocHandlerGenerator(codegenContext, operation, builderFieldNames[operation]!!, "//!").docSignature() } .join("//!\n") @@ -110,6 +110,7 @@ open class ServerRootGenerator( //! Plugins allow you to build middleware which is aware of the operation it is being applied to. //! //! ```rust + //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin; //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as LoggingPlugin; //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as MetricsPlugin; //! ## use #{Hyper}::Body; @@ -119,7 +120,7 @@ open class ServerRootGenerator( //! let plugins = PluginPipeline::new() //! .push(LoggingPlugin) //! .push(MetricsPlugin); - //! let builder: $builderName = $serviceName::builder_with_plugins(plugins); + //! let builder: $builderName = $serviceName::builder_with_plugins(plugins, IdentityPlugin); //! ``` //! //! Check out [`#{SmithyHttpServer}::plugin`] to learn more about plugins. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt index b184ccbaa4..6318c0483c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt @@ -28,7 +28,6 @@ class ServerRuntimeTypesReExportsGenerator( } pub mod operation { pub use #{SmithyHttpServer}::operation::OperationShape; - pub use #{SmithyHttpServer}::operation::Operation; } pub mod plugin { pub use #{SmithyHttpServer}::plugin::Plugin; diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt index d78a7aa903..57dd3c82b4 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt @@ -52,7 +52,6 @@ class ServerServiceGenerator( private val service = codegenContext.serviceShape private val serviceName = service.id.name.toPascalCase() private val builderName = "${serviceName}Builder" - private val builderPluginGenericTypeName = "Plugin" private val builderBodyGenericTypeName = "Body" /** Calculate all `operationShape`s contained within the `ServiceShape`. */ @@ -105,6 +104,9 @@ class ServerServiceGenerator( private fun builderSetters(): Writable = writable { for ((operationShape, structName) in operationStructNames) { val fieldName = builderFieldNames[operationShape] + val docHandler = DocHandlerGenerator(codegenContext, operationShape, "handler", "///") + val handler = docHandler.docSignature() + val handlerFixed = docHandler.docFixedSignature() rustTemplate( """ /// Sets the [`$structName`](crate::operation_shape::$structName) operation. @@ -129,44 +131,123 @@ class ServerServiceGenerator( /// ## let app: $serviceName<#{SmithyHttpServer}::routing::Route<#{SmithyHttp}::body::SdkBody>> = app; /// ``` /// - pub fn $fieldName(self, handler: HandlerType) -> Self + pub fn $fieldName(self, handler: HandlerType) -> Self where HandlerType: #{SmithyHttpServer}::operation::Handler, - #{SmithyHttpServer}::operation::Operation<#{SmithyHttpServer}::operation::IntoService>: - #{SmithyHttpServer}::operation::Upgradable< - #{Protocol}, - crate::operation_shape::$structName, - ServiceExtractors, - $builderBodyGenericTypeName, - $builderPluginGenericTypeName, - > + + ModelPlugin: #{SmithyHttpServer}::plugin::Plugin< + #{Protocol}, + crate::operation_shape::$structName, + #{SmithyHttpServer}::operation::IntoService + >, + #{SmithyHttpServer}::operation::UpgradePlugin::: #{SmithyHttpServer}::plugin::Plugin< + #{Protocol}, + crate::operation_shape::$structName, + ModelPlugin::Service + >, + HttpPlugin: #{SmithyHttpServer}::plugin::Plugin< + #{Protocol}, + crate::operation_shape::$structName, + < + #{SmithyHttpServer}::operation::UpgradePlugin:: + as #{SmithyHttpServer}::plugin::Plugin< + #{Protocol}, + crate::operation_shape::$structName, + ModelPlugin::Service + > + >::Service + >, + + HttpPlugin::Service: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, + >>::Future: Send + 'static, + { use #{SmithyHttpServer}::operation::OperationShapeExt; - self.${fieldName}_operation(crate::operation_shape::$structName::from_handler(handler)) + use #{SmithyHttpServer}::plugin::Plugin; + let svc = crate::operation_shape::$structName::from_handler(handler); + let svc = self.model_plugin.apply(svc); + let svc = #{SmithyHttpServer}::operation::UpgradePlugin::::new().apply(svc); + let svc = self.http_plugin.apply(svc); + self.${fieldName}_custom(svc) } /// Sets the [`$structName`](crate::operation_shape::$structName) operation. /// - /// This should be an [`Operation`](#{SmithyHttpServer}::operation::Operation) created from - /// [`$structName`](crate::operation_shape::$structName) using either - /// [`OperationShape::from_handler`](#{SmithyHttpServer}::operation::OperationShapeExt::from_handler) or - /// [`OperationShape::from_service`](#{SmithyHttpServer}::operation::OperationShapeExt::from_service). - pub fn ${fieldName}_operation(mut self, operation: Operation) -> Self + /// This should be an async function satisfying the [`Handler`](#{SmithyHttpServer}::operation::Handler) trait. + /// See the [operation module documentation](#{SmithyHttpServer}::operation) for more information. + /// + /// ## Example + /// + /// ```no_run + /// use $crateName::$serviceName; + /// + #{HandlerImports:W} + /// + #{HandlerFixed:W} + /// + /// let svc = #{Tower}::util::service_fn(handler); + /// let app = $serviceName::builder_without_plugins() + /// .${fieldName}_service(svc) + /// /* Set other handlers */ + /// .build() + /// .unwrap(); + /// ## let app: $serviceName<#{SmithyHttpServer}::routing::Route<#{SmithyHttp}::body::SdkBody>> = app; + /// ``` + /// + pub fn ${fieldName}_service(self, service: S) -> Self where - Operation: #{SmithyHttpServer}::operation::Upgradable< + S: #{SmithyHttpServer}::operation::OperationService, + + ModelPlugin: #{SmithyHttpServer}::plugin::Plugin< + #{Protocol}, + crate::operation_shape::$structName, + #{SmithyHttpServer}::operation::Normalize + >, + #{SmithyHttpServer}::operation::UpgradePlugin::: #{SmithyHttpServer}::plugin::Plugin< + #{Protocol}, + crate::operation_shape::$structName, + ModelPlugin::Service + >, + HttpPlugin: #{SmithyHttpServer}::plugin::Plugin< #{Protocol}, crate::operation_shape::$structName, - Extractors, - $builderBodyGenericTypeName, - $builderPluginGenericTypeName, - > + < + #{SmithyHttpServer}::operation::UpgradePlugin:: + as #{SmithyHttpServer}::plugin::Plugin< + #{Protocol}, + crate::operation_shape::$structName, + ModelPlugin::Service + > + >::Service + >, + + HttpPlugin::Service: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, + >>::Future: Send + 'static, + + { + use #{SmithyHttpServer}::operation::OperationShapeExt; + use #{SmithyHttpServer}::plugin::Plugin; + let svc = crate::operation_shape::$structName::from_service(service); + let svc = self.model_plugin.apply(svc); + let svc = #{SmithyHttpServer}::operation::UpgradePlugin::::new().apply(svc); + let svc = self.http_plugin.apply(svc); + self.${fieldName}_custom(svc) + } + + /// Sets the [`$structName`](crate::operation_shape::$structName) to a custom [`Service`](tower::Service). + /// not constrained by the Smithy contract. + fn ${fieldName}_custom(mut self, svc: S) -> Self + where + S: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, + S::Future: Send + 'static, { - self.$fieldName = Some(operation.upgrade(&self.plugin)); + self.$fieldName = Some(#{SmithyHttpServer}::routing::Route::new(svc)); self } """, "Protocol" to protocol.markerStruct(), - "Handler" to DocHandlerGenerator(codegenContext, operationShape, "handler", "///")::render, + "Handler" to handler, + "HandlerFixed" to handlerFixed, "HandlerImports" to handlerImports(crateName, operations), *codegenScope, ) @@ -187,7 +268,7 @@ class ServerServiceGenerator( rust( """ if self.$fieldName.is_none() { - $missingOperationsVariableName.insert(crate::operation_shape::$operationZstTypeName::NAME, ".$fieldName()"); + $missingOperationsVariableName.insert(crate::operation_shape::$operationZstTypeName::ID, ".$fieldName()"); } """, ) @@ -278,13 +359,8 @@ class ServerServiceGenerator( ( $requestSpecsModuleName::$specBuilderFunctionName(), self.$fieldName.unwrap_or_else(|| { - #{SmithyHttpServer}::routing::Route::new(<#{SmithyHttpServer}::operation::FailOnMissingOperation as #{SmithyHttpServer}::operation::Upgradable< - #{Protocol}, - crate::operation_shape::$operationZstTypeName, - (), - _, - _, - >>::upgrade(#{SmithyHttpServer}::operation::FailOnMissingOperation, &self.plugin)) + let svc = #{SmithyHttpServer}::operation::MissingFailure::<#{Protocol}>::default(); + #{SmithyHttpServer}::routing::Route::new(svc) }) ), """, @@ -318,7 +394,7 @@ class ServerServiceGenerator( /** Returns a `Writable` containing the builder struct definition and its implementations. */ private fun builder(): Writable = writable { - val builderGenerics = listOf(builderBodyGenericTypeName, builderPluginGenericTypeName).joinToString(", ") + val builderGenerics = listOf(builderBodyGenericTypeName, "HttpPlugin", "ModelPlugin").joinToString(", ") rustTemplate( """ /// The service builder for [`$serviceName`]. @@ -326,7 +402,8 @@ class ServerServiceGenerator( /// Constructed via [`$serviceName::builder_with_plugins`] or [`$serviceName::builder_without_plugins`]. pub struct $builderName<$builderGenerics> { ${builderFields.joinToString(", ")}, - plugin: $builderPluginGenericTypeName, + http_plugin: HttpPlugin, + model_plugin: ModelPlugin } impl<$builderGenerics> $builderName<$builderGenerics> { @@ -398,18 +475,19 @@ class ServerServiceGenerator( /// /// Check out [`PluginPipeline`](#{SmithyHttpServer}::plugin::PluginPipeline) if you need to apply /// multiple plugins. - pub fn builder_with_plugins(plugin: Plugin) -> $builderName { + pub fn builder_with_plugins(http_plugin: HttpPlugin, model_plugin: ModelPlugin) -> $builderName { $builderName { #{NotSetFields:W}, - plugin + http_plugin, + model_plugin } } /// Constructs a builder for [`$serviceName`]. /// /// Use [`$serviceName::builder_with_plugins`] if you need to specify plugins. - pub fn builder_without_plugins() -> $builderName { - Self::builder_with_plugins(#{SmithyHttpServer}::plugin::IdentityPlugin) + pub fn builder_without_plugins() -> $builderName { + Self::builder_with_plugins(#{SmithyHttpServer}::plugin::IdentityPlugin, #{SmithyHttpServer}::plugin::IdentityPlugin) } } diff --git a/design/src/server/anatomy.md b/design/src/server/anatomy.md index 9c65c87bab..6ed01d0f50 100644 --- a/design/src/server/anatomy.md +++ b/design/src/server/anatomy.md @@ -41,36 +41,38 @@ service PokemonService { Smithy Rust will use this model to produce the following API: -```rust,ignore +```rust +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# use pokemon_service_server_sdk::{input::*, output::*, error::*, operation_shape::*, PokemonService}; // A handler for the `GetPokemonSpecies` operation (the `PokemonSpecies` resource). async fn get_pokemon_species(input: GetPokemonSpeciesInput) -> Result { - /* implementation */ + todo!() } -// Apply a `tower::Layer` to a handler. -let get_pokemon_species_op = GetPokemonSpecies::from_handler(get_pokemon_species).layer(/* some `tower::Layer` */); - // Use the service builder to create `PokemonService`. let pokemon_service = PokemonService::builder_without_plugins() // Pass the handler directly to the service builder... .get_pokemon_species(get_pokemon_species) - // ...or pass the layered handler. - .get_pokemon_species_operation(get_pokemon_species_op) /* other operation setters */ .build() + # ; Result::<(), ()>::Ok(()) .expect("failed to create an instance of the Pokémon service"); +# let pokemon_service: Result, _> = pokemon_service; ``` ## Operations A [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) specifies the input, output, and possible errors of an API operation. One might characterize a Smithy Operation as syntax for specifying a function type. -We represent this in Rust using the [`OperationShape`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L8-L22) trait: +We represent this in Rust using the [`OperationShape`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/trait.OperationShape.html) trait: -```rust,ignore +```rust +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::shape_id::ShapeId; pub trait OperationShape { /// The name of the operation. - const NAME: &'static str; + const ID: ShapeId; /// The operation input. type Input; @@ -80,6 +82,13 @@ pub trait OperationShape { /// exists. type Error; } +# use aws_smithy_http_server::operation::OperationShape as OpS; +# impl OperationShape for T { +# const ID: ShapeId = ::ID; +# type Input = ::Input; +# type Output = ::Output; +# type Error = ::Error; +# } ``` For each Smithy Operation shape, @@ -97,12 +106,16 @@ operation GetPokemonSpecies { the following implementation is generated -```rust,ignore +```rust +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::{operation::OperationShape, shape_id::ShapeId}; +# use pokemon_service_server_sdk::{input::*, output::*, error::*}; /// Retrieve information about a Pokémon species. pub struct GetPokemonSpecies; impl OperationShape for GetPokemonSpecies { - const NAME: &'static str = "com.aws.example#GetPokemonSpecies"; + const ID: ShapeId = ShapeId::new("com.aws.example#GetPokemonSpecies", "com.aws.example", "GetPokemonSpecies"); type Input = GetPokemonSpeciesInput; type Output = GetPokemonSpeciesOutput; @@ -116,119 +129,117 @@ Note that the `GetPokemonSpecies` marker structure is a zero-sized type (ZST), a The following nomenclature will aid us in our survey. We describe a `tower::Service` as a "model service" if its request and response are Smithy structures, as defined by the `OperationShape` trait - the `GetPokemonSpeciesInput`, `GetPokemonSpeciesOutput`, and `GetPokemonSpeciesError` described above. Similarly, we describe a `tower::Service` as a "HTTP service" if its request and response are [`http`](https://github.com/hyperium/http) structures - `http::Request` and `http::Response`. -In contrast to the marker ZSTs above, the [`Operation`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/mod.rs#L192-L198) structure holds the actual runtime behavior of an operation, which is specified, during construction, by the customer. - -```rust,ignore -/// A Smithy operation, represented by a [`Service`](tower::Service) `S` and a [`Layer`](tower::Layer) `L`. -/// -/// The `L` is held and applied lazily during [`Upgradable::upgrade`]. -pub struct Operation { - inner: S, - layer: L, -} -``` - -The `S` here is a model service, this is specified during construction of the `Operation`. The constructors exist on the marker ZSTs as an extension trait to `OperationShape`, namely [`OperationShapeExt`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/shape.rs#L24-L45): +The constructors exist on the marker ZSTs as an extension trait to `OperationShape`, namely [`OperationShapeExt`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/trait.OperationShapeExt.html): -```rust,ignore +```rust +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::operation::*; /// An extension trait over [`OperationShape`]. pub trait OperationShapeExt: OperationShape { - /// Creates a new [`Operation`] for well-formed [`Handler`]s. - fn from_handler(handler: H) -> Operation> + /// Creates a new [`Service`] for well-formed [`Handler`]s. + fn from_handler(handler: H) -> IntoService where - H: Handler, - Self: Sized, - { - Operation::from_handler(handler) - } + H: Handler, + Self: Sized; - /// Creates a new [`Operation`] for well-formed [`Service`](tower::Service)s. - fn from_service(svc: S) -> Operation> + /// Creates a new [`Service`] for well-formed [`Service`](tower::Service)s. + fn from_service(svc: S) -> Normalize where - S: OperationService, - Self: Sized, - { - Operation::from_service(svc) - } + S: OperationService, + Self: Sized; } +# use aws_smithy_http_server::operation::OperationShapeExt as OpS; +# impl OperationShapeExt for T { +# fn from_handler(handler: H) -> IntoService where H: Handler, Self: Sized { ::from_handler(handler) } +# fn from_service(svc: S) -> Normalize where S: OperationService, Self: Sized { ::from_service(svc) } +# } ``` Observe that there are two constructors provided: `from_handler` which takes a `H: Handler` and `from_service` which takes a `S: OperationService`. In both cases `Self` is passed as a parameter to the traits - this constrains `handler: H` and `svc: S` to the signature given by the implementation of `OperationShape` on `Self`. -The [`Handler`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/handler.rs#L21-L29) and [`OperationService`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs#L15-L29) both serve a similar purpose - they provide a common interface for converting to a model service `S`. +The [`Handler`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/trait.Handler.html) and [`OperationService`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/trait.OperationService.html) both serve a similar purpose - they provide a common interface for converting to a model service `S`. - The `Handler` trait covers all async functions taking `GetPokemonSpeciesInput` and asynchronously returning a `Result`. - The `OperationService` trait covers all `tower::Service`s with request `GetPokemonSpeciesInput`, response `GetPokemonSpeciesOutput` and error `GetPokemonSpeciesOutput`. The `from_handler` constructor is used in the following way: -```rust,ignore -async fn get_pokemon_service(input: GetPokemonServiceInput) -> Result { - /* Handler logic */ +```rust +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +use pokemon_service_server_sdk::{ + input::GetPokemonSpeciesInput, + output::GetPokemonSpeciesOutput, + error::GetPokemonSpeciesError, + operation_shape::GetPokemonSpecies +}; +use aws_smithy_http_server::operation::OperationShapeExt; + +async fn get_pokemon_service(input: GetPokemonSpeciesInput) -> Result { + todo!() } -let operation = GetPokemonService::from_handler(get_pokemon_service); +let operation = GetPokemonSpecies::from_handler(get_pokemon_service); ``` Alternatively, `from_service` constructor: -```rust,ignore -struct Svc { - /* ... */ -} - -impl Service for Svc { - type Response = GetPokemonServiceOutput; - type Error = GetPokemonServiceError; +```rust +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# extern crate tower; +use pokemon_service_server_sdk::{ + input::GetPokemonSpeciesInput, + output::GetPokemonSpeciesOutput, + error::GetPokemonSpeciesError, + operation_shape::GetPokemonSpecies +}; +use aws_smithy_http_server::operation::OperationShapeExt; +use std::task::{Context, Poll}; +use tower::Service; +struct Svc { /* ... */ } -let svc: Svc = /* ... */; -let operation = GetPokemonService::from_service(svc); -``` - -To summarize, the `S`, in `Operation`, is a _model service_ constructed from a `Handler` or a `OperationService` subject to the constraints of an `OperationShape`. More detailed information on these conversions is provided in the [Handler and OperationService section](https://github.com/awslabs/smithy-rs/blob/39c0096c33417d44f125a042c112b3c16918098a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs#L50-L100) Rust docs. +impl Service for Svc { + type Response = GetPokemonSpeciesOutput; + type Error = GetPokemonSpeciesError; + type Future = /* Future> */ + # std::future::Ready>; -Now, what about the `L` in `Operation`? The `L` is a [`tower::Layer`](https://docs.rs/tower/latest/tower/layer/trait.Layer.html), or colloquially "middleware", that is applied to a _HTTP service_. Note that this means that `L` is _not_ applied directly to `S`. We can append to `L` using the `Operation::layer` method: + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + todo!() + } -```rust,ignore -impl Operation { - /// Applies a [`Layer`] to the operation _after_ it has been upgraded via [`Operation::upgrade`]. - pub fn layer(self, layer: NewL) -> Operation> { - Operation { - inner: self.inner, - layer: Stack::new(self.layer, layer), - } + fn call(&mut self, input: GetPokemonSpeciesInput) -> Self::Future { + todo!() } } -``` - -where [`tower::layer::util::Stack`](https://docs.rs/tower/latest/tower/layer/util/struct.Stack.html) is used to chain layers together. -A typical use of this might be: - -```rust,ignore -let operation = GetPokemonSpecies::from_handler(handler).layer(RequestBodyLimitLayer::new(500)); +let svc: Svc = Svc { /* ... */ }; +let operation = GetPokemonSpecies::from_service(svc); ``` -where [`RequestBodyLimitLayer`](https://docs.rs/tower-http/latest/tower_http/limit/struct.RequestBodyLimitLayer.html) limits the size of the HTTP request body to the `GetPokemonSpecies` operation. - -As mentioned, `L` is applied _after_ the `Operation` has been "upgraded" to a HTTP service. The procedure of upgrading a model service to a HTTP service is described in the [Upgrading a Model Service](#upgrading-a-model-service) section below. +To summarize a _model service_ constructed can be constructed from a `Handler` or a `OperationService` subject to the constraints of an `OperationShape`. More detailed information on these conversions is provided in the [Handler and OperationService section](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/index.html) Rust docs. ## Serialization and Deserialization -A [Smithy protocol](https://awslabs.github.io/smithy/2.0/spec/protocol-traits.html#serialization-and-protocol-traits) specifies the serialization/deserialization scheme - how a HTTP request is transformed into a modelled input and a modelled output to a HTTP response. The is formalized using the [`FromRequest`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/request.rs#L156-L164) and [`IntoResponse`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/response.rs#L40-L44) traits: +A [Smithy protocol](https://awslabs.github.io/smithy/2.0/spec/protocol-traits.html#serialization-and-protocol-traits) specifies the serialization/deserialization scheme - how a HTTP request is transformed into a modelled input and a modelled output to a HTTP response. The is formalized using the [`FromRequest`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/request/trait.FromRequest.html) and [`IntoResponse`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/response.rs#L40-L44) traits: -```rust,ignore +```rust +# extern crate aws_smithy_http_server; +# extern crate http; +# use aws_smithy_http_server::body::BoxBody; +# use std::future::Future; /// Provides a protocol aware extraction from a [`Request`]. This consumes the /// [`Request`], in contrast to [`FromParts`]. -pub trait FromRequest: Sized { +pub trait FromRequest: Sized { type Rejection: IntoResponse; type Future: Future>; /// Extracts `self` from a [`Request`] asynchronously. - fn from_request(request: http::Request) -> Self::Future; + fn from_request(request: http::Request) -> Self::Future; } /// A protocol aware function taking `self` to [`http::Response`]. @@ -236,11 +247,30 @@ pub trait IntoResponse { /// Performs a conversion into a [`http::Response`]. fn into_response(self) -> http::Response; } +# use aws_smithy_http_server::request::FromRequest as FR; +# impl> FromRequest for T { +# type Rejection = >::Rejection; +# type Future = >::Future; +# fn from_request(request: http::Request) -> Self::Future { +# >::from_request(request) +# } +# } +# use aws_smithy_http_server::response::IntoResponse as IR; +# impl> IntoResponse

for T { +# fn into_response(self) -> http::Response { >::into_response(self) } +# } ``` Note that both traits are parameterized by `Protocol`. These [protocols](https://awslabs.github.io/smithy/2.0/aws/protocols/index.html) exist as ZST marker structs: -```rust,ignore +```rust +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::proto::{ +# aws_json_10::AwsJson1_0 as _, +# aws_json_11::AwsJson1_1 as _, +# rest_json_1::RestJson1 as _, +# rest_xml::RestXml as _, +# }; /// [AWS REST JSON 1.0 Protocol](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restjson1-protocol.html). pub struct RestJson1; @@ -272,23 +302,16 @@ stateDiagram-v2 into_response --> [*]: HTTP Response ``` -This is formalized by the [`Upgrade`](https://github.com/awslabs/smithy-rs/blob/9a6de1f533f8743dbbc3fa6ad974d104c8b841f4/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L74-L82) HTTP service. The `tower::Service` implementation is approximately: +This is formalized by the [`Upgrade`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/struct.Upgrade.html) HTTP service. The `tower::Service` implementation is approximately: ```rust,ignore impl Service for Upgrade where - // `Op` is used to specify the operation shape - Op: OperationShape, - // Smithy input must convert from a HTTP request - Op::Input: FromRequest

, - // Smithy output must convert into a HTTP response - Op::Output: IntoResponse

, - // Smithy error must convert into a HTTP response - OpError: IntoResponse

, - - // The signature of the inner service is correct - S: Service, - + Input: FromRequest, + S: Service, + S::Response: IntoResponse

, + S::Error: IntoResponse

, +{ async fn call(&mut self, request: http::Request) -> http::Response { let model_request = match ::from_request(request).await { Ok(ok) => ok, @@ -297,75 +320,50 @@ where let model_response = self.model_service.call(model_request).await; model_response.into_response() } +} ``` -When we `GetPokemonService::from_handler` or `GetPokemonService::from_service`, the model service produced, `S`, will meet the constraints above. +When we `GetPokemonSpecies::from_handler` or `GetPokemonSpecies::from_service`, the model service produced, `S`, will meet the constraints above. -There is an associated `Layer`, `UpgradeLayer` which constructs `Upgrade` from a service. +There is an associated `Plugin`, `UpgradePlugin` which constructs `Upgrade` from a service. The upgrade procedure is finalized by the application of the `Layer` `L`, referenced in `Operation`. In this way the entire upgrade procedure takes an `Operation` and returns a HTTP service. ```mermaid stateDiagram-v2 direction LR - [*] --> S: HTTP Request - state L { - state Upgrade { - S + [*] --> UpgradePlugin: HTTP Request + state HttpPlugin { + state UpgradePlugin { + direction LR + [*] --> S: Model Input + S --> [*] : Model Output + state ModelPlugin { + S + } } } - S --> [*]: HTTP Response -``` - -Note that the `S` and `L` are specified by logic written, in Rust, by the customer, whereas `Upgrade`/`UpgradeLayer` is specified entirely by Smithy model via the protocol, [HTTP bindings](https://awslabs.github.io/smithy/2.0/spec/http-bindings.html), etc. - -The procedure of taking a struct and transforming it into a HTTP service is formalized by the [`Upgradable`](https://github.com/awslabs/smithy-rs/blob/9a6de1f533f8743dbbc3fa6ad974d104c8b841f4/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs#L220-L225) trait: - -```rust,ignore -/// An interface to convert a representation of a Smithy operation into a [`Route`]. -pub trait Upgradable { - /// Upgrade the representation of a Smithy operation to a [`Route`]. - fn upgrade(self, plugin: &Plugin) -> Route; -} + UpgradePlugin --> [*]: HTTP Response ``` -Why do we need a trait for this? Why not simply write an `upgrade` method on `Operation`? The reason is that we might _not_ want to supply an `Operation` to the service builder, instead we might want to supply something that overrides the typical upgrade procedure. - -Below we give an example of a ZST which can be provided to the builder, which also satisfies `Upgradable` and returns a `MissingFailure` `tower::Service`. This `MissingFailure` service simply returns a status code 500. - -```rust,ignore -/// A marker struct indicating an [`Operation`] has not been set in a builder. -/// -/// This _does_ implement [`Upgradable`] but produces a [`Service`] which always returns an internal failure message. -pub struct FailOnMissingOperation; - -impl Upgradable for FailOnMissingOperation -where - InternalFailureException: IntoResponse, - Protocol: 'static, -{ - fn upgrade(self, _plugin: &Plugin) -> Route { - Route::new(MissingFailure { _protocol: PhantomData }) - } -} -``` - -We go into more detail on how the `Upgradable` trait is used in conjunction with builders in the [Builders](#builders) section below. +Note that the `S` is specified by logic written, in Rust, by the customer, whereas `UpgradePlugin` is specified entirely by Smithy model via the protocol, [HTTP bindings](https://awslabs.github.io/smithy/2.0/spec/http-bindings.html), etc. ## Routers Different protocols supported by Smithy enjoy different routing mechanisms, for example, [AWS JSON 1.0](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-json-1_0-protocol.html#protocol-behaviors) uses the `X-Amz-Target` header to select an operation, whereas [AWS REST XML](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restxml-protocol.html) uses the [HTTP label trait](https://awslabs.github.io/smithy/2.0/spec/http-bindings.html#httplabel-trait). -Despite their differences, all routing mechanisms satisfy a common interface. This is formalized using the `Router` trait: +Despite their differences, all routing mechanisms satisfy a common interface. This is formalized using the [Router](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/routing/trait.Router.html) trait: -```rust,ignore +```rust +# extern crate aws_smithy_http_server; +# extern crate http; /// An interface for retrieving an inner [`Service`] given a [`http::Request`]. -pub trait Router { +pub trait Router { type Service; type Error; /// Matches a [`http::Request`] to a target [`Service`]. - fn match_route(&self, request: &http::Request) -> Result; + fn match_route(&self, request: &http::Request) -> Result; } ``` @@ -423,6 +421,90 @@ state in <> ServiceC --> [*] ``` +## Plugins + + +A [`Plugin`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/plugin/trait.Plugin.html) is a +[`tower::Layer`] with two extra type parameters, `Protocol` and `Operation`. This allows the middleware to be +parameterized them and change behavior depending on the context in which it's applied. + +```rust +# extern crate aws_smithy_http_server; +pub trait Plugin { + type Service; + + fn apply(&self, svc: S) -> Self::Service; +} +# use aws_smithy_http_server::plugin::Plugin as Pl; +# impl> Plugin for T { +# type Service = >::Service; +# fn apply(&self, svc: S) -> Self::Service { >::apply(self, svc) } +# } +``` + +An example `Plugin` implementation can be found in [/examples/pokemon-service/src/plugin.rs](https://github.com/awslabs/smithy-rs/blob/main/examples/pokemon-service/src/plugin.rs). + +Plugins can be applied in two places: + +- HTTP plugins, which are applied pre-deserialization/post-serialization, acting on HTTP requests/responses. +- Model plugins, which are applied post-deserialization/pre-serialization, acting on model inputs/outputs/errors. + +```mermaid +stateDiagram-v2 + direction LR + [*] --> S: HTTP Request + state HttpPlugin { + state UpgradePlugin { + state ModelPlugin { + S + } + } + } + S --> [*]: HTTP Response +``` + +The service builder API requires plugins to be specified upfront - they must be passed as an argument to `builder_with_plugins` and cannot be modified afterwards. + +You might find yourself wanting to apply _multiple_ plugins to your service. +This can be accommodated via [`PluginPipeline`]. + +```rust +# extern crate aws_smithy_http_server; +use aws_smithy_http_server::plugin::PluginPipeline; +# use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; +# use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; + +let pipeline = PluginPipeline::new().push(LoggingPlugin).push(MetricsPlugin); +``` + +The plugins' runtime logic is executed in registration order. +In the example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last. + +If you are vending a plugin, you can leverage `PluginPipeline` as an extension point: you can add custom methods to it using an extension trait. +For example: + +```rust +# extern crate aws_smithy_http_server; +use aws_smithy_http_server::plugin::{PluginPipeline, PluginStack}; +# use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; +# use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; + +pub trait AuthPluginExt { + fn with_auth(self) -> PluginPipeline>; +} + +impl AuthPluginExt for PluginPipeline { + fn with_auth(self) -> PluginPipeline> { + self.push(AuthPlugin) + } +} + +let pipeline = PluginPipeline::new() + .push(LoggingPlugin) + // Our custom method! + .with_auth(); +``` + ## Builders The service builder is the primary public API, generated for every [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html). @@ -430,56 +512,88 @@ At a high-level, the service builder takes as input a function for each Smithy O You can create an instance of a service builder by calling either `builder_without_plugins` or `builder_with_plugins` on the corresponding service struct. -> Plugins? What plugins? Don't worry, they'll be covered in a [dedicated section](#plugins) later on! - -```rust,ignore +```rust +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::routing::Route; /// The service builder for [`PokemonService`]. /// /// Constructed via [`PokemonService::builder`]. -pub struct PokemonServiceBuilder { +pub struct PokemonServiceBuilder { capture_pokemon_operation: Option>, empty_operation: Option>, get_pokemon_species: Option>, get_server_statistics: Option>, get_storage: Option>, health_check_operation: Option>, - plugin: Plugin + http_plugin: HttpPlugin, + model_plugin: ModelPlugin } ``` The builder has two setter methods for each [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation) in the [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service): ```rust,ignore - /// Sets the [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) operation. - /// - /// This should be an async function satisfying the [`Handler`](aws_smithy_http_server::operation::Handler) trait. - /// See the [operation module documentation](aws_smithy_http_server::operation) for more information. - pub fn get_pokemon_species( - self, - handler: HandlerType, - ) -> Self + pub fn get_pokemon_species(self, handler: HandlerType) -> Self + where + HandlerType:Handler, + + ModelPlugin: Plugin< + RestJson1, + GetPokemonSpecies, + IntoService + >, + UpgradePlugin::: Plugin< + RestJson1, + GetPokemonSpecies, + ModelPlugin::Service + >, + HttpPlugin: Plugin< + RestJson1, + GetPokemonSpecies, + UpgradePlugin::::Service + >, + { + let svc = GetPokemonSpecies::from_handler(handler); + let svc = self.model_plugin.apply(svc); + let svc = UpgradePlugin::::new() + .apply(svc); + let svc = self.http_plugin.apply(svc); + self.get_pokemon_species_custom(svc) + } + + pub fn get_pokemon_species_service(self, service: S) -> Self where - HandlerType: Handler, - Operation>: - Upgradable, + S: OperationService, + + ModelPlugin: Plugin< + RestJson1, + GetPokemonSpecies, + Normalize + >, + UpgradePlugin::: Plugin< + RestJson1, + GetPokemonSpecies, + ModelPlugin::Service + >, + HttpPlugin: Plugin< + RestJson1, + GetPokemonSpecies, + UpgradePlugin::::Service + >, { - self.get_pokemon_species_operation(GetPokemonSpecies::from_handler(handler)) + let svc = GetPokemonSpecies::from_service(service); + let svc = self.model_plugin.apply(svc); + let svc = UpgradePlugin::::new().apply(svc); + let svc = self.http_plugin.apply(svc); + self.get_pokemon_species_custom(svc) } - /// Sets the [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) operation. - /// - /// This should be an [`Operation`](aws_smithy_http_server::operation::Operation) created from - /// [`GetPokemonSpecies`](crate::operation_shape::GetPokemonSpecies) using either - /// [`OperationShape::from_handler`](aws_smithy_http_server::operation::OperationShapeExt::from_handler) or - /// [`OperationShape::from_service`](aws_smithy_http_server::operation::OperationShapeExt::from_service). - pub fn get_pokemon_species_operation( - self, - operation: Operation, - ) -> Self + pub fn get_pokemon_species_custom(mut self, svc: S) -> Self where - Operation: Upgradable, + S: Service, Response = Response, Error = Infallible>, { - self.get_pokemon_species = Some(operation.upgrade(&self.plugin)) + self.get_pokemon_species = Some(Route::new(svc)); + self } ``` @@ -499,7 +613,9 @@ Both builder methods take care of: The final outcome, an instance of `PokemonService`, looks roughly like this: -```rust,ignore +```rust +# extern crate aws_smithy_http_server; +# use aws_smithy_http_server::{routing::RoutingService, proto::rest_json_1::{router::RestRouter, RestJson1}}; /// The Pokémon Service allows you to retrieve information about Pokémon species. #[derive(Clone)] pub struct PokemonService { @@ -518,19 +634,22 @@ stateDiagram-v2 state "..." as C4 direction LR [*] --> in : HTTP Request - UpgradeLayer --> [*]: HTTP Response + UpgradePlugin --> [*]: HTTP Response state PokemonService { state RoutingService { - in --> UpgradeLayer: HTTP Request + in --> UpgradePlugin: HTTP Request in --> C2: HTTP Request in --> C3: HTTP Request in --> C4: HTTP Request state C1 { - state L { - state UpgradeLayer { + state HttpPlugin { + state UpgradePlugin { direction LR [*] --> S: Model Input S --> [*] : Model Output + state ModelPlugin { + S + } } } } @@ -545,159 +664,15 @@ stateDiagram-v2 C4 --> [*]: HTTP Response ``` -## Plugins - - -There are a variety of places in which the customer can apply middleware. During the build: - -- For a specific operation, for example `GetPokemonSpecies`, the model service can be wrapped by a `Layer` before passing it to `GetPokemonSpecies::from_service` constructor. -- The `Operation::layer` method can be used to apply a `Layer` to a specific operation _after_ it's been upgraded. - -After the build is finalized: - -- The entire `PokemonService` HTTP service can be wrapped by a `Layer`. -- Every `Route` in the `Router` can be wrapped by a `Layer` using `PokemonService::layer`. - -Although this provides a reasonably "complete" API, it can be cumbersome in some use cases. Suppose a customer wants to log the operation name when a request is routed to said operation. Writing a `Layer`, `NameLogger`, to log an operation name is simple, however with the current API the customer is forced to do the following - -```rust,ignore -let get_pokemon_species = GetPokemonSpecies::from_handler(/* handler */).layer(NameLogger::new("GetPokemonSpecies")); -let get_storage = GetStorage::from_handler(/* handler */).layer(NameLogger::new("GetStorage")); -let do_nothing = DoNothing::from_handler(/* handler */).layer(NameLogger::new("DoNothing")); -/* Repeat for every route... */ -``` - -Note that `PokemonService::layer` cannot be used here because it applies a _single_ layer uniformly across all `Route`s stored in the `Router`. - -```rust,ignore -impl PokemonService { - /// Applies a [`Layer`](tower::Layer) uniformly to all routes. - pub fn layer(self, layer: &L) -> PokemonService - where - L: Layer, - { - PokemonService { - router: self.router.map(|s| s.layer(layer)), - } - } -} -``` - -The plugin system solves the general problem of modifying `Operation` prior to the upgrade procedure in a way parameterized by the protocol and operation marker structures. This parameterization removes the excessive boilerplate above. - -The central trait is [`Plugin`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/plugin.rs#L31-L41): - -```rust,ignore -/// A mapping from one [`Operation`] to another. Used to modify the behavior of -/// [`Upgradable`](crate::operation::Upgradable) and therefore the resulting service builder. -/// -/// The generics `Protocol` and `Op` allow the behavior to be parameterized. -pub trait Plugin { - type Service; - type Layer; - - /// Maps an [`Operation`] to another. - fn map(&self, input: Operation) -> Operation; -} -``` - -The `Upgradable::upgrade` method on `Operation`, previously presented in [Upgrading a Model Service](#upgrading-a-model-service), is more accurately: - -```rust,ignore - /// Takes the [`Operation`](Operation), applies [`Plugin`], then applies [`UpgradeLayer`] to - /// the modified `S`, then finally applies the modified `L`. - /// - /// The composition is made explicit in the method constraints and return type. - fn upgrade(self, plugin: &Pl) -> Route { - let mapped = plugin.map(self); - let layer = Stack::new(UpgradeLayer::new(), mapped.layer); - Route::new(layer.layer(mapped.inner)) - } -``` - -```mermaid -stateDiagram-v2 - direction TB - Op1: Operation#60;S1, L1#62; - state Op1 { - direction LR - [*] --> S1 : HTTP Request - S1 --> [*]: HTTP Response - state L1 { - Upgrade1 : Upgrade - state Upgrade1 { - S1 - } - } - - } - - Op2: Operation#60;S2, L2#62; - state Op2 { - direction LR - [*] --> S2: HTTP Request - S2 --> [*]: HTTP Response - state L2 { - Upgrade2 : Upgrade - state Upgrade2 { - S2 - } - } - } - - Op1 --> Op2 : Plugin#colon;#colon;map -``` - -An example `Plugin` implementation can be found in [/examples/pokemon-service/src/plugin.rs](https://github.com/awslabs/smithy-rs/blob/main/examples/pokemon-service/src/plugin.rs). - -The service builder API requires plugins to be specified upfront - they must be passed as an argument to `builder_with_plugins` and cannot be modified afterwards. -This constraint is in place to ensure that all handlers are upgraded using the same set of plugins. - -You might find yourself wanting to apply _multiple_ plugins to your service. -This can be accommodated via [`PluginPipeline`]. - -```rust,ignore -use aws_smithy_http_server::plugin::PluginPipeline; -# use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; -# use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; - -let pipeline = PluginPipeline::new().push(LoggingPlugin).push(MetricsPlugin); -``` - -The plugins' runtime logic is executed in registration order. -In the example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last. - -If you are vending a plugin, you can leverage `PluginPipeline` as an extension point: you can add custom methods to it using an extension trait. -For example: - -```rust,ignore -use aws_smithy_http_server::plugin::{PluginPipeline, PluginStack}; -# use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; -# use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; - -pub trait AuthPluginExt { - fn with_auth(self) -> PluginPipeline>; -} - -impl AuthPluginExt for PluginPipeline { - fn with_auth(self) -> PluginPipeline> { - self.push(AuthPlugin) - } -} - -let pipeline = PluginPipeline::new() - .push(LoggingPlugin) - // Our custom method! - .with_auth(); -``` - ## Accessing Unmodelled Data -An additional omitted detail is that we provide an "escape hatch" allowing `Handler`s and `OperationService`s to accept data that isn't modelled. In addition to accepting `Op::Input` they can accept additional arguments which implement the [`FromParts`](https://github.com/awslabs/smithy-rs/blob/4c5cbc39384f0d949d7693eb87b5853fe72629cd/rust-runtime/aws-smithy-http-server/src/request.rs#L114-L121) trait: - -```rust,ignore -use http::request::Parts; +An additional omitted detail is that we provide an "escape hatch" allowing `Handler`s and `OperationService`s to accept data that isn't modelled. In addition to accepting `Op::Input` they can accept additional arguments which implement the [`FromParts`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/request/trait.FromParts.html) trait: +```rust +# extern crate aws_smithy_http_server; +# extern crate http; +# use http::request::Parts; +# use aws_smithy_http_server::response::IntoResponse; /// Provides a protocol aware extraction from a [`Request`]. This borrows the /// [`Parts`], in contrast to [`FromRequest`]. pub trait FromParts: Sized { @@ -707,9 +682,14 @@ pub trait FromParts: Sized { /// Extracts `self` from a [`Parts`] synchronously. fn from_parts(parts: &mut Parts) -> Result; } +# use aws_smithy_http_server::request::FromParts as FP; +# impl> FromParts

for T { +# type Rejection = >::Rejection; +# fn from_parts(parts: &mut Parts) -> Result { >::from_parts(parts) } +# } ``` -This differs from `FromRequest` trait, introduced in [Serialization and Deserialization](#serialization-and-deserialization), as it's synchronous and has non-consuming access to [`Parts`](https://docs.rs/http/0.2.8/http/request/struct.Parts.html), rather than the entire [Request](https://docs.rs/http/0.2.8/http/request/struct.Request.html). +This differs from `FromRequest` trait, introduced in [Serialization and Deserialization](#serialization-and-deserialization), as it's synchronous and has non-consuming access to [`Parts`](https://docs.rs/http/latest/http/request/struct.Parts.html), rather than the entire [Request](https://docs.rs/http/latest/http/request/struct.Request.html). ```rust,ignore pub struct Parts { @@ -724,7 +704,14 @@ pub struct Parts { This is commonly used to access types stored within [`Extensions`](https://docs.rs/http/0.2.8/http/struct.Extensions.html) which have been inserted by a middleware. An `Extension` struct implements `FromParts` to support this use case: -```rust,ignore +```rust +# extern crate aws_smithy_http_server; +# extern crate http; +# extern crate thiserror; +# use aws_smithy_http_server::{body::BoxBody, request::FromParts, response::IntoResponse}; +# use http::status::StatusCode; +# use thiserror::Error; +# fn empty() -> BoxBody { todo!() } /// Generic extension type stored in and extracted from [request extensions]. /// /// This is commonly used to share state across handlers. diff --git a/design/src/server/instrumentation.md b/design/src/server/instrumentation.md index b24ba6775c..dfb5ee5252 100644 --- a/design/src/server/instrumentation.md +++ b/design/src/server/instrumentation.md @@ -19,7 +19,11 @@ RUST_LOG=aws_smithy_http_server=warn,aws_smithy_http_server_python=error and -```rust,ignore,ignore +```rust +# extern crate tracing_subscriber; +# extern crate tracing; +# use tracing_subscriber::filter; +# use tracing::Level; let filter = filter::Targets::new().with_target("aws_smithy_http_server", Level::DEBUG); ``` @@ -55,14 +59,24 @@ Smithy provides an out-the-box middleware which: This is enabled via the `instrument` method provided by the `aws_smithy_http_server::instrumentation::InstrumentExt` trait. -```rust,ignore -use aws_smithy_http_server::instrumentation::InstrumentExt; - -let plugins = PluginPipeline::new().instrument(); -let app = PokemonService::builder_with_plugins(plugins) - .get_pokemon_species(/* handler */) +```rust,no_run +# extern crate aws_smithy_http_server; +# extern crate pokemon_service_server_sdk; +# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, input::*, output::*, error::*}; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +use aws_smithy_http_server::{ + instrumentation::InstrumentExt, + plugin::{IdentityPlugin, PluginPipeline} +}; +use pokemon_service_server_sdk::PokemonService; + +let http_plugins = PluginPipeline::new().instrument(); +let app = PokemonService::builder_with_plugins(http_plugins, IdentityPlugin) + .get_pokemon_species(handler) /* ... */ - .build(); + .build() + .unwrap(); +# let app: PokemonService = app; ``` @@ -71,7 +85,10 @@ let app = PokemonService::builder_with_plugins(plugins) The Pokémon service example, located at `/examples/pokemon-service`, sets up a `tracing` `Subscriber` as follows: -```rust,ignore,ignore +```rust +# extern crate tracing_subscriber; +use tracing_subscriber::{prelude::*, EnvFilter}; + /// Setup `tracing::subscriber` to read the log level from RUST_LOG environment variable. pub fn setup_tracing() { let format = tracing_subscriber::fmt::layer().pretty(); diff --git a/design/src/server/middleware.md b/design/src/server/middleware.md index eb8d46c9ae..a04ae3abec 100644 --- a/design/src/server/middleware.md +++ b/design/src/server/middleware.md @@ -49,7 +49,7 @@ The `Service` trait can be thought of as an asynchronous function from a request Middleware in `tower` typically conforms to the following pattern, a `Service` implementation of the form -```rust,ignore +```rust pub struct NewService { inner: S, /* auxillary data */ @@ -58,7 +58,11 @@ pub struct NewService { and a complementary -```rust,ignore +```rust +# extern crate tower; +# pub struct NewService { inner: S } +use tower::{Layer, Service}; + pub struct NewLayer { /* auxiliary data */ } @@ -129,16 +133,30 @@ stateDiagram-v2 where `UpgradeLayer` is the `Layer` converting Smithy model structures to HTTP structures and the `RoutingService` is responsible for routing requests to the appropriate operation. -### A) Outer Middleware +### A. Outer Middleware The output of the Smithy service builder provides the user with a `Service` implementation. A `Layer` can be applied around the entire `Service`. -```rust,ignore +```rust,no_run +# extern crate aws_smithy_http_server; +# extern crate pokemon_service_server_sdk; +# extern crate tower; +# use std::time::Duration; +# struct TimeoutLayer; +# impl TimeoutLayer { fn new(t: Duration) -> Self { Self }} +# impl Layer for TimeoutLayer { type Service = S; fn layer(&self, svc: S) -> Self::Service { svc } } +# use pokemon_service_server_sdk::{input::*, output::*, error::*}; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +use pokemon_service_server_sdk::PokemonService; +use tower::Layer; + // This is a HTTP `Service`. let app /* : PokemonService> */ = PokemonService::builder_without_plugins() - .get_pokemon_species(/* handler */) + .get_pokemon_species(handler) /* ... */ - .build(); + .build() + .unwrap(); +# let app: PokemonService = app; // Construct `TimeoutLayer`. let timeout_layer = TimeoutLayer::new(Duration::from_secs(3)); @@ -147,76 +165,98 @@ let timeout_layer = TimeoutLayer::new(Duration::from_secs(3)); let app = timeout_layer.layer(app); ``` -### B) Route Middleware +### B. Route Middleware A _single_ layer can be applied to _all_ routes inside the `Router`. This exists as a method on the output of the service builder. -```rust,ignore -// Construct `TraceLayer`. -let trace_layer = TraceLayer::new_for_http(Duration::from_secs(3)); +```rust,no_run +# extern crate tower; +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# use tower::{util::service_fn, Layer}; +# use std::time::Duration; +# use pokemon_service_server_sdk::{input::*, output::*, error::*}; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +# struct MetricsLayer; +# impl MetricsLayer { pub fn new() -> Self { Self } } +# impl Layer for MetricsLayer { type Service = S; fn layer(&self, svc: S) -> Self::Service { svc } } +use pokemon_service_server_sdk::PokemonService; + +// Construct `MetricsLayer`. +let metrics_layer = MetricsLayer::new(); let app /* : PokemonService> */ = PokemonService::builder_without_plugins() - .get_pokemon_species(/* handler */) + .get_pokemon_species(handler) /* ... */ .build() + .unwrap() + # ; let app: PokemonService = app; + # app // Apply HTTP logging after routing. - .layer(&trace_layer); + .layer(&metrics_layer); ``` Note that requests pass through this middleware immediately _after_ routing succeeds and therefore will _not_ be encountered if routing fails. This means that the [TraceLayer](https://docs.rs/tower-http/latest/tower_http/trace/struct.TraceLayer.html) in the example above does _not_ provide logs unless routing has completed. This contrasts to [middleware A](#a-outer-middleware), which _all_ requests/responses pass through when entering/leaving the service. -### C) Operation Specific HTTP Middleware +### C. Operation Specific HTTP Middleware A "HTTP layer" can be applied to specific operations. -```rust,ignore -// Construct `TraceLayer`. -let trace_layer = TraceLayer::new_for_http(Duration::from_secs(3)); - -// Apply HTTP logging to only the `GetPokemonSpecies` operation. -let layered_handler = GetPokemonSpecies::from_handler(/* handler */).layer(trace_layer); - -let app /* : PokemonService> */ = PokemonService::builder_without_plugins() - .get_pokemon_species_operation(layered_handler) +```rust,no_run +# extern crate tower; +# extern crate pokemon_service_server_sdk; +# extern crate aws_smithy_http_server; +# use tower::{util::service_fn, Layer}; +# use std::time::Duration; +# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, PokemonService, input::*, output::*, error::*}; +# use aws_smithy_http_server::{operation::OperationShapeExt, plugin::*, operation::*}; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +# struct LoggingLayer; +# impl LoggingLayer { pub fn new() -> Self { Self } } +# impl Layer for LoggingLayer { type Service = S; fn layer(&self, svc: S) -> Self::Service { svc } } +// Construct `LoggingLayer`. +let logging_plugin = LayerPlugin(LoggingLayer::new()); +let logging_plugin = filter_by_operation_id(logging_plugin, |name| name == GetPokemonSpecies::ID); +let http_plugins = PluginPipeline::new().push(logging_plugin); + +let app /* : PokemonService> */ = PokemonService::builder_with_plugins(http_plugins, IdentityPlugin) + .get_pokemon_species(handler) /* ... */ - .build(); + .build() + .unwrap(); +# let app: PokemonService = app; ``` This middleware transforms the operations HTTP requests and responses. -### D) Operation Specific Model Middleware +### D. Operation Specific Model Middleware A "model layer" can be applied to specific operations. -```rust +```rust,no_run # extern crate tower; # extern crate pokemon_service_server_sdk; # extern crate aws_smithy_http_server; # use tower::{util::service_fn, Layer}; -# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, PokemonService, input::*, output::*, error::*}; -# use aws_smithy_http_server::operation::OperationShapeExt; +# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, input::*, output::*, error::*}; # let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +# use aws_smithy_http_server::{operation::*, plugin::*}; # struct BufferLayer; # impl BufferLayer { pub fn new(size: usize) -> Self { Self } } # impl Layer for BufferLayer { type Service = S; fn layer(&self, svc: S) -> Self::Service { svc } } -// A handler `Service`. -let handler_svc = service_fn(handler); +use pokemon_service_server_sdk::PokemonService; // Construct `BufferLayer`. -let buffer_layer = BufferLayer::new(3); - -// Apply a 3 item buffer to `handler_svc`. -let handler_svc = buffer_layer.layer(handler_svc); - -let layered_handler = GetPokemonSpecies::from_service(handler_svc); +let buffer_plugin = LayerPlugin(BufferLayer::new(3)); +let buffer_plugin = filter_by_operation_id(buffer_plugin, |name| name != GetPokemonSpecies::ID); +let model_plugins = PluginPipeline::new().push(buffer_plugin); -let app /* : PokemonService> */ = PokemonService::builder_without_plugins() - .get_pokemon_species_operation(layered_handler) +let app /* : PokemonService> */ = PokemonService::builder_with_plugins(IdentityPlugin, model_plugins) + .get_pokemon_species(handler) /* ... */ .build() - # ;Result::<(), ()>::Ok(()) .unwrap(); -# let app: Result, _> = app; +# let app: PokemonService = app; ``` In contrast to [position C](#c-operation-specific-http-middleware), this middleware transforms the operations modelled inputs to modelled outputs. @@ -227,9 +267,12 @@ Suppose we want to apply a different `Layer` to every operation. In this case, p Consider the following middleware: -```rust,ignore +```rust +# extern crate tower; +use std::task::{Context, Poll}; +use tower::Service; + /// A [`Service`] that adds a print log. -#[derive(Clone, Debug)] pub struct PrintService { inner: S, name: &'static str, @@ -252,77 +295,76 @@ where self.inner.call(req) } } - -/// A [`Layer`] which constructs the [`PrintService`]. -#[derive(Debug)] -pub struct PrintLayer { - name: &'static str, -} -impl Layer for PrintLayer { - type Service = PrintService; - - fn layer(&self, service: S) -> Self::Service { - PrintService { - inner: service, - name: self.name, - } - } -} ``` The plugin system provides a way to construct then apply `Layer`s in position [C](#c-operation-specific-http-middleware) and [D](#d-operation-specific-model-middleware), using the [protocol](https://awslabs.github.io/smithy/2.0/aws/protocols/index.html) and [operation shape](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service-operations) as parameters. -An example of a `PrintPlugin` which applies a layer printing the operation name: +An example of a `PrintPlugin` which prints the operation name: + +```rust +# extern crate aws_smithy_http_server; +# pub struct PrintService { inner: S, name: &'static str } +use aws_smithy_http_server::{plugin::Plugin, operation::OperationShape}; -```rust,ignore -/// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations. +/// A [`Plugin`] for a service builder to add a [`PrintService`] over operations. #[derive(Debug)] pub struct PrintPlugin; -impl Plugin for PrintPlugin +impl Plugin for PrintPlugin where Op: OperationShape, { - type Service = S; - type Layer = Stack; + type Service = PrintService; - fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { name: Op::NAME }) + fn apply(&self, inner: S) -> Self::Service { + PrintService { name: Op::ID.name(), inner } } } ``` -An alternative example which applies a layer for a given protocol: +An alternative example which prints the protocol name: + +```rust +# extern crate aws_smithy_http_server; +# pub struct PrintService { name: &'static str, inner: S} +use aws_smithy_http_server::{ + plugin::Plugin, + proto::{ + aws_json_10::AwsJson1_0, + rest_xml::RestXml, + } +}; -```rust,ignore -/// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations. +/// A [`Plugin`] for a service builder to add a [`PrintService`] over operations. #[derive(Debug)] pub struct PrintPlugin; -impl Plugin for PrintPlugin +impl Plugin for PrintPlugin { - type Service = S; - type Layer = Stack; + type Service = PrintService; - fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { name: "AWS REST JSON v1" }) + fn apply(&self, inner: S) -> Self::Service { + PrintService { name: "AWS JSON 1.0", inner } } } -impl Plugin for PrintPlugin +impl Plugin for PrintPlugin { - type Service = S; - type Layer = Stack; + type Service = PrintService; - fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { name: "AWS REST XML" }) + fn apply(&self, inner: S) -> Self::Service { + PrintService { name: "AWS REST XML", inner } } } ``` You can provide a custom method to add your plugin to a `PluginPipeline` via an extension trait: -```rust,ignore +```rust +# extern crate aws_smithy_http_server; +# pub struct PrintPlugin; +use aws_smithy_http_server::plugin::{PluginPipeline, PluginStack}; + /// This provides a [`print`](PrintExt::print) method on [`PluginPipeline`]. pub trait PrintExt { /// Causes all operations to print the operation name when called. @@ -340,15 +382,29 @@ impl PrintExt for PluginPipeline Plugin for PrintPlugin { type Service = S; fn apply(&self, svc: S) -> Self::Service { svc }} +# trait PrintExt { fn print(self) -> PluginPipeline>; } +# impl PrintExt for PluginPipeline { fn print(self) -> PluginPipeline> { self.push(PrintPlugin) }} +# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, input::*, output::*, error::*}; +# let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; +use aws_smithy_http_server::plugin::{IdentityPlugin, PluginPipeline}; +use pokemon_service_server_sdk::PokemonService; + +let http_plugins = PluginPipeline::new() // [..other plugins..] // The custom method! .print(); -let app /* : PokemonService> */ = PokemonService::builder_with_plugins(plugin_pipeline) - .get_pokemon_species_operation(layered_handler) +let app /* : PokemonService> */ = PokemonService::builder_with_plugins(http_plugins, IdentityPlugin) + .get_pokemon_species(handler) /* ... */ - .build(); + .build() + .unwrap(); +# let app: PokemonService = app; ``` The custom `print` method hides the details of the `Plugin` trait from the average consumer. diff --git a/examples/pokemon-service-common/tests/plugins_execution_order.rs b/examples/pokemon-service-common/tests/plugins_execution_order.rs index 4965bbf9d8..901ed89fc1 100644 --- a/examples/pokemon-service-common/tests/plugins_execution_order.rs +++ b/examples/pokemon-service-common/tests/plugins_execution_order.rs @@ -11,11 +11,7 @@ use std::{ }; use aws_smithy_http::body::SdkBody; -use aws_smithy_http_server::{ - operation::Operation, - plugin::{Plugin, PluginPipeline}, -}; -use tower::layer::util::Stack; +use aws_smithy_http_server::plugin::{IdentityPlugin, Plugin, PluginPipeline}; use tower::{Layer, Service}; use pokemon_service_client::{operation::do_nothing::DoNothingInput, Config}; @@ -41,9 +37,10 @@ async fn plugin_layers_are_executed_in_registration_order() { let pipeline = PluginPipeline::new() .push(SentinelPlugin::new("first", output.clone())) .push(SentinelPlugin::new("second", output.clone())); - let mut app = pokemon_service_server_sdk::PokemonService::builder_with_plugins(pipeline) - .do_nothing(do_nothing) - .build_unchecked(); + let mut app = + pokemon_service_server_sdk::PokemonService::builder_with_plugins(pipeline, IdentityPlugin) + .do_nothing(do_nothing) + .build_unchecked(); let request = DoNothingInput::builder() .build() .unwrap() @@ -68,15 +65,15 @@ impl SentinelPlugin { } } -impl Plugin for SentinelPlugin { - type Service = S; - type Layer = Stack; +impl Plugin for SentinelPlugin { + type Service = SentinelService; - fn map(&self, input: Operation) -> Operation { - input.layer(SentinelLayer { + fn apply(&self, inner: S) -> Self::Service { + SentinelService { + inner, name: self.name, output: self.output.clone(), - }) + } } } diff --git a/examples/pokemon-service/src/main.rs b/examples/pokemon-service/src/main.rs index db7878995b..76a8b8e594 100644 --- a/examples/pokemon-service/src/main.rs +++ b/examples/pokemon-service/src/main.rs @@ -10,7 +10,7 @@ use std::{net::SocketAddr, sync::Arc}; use aws_smithy_http_server::{ extension::OperationExtensionExt, instrumentation::InstrumentExt, - plugin::{alb_health_check::AlbHealthCheckLayer, PluginPipeline}, + plugin::{alb_health_check::AlbHealthCheckLayer, IdentityPlugin, PluginPipeline}, request::request_id::ServerRequestIdProviderLayer, AddExtensionLayer, }; @@ -54,11 +54,11 @@ pub async fn main() { // Adds `tracing` spans and events to the request lifecycle. .instrument() // Handle `/ping` health check requests. - .http_layer(AlbHealthCheckLayer::from_handler("/ping", |_req| async { + .layer(AlbHealthCheckLayer::from_handler("/ping", |_req| async { StatusCode::OK })); - let app = PokemonService::builder_with_plugins(plugins) + let app = PokemonService::builder_with_plugins(plugins, IdentityPlugin) // Build a registry containing implementations to all the operations in the service. These // are async functions or async closures that take as input the operation's input and // return the operation's output. diff --git a/examples/pokemon-service/src/plugin.rs b/examples/pokemon-service/src/plugin.rs index ea6ee09d91..e423663164 100644 --- a/examples/pokemon-service/src/plugin.rs +++ b/examples/pokemon-service/src/plugin.rs @@ -6,11 +6,11 @@ //! Provides an example [`Plugin`] implementation - [`PrintPlugin`]. use aws_smithy_http_server::{ - operation::{Operation, OperationShape}, + operation::OperationShape, plugin::{Plugin, PluginPipeline, PluginStack}, shape_id::ShapeId, }; -use tower::{layer::util::Stack, Layer, Service}; +use tower::Service; use std::task::{Context, Poll}; @@ -38,49 +38,30 @@ where self.inner.call(req) } } - -/// A [`Layer`] which constructs the [`PrintService`]. -#[derive(Debug)] -pub struct PrintLayer { - id: ShapeId, -} -impl Layer for PrintLayer { - type Service = PrintService; - - fn layer(&self, service: S) -> Self::Service { - PrintService { - inner: service, - id: self.id.clone(), - } - } -} - /// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations. #[derive(Debug)] pub struct PrintPlugin; -impl Plugin for PrintPlugin +impl Plugin for PrintPlugin where Op: OperationShape, { - type Service = S; - type Layer = Stack; + type Service = PrintService; - fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { id: Op::NAME }) + fn apply(&self, inner: S) -> Self::Service { + PrintService { inner, id: Op::ID } } } - /// This provides a [`print`](PrintExt::print) method on [`PluginPipeline`]. -pub trait PrintExt { +pub trait PrintExt { /// Causes all operations to print the operation name when called. /// /// This works by applying the [`PrintPlugin`]. - fn print(self) -> PluginPipeline>; + fn print(self) -> PluginPipeline>; } -impl PrintExt for PluginPipeline { - fn print(self) -> PluginPipeline> { +impl PrintExt for PluginPipeline { + fn print(self) -> PluginPipeline> { self.push(PrintPlugin) } } diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index 371df01529..bd25a3f5f9 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -22,14 +22,13 @@ use std::hash::Hash; use std::{fmt, fmt::Debug, future::Future, ops::Deref, pin::Pin, task::Context, task::Poll}; -use crate::extension; use futures_util::ready; use futures_util::TryFuture; use thiserror::Error; -use tower::{layer::util::Stack, Layer, Service}; +use tower::Service; -use crate::operation::{Operation, OperationShape}; -use crate::plugin::{plugin_from_operation_id_fn, OperationIdFn, Plugin, PluginPipeline, PluginStack}; +use crate::operation::OperationShape; +use crate::plugin::{Plugin, PluginPipeline, PluginStack}; use crate::shape_id::ShapeId; pub use crate::request::extension::{Extension, MissingExtension}; @@ -106,23 +105,8 @@ where } } -/// A [`Layer`] applying the [`OperationExtensionService`] to an inner [`Service`]. -#[derive(Debug, Clone)] -pub struct OperationExtensionLayer(OperationExtension); - -impl Layer for OperationExtensionLayer { - type Service = OperationExtensionService; - - fn layer(&self, inner: S) -> Self::Service { - OperationExtensionService { - inner, - operation_extension: self.0.clone(), - } - } -} - -/// A [`Plugin`] which applies [`OperationExtensionLayer`] to every operation. -pub struct OperationExtensionPlugin(OperationIdFn OperationExtensionLayer>); +/// A [`Plugin`] which applies [`OperationExtensionService`] to every operation. +pub struct OperationExtensionPlugin; impl fmt::Debug for OperationExtensionPlugin { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -130,32 +114,31 @@ impl fmt::Debug for OperationExtensionPlugin { } } -impl Plugin for OperationExtensionPlugin +impl Plugin for OperationExtensionPlugin where Op: OperationShape, { - type Service = S; - type Layer = Stack; + type Service = OperationExtensionService; - fn map(&self, input: Operation) -> Operation { - OperationExtensionLayer> as Plugin>::map(&self.0, input) + fn apply(&self, inner: S) -> Self::Service { + OperationExtensionService { + inner, + operation_extension: OperationExtension(Op::ID), + } } } /// An extension trait on [`PluginPipeline`] allowing the application of [`OperationExtensionPlugin`]. /// /// See [`module`](crate::extension) documentation for more info. -pub trait OperationExtensionExt

{ +pub trait OperationExtensionExt { /// Apply the [`OperationExtensionPlugin`], which inserts the [`OperationExtension`] into every [`http::Response`]. - fn insert_operation_extension(self) -> PluginPipeline>; + fn insert_operation_extension(self) -> PluginPipeline>; } -impl

OperationExtensionExt

for PluginPipeline

{ - fn insert_operation_extension(self) -> PluginPipeline> { - let plugin = OperationExtensionPlugin(plugin_from_operation_id_fn(|shape_id| { - OperationExtensionLayer(extension::OperationExtension(shape_id)) - })); - self.push(plugin) +impl OperationExtensionExt for PluginPipeline { + fn insert_operation_extension(self) -> PluginPipeline> { + self.push(OperationExtensionPlugin) } } @@ -201,9 +184,9 @@ impl Deref for RuntimeErrorExtension { #[cfg(test)] mod tests { - use tower::{service_fn, ServiceExt}; + use tower::{service_fn, Layer, ServiceExt}; - use crate::{operation::OperationShapeExt, proto::rest_json_1::RestJson1}; + use crate::{plugin::PluginLayer, proto::rest_json_1::RestJson1}; use super::*; @@ -226,7 +209,7 @@ mod tests { struct DummyOp; impl OperationShape for DummyOp { - const NAME: ShapeId = ShapeId::new( + const ID: ShapeId = ShapeId::new( "com.amazonaws.ebs#CompleteSnapshot", "com.amazonaws.ebs", "CompleteSnapshot", @@ -238,18 +221,16 @@ mod tests { } // Apply `Plugin`. - let operation = DummyOp::from_handler(|_| async { Ok(()) }); let plugins = PluginPipeline::new().insert_operation_extension(); - let op = Plugin::::map(&plugins, operation); // Apply `Plugin`s `Layer`. - let layer = op.layer; + let layer = PluginLayer::new::(plugins); let svc = service_fn(|_: http::Request<()>| async { Ok::<_, ()>(http::Response::new(())) }); let svc = layer.layer(svc); // Check for `OperationExtension`. let response = svc.oneshot(http::Request::new(())).await.unwrap(); - let expected = DummyOp::NAME; + let expected = DummyOp::ID; let actual = response.extensions().get::().unwrap(); assert_eq!(actual.0, expected); } diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs deleted file mode 100644 index c070d1297e..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/layer.rs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use tower::Layer; - -use crate::shape_id::ShapeId; - -use super::{InstrumentOperation, MakeIdentity}; - -/// A [`Layer`] used to apply [`InstrumentOperation`]. -#[derive(Debug)] -pub struct InstrumentLayer { - operation_id: ShapeId, - make_request: RequestMakeFmt, - make_response: ResponseMakeFmt, -} - -impl InstrumentLayer { - /// Constructs a new [`InstrumentLayer`] with no data redacted. - pub fn new(operation_id: ShapeId) -> Self { - Self { - operation_id, - make_request: MakeIdentity, - make_response: MakeIdentity, - } - } -} - -impl InstrumentLayer { - /// Configures the request format. - /// - /// The argument is typically [`RequestFmt`](super::sensitivity::RequestFmt). - pub fn request_fmt(self, make_request: R) -> InstrumentLayer { - InstrumentLayer { - operation_id: self.operation_id, - make_request, - make_response: self.make_response, - } - } - - /// Configures the response format. - /// - /// The argument is typically [`ResponseFmt`](super::sensitivity::ResponseFmt). - pub fn response_fmt(self, make_response: R) -> InstrumentLayer { - InstrumentLayer { - operation_id: self.operation_id, - make_request: self.make_request, - make_response, - } - } -} - -impl Layer for InstrumentLayer -where - RequestMakeFmt: Clone, - ResponseMakeFmt: Clone, -{ - type Service = InstrumentOperation; - - fn layer(&self, service: S) -> Self::Service { - InstrumentOperation::new(service, self.operation_id.clone()) - .request_fmt(self.make_request.clone()) - .response_fmt(self.make_response.clone()) - } -} diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs index 2519e10137..bad93a9ae1 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/mod.rs @@ -21,7 +21,7 @@ //! # } //! # async fn example() { //! # let service = service_fn(service); -//! # const NAME: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); +//! # const ID: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); //! let request = Request::get("http://localhost/a/b/c/d?bar=hidden") //! .header("header-name-a", "hidden") //! .body(()) @@ -49,7 +49,7 @@ //! } //! }) //! .status_code(); -//! let mut service = InstrumentOperation::new(service, NAME) +//! let mut service = InstrumentOperation::new(service, ID) //! .request_fmt(request_fmt) //! .response_fmt(response_fmt); //! @@ -59,14 +59,12 @@ //! //! [sensitive trait]: https://awslabs.github.io/smithy/1.0/spec/core/documentation-traits.html?highlight=sensitive%20trait#sensitive-trait -mod layer; mod plugin; pub mod sensitivity; mod service; use std::fmt::{Debug, Display}; -pub use layer::*; pub use plugin::*; pub use service::*; diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs index 7da5e2fbeb..ba7104801d 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs @@ -3,48 +3,41 @@ * SPDX-License-Identifier: Apache-2.0 */ -use tower::layer::util::Stack; - use crate::plugin::{PluginPipeline, PluginStack}; -use crate::{ - operation::{Operation, OperationShape}, - plugin::Plugin, -}; +use crate::{operation::OperationShape, plugin::Plugin}; -use super::{layer::InstrumentLayer, sensitivity::Sensitivity}; +use super::sensitivity::Sensitivity; +use super::InstrumentOperation; -/// A [`Plugin`] which applies [`InstrumentLayer`] to all operations in the builder. +/// A [`Plugin`] which applies [`InstrumentOperation`] to every operation. #[derive(Debug)] pub struct InstrumentPlugin; -impl Plugin for InstrumentPlugin +impl Plugin for InstrumentPlugin where Op: OperationShape, Op: Sensitivity, { - type Service = S; - type Layer = Stack>; + type Service = InstrumentOperation; - fn map(&self, operation: Operation) -> Operation { - let operation_id = Op::NAME; - let layer = InstrumentLayer::new(operation_id) + fn apply(&self, svc: S) -> Self::Service { + InstrumentOperation::new(svc, Op::ID) .request_fmt(Op::request_fmt()) - .response_fmt(Op::response_fmt()); - operation.layer(layer) + .response_fmt(Op::response_fmt()) } } -/// An extension trait for applying [`InstrumentLayer`] to all operations in a service. -pub trait InstrumentExt { - /// Applies an [`InstrumentLayer`] to all operations which respects the [@sensitive] trait given on the input and +/// An extension trait for applying [`InstrumentPlugin`]. +pub trait InstrumentExt { + /// Applies an [`InstrumentOperation`] to every operation, respecting the [@sensitive] trait given on the input and /// output models. See [`InstrumentOperation`](super::InstrumentOperation) for more information. /// /// [@sensitive]: https://awslabs.github.io/smithy/2.0/spec/documentation-traits.html#sensitive-trait - fn instrument(self) -> PluginPipeline>; + fn instrument(self) -> PluginPipeline>; } -impl InstrumentExt for PluginPipeline { - fn instrument(self) -> PluginPipeline> { +impl InstrumentExt for PluginPipeline { + fn instrument(self) -> PluginPipeline> { self.push(InstrumentPlugin) } } diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs index 76410cfa65..21f211bc06 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/service.rs @@ -94,12 +94,12 @@ where /// # use http::{Request, Response}; /// # async fn f(request: Request<()>) -> Result, ()> { Ok(Response::new(())) } /// # let mut svc = service_fn(f); -/// # const NAME: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); +/// # const ID: ShapeId = ShapeId::new("namespace#foo-operation", "namespace", "foo-operation"); /// let request_fmt = RequestFmt::new() /// .label(|index| index == 1, None) /// .query(|_| QueryMarker { key: false, value: true }); /// let response_fmt = ResponseFmt::new().status_code(); -/// let mut svc = InstrumentOperation::new(svc, NAME) +/// let mut svc = InstrumentOperation::new(svc, ID) /// .request_fmt(request_fmt) /// .response_fmt(response_fmt); /// # svc.call(Request::new(())); diff --git a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs index c40c0f7742..28ad3fd743 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/handler.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/handler.rs @@ -118,8 +118,8 @@ where /// A [`Service`] provided for every [`Handler`]. pub struct IntoService { - handler: H, - _operation: PhantomData, + pub(crate) handler: H, + pub(crate) _operation: PhantomData, } impl Clone for IntoService diff --git a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs index b9935b86d5..8450da8366 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/mod.rs @@ -32,7 +32,7 @@ //! pub struct GetShopping; //! //! impl OperationShape for GetShopping { -//! const NAME: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); +//! const ID: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); //! //! type Input = CartIdentifier; //! type Output = ShoppingCart; @@ -40,11 +40,11 @@ //! } //! ``` //! -//! The behavior of a Smithy operation is encoded by an [`Operation`]. The [`OperationShape`] types can be used to -//! construct specific operations using [`OperationShapeExt::from_handler`] and [`OperationShapeExt::from_service`]. -//! The [from_handler](OperationShapeExt::from_handler) constructor takes a [`Handler`] whereas the -//! [from_service](OperationShapeExt::from_service) takes a [`OperationService`]. Both traits serve a similar purpose - -//! they provide a common interface over a class of structures. +//! The behavior of a Smithy operation is encoded by a [`Service`](tower::Service). The [`OperationShape`] types can +//! be used to construct specific operations using [`OperationShapeExt::from_handler`] and +//! [`OperationShapeExt::from_service`] methods. The [from_handler](OperationShapeExt::from_handler) constructor takes +//! a [`Handler`] whereas the [from_service](OperationShapeExt::from_service) takes a [`OperationService`]. Both traits +//! serve a similar purpose - they provide a common interface over a class of structures. //! //! ## [`Handler`] //! @@ -95,7 +95,7 @@ //! //! Notice the parallels between [`OperationService`] and [`Handler`]. //! -//! ## Constructing an [`Operation`] +//! ## Constructing an Operation //! //! The following is an example of using both construction approaches: //! @@ -109,7 +109,7 @@ //! # pub enum GetShoppingError {} //! # pub struct GetShopping; //! # impl OperationShape for GetShopping { -//! # const NAME: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); +//! # const ID: ShapeId = ShapeId::new("namespace#GetShopping", "namespace", "GetShopping"); //! # //! # type Input = CartIdentifier; //! # type Output = ShoppingCart; @@ -134,12 +134,10 @@ //! type Future = OpFuture; //! //! fn poll_ready(&mut self, cx: &mut Context) -> Poll> { -//! // NOTE: This MUST NOT return `Err(OperationError::Model(_))`. //! todo!() //! } //! //! fn call(&mut self, request: CartIdentifier) -> Self::Future { -//! // NOTE: This MUST NOT return `Err(OperationError::Poll(_))`. //! todo!() //! } //! } @@ -150,19 +148,15 @@ //! //! ## Upgrading Smithy services to HTTP services //! -//! Both [`Handler`] and [`OperationService`] accept and return Smithy model structures. After an [`Operation`] is -//! constructed they are converted to a canonical form -//! `Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error>`. The -//! [`UpgradeLayer`] acts upon such services by converting them to -//! `Service`. +//! Both [`Handler`] and [`OperationService`] accept and return Smithy model structures. They are converted to a +//! canonical form `Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error>`. The +//! [`UpgradePlugin`] acts upon such services by converting them to +//! `Service` by applying serialization/deserialization +//! and validation specified by the Smithy contract. //! //! -//! The [`UpgradeLayer`] and it's [`Layer::Service`](tower::Layer::Service) [`Upgrade`] are both parameterized by a -//! protocol. This allows for upgrading to `Service` to be -//! protocol dependent. -//! -//! The [`Operation::upgrade`] will apply [`UpgradeLayer`] to `S` then apply the [`Layer`](tower::Layer) `L`. The -//! service builder provided to the user will perform this composition on `build`. +//! The [`UpgradePlugin`], being a [`Plugin`](crate::plugin::Plugin), is parameterized by a protocol. This allows for +//! upgrading to `Service` to be protocol dependent. //! //! [Smithy operation]: https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation @@ -171,58 +165,7 @@ mod operation_service; mod shape; mod upgrade; -use tower::layer::util::{Identity, Stack}; - pub use handler::*; pub use operation_service::*; pub use shape::*; pub use upgrade::*; - -/// A Smithy operation, represented by a [`Service`](tower::Service) `S` and a [`Layer`](tower::Layer) `L`. -/// -/// The `L` is held and applied lazily during [`Upgradable::upgrade`]. -pub struct Operation { - /// The inner [`Service`](tower::Service) representing the logic of the operation. - pub inner: S, - /// The [`Layer`](tower::Layer) applied to the HTTP [`Service`](tower::Service) after `S` has been wrapped in - /// [`Upgrade`]. - pub layer: L, -} - -impl Operation { - /// Applies a [`Layer`](tower::Layer) to the operation _after_ it has been upgraded via [`Operation::upgrade`]. - pub fn layer(self, layer: NewL) -> Operation> { - Operation { - inner: self.inner, - layer: Stack::new(self.layer, layer), - } - } -} - -impl Operation> { - /// Creates an [`Operation`] from a [`Service`](tower::Service). - pub fn from_service(inner: S) -> Self - where - Op: OperationShape, - S: OperationService, - { - Self { - inner: inner.normalize(), - layer: Identity::new(), - } - } -} - -impl Operation> { - /// Creates an [`Operation`] from a [`Handler`]. - pub fn from_handler(handler: H) -> Self - where - Op: OperationShape, - H: Handler, - { - Self { - inner: handler.into_service(), - layer: Identity::new(), - } - } -} diff --git a/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs b/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs index f759f297a2..b9a87aa6e9 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/operation_service.rs @@ -95,8 +95,8 @@ where /// A [`Service`] normalizing the request type of a [`OperationService`]. #[derive(Debug)] pub struct Normalize { - inner: S, - _operation: PhantomData, + pub(crate) inner: S, + pub(crate) _operation: PhantomData, } impl Clone for Normalize diff --git a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs index 72f712f2c0..6d3d774868 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/shape.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/shape.rs @@ -3,15 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::{Handler, IntoService, Normalize, Operation, OperationService}; +use std::marker::PhantomData; + +use super::{Handler, IntoService, Normalize, OperationService}; use crate::shape_id::ShapeId; /// Models the [Smithy Operation shape]. /// /// [Smithy Operation shape]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#operation pub trait OperationShape { - /// The name of the operation. - const NAME: ShapeId; + /// The ID of the operation. + const ID: ShapeId; /// The operation input. type Input; @@ -24,22 +26,29 @@ pub trait OperationShape { /// An extension trait over [`OperationShape`]. pub trait OperationShapeExt: OperationShape { - /// Creates a new [`Operation`] for well-formed [`Handler`]s. - fn from_handler(handler: H) -> Operation> + /// Creates a new [`Service`](tower::Service), [`IntoService`], for well-formed [`Handler`]s. + fn from_handler(handler: H) -> IntoService where H: Handler, Self: Sized, { - Operation::from_handler(handler) + IntoService { + handler, + _operation: PhantomData, + } } - /// Creates a new [`Operation`] for well-formed [`Service`](tower::Service)s. - fn from_service(svc: S) -> Operation> + /// Creates a new normalized [`Service`](tower::Service), [`Normalize`], for well-formed + /// [`Service`](tower::Service)s. + fn from_service(svc: S) -> Normalize where S: OperationService, Self: Sized, { - Operation::from_service(svc) + Normalize { + inner: svc, + _operation: PhantomData, + } } } diff --git a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs index d1c95672e4..f0ed2a0604 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs @@ -13,56 +13,50 @@ use std::{ use futures_util::ready; use pin_project_lite::pin_project; -use tower::{layer::util::Stack, util::Oneshot, Layer, Service, ServiceExt}; +use tower::{util::Oneshot, Service, ServiceExt}; use tracing::error; use crate::{ - body::BoxBody, - plugin::Plugin, - request::{FromParts, FromRequest}, - response::IntoResponse, - routing::Route, + body::BoxBody, plugin::Plugin, request::FromRequest, response::IntoResponse, runtime_error::InternalFailureException, }; -use super::{Operation, OperationShape}; +use super::OperationShape; -/// A [`Layer`] responsible for taking an operation [`Service`], accepting and returning Smithy +/// A [`Plugin`] responsible for taking an operation [`Service`], accepting and returning Smithy /// types and converting it into a [`Service`] taking and returning [`http`] types. /// /// See [`Upgrade`]. #[derive(Debug, Clone)] -pub struct UpgradeLayer { - _protocol: PhantomData, - _operation: PhantomData, - _exts: PhantomData, +pub struct UpgradePlugin { + _extractors: PhantomData, } -impl Default for UpgradeLayer { +impl Default for UpgradePlugin { fn default() -> Self { Self { - _protocol: PhantomData, - _operation: PhantomData, - _exts: PhantomData, + _extractors: PhantomData, } } } -impl UpgradeLayer { - /// Creates a new [`UpgradeLayer`]. +impl UpgradePlugin { + /// Creates a new [`UpgradePlugin`]. pub fn new() -> Self { Self::default() } } -impl Layer for UpgradeLayer { - type Service = Upgrade; +impl Plugin for UpgradePlugin +where + Op: OperationShape, +{ + type Service = Upgrade; - fn layer(&self, inner: S) -> Self::Service { + fn apply(&self, inner: S) -> Self::Service { Upgrade { _protocol: PhantomData, - _operation: PhantomData, - _exts: PhantomData, + _input: PhantomData, inner, } } @@ -70,22 +64,20 @@ impl Layer for UpgradeLayer { /// A [`Service`] responsible for wrapping an operation [`Service`] accepting and returning Smithy /// types, and converting it into a [`Service`] accepting and returning [`http`] types. -pub struct Upgrade { +pub struct Upgrade { _protocol: PhantomData, - _operation: PhantomData, - _exts: PhantomData, + _input: PhantomData, inner: S, } -impl Clone for Upgrade +impl Clone for Upgrade where S: Clone, { fn clone(&self) -> Self { Self { _protocol: PhantomData, - _operation: PhantomData, - _exts: PhantomData, + _input: PhantomData, inner: self.inner.clone(), } } @@ -106,39 +98,27 @@ pin_project! { } } -type InnerAlias = - Inner<<(Input, Exts) as FromRequest>::Future, Oneshot>; +type InnerAlias = Inner<>::Future, Oneshot>; pin_project! { /// The [`Service::Future`] of [`Upgrade`]. - pub struct UpgradeFuture + pub struct UpgradeFuture where - Operation: OperationShape, - (Operation::Input, Exts): FromRequest, - S: Service<(Operation::Input, Exts)>, + Input: FromRequest, + S: Service, { - service: S, + service: Option, #[pin] - inner: InnerAlias + inner: InnerAlias } } -impl Future for UpgradeFuture +impl Future for UpgradeFuture where - // `Op` is used to specify the operation shape - Op: OperationShape, - // Smithy input must convert from a HTTP request - Op::Input: FromRequest, - // Smithy output must convert into a HTTP response - Op::Output: IntoResponse

, - // Smithy error must convert into a HTTP response - Op::Error: IntoResponse

, - - // Must be able to convert extensions - Exts: FromParts

, - - // The signature of the inner service is correct - S: Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error> + Clone, + Input: FromRequest, + S: Service, + S::Response: IntoResponse

, + S::Error: IntoResponse

, { type Output = Result, Infallible>; @@ -151,7 +131,11 @@ where InnerProj::FromRequest { inner } => { let result = ready!(inner.poll(cx)); match result { - Ok(ok) => this.service.clone().oneshot(ok), + Ok(ok) => this + .service + .take() + .expect("futures cannot be polled after completion") + .oneshot(ok), Err(err) => return Poll::Ready(Ok(err.into_response())), } } @@ -170,26 +154,16 @@ where } } -impl Service> for Upgrade +impl Service> for Upgrade where - // `Op` is used to specify the operation shape - Op: OperationShape, - // Smithy input must convert from a HTTP request - Op::Input: FromRequest, - // Smithy output must convert into a HTTP response - Op::Output: IntoResponse

, - // Smithy error must convert into a HTTP response - Op::Error: IntoResponse

, - - // Must be able to convert extensions - Exts: FromParts

, - - // The signature of the inner service is correct - S: Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error> + Clone, + Input: FromRequest, + S: Service + Clone, + S::Response: IntoResponse

, + S::Error: IntoResponse

, { type Response = http::Response; type Error = Infallible; - type Future = UpgradeFuture; + type Future = UpgradeFuture; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) @@ -199,87 +173,26 @@ where let clone = self.inner.clone(); let service = std::mem::replace(&mut self.inner, clone); UpgradeFuture { - service, + service: Some(service), inner: Inner::FromRequest { - inner: <(Op::Input, Exts) as FromRequest>::from_request(req), + inner: >::from_request(req), }, } } } -/// An interface to convert a representation of a Smithy operation into a [`Route`]. -/// -/// See the [module](crate::operation) documentation for more information. -pub trait Upgradable { - /// Upgrade the representation of a Smithy operation to a [`Route`]. - fn upgrade(self, plugin: &Plugin) -> Route; -} - -type UpgradedService = - <>::Layer as Layer>::Service>>>::Service; - -impl Upgradable for Operation -where - // `Op` is used to specify the operation shape - Op: OperationShape, - - // Smithy input must convert from a HTTP request - Op::Input: FromRequest, - // Smithy output must convert into a HTTP response - Op::Output: IntoResponse

, - // Smithy error must convert into a HTTP response - Op::Error: IntoResponse

, - - // Must be able to convert extensions - Exts: FromParts

, - - // The signature of the inner service is correct - Pl::Service: Service<(Op::Input, Exts), Response = Op::Output, Error = Op::Error> + Clone, - - // The plugin takes this operation as input - Pl: Plugin, - - // The modified Layer applies correctly to `Upgrade` - Pl::Layer: Layer>, - - // For `Route::new` for the resulting service - UpgradedService: - Service, Response = http::Response, Error = Infallible> + Clone + Send + 'static, - as Service>>::Future: Send + 'static, -{ - /// Takes the [`Operation`](Operation), applies [`Plugin`], then applies [`UpgradeLayer`] to - /// the modified `S`, then finally applies the modified `L`. - /// - /// The composition is made explicit in the method constraints and return type. - fn upgrade(self, plugin: &Pl) -> Route { - let mapped = plugin.map(self); - let layer = Stack::new(UpgradeLayer::new(), mapped.layer); - let svc = layer.layer(mapped.inner); - Route::new(svc) - } -} - -/// A marker struct indicating an [`Operation`] has not been set in a builder. -/// -/// This _does_ implement [`Upgradable`] but produces a [`Service`] which always returns an internal failure message. -pub struct FailOnMissingOperation; - -impl Upgradable for FailOnMissingOperation -where - InternalFailureException: IntoResponse

, - P: 'static, -{ - fn upgrade(self, _plugin: &Pl) -> Route { - Route::new(MissingFailure { _protocol: PhantomData }) - } -} - /// A [`Service`] which always returns an internal failure message and logs an error. #[derive(Copy)] pub struct MissingFailure

{ _protocol: PhantomData, } +impl

Default for MissingFailure

{ + fn default() -> Self { + Self { _protocol: PhantomData } + } +} + impl

Clone for MissingFailure

{ fn clone(&self) -> Self { MissingFailure { _protocol: PhantomData } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs b/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs index 33932ddd7d..c87e86619c 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs @@ -13,7 +13,7 @@ //! # use hyper::{Body, Response, StatusCode}; //! let plugins = PluginPipeline::new() //! // Handle all `/ping` health check requests by returning a `200 OK`. -//! .http_layer(AlbHealthCheckLayer::from_handler("/ping", |_req| async { +//! .layer(AlbHealthCheckLayer::from_handler("/ping", |_req| async { //! StatusCode::OK //! })); //! @@ -92,7 +92,7 @@ pub struct AlbHealthCheckService { impl Service> for AlbHealthCheckService where S: Service, Response = Response> + Clone, - S::Future: std::marker::Send + 'static, + S::Future: Send + 'static, H: Service, Response = StatusCode, Error = Infallible> + Clone, { type Response = S::Response; diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs index f1f5951e8a..2429cc25f1 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs @@ -3,9 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use tower::layer::util::Stack; - -use crate::operation::{Operation, OperationShape}; +use crate::operation::OperationShape; use crate::shape_id::ShapeId; use super::Plugin; @@ -15,17 +13,15 @@ pub struct OperationIdFn { f: F, } -impl Plugin for OperationIdFn +impl Plugin for OperationIdFn where - F: Fn(ShapeId) -> NewLayer, + F: Fn(ShapeId, S) -> NewService, Op: OperationShape, { - type Service = S; - type Layer = Stack; + type Service = NewService; - fn map(&self, input: Operation) -> Operation { - let operation_id = Op::NAME; - input.layer((self.f)(operation_id)) + fn apply(&self, svc: S) -> Self::Service { + (self.f)(Op::ID, svc) } } @@ -56,9 +52,9 @@ where /// // This plugin applies the `PrintService` middleware around every operation. /// let plugin = plugin_from_operation_id_fn(f); /// ``` -pub fn plugin_from_operation_id_fn(f: F) -> OperationIdFn +pub fn plugin_from_operation_id_fn(f: F) -> OperationIdFn where - F: Fn(ShapeId) -> L, + F: Fn(ShapeId) -> NewService, { OperationIdFn { f } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/either.rs b/rust-runtime/aws-smithy-http-server/src/plugin/either.rs index bf33546ca1..2054e906e8 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/either.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/either.rs @@ -13,18 +13,17 @@ use std::{ }; use tower::{Layer, Service}; -use crate::operation::Operation; - use super::Plugin; pin_project! { - /// Combine two different [`Future`]/[`Service`]/[`Layer`]/[`Plugin`] types into a single type. + /// Combine two different [`Futures`](std::future::Future)/[`Services`](tower::Service)/ + /// [`Layers`](tower::Layer)/[`Plugins`](super::Plugin) into a single type. /// - /// # Notes on [`Future`] + /// # Notes on [`Future`](std::future::Future) /// /// The [`Future::Output`] must be identical. /// - /// # Notes on [`Service`] + /// # Notes on [`Service`](tower::Service) /// /// The [`Service::Response`] and [`Service::Error`] must be identical. #[derive(Clone, Debug)] @@ -103,30 +102,21 @@ where } } -impl Plugin for Either +impl Plugin for Either where - Le: Plugin, - Ri: Plugin, + Le: Plugin, + Ri: Plugin, { type Service = Either; - type Layer = Either; - fn map(&self, input: Operation) -> Operation { + fn apply(&self, svc: S) -> Self::Service { match self { - Either::Left { value } => { - let Operation { inner, layer } = value.map(input); - Operation { - inner: Either::Left { value: inner }, - layer: Either::Left { value: layer }, - } - } - Either::Right { value } => { - let Operation { inner, layer } = value.map(input); - Operation { - inner: Either::Right { value: inner }, - layer: Either::Right { value: layer }, - } - } + Either::Left { value } => Either::Left { + value: value.apply(svc), + }, + Either::Right { value } => Either::Right { + value: value.apply(svc), + }, } } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs index a9108a0747..46608483c0 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs @@ -5,13 +5,13 @@ use super::{either::Either, IdentityPlugin}; -use crate::operation::{Operation, OperationShape}; +use crate::operation::OperationShape; use crate::shape_id::ShapeId; use super::Plugin; /// Filters the application of an inner [`Plugin`] using a predicate over the -/// [`OperationShape::NAME`](crate::operation::OperationShape). +/// [`OperationShape::ID`](crate::operation::OperationShape). /// /// See [`filter_by_operation_id`] for more details. pub struct FilterByOperationId { @@ -20,23 +20,22 @@ pub struct FilterByOperationId { } /// Filters the application of an inner [`Plugin`] using a predicate over the -/// [`OperationShape::NAME`](crate::operation::OperationShape). +/// [`OperationShape::ID`](crate::operation::OperationShape). /// /// # Example /// /// ```rust /// use aws_smithy_http_server::plugin::filter_by_operation_id; -/// use aws_smithy_http_server::shape_id::ShapeId; -/// # use aws_smithy_http_server::{plugin::Plugin, operation::{Operation, OperationShape}}; +/// # use aws_smithy_http_server::{plugin::Plugin, operation::OperationShape, shape_id::ShapeId}; /// # struct Pl; /// # struct CheckHealth; -/// # impl OperationShape for CheckHealth { const NAME: ShapeId = ShapeId::new("ns#CheckHealth", "ns", "CheckHealth"); type Input = (); type Output = (); type Error = (); } -/// # impl Plugin<(), CheckHealth, (), ()> for Pl { type Service = (); type Layer = (); fn map(&self, input: Operation<(), ()>) -> Operation<(), ()> { input }} +/// # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +/// # impl Plugin<(), CheckHealth, ()> for Pl { type Service = (); fn apply(&self, input: ()) -> Self::Service { input }} /// # let plugin = Pl; -/// # let operation = Operation { inner: (), layer: () }; +/// # let svc = (); /// // Prevents `plugin` from being applied to the `CheckHealth` operation. -/// let filtered_plugin = filter_by_operation_id(plugin, |id| id.name() != CheckHealth::NAME.name()); -/// let new_operation = filtered_plugin.map(operation); +/// let filtered_plugin = filter_by_operation_id(plugin, |name| name != CheckHealth::ID); +/// let new_operation = filtered_plugin.apply(svc); /// ``` pub fn filter_by_operation_id(plugins: Inner, predicate: F) -> FilterByOperationId where @@ -52,21 +51,20 @@ impl FilterByOperationId { } } -impl Plugin for FilterByOperationId +impl Plugin for FilterByOperationId where F: Fn(ShapeId) -> bool, - Inner: Plugin, + Inner: Plugin, Op: OperationShape, { type Service = Either; - type Layer = Either; - fn map(&self, input: Operation) -> Operation { - let either_plugin = if (self.predicate)(Op::NAME) { + fn apply(&self, svc: S) -> Self::Service { + let either_plugin = if (self.predicate)(Op::ID) { Either::Left { value: &self.inner } } else { Either::Right { value: IdentityPlugin } }; - either_plugin.map(input) + either_plugin.apply(svc) } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs b/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs index d0a6d1390b..52cd7e6965 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs @@ -3,18 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::operation::Operation; - use super::Plugin; -/// A [`Plugin`] that maps an `input` [`Operation`] to itself. +/// A [`Plugin`] that maps a service to itself. pub struct IdentityPlugin; -impl Plugin for IdentityPlugin { +impl Plugin for IdentityPlugin { type Service = S; - type Layer = L; - fn map(&self, input: Operation) -> Operation { - input + fn apply(&self, svc: S) -> S { + svc } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs b/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs index d7a055f291..6d5e9188f6 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs @@ -3,23 +3,50 @@ * SPDX-License-Identifier: Apache-2.0 */ -use tower::layer::util::Stack; +use std::marker::PhantomData; -use crate::operation::Operation; +use tower::Layer; use super::Plugin; -/// A [`Plugin`] which appends a HTTP [`Layer`](tower::Layer) `L` to the existing `Layer` in [`Operation`](Operation). -pub struct HttpLayer(pub L); +/// A [`Plugin`] which acts as a [`Layer`] `L`. +pub struct LayerPlugin(pub L); -impl Plugin for HttpLayer +impl Plugin for LayerPlugin where - NewLayer: Clone, + L: Layer, { - type Service = S; - type Layer = Stack; + type Service = L::Service; - fn map(&self, input: Operation) -> Operation { - input.layer(self.0.clone()) + fn apply(&self, svc: S) -> Self::Service { + self.0.layer(svc) + } +} + +/// A [`Layer`] which acts as a [`Plugin`] `Pl` for specific protocol `P` and operation `Op`. +pub struct PluginLayer { + plugin: Pl, + _protocol: PhantomData

, + _op: PhantomData, +} + +impl Layer for PluginLayer +where + Pl: Plugin, +{ + type Service = Pl::Service; + + fn layer(&self, inner: S) -> Self::Service { + self.plugin.apply(inner) + } +} + +impl PluginLayer<(), (), Pl> { + pub fn new(plugin: Pl) -> PluginLayer { + PluginLayer { + plugin, + _protocol: PhantomData, + _op: PhantomData, + } } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs index b6acaa038a..cda732e699 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -15,12 +15,12 @@ //! # use aws_smithy_http_server::shape_id::ShapeId; //! # let layer = (); //! # struct GetPokemonSpecies; -//! # impl GetPokemonSpecies { const NAME: ShapeId = ShapeId::new("namespace#name", "namespace", "name"); }; +//! # impl GetPokemonSpecies { const ID: ShapeId = ShapeId::new("namespace#name", "namespace", "name"); }; //! // Create a `Plugin` from a HTTP `Layer` -//! let plugin = HttpLayer(layer); +//! let plugin = LayerPlugin(layer); //! //! // Only apply the layer to operations with name "GetPokemonSpecies" -//! let plugin = filter_by_operation_id(plugin, |id| id.name() == GetPokemonSpecies::NAME.name()); +//! let plugin = filter_by_operation_id(plugin, |id| id.name() == GetPokemonSpecies::ID.name()); //! ``` //! //! # Construct a [`Plugin`] from a closure that takes as input the operation name @@ -55,7 +55,7 @@ //! //! ```rust //! use aws_smithy_http_server::{ -//! operation::{Operation, OperationShape}, +//! operation::{OperationShape}, //! plugin::{Plugin, PluginPipeline, PluginStack}, //! shape_id::ShapeId, //! }; @@ -87,35 +87,18 @@ //! } //! } //! -//! /// A [`Layer`] which constructs the [`PrintService`]. -//! #[derive(Debug)] -//! pub struct PrintLayer { -//! id: ShapeId, -//! } -//! impl Layer for PrintLayer { -//! type Service = PrintService; -//! -//! fn layer(&self, service: S) -> Self::Service { -//! PrintService { -//! inner: service, -//! id: self.id.clone(), -//! } -//! } -//! } -//! //! /// A [`Plugin`] for a service builder to add a [`PrintLayer`] over operations. //! #[derive(Debug)] //! pub struct PrintPlugin; //! -//! impl Plugin for PrintPlugin +//! impl Plugin for PrintPlugin //! where //! Op: OperationShape, //! { -//! type Service = S; -//! type Layer = Stack; +//! type Service = PrintService; //! -//! fn map(&self, input: Operation) -> Operation { -//! input.layer(PrintLayer { id: Op::NAME }) +//! fn apply(&self, inner: S) -> Self::Service { +//! PrintService { inner, id: Op::ID } //! } //! } //! ``` @@ -130,40 +113,35 @@ mod layer; mod pipeline; mod stack; -use crate::operation::Operation; - pub use closure::{plugin_from_operation_id_fn, OperationIdFn}; pub use either::Either; pub use filter::{filter_by_operation_id, FilterByOperationId}; pub use identity::IdentityPlugin; -pub use layer::HttpLayer; +pub use layer::{LayerPlugin, PluginLayer}; pub use pipeline::PluginPipeline; pub use stack::PluginStack; -/// A mapping from one [`Operation`] to another. Used to modify the behavior of -/// [`Upgradable`](crate::operation::Upgradable) and therefore the resulting service builder. +/// A mapping from one [`Service`](tower::Service) to another. This should be viewed as a +/// [`Layer`](tower::Layer) parameterized by the protocol and operation. /// /// The generics `Protocol` and `Op` allow the behavior to be parameterized. /// /// See [module](crate::plugin) documentation for more information. -pub trait Plugin { +pub trait Plugin { /// The type of the new [`Service`](tower::Service). type Service; - /// The type of the new [`Layer`](tower::Layer). - type Layer; - /// Maps an [`Operation`] to another. - fn map(&self, input: Operation) -> Operation; + /// Maps a [`Service`](tower::Service) to another. + fn apply(&self, svc: S) -> Self::Service; } -impl<'a, P, Op, S, L, Pl> Plugin for &'a Pl +impl<'a, P, Op, S, Pl> Plugin for &'a Pl where - Pl: Plugin, + Pl: Plugin, { type Service = Pl::Service; - type Layer = Pl::Layer; - fn map(&self, input: Operation) -> Operation { - >::map(*self, input) + fn apply(&self, inner: S) -> Self::Service { + >::apply(self, inner) } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs index d38ecfd782..2fde128511 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs @@ -3,10 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::operation::Operation; use crate::plugin::{IdentityPlugin, Plugin, PluginStack}; -use super::HttpLayer; +use super::LayerPlugin; /// A wrapper struct for composing [`Plugin`]s. /// It is used as input for the `builder_with_plugins` method on the generate service struct @@ -35,7 +34,7 @@ use super::HttpLayer; /// /// `PluginPipeline` is itself a [`Plugin`]: you can apply any transformation that expects a /// [`Plugin`] to an entire pipeline. In this case, we want to use -/// [`filter_by_operation_name`](crate::plugin::filter_by_operation_id) to limit the scope of +/// [`filter_by_operation_id`](crate::plugin::filter_by_operation_id) to limit the scope of /// the logging and metrics plugins to the `CheckHealth` operation: /// /// ```rust @@ -45,14 +44,14 @@ use super::HttpLayer; /// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; /// use aws_smithy_http_server::shape_id::ShapeId; /// # struct CheckHealth; -/// # impl CheckHealth { const NAME: ShapeId = ShapeId::new("namespace#MyName", "namespace", "MyName"); } +/// # impl CheckHealth { const ID: ShapeId = ShapeId::new("namespace#MyName", "namespace", "MyName"); } /// /// // The logging and metrics plugins will only be applied to the `CheckHealth` operation. /// let operation_specific_pipeline = filter_by_operation_id( /// PluginPipeline::new() /// .push(LoggingPlugin) /// .push(MetricsPlugin), -/// |name| name == CheckHealth::NAME +/// |name| name == CheckHealth::ID /// ); /// let pipeline = PluginPipeline::new() /// .push(operation_specific_pipeline) @@ -109,7 +108,7 @@ use super::HttpLayer; /// // Our custom method! /// .with_auth(); /// ``` -pub struct PluginPipeline

(P); +pub struct PluginPipeline

(pub(crate) P); impl Default for PluginPipeline { fn default() -> Self { @@ -142,50 +141,42 @@ impl

PluginPipeline

{ /// /// ## Implementation notes /// - /// Plugins are applied to the underlying [`Operation`] in opposite order compared + /// Plugins are applied to the underlying [`Service`](tower::Service) in opposite order compared /// to their registration order. - /// But most [`Plugin::map`] implementations desugar to appending a layer to [`Operation`], - /// usually via [`Operation::layer`]. + /// /// As an example: /// /// ```rust,compile_fail /// #[derive(Debug)] /// pub struct PrintPlugin; /// - /// impl Plugin for PrintPlugin + /// impl Plugin for PrintPlugin /// // [...] /// { /// // [...] - /// fn map(&self, input: Operation) -> Operation { - /// input.layer(PrintLayer { id: Op::NAME }) + /// fn apply(&self, inner: S) -> Self::Service { + /// PrintService { inner, name: Op::ID } /// } /// } /// ``` /// - /// The layer that is registered **last** via [`Operation::layer`] is the one that gets executed - /// **first** at runtime when a new request comes in, since it _wraps_ the underlying service. - /// - /// This is why plugins in [`PluginPipeline`] are applied in opposite order compared to their - /// registration order: this ensures that, _at runtime_, their logic is executed - /// in registration order. pub fn push(self, new_plugin: NewPlugin) -> PluginPipeline> { PluginPipeline(PluginStack::new(new_plugin, self.0)) } /// Applies a single [`tower::Layer`] to all operations _before_ they are deserialized. - pub fn http_layer(self, layer: L) -> PluginPipeline, P>> { - PluginPipeline(PluginStack::new(HttpLayer(layer), self.0)) + pub fn layer(self, layer: L) -> PluginPipeline, P>> { + PluginPipeline(PluginStack::new(LayerPlugin(layer), self.0)) } } -impl Plugin for PluginPipeline +impl Plugin for PluginPipeline where - InnerPlugin: Plugin, + InnerPlugin: Plugin, { type Service = InnerPlugin::Service; - type Layer = InnerPlugin::Layer; - fn map(&self, input: Operation) -> Operation { - self.0.map(input) + fn apply(&self, svc: S) -> Self::Service { + self.0.apply(svc) } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs b/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs index a68e3086d3..e5aa54d219 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs @@ -3,8 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::operation::Operation; - use super::Plugin; /// A wrapper struct which composes an `Inner` and an `Outer` [`Plugin`]. @@ -24,16 +22,15 @@ impl PluginStack { } } -impl Plugin for PluginStack +impl Plugin for PluginStack where - Inner: Plugin, - Outer: Plugin, + Inner: Plugin, + Outer: Plugin, { type Service = Outer::Service; - type Layer = Outer::Layer; - fn map(&self, input: Operation) -> Operation { - let inner = self.inner.map(input); - self.outer.map(inner) + fn apply(&self, svc: S) -> Self::Service { + let svc = self.inner.apply(svc); + self.outer.apply(svc) } } From 8c4d186487eec34fb684852fa1b9a605f1ee6a87 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Mon, 12 Jun 2023 12:09:29 -0500 Subject: [PATCH 145/253] Update dependencies flagged by cargo audit (#2753) Also, sort `Cargo.toml` dependencies if they were disordered. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .cargo-deny-config.toml | 14 ++++++++++++++ aws/rust-runtime/aws-config/Cargo.toml | 4 ++-- aws/rust-runtime/aws-credential-types/Cargo.toml | 2 +- aws/rust-runtime/aws-http/Cargo.toml | 4 ++-- aws/rust-runtime/aws-inlineable/Cargo.toml | 4 ++-- aws/rust-runtime/aws-runtime/Cargo.toml | 4 ++-- aws/rust-runtime/aws-sig-auth/Cargo.toml | 2 +- aws/rust-runtime/aws-sigv4/Cargo.toml | 2 +- aws/rust-runtime/aws-types/Cargo.toml | 4 ++-- aws/sdk/integration-tests/s3/Cargo.toml | 2 +- .../rust/codegen/core/rustlang/CargoDependency.kt | 2 +- examples/pokemon-service-lambda/Cargo.toml | 2 +- examples/pokemon-service-tls/Cargo.toml | 8 ++++---- examples/pokemon-service/Cargo.toml | 6 +++--- examples/python/pokemon-service-test/Cargo.toml | 2 +- rust-runtime/aws-smithy-client/Cargo.toml | 6 +++--- rust-runtime/aws-smithy-eventstream/Cargo.toml | 2 +- .../aws-smithy-eventstream/fuzz/Cargo.toml | 2 +- .../aws-smithy-http-server-python/Cargo.toml | 4 ++-- rust-runtime/aws-smithy-http-server/Cargo.toml | 12 ++++++------ rust-runtime/aws-smithy-http/Cargo.toml | 4 ++-- rust-runtime/aws-smithy-protocol-test/Cargo.toml | 9 ++++----- rust-runtime/aws-smithy-types/Cargo.toml | 8 ++++---- tools/ci-build/publisher/Cargo.toml | 2 +- tools/ci-build/sdk-lints/Cargo.toml | 4 ++-- tools/ci-build/sdk-versioner/Cargo.toml | 2 +- tools/echo-server/Cargo.toml | 4 ++-- 27 files changed, 67 insertions(+), 54 deletions(-) diff --git a/.cargo-deny-config.toml b/.cargo-deny-config.toml index b79e85383f..a90029c388 100644 --- a/.cargo-deny-config.toml +++ b/.cargo-deny-config.toml @@ -35,6 +35,20 @@ name = "ring" expression = "MIT AND ISC AND OpenSSL" license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] +[[licenses.clarify]] +name = "webpki" +expression = "ISC" +license-files = [ + { path = "LICENSE", hash = 0x001c7e6c }, +] + +[[licenses.clarify]] +name = "rustls-webpki" +expression = "ISC" +license-files = [ + { path = "LICENSE", hash = 0x001c7e6c }, +] + # This section is considered when running `cargo deny check bans`. # More documentation about the 'bans' section can be found here: # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index b15a573422..cd4bc4d110 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -29,7 +29,7 @@ aws-smithy-http-tower = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-http-to aws-smithy-json = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-json" } aws-smithy-types = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-types" } aws-types = { path = "../../sdk/build/aws-sdk/sdk/aws-types" } -hyper = { version = "0.14.25", default-features = false } +hyper = { version = "0.14.26", default-features = false } time = { version = "0.3.4", features = ["parsing"] } tokio = { version = "1.13.1", features = ["sync"] } tracing = { version = "0.1" } @@ -65,7 +65,7 @@ aws-credential-types = { path = "../../sdk/build/aws-sdk/sdk/aws-credential-type aws-smithy-client = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-client", features = ["test-util"] } # used for a usage example -hyper-rustls = { version = "0.23.0", features = ["webpki-tokio", "http2", "http1"] } +hyper-rustls = { version = "0.24", features = ["webpki-tokio", "http2", "http1"] } [package.metadata.docs.rs] all-features = true diff --git a/aws/rust-runtime/aws-credential-types/Cargo.toml b/aws/rust-runtime/aws-credential-types/Cargo.toml index 4a0b8a9390..9970072b9e 100644 --- a/aws/rust-runtime/aws-credential-types/Cargo.toml +++ b/aws/rust-runtime/aws-credential-types/Cargo.toml @@ -27,7 +27,7 @@ async-trait = "0.1.51" env_logger = "0.9.0" tokio = { version = "1.23.1", features = ["full", "test-util", "rt"] } -tracing-test = "0.2.1" +tracing-test = "0.2.4" # TODO(https://github.com/awslabs/smithy-rs/issues/2619): Remove this # workaround once the fixed is upstreamed. regex = { version = "1.0", features = ["unicode-case", "unicode-perl"] } diff --git a/aws/rust-runtime/aws-http/Cargo.toml b/aws/rust-runtime/aws-http/Cargo.toml index f09f8bc8e3..7b01057016 100644 --- a/aws/rust-runtime/aws-http/Cargo.toml +++ b/aws/rust-runtime/aws-http/Cargo.toml @@ -29,8 +29,8 @@ aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-t bytes-utils = "0.1.2" env_logger = "0.9" tokio = { version = "1.23.1", features = ["macros", "rt", "rt-multi-thread", "test-util", "time"] } -tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } -proptest = "1" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +proptest = "1.2" serde = { version = "1", features = ["derive"]} serde_json = "1" diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml index 727202f2aa..d941f059b2 100644 --- a/aws/rust-runtime/aws-inlineable/Cargo.toml +++ b/aws/rust-runtime/aws-inlineable/Cargo.toml @@ -43,8 +43,8 @@ aws-credential-types = { path = "../aws-credential-types", features = ["test-uti aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client", features = ["test-util"] } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http", features = ["rt-tokio"] } aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } -tempfile = "3.2.0" -tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } +tempfile = "3.6.0" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] } [package.metadata.docs.rs] diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index 08411f4584..aa6fdc1dde 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -33,10 +33,10 @@ aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types", features = ["test-util"] } aws-smithy-protocol-test = { path = "../../../rust-runtime/aws-smithy-protocol-test" } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["test-util"] } -proptest = "1" +proptest = "1.2" serde = { version = "1", features = ["derive"]} serde_json = "1" -tracing-test = "0.2.1" +tracing-test = "0.2.4" [package.metadata.docs.rs] all-features = true diff --git a/aws/rust-runtime/aws-sig-auth/Cargo.toml b/aws/rust-runtime/aws-sig-auth/Cargo.toml index 6ef8e4374f..deece1ac30 100644 --- a/aws/rust-runtime/aws-sig-auth/Cargo.toml +++ b/aws/rust-runtime/aws-sig-auth/Cargo.toml @@ -24,7 +24,7 @@ tracing = "0.1" aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } aws-endpoint = { path = "../aws-endpoint" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } -tracing-test = "0.2.1" +tracing-test = "0.2.4" aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] } [package.metadata.docs.rs] diff --git a/aws/rust-runtime/aws-sigv4/Cargo.toml b/aws/rust-runtime/aws-sigv4/Cargo.toml index 90cf5f42f3..ea54025552 100644 --- a/aws/rust-runtime/aws-sigv4/Cargo.toml +++ b/aws/rust-runtime/aws-sigv4/Cargo.toml @@ -33,7 +33,7 @@ criterion = "0.4" bytes = "1" httparse = "1.5" pretty_assertions = "1.3" -proptest = "1" +proptest = "1.2" time = { version = "0.3.4", features = ["parsing"] } [target.'cfg(not(any(target_arch = "powerpc", target_arch = "powerpc64")))'.dev-dependencies] diff --git a/aws/rust-runtime/aws-types/Cargo.toml b/aws/rust-runtime/aws-types/Cargo.toml index 7a4757b996..eb0b527f42 100644 --- a/aws/rust-runtime/aws-types/Cargo.toml +++ b/aws/rust-runtime/aws-types/Cargo.toml @@ -22,12 +22,12 @@ http = "0.2.6" # cargo does not support optional test dependencies, so to completely disable rustls # we need to add the webpki-roots feature here. # https://github.com/rust-lang/cargo/issues/1596 -hyper-rustls = { version = "0.23.0", optional = true, features = ["rustls-native-certs", "http2", "webpki-roots"] } +hyper-rustls = { version = "0.24", optional = true, features = ["rustls-native-certs", "http2", "webpki-roots"] } [dev-dependencies] futures-util = { version = "0.3.16", default-features = false } http = "0.2.4" -tracing-test = "0.2.1" +tracing-test = "0.2.4" [build-dependencies] rustc_version = "0.4.0" diff --git a/aws/sdk/integration-tests/s3/Cargo.toml b/aws/sdk/integration-tests/s3/Cargo.toml index b8972c8c17..554ff97101 100644 --- a/aws/sdk/integration-tests/s3/Cargo.toml +++ b/aws/sdk/integration-tests/s3/Cargo.toml @@ -30,7 +30,7 @@ futures-util = { version = "0.3.16", default-features = false } hdrhistogram = "7.5.2" http = "0.2.3" http-body = "0.4.5" -hyper = "0.14.25" +hyper = "0.14.26" pretty_assertions = "1.3" serde_json = "1" smol = "1.2" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index d501349057..72eba06a7d 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -215,7 +215,7 @@ data class CargoDependency( val Hex: CargoDependency = CargoDependency("hex", CratesIo("0.4.3")) val Http: CargoDependency = CargoDependency("http", CratesIo("0.2.9")) val HttpBody: CargoDependency = CargoDependency("http-body", CratesIo("0.4.4")) - val Hyper: CargoDependency = CargoDependency("hyper", CratesIo("0.14.12")) + val Hyper: CargoDependency = CargoDependency("hyper", CratesIo("0.14.26")) val HyperWithStream: CargoDependency = Hyper.withFeature("stream") val LazyStatic: CargoDependency = CargoDependency("lazy_static", CratesIo("1.4.0")) val Md5: CargoDependency = CargoDependency("md-5", CratesIo("0.10.0"), rustName = "md5") diff --git a/examples/pokemon-service-lambda/Cargo.toml b/examples/pokemon-service-lambda/Cargo.toml index b806c53891..137397de01 100644 --- a/examples/pokemon-service-lambda/Cargo.toml +++ b/examples/pokemon-service-lambda/Cargo.toml @@ -9,7 +9,7 @@ description = "A smithy Rust service to retrieve information about Pokémon via [dependencies] async-stream = "0.3.4" clap = { version = "4.1.11", features = ["derive"] } -hyper = {version = "0.14.25", features = ["server"] } +hyper = {version = "0.14.26", features = ["server"] } tokio = "1.26.0" tracing = "0.1" diff --git a/examples/pokemon-service-tls/Cargo.toml b/examples/pokemon-service-tls/Cargo.toml index ac3b32b7fe..e1c3b3a082 100644 --- a/examples/pokemon-service-tls/Cargo.toml +++ b/examples/pokemon-service-tls/Cargo.toml @@ -8,12 +8,12 @@ description = "A smithy Rust service to retrieve information about Pokémon." [dependencies] clap = { version = "4.1.11", features = ["derive"] } -hyper = { version = "0.14.25", features = ["server"] } +hyper = { version = "0.14.26", features = ["server"] } tokio = "1.26.0" # These dependencies are only required for the `pokemon-service-tls` program. -tls-listener = { version = "0.6.0", features = ["rustls", "hyper-h2"] } -tokio-rustls = "0.23.4" +tls-listener = { version = "0.7.0", features = ["rustls", "hyper-h2"] } +tokio-rustls = "0.24.0" rustls-pemfile = "1.0.2" futures-util = { version = "0.3.27", default-features = false } @@ -27,7 +27,7 @@ assert_cmd = "2.0" serial_test = "1.0.0" # This dependency is only required for testing the `pokemon-service-tls` program. -hyper-rustls = { version = "0.23.2", features = ["http2"] } +hyper-rustls = { version = "0.24", features = ["http2"] } # Local paths aws-smithy-client = { path = "../../rust-runtime/aws-smithy-client/", features = ["rustls"] } diff --git a/examples/pokemon-service/Cargo.toml b/examples/pokemon-service/Cargo.toml index 8324ffcc8c..d3bc81ea0b 100644 --- a/examples/pokemon-service/Cargo.toml +++ b/examples/pokemon-service/Cargo.toml @@ -8,7 +8,7 @@ description = "A smithy Rust service to retrieve information about Pokémon." [dependencies] clap = { version = "4.1.11", features = ["derive"] } -hyper = {version = "0.14.25", features = ["server"] } +hyper = { version = "0.14.26", features = ["server"] } tokio = "1.26.0" tower = "0.4" tracing = "0.1" @@ -25,10 +25,10 @@ rand = "0.8.5" serial_test = "1.0.0" # We use hyper client in tests -hyper = {version = "0.14.25", features = ["server", "client"] } +hyper = { version = "0.14.26", features = ["server", "client"] } # This dependency is only required for testing the `pokemon-service-tls` program. -hyper-rustls = { version = "0.23.2", features = ["http2"] } +hyper-rustls = { version = "0.24", features = ["http2"] } # Local paths aws-smithy-client = { path = "../../rust-runtime/aws-smithy-client/", features = ["rustls"] } diff --git a/examples/python/pokemon-service-test/Cargo.toml b/examples/python/pokemon-service-test/Cargo.toml index 8dbe8c6974..b4084185c2 100644 --- a/examples/python/pokemon-service-test/Cargo.toml +++ b/examples/python/pokemon-service-test/Cargo.toml @@ -14,7 +14,7 @@ tokio = { version = "1.20.1", features = ["full"] } serial_test = "2.0.0" rustls-pemfile = "1.0.1" tokio-rustls = "0.24.0" -hyper-rustls = { version = "0.24.0", features = ["http2"] } +hyper-rustls = { version = "0.24", features = ["http2"] } # Local paths aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client/", features = ["rustls"] } diff --git a/rust-runtime/aws-smithy-client/Cargo.toml b/rust-runtime/aws-smithy-client/Cargo.toml index 95d8e0c005..8d6ac25eaf 100644 --- a/rust-runtime/aws-smithy-client/Cargo.toml +++ b/rust-runtime/aws-smithy-client/Cargo.toml @@ -26,13 +26,13 @@ bytes = "1" fastrand = "1.4.0" http = "0.2.3" http-body = "0.4.4" -hyper = { version = "0.14.25", features = ["client", "http2", "http1", "tcp"], optional = true } +hyper = { version = "0.14.26", features = ["client", "http2", "http1", "tcp"], optional = true } # cargo does not support optional test dependencies, so to completely disable rustls # we need to add the webpki-roots feature here. # https://github.com/rust-lang/cargo/issues/1596 -hyper-rustls = { version = "0.23.0", optional = true, features = ["rustls-native-certs", "http2"] } +hyper-rustls = { version = "0.24", optional = true, features = ["rustls-native-certs", "http2"] } hyper-tls = { version = "0.5.0", optional = true } -rustls = { version = "0.20", optional = true } +rustls = { version = "0.21.1", optional = true } lazy_static = { version = "1", optional = true } pin-project-lite = "0.2.7" serde = { version = "1", features = ["derive"], optional = true } diff --git a/rust-runtime/aws-smithy-eventstream/Cargo.toml b/rust-runtime/aws-smithy-eventstream/Cargo.toml index a77aff9e85..e3c0e0316e 100644 --- a/rust-runtime/aws-smithy-eventstream/Cargo.toml +++ b/rust-runtime/aws-smithy-eventstream/Cargo.toml @@ -11,11 +11,11 @@ repository = "https://github.com/awslabs/smithy-rs" derive-arbitrary = ["arbitrary", "derive_arbitrary"] [dependencies] -derive_arbitrary = { version = "=1.1.6", optional = true } # 1.2.0 requires Rust 1.63 to compile arbitrary = { version = "=1.1.3", optional = true } # 1.1.4 requires Rust 1.63 to compile aws-smithy-types = { path = "../aws-smithy-types" } bytes = "1" crc32fast = "1.3" +derive_arbitrary = { version = "=1.1.6", optional = true } # 1.2.0 requires Rust 1.63 to compile [dev-dependencies] bytes-utils = "0.1" diff --git a/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml b/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml index d43627339b..0743de3809 100644 --- a/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml +++ b/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml @@ -9,11 +9,11 @@ edition = "2021" cargo-fuzz = true [dependencies] -derive_arbitrary = "=1.1.6" # 1.2.0 requires Rust 1.63 to compile arbitrary = "=1.1.3" # 1.1.4 requires Rust 1.63 to compile aws-smithy-types = { path = "../../aws-smithy-types" } bytes = "1" crc32fast = "1" +derive_arbitrary = "=1.1.6" # 1.2.0 requires Rust 1.63 to compile libfuzzer-sys = "0.4" [dependencies.aws-smithy-eventstream] diff --git a/rust-runtime/aws-smithy-http-server-python/Cargo.toml b/rust-runtime/aws-smithy-http-server-python/Cargo.toml index 334b049191..b454eab626 100644 --- a/rust-runtime/aws-smithy-http-server-python/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server-python/Cargo.toml @@ -21,7 +21,7 @@ aws-smithy-xml = { path = "../aws-smithy-xml" } bytes = "1.2" futures = "0.3" http = "0.2" -hyper = { version = "0.14.20", features = ["server", "http1", "http2", "tcp", "stream"] } +hyper = { version = "0.14.26", features = ["server", "http1", "http2", "tcp", "stream"] } tls-listener = { version = "0.7.0", features = ["rustls", "hyper-h2"] } rustls-pemfile = "1.0.1" tokio-rustls = "0.24.0" @@ -48,7 +48,7 @@ tower-test = "0.4" tokio-test = "0.4" pyo3-asyncio = { version = "0.18.0", features = ["testing", "attributes", "tokio-runtime", "unstable-streams"] } rcgen = "0.10.0" -hyper-rustls = { version = "0.24.0", features = ["http2"] } +hyper-rustls = { version = "0.24", features = ["http2"] } # PyO3 Asyncio tests cannot use Cargo's default testing harness because `asyncio` # wants to control the main thread. So we need to use testing harness provided by `pyo3_asyncio` diff --git a/rust-runtime/aws-smithy-http-server/Cargo.toml b/rust-runtime/aws-smithy-http-server/Cargo.toml index 57f6bc761a..f1cd62c2d4 100644 --- a/rust-runtime/aws-smithy-http-server/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server/Cargo.toml @@ -18,28 +18,28 @@ unredacted-logging = [] request-id = ["dep:uuid"] [dependencies] +async-trait = "0.1" aws-smithy-http = { path = "../aws-smithy-http", features = ["rt-tokio"] } -aws-smithy-types = { path = "../aws-smithy-types" } aws-smithy-json = { path = "../aws-smithy-json" } +aws-smithy-types = { path = "../aws-smithy-types" } aws-smithy-xml = { path = "../aws-smithy-xml" } -async-trait = "0.1" bytes = "1.1" futures-util = { version = "0.3.16", default-features = false } http = "0.2" http-body = "0.4" -hyper = { version = "0.14.12", features = ["server", "http1", "http2", "tcp", "stream"] } +hyper = { version = "0.14.26", features = ["server", "http1", "http2", "tcp", "stream"] } lambda_http = { version = "0.8.0", optional = true } mime = "0.3.4" nom = "7" -pin-project-lite = "0.2" once_cell = "1.13" +pin-project-lite = "0.2" regex = "1.5.5" serde_urlencoded = "0.7" -thiserror = "1.0.0" -tracing = "0.1.35" +thiserror = "1.0.40" tokio = { version = "1.23.1", features = ["full"] } tower = { version = "0.4.11", features = ["util", "make"], default-features = false } tower-http = { version = "0.3", features = ["add-extension", "map-response-body"] } +tracing = "0.1.35" uuid = { version = "1", features = ["v4", "fast-rng"], optional = true } [dev-dependencies] diff --git a/rust-runtime/aws-smithy-http/Cargo.toml b/rust-runtime/aws-smithy-http/Cargo.toml index e26419e4df..0238f5dfc6 100644 --- a/rust-runtime/aws-smithy-http/Cargo.toml +++ b/rust-runtime/aws-smithy-http/Cargo.toml @@ -28,7 +28,7 @@ pin-utils = "0.1.0" tracing = "0.1" # We are using hyper for our streaming body implementation, but this is an internal detail. -hyper = "0.14.25" +hyper = "0.14.26" # ByteStream internals futures-core = "0.3.14" @@ -38,7 +38,7 @@ tokio-util = { version = "0.7", optional = true } [dev-dependencies] async-stream = "0.3" futures-util = { version = "0.3.16", default-features = false } -hyper = { version = "0.14.25", features = ["stream"] } +hyper = { version = "0.14.26", features = ["stream"] } pretty_assertions = "1.3" proptest = "1" tokio = { version = "1.23.1", features = [ diff --git a/rust-runtime/aws-smithy-protocol-test/Cargo.toml b/rust-runtime/aws-smithy-protocol-test/Cargo.toml index e18aea12b4..8d67abb2a5 100644 --- a/rust-runtime/aws-smithy-protocol-test/Cargo.toml +++ b/rust-runtime/aws-smithy-protocol-test/Cargo.toml @@ -8,15 +8,14 @@ license = "Apache-2.0" repository = "https://github.com/awslabs/smithy-rs" [dependencies] -http = "0.2.1" -thiserror = "1" -serde_json = "1" -regex = "1.5" # Not perfect for our needs, but good for now assert-json-diff = "1.1" - +http = "0.2.1" pretty_assertions = "1.3" +regex = "1.5" roxmltree = "0.14.1" +serde_json = "1" +thiserror = "1.0.40" [package.metadata.docs.rs] diff --git a/rust-runtime/aws-smithy-types/Cargo.toml b/rust-runtime/aws-smithy-types/Cargo.toml index 9b61280f8f..ba4d59bb82 100644 --- a/rust-runtime/aws-smithy-types/Cargo.toml +++ b/rust-runtime/aws-smithy-types/Cargo.toml @@ -16,21 +16,21 @@ serde-serialize = [] serde-deserialize = [] [dependencies] +base64-simd = "0.8" itoa = "1.0.0" num-integer = "0.1.44" ryu = "1.0.5" time = { version = "0.3.4", features = ["parsing"] } -base64-simd = "0.8" [dev-dependencies] base64 = "0.13.0" +ciborium = { version = "0.2.1" } +criterion = "0.4" lazy_static = "1.4" proptest = "1" +rand = "0.8.4" serde = { version = "1", features = ["derive"] } serde_json = "1" -criterion = "0.4" -rand = "0.8.4" -ciborium = { version = "0.2.1" } [package.metadata.docs.rs] all-features = true diff --git a/tools/ci-build/publisher/Cargo.toml b/tools/ci-build/publisher/Cargo.toml index f076cedacf..772b80ae0d 100644 --- a/tools/ci-build/publisher/Cargo.toml +++ b/tools/ci-build/publisher/Cargo.toml @@ -30,12 +30,12 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" sha256 = "1" smithy-rs-tool-common = { version = "0.1", path = "../smithy-rs-tool-common", features = ["async-shell"] } +tempfile = "3.3.0" thiserror = "1.0" tokio = { version = "1.20.1", features = ["full"] } toml = { version = "0.5.8", features = ["preserve_order"] } tracing = "0.1.29" tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } -tempfile = "3.3.0" [dev-dependencies] pretty_assertions = "1.3" diff --git a/tools/ci-build/sdk-lints/Cargo.toml b/tools/ci-build/sdk-lints/Cargo.toml index 394c12e68d..99a567c13a 100644 --- a/tools/ci-build/sdk-lints/Cargo.toml +++ b/tools/ci-build/sdk-lints/Cargo.toml @@ -14,7 +14,7 @@ opt-level = 0 anyhow = "1" cargo_toml = "0.10.1" clap = { version = "~3.1.18", features = ["derive"]} -toml = "0.5.8" -serde = { version = "1", features = ["derive"]} lazy_static = "1.4.0" +serde = { version = "1", features = ["derive"]} smithy-rs-tool-common = { path = "../smithy-rs-tool-common" } +toml = "0.5.8" diff --git a/tools/ci-build/sdk-versioner/Cargo.toml b/tools/ci-build/sdk-versioner/Cargo.toml index f7fc947722..7a20bbdd14 100644 --- a/tools/ci-build/sdk-versioner/Cargo.toml +++ b/tools/ci-build/sdk-versioner/Cargo.toml @@ -15,8 +15,8 @@ opt-level = 0 [dependencies] anyhow = "1.0" clap = { version = "~3.1.18", features = ["derive"] } -toml_edit = { version = "0.19.6" } smithy-rs-tool-common = { version = "0.1", path = "../smithy-rs-tool-common" } +toml_edit = { version = "0.19.6" } [dev-dependencies] pretty_assertions = "1.3" diff --git a/tools/echo-server/Cargo.toml b/tools/echo-server/Cargo.toml index df50f243be..15331de6e8 100644 --- a/tools/echo-server/Cargo.toml +++ b/tools/echo-server/Cargo.toml @@ -7,8 +7,8 @@ edition = "2021" [dependencies] axum = "0.5.6" +hyper = { version = "0.14.26", features = ["full"] } tokio = { version = "1.20.1", features = ["full"] } +tower = { version = "0.4", features = ["util", "filter"] } tracing = "0.1" tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } -tower = { version = "0.4", features = ["util", "filter"] } -hyper = { version = "0.14.25", features = ["full"] } From 62dca85dc83209a8ff0e98e19baa71107ad10b07 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 12 Jun 2023 14:35:27 -0400 Subject: [PATCH 146/253] lazy-load signing region and scope from the ConfigBag (#2763) ## Motivation and Context RuntimePlugins **should not** consider other sources of configuration, instead, if needed, they should demonstrate these behaviors in RuntimePlugins. ## Description - Set region/signing service to `None` during initialization - Load region/signing service from the ConfigBag during signing along with loading them from EndpointConfig ## Testing - CI ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-runtime/src/auth/sigv4.rs | 8 +++++++- .../software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt | 6 ++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs index 1b4f85cb12..8dc9fb44f8 100644 --- a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -255,12 +255,18 @@ impl SigV4HttpRequestSigner { .get::() .ok_or(SigV4SigningError::MissingOperationSigningConfig)?; + let signing_region = config_bag.get::(); + let signing_service = config_bag.get::(); + let EndpointAuthSchemeConfig { signing_region_override, signing_service_override, } = Self::extract_endpoint_auth_scheme_config(auth_scheme_endpoint_config)?; - match (signing_region_override, signing_service_override) { + match ( + signing_region_override.or_else(|| signing_region.cloned()), + signing_service_override.or_else(|| signing_service.cloned()), + ) { (None, None) => Ok(Cow::Borrowed(operation_config)), (region, service) => { let mut operation_config = operation_config.clone(); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index d444531870..336e17472f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -131,8 +131,6 @@ private class AuthOperationCustomization(private val codegenContext: ClientCodeg val signingOptional = section.operationShape.hasTrait() rustTemplate( """ - let signing_region = cfg.get::<#{SigningRegion}>().cloned(); - let signing_service = cfg.get::<#{SigningService}>().cloned(); let mut signing_options = #{SigningOptions}::default(); signing_options.double_uri_encode = $doubleUriEncode; signing_options.content_sha256_header = $contentSha256Header; @@ -141,8 +139,8 @@ private class AuthOperationCustomization(private val codegenContext: ClientCodeg signing_options.payload_override = #{payload_override}; ${section.configBagName}.put(#{SigV4OperationSigningConfig} { - region: signing_region, - service: signing_service, + region: None, + service: None, signing_options, }); // TODO(enableNewSmithyRuntime): Make auth options additive in the config bag so that multiple codegen decorators can register them From 356444bf951813e7a19efcef6a32f5a4ced6e7ae Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 13 Jun 2023 11:36:52 +0200 Subject: [PATCH 147/253] Fix server SDK bug with directly constrained list/map shapes in operation output (#2761) Fixes https://github.com/awslabs/smithy-rs/issues/2760. ## Testing I verified that the updated `constraints.smithy` integration test does not compile without the fix applied. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../common-test-models/constraints.smithy | 36 +++++ .../CollectionConstraintViolationGenerator.kt | 2 +- .../MapConstraintViolationGenerator.kt | 10 +- .../UnconstrainedCollectionGeneratorTest.kt | 129 +++++++----------- .../UnconstrainedMapGeneratorTest.kt | 117 +++++++--------- 5 files changed, 140 insertions(+), 154 deletions(-) diff --git a/codegen-core/common-test-models/constraints.smithy b/codegen-core/common-test-models/constraints.smithy index a2add6c86a..c30c669b16 100644 --- a/codegen-core/common-test-models/constraints.smithy +++ b/codegen-core/common-test-models/constraints.smithy @@ -11,6 +11,9 @@ use smithy.framework#ValidationException service ConstraintsService { operations: [ ConstrainedShapesOperation, + // See https://github.com/awslabs/smithy-rs/issues/2760 for why testing operations reaching + // constrained shapes that only lie in the output is important. + ConstrainedShapesOnlyInOutputOperation, ConstrainedHttpBoundShapesOperation, ConstrainedHttpPayloadBoundShapeOperation, ConstrainedRecursiveShapesOperation, @@ -51,6 +54,11 @@ operation ConstrainedShapesOperation { errors: [ValidationException] } +@http(uri: "/constrained-shapes-only-in-output-operation", method: "POST") +operation ConstrainedShapesOnlyInOutputOperation { + output: ConstrainedShapesOnlyInOutputOperationOutput, +} + @http( uri: "/constrained-http-bound-shapes-operation/{rangeIntegerLabel}/{rangeShortLabel}/{rangeLongLabel}/{rangeByteLabel}/{lengthStringLabel}/{enumStringLabel}", method: "POST" @@ -935,3 +943,31 @@ map MapOfListOfListOfConB { key: String, value: ConBList } + +structure ConstrainedShapesOnlyInOutputOperationOutput { + list: ConstrainedListInOutput + map: ConstrainedMapInOutput + // Unions were not affected by + // https://github.com/awslabs/smithy-rs/issues/2760, but testing anyway for + // good measure. + union: ConstrainedUnionInOutput +} + +@length(min: 69) +list ConstrainedListInOutput { + member: ConstrainedUnionInOutput +} + +@length(min: 69) +map ConstrainedMapInOutput { + key: String + value: TransitivelyConstrainedStructureInOutput +} + +union ConstrainedUnionInOutput { + structure: TransitivelyConstrainedStructureInOutput +} + +structure TransitivelyConstrainedStructureInOutput { + lengthString: LengthString +} diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/CollectionConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/CollectionConstraintViolationGenerator.kt index e2a177f536..b04eaa2f93 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/CollectionConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/CollectionConstraintViolationGenerator.kt @@ -48,7 +48,7 @@ class CollectionConstraintViolationGenerator( inlineModuleCreator(constraintViolationSymbol) { val constraintViolationVariants = constraintsInfo.map { it.constraintViolationVariant }.toMutableList() - if (isMemberConstrained) { + if (shape.isReachableFromOperationInput() && isMemberConstrained) { constraintViolationVariants += { val memberConstraintViolationSymbol = constraintViolationSymbolProvider.toSymbol(targetShape).letIf( diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt index 065a4067c7..4c1025a0d2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/MapConstraintViolationGenerator.kt @@ -45,10 +45,12 @@ class MapConstraintViolationGenerator( val constraintViolationName = constraintViolationSymbol.name val constraintViolationCodegenScopeMutableList: MutableList> = mutableListOf() - if (isKeyConstrained(keyShape, symbolProvider)) { + val keyConstraintViolationExists = shape.isReachableFromOperationInput() && isKeyConstrained(keyShape, symbolProvider) + val valueConstraintViolationExists = shape.isReachableFromOperationInput() && isValueConstrained(valueShape, model, symbolProvider) + if (keyConstraintViolationExists) { constraintViolationCodegenScopeMutableList.add("KeyConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(keyShape)) } - if (isValueConstrained(valueShape, model, symbolProvider)) { + if (valueConstraintViolationExists) { constraintViolationCodegenScopeMutableList.add( "ValueConstraintViolationSymbol" to constraintViolationSymbolProvider.toSymbol(valueShape).letIf( @@ -78,8 +80,8 @@ class MapConstraintViolationGenerator( ##[derive(Debug, PartialEq)] pub${ if (constraintViolationVisibility == Visibility.PUBCRATE) " (crate) " else "" } enum $constraintViolationName { ${if (shape.hasTrait()) "Length(usize)," else ""} - ${if (isKeyConstrained(keyShape, symbolProvider)) "##[doc(hidden)] Key(#{KeyConstraintViolationSymbol})," else ""} - ${if (isValueConstrained(valueShape, model, symbolProvider)) "##[doc(hidden)] Value(#{KeySymbol}, #{ValueConstraintViolationSymbol})," else ""} + ${if (keyConstraintViolationExists) "##[doc(hidden)] Key(#{KeyConstraintViolationSymbol})," else ""} + ${if (valueConstraintViolationExists) "##[doc(hidden)] Value(#{KeySymbol}, #{ValueConstraintViolationSymbol})," else ""} } """, *constraintViolationCodegenScope, diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 7da7a67e62..885b54f61f 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -6,20 +6,11 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test -import software.amazon.smithy.model.shapes.ListShape -import software.amazon.smithy.model.shapes.StructureShape -import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace +import software.amazon.smithy.rust.codegen.core.rustlang.rust 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.testModule import software.amazon.smithy.rust.codegen.core.testutil.unitTest -import software.amazon.smithy.rust.codegen.core.util.lookup -import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule -import software.amazon.smithy.rust.codegen.server.smithy.createTestInlineModuleCreator -import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionConversionGenerator -import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerRestJsonProtocol -import software.amazon.smithy.rust.codegen.server.smithy.renderInlineMemoryModules -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest class UnconstrainedCollectionGeneratorTest { @Test @@ -27,6 +18,25 @@ class UnconstrainedCollectionGeneratorTest { val model = """ namespace test + + use aws.protocols#restJson1 + use smithy.framework#ValidationException + + @restJson1 + service TestService { + operations: ["Operation"] + } + + @http(uri: "/operation", method: "POST") + operation Operation { + input: OperationInputOutput + output: OperationInputOutput + errors: [ValidationException] + } + + structure OperationInputOutput { + list: ListA + } list ListA { member: ListB @@ -44,58 +54,16 @@ class UnconstrainedCollectionGeneratorTest { string: String } """.asSmithyModel() - val codegenContext = serverTestCodegenContext(model) - val symbolProvider = codegenContext.symbolProvider - - val listA = model.lookup("test#ListA") - val listB = model.lookup("test#ListB") - - val project = TestWorkspace.testProject(symbolProvider) - - project.withModule(ServerRustModule.Model) { - model.lookup("test#StructureC").serverRenderWithModelBuilder( - project, - model, - symbolProvider, - this, - ServerRestJsonProtocol(codegenContext), - ) - } - - project.withModule(ServerRustModule.ConstrainedModule) { - listOf(listA, listB).forEach { - PubCrateConstrainedCollectionGenerator( - codegenContext, - this.createTestInlineModuleCreator(), - it, - ).render() - } - } - project.withModule(ServerRustModule.UnconstrainedModule) unconstrainedModuleWriter@{ - project.withModule(ServerRustModule.Model) modelsModuleWriter@{ - listOf(listA, listB).forEach { - UnconstrainedCollectionGenerator( - codegenContext, - this@unconstrainedModuleWriter.createTestInlineModuleCreator(), - it, - ).render() - - CollectionConstraintViolationGenerator( - codegenContext, - this@modelsModuleWriter.createTestInlineModuleCreator(), - it, - CollectionTraitInfo.fromShape(it, codegenContext.constrainedShapeSymbolProvider), - SmithyValidationExceptionConversionGenerator(codegenContext), - ).render() - } - this@unconstrainedModuleWriter.unitTest( - name = "list_a_unconstrained_fail_to_constrain_with_first_error", - test = """ + serverIntegrationTest(model) { _, rustCrate -> + rustCrate.testModule { + unitTest("list_a_unconstrained_fail_to_constrain_with_first_error") { + rust( + """ let c_builder1 = crate::model::StructureC::builder().int(69); let c_builder2 = crate::model::StructureC::builder().string("david".to_owned()); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder1, c_builder2]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + let list_b_unconstrained = crate::unconstrained::list_b_unconstrained::ListBUnconstrained(vec![c_builder1, c_builder2]); + let list_a_unconstrained = crate::unconstrained::list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let expected_err = crate::model::list_a::ConstraintViolation::Member(0, crate::model::list_b::ConstraintViolation::Member( @@ -106,15 +74,16 @@ class UnconstrainedCollectionGeneratorTest { expected_err, crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap_err() ); - """, - ) + """, + ) + } - this@unconstrainedModuleWriter.unitTest( - name = "list_a_unconstrained_succeed_to_constrain", - test = """ + unitTest("list_a_unconstrained_succeed_to_constrain") { + rust( + """ let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + let list_b_unconstrained = crate::unconstrained::list_b_unconstrained::ListBUnconstrained(vec![c_builder]); + let list_a_unconstrained = crate::unconstrained::list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); let expected: Vec> = vec![vec![crate::model::StructureC { string: "david".to_owned(), @@ -124,22 +93,22 @@ class UnconstrainedCollectionGeneratorTest { crate::constrained::list_a_constrained::ListAConstrained::try_from(list_a_unconstrained).unwrap().into(); assert_eq!(expected, actual); - """, - ) + """, + ) + } - this@unconstrainedModuleWriter.unitTest( - name = "list_a_unconstrained_converts_into_constrained", - test = """ + unitTest("list_a_unconstrained_converts_into_constrained") { + rust( + """ let c_builder = crate::model::StructureC::builder(); - let list_b_unconstrained = list_b_unconstrained::ListBUnconstrained(vec![c_builder]); - let list_a_unconstrained = list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); - + let list_b_unconstrained = crate::unconstrained::list_b_unconstrained::ListBUnconstrained(vec![c_builder]); + let list_a_unconstrained = crate::unconstrained::list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); + let _list_a: crate::constrained::MaybeConstrained = list_a_unconstrained.into(); - """, - ) + """, + ) + } } } - project.renderInlineMemoryModules() - project.compileAndTest() } } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index 9cc5fe5870..8b2cbd3907 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -7,20 +7,14 @@ package software.amazon.smithy.rust.codegen.server.smithy.generators import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.MapShape -import software.amazon.smithy.model.shapes.StructureShape +import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.smithy.CoreCodegenConfig 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.core.testutil.testModule import software.amazon.smithy.rust.codegen.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup -import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule -import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule.Model -import software.amazon.smithy.rust.codegen.server.smithy.createTestInlineModuleCreator -import software.amazon.smithy.rust.codegen.server.smithy.customizations.SmithyValidationExceptionConversionGenerator -import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerRestJsonProtocol -import software.amazon.smithy.rust.codegen.server.smithy.renderInlineMemoryModules -import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverRenderWithModelBuilder +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverIntegrationTest import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestCodegenContext class UnconstrainedMapGeneratorTest { @@ -29,6 +23,25 @@ class UnconstrainedMapGeneratorTest { val model = """ namespace test + + use aws.protocols#restJson1 + use smithy.framework#ValidationException + + @restJson1 + service TestService { + operations: ["Operation"] + } + + @http(uri: "/operation", method: "POST") + operation Operation { + input: OperationInputOutput + output: OperationInputOutput + errors: [ValidationException] + } + + structure OperationInputOutput { + map: MapA + } map MapA { key: String, @@ -56,53 +69,20 @@ class UnconstrainedMapGeneratorTest { val project = TestWorkspace.testProject(symbolProvider, CoreCodegenConfig(debugMode = true)) - project.withModule(Model) { - model.lookup("test#StructureC").serverRenderWithModelBuilder( - project, - model, - symbolProvider, - this, - ServerRestJsonProtocol(codegenContext), - ) - } - - project.withModule(ServerRustModule.ConstrainedModule) { - listOf(mapA, mapB).forEach { - PubCrateConstrainedMapGenerator( - codegenContext, - this.createTestInlineModuleCreator(), - it, - ).render() - } - } - project.withModule(ServerRustModule.UnconstrainedModule) unconstrainedModuleWriter@{ - project.withModule(Model) modelsModuleWriter@{ - listOf(mapA, mapB).forEach { - UnconstrainedMapGenerator( - codegenContext, - this@unconstrainedModuleWriter.createTestInlineModuleCreator(), it, - ).render() - - MapConstraintViolationGenerator( - codegenContext, - this@modelsModuleWriter.createTestInlineModuleCreator(), - it, - SmithyValidationExceptionConversionGenerator(codegenContext), - ).render() - } - - this@unconstrainedModuleWriter.unitTest( - name = "map_a_unconstrained_fail_to_constrain_with_some_error", - test = """ + serverIntegrationTest(model) { _, rustCrate -> + rustCrate.testModule { + unitTest("map_a_unconstrained_fail_to_constrain_with_some_error") { + rust( + """ let c_builder1 = crate::model::StructureC::builder().int(69); let c_builder2 = crate::model::StructureC::builder().string(String::from("david")); - let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( + let map_b_unconstrained = crate::unconstrained::map_b_unconstrained::MapBUnconstrained( std::collections::HashMap::from([ (String::from("KeyB1"), c_builder1), (String::from("KeyB2"), c_builder2), ]) ); - let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( + let map_a_unconstrained = crate::unconstrained::map_a_unconstrained::MapAUnconstrained( std::collections::HashMap::from([ (String::from("KeyA"), map_b_unconstrained), ]) @@ -127,19 +107,19 @@ class UnconstrainedMapGeneratorTest { let actual_err = crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap_err(); assert!(actual_err == missing_string_expected_err || actual_err == missing_int_expected_err); - """, - ) - - this@unconstrainedModuleWriter.unitTest( - name = "map_a_unconstrained_succeed_to_constrain", - test = """ + """, + ) + } + unitTest("map_a_unconstrained_succeed_to_constrain") { + rust( + """ let c_builder = crate::model::StructureC::builder().int(69).string(String::from("david")); - let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( + let map_b_unconstrained = crate::unconstrained::map_b_unconstrained::MapBUnconstrained( std::collections::HashMap::from([ (String::from("KeyB"), c_builder), ]) ); - let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( + let map_a_unconstrained = crate::unconstrained::map_a_unconstrained::MapAUnconstrained( std::collections::HashMap::from([ (String::from("KeyA"), map_b_unconstrained), ]) @@ -158,30 +138,29 @@ class UnconstrainedMapGeneratorTest { expected, crate::constrained::map_a_constrained::MapAConstrained::try_from(map_a_unconstrained).unwrap().into() ); - """, - ) - - this@unconstrainedModuleWriter.unitTest( - name = "map_a_unconstrained_converts_into_constrained", - test = """ + """, + ) + } + unitTest("map_a_unconstrained_converts_into_constrained") { + rust( + """ let c_builder = crate::model::StructureC::builder(); - let map_b_unconstrained = map_b_unconstrained::MapBUnconstrained( + let map_b_unconstrained = crate::unconstrained::map_b_unconstrained::MapBUnconstrained( std::collections::HashMap::from([ (String::from("KeyB"), c_builder), ]) ); - let map_a_unconstrained = map_a_unconstrained::MapAUnconstrained( + let map_a_unconstrained = crate::unconstrained::map_a_unconstrained::MapAUnconstrained( std::collections::HashMap::from([ (String::from("KeyA"), map_b_unconstrained), ]) ); let _map_a: crate::constrained::MaybeConstrained = map_a_unconstrained.into(); - """, - ) + """, + ) + } } } - project.renderInlineMemoryModules() - project.compileAndTest() } } From cfff41a8837b00d808ec85648ca3bf1f72152766 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 13 Jun 2023 08:23:04 -0700 Subject: [PATCH 148/253] Upgrade MSRV to 1.68.2 (#2745) ## Motivation and Context This PR upgrades the MSRV to 1.68.2. ## Checklist - [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._ --------- Co-authored-by: Zelda Hessler Co-authored-by: 82marbag <69267416+82marbag@users.noreply.github.com> --- .cargo/config.toml | 4 ++ .github/workflows/ci.yml | 2 +- .github/workflows/claim-crate-names.yml | 2 +- .github/workflows/github-pages.yml | 2 +- .github/workflows/pull-request-bot.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/update-sdk-next.yml | 2 +- CHANGELOG.next.toml | 12 +++++ .../aws-inlineable/src/glacier_checksums.rs | 2 +- .../src/glacier_interceptors.rs | 2 +- .../smithy/rustsdk/AwsPresigningDecorator.kt | 2 +- .../webassembly/.cargo/config.toml | 4 ++ .../smithy/rust/codegen/core/testutil/Rust.kt | 2 +- gradle.properties | 2 +- .../src/proto/aws_json_10/router.rs | 1 - .../src/proto/aws_json_11/router.rs | 1 - .../src/proto/rest_json_1/router.rs | 1 - .../src/proto/rest_xml/router.rs | 1 - .../src/client/test_util/serializer.rs | 6 +-- rust-toolchain.toml | 2 +- tools/.cargo/config.toml | 4 ++ tools/ci-build/Dockerfile | 44 ++++++++----------- .../smithy-rs-tool-common/src/changelog.rs | 10 +---- .../ci-build/smithy-rs-tool-common/src/git.rs | 1 - 24 files changed, 59 insertions(+), 54 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 0015055659..5afa0e9471 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,7 @@ [build] # Share one `target` directory at the project root for all Cargo projects and workspaces in smithy-rs target-dir = "target" + +# TODO(https://github.com/awslabs/smithy-rs/issues/2766): The sparse registry config can be removed when upgrading to Rust 1.70 +[registries.crates-io] +protocol = "sparse" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fa47463eab..2fb0575046 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ on: required: false env: - rust_version: 1.67.1 + rust_version: 1.68.2 rust_toolchain_components: clippy,rustfmt ENCRYPTED_DOCKER_PASSWORD: ${{ secrets.ENCRYPTED_DOCKER_PASSWORD }} DOCKER_LOGIN_TOKEN_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} diff --git a/.github/workflows/claim-crate-names.yml b/.github/workflows/claim-crate-names.yml index a7019823c8..d6e105d45f 100644 --- a/.github/workflows/claim-crate-names.yml +++ b/.github/workflows/claim-crate-names.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true env: - rust_version: 1.67.1 + rust_version: 1.68.2 name: Claim unpublished crate names on crates.io run-name: ${{ github.workflow }} diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 9e0f52d9b6..c597e186c6 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -8,7 +8,7 @@ on: name: Update GitHub Pages env: - rust_version: 1.67.1 + rust_version: 1.68.2 # Allow only one doc pages build to run at a time for the entire smithy-rs repo concurrency: diff --git a/.github/workflows/pull-request-bot.yml b/.github/workflows/pull-request-bot.yml index aafeaf773c..c24ac70aae 100644 --- a/.github/workflows/pull-request-bot.yml +++ b/.github/workflows/pull-request-bot.yml @@ -28,7 +28,7 @@ concurrency: env: java_version: 11 - rust_version: 1.67.1 + rust_version: 1.68.2 rust_toolchain_components: clippy,rustfmt apt_dependencies: libssl-dev gnuplot jq diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db3af0cb9c..41a85393f8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true env: - rust_version: 1.67.1 + rust_version: 1.68.2 name: Release smithy-rs run-name: ${{ github.workflow }} ${{ inputs.semantic_version }} (${{ inputs.commit_sha }}) - ${{ inputs.dry_run && 'Dry run' || 'Production run' }} diff --git a/.github/workflows/update-sdk-next.yml b/.github/workflows/update-sdk-next.yml index 728f645169..47a3af6221 100644 --- a/.github/workflows/update-sdk-next.yml +++ b/.github/workflows/update-sdk-next.yml @@ -32,7 +32,7 @@ jobs: - name: Set up Rust uses: dtolnay/rust-toolchain@master with: - toolchain: 1.67.1 + toolchain: 1.68.2 - name: Delete old SDK run: | - name: Generate a fresh SDK diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index fa8f427f1b..34fc2b7659 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -134,6 +134,18 @@ references = ["smithy-rs#2742"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "ysaito1001" +[[aws-sdk-rust]] +message = "Update MSRV to Rust 1.68.2" +references = ["smithy-rs#2745"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "jdisanti" + +[[smithy-rs]] +message = "Update MSRV to Rust 1.68.2" +references = ["smithy-rs#2745"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all"} +author = "jdisanti" + [[smithy-rs]] message = """`ShapeId` is the new structure used to represent a shape, with its absolute name, namespace and name. `OperationExtension`'s members are replaced by the `ShapeId` and operations' names are now replced by a `ShapeId`. diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs b/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs index d22d28ed92..9cb8cc0164 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs @@ -109,7 +109,7 @@ fn compute_hash_tree(mut hashes: Vec) -> Digest { "even an empty file will produce a digest. this function assumes that hashes is non-empty" ); while hashes.len() > 1 { - let next = hashes.chunks(2).into_iter().map(|chunk| match *chunk { + let next = hashes.chunks(2).map(|chunk| match *chunk { [left, right] => { let mut ctx = Context::new(&SHA256); ctx.update(left.as_ref()); diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs index de08e9f582..e4b313da60 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -215,7 +215,7 @@ fn compute_hash_tree(mut hashes: Vec) -> Digest { "even an empty file will produce a digest. this function assumes that hashes is non-empty" ); while hashes.len() > 1 { - let next = hashes.chunks(2).into_iter().map(|chunk| match *chunk { + let next = hashes.chunks(2).map(|chunk| match *chunk { [left, right] => { let mut ctx = Context::new(&SHA256); ctx.update(left.as_ref()); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index ba304c6de9..bbf5f80fb5 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -229,7 +229,7 @@ class AwsInputPresignedMethod( } rustTemplate( """ - let mut config = props.get_mut::<#{sig_auth}::signer::OperationSigningConfig>() + let config = props.get_mut::<#{sig_auth}::signer::OperationSigningConfig>() .expect("signing config added by make_operation()"); config.signature_type = #{sig_auth}::signer::HttpSignatureType::HttpRequestQueryParams; config.expires_in = Some(presigning_config.expires()); diff --git a/aws/sdk/integration-tests/webassembly/.cargo/config.toml b/aws/sdk/integration-tests/webassembly/.cargo/config.toml index 031dad7377..e6e321b297 100644 --- a/aws/sdk/integration-tests/webassembly/.cargo/config.toml +++ b/aws/sdk/integration-tests/webassembly/.cargo/config.toml @@ -3,3 +3,7 @@ target = "wasm32-wasi" [target.wasm32-wasi] rustflags = ["-C", "opt-level=1"] + +# TODO(Rust 1.70): The sparse registry config can be removed when upgrading to Rust 1.70 +[registries.crates-io] +protocol = "sparse" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt index 4e0b0f67f9..fa98e87fcb 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt @@ -109,7 +109,7 @@ object TestWorkspace { // help rust select the right version when we run cargo test // TODO(https://github.com/awslabs/smithy-rs/issues/2048): load this from the msrv property using a // method as we do for runtime crate versions - "[toolchain]\nchannel = \"1.67.1\"\n", + "[toolchain]\nchannel = \"1.68.2\"\n", ) // ensure there at least an empty lib.rs file to avoid broken crates newProject.resolve("src").mkdirs() diff --git a/gradle.properties b/gradle.properties index 16f1511023..aa791db007 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ # # Rust MSRV (entered into the generated README) -rust.msrv=1.67.1 +rust.msrv=1.68.2 # To enable debug, swap out the two lines below. # When changing this value, be sure to run `./gradlew --stop` to kill the Gradle daemon. diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/router.rs b/rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/router.rs index 50e73e2198..7b130b24a7 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/router.rs @@ -5,7 +5,6 @@ use crate::body::{empty, BoxBody}; use crate::extension::RuntimeErrorExtension; -use crate::proto::aws_json::router::Error; use crate::response::IntoResponse; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/router.rs b/rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/router.rs index c7d1176e98..730feeb197 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/router.rs @@ -5,7 +5,6 @@ use crate::body::{empty, BoxBody}; use crate::extension::RuntimeErrorExtension; -use crate::proto::aws_json::router::Error; use crate::response::IntoResponse; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/router.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/router.rs index 3a2a5111b6..508812e554 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/router.rs @@ -5,7 +5,6 @@ use crate::body::BoxBody; use crate::extension::RuntimeErrorExtension; -use crate::proto::rest::router::Error; use crate::response::IntoResponse; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/router.rs b/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/router.rs index b771884b04..9dbdf1e43b 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/router.rs @@ -6,7 +6,6 @@ use crate::body::empty; use crate::body::BoxBody; use crate::extension::RuntimeErrorExtension; -use crate::proto::rest::router::Error; use crate::response::IntoResponse; use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs index 9c27e07603..705324deca 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -44,10 +44,8 @@ impl RequestSerializer for CannedRequestSerializer { _input: Input, _cfg: &mut ConfigBag, ) -> Result { - let req = self - .take() - .ok_or("CannedRequestSerializer's inner value has already been taken.")?; - req + self.take() + .ok_or("CannedRequestSerializer's inner value has already been taken.")? } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 588ffd5788..864d3c411a 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.67.1" +channel = "1.68.2" diff --git a/tools/.cargo/config.toml b/tools/.cargo/config.toml index 2bf2a2e3e4..6636645498 100644 --- a/tools/.cargo/config.toml +++ b/tools/.cargo/config.toml @@ -2,3 +2,7 @@ # Tools shouldn't share `target` with the rest of the project to # avoid thrash from differing compiler flags target-dir = "target" + +# TODO(Rust 1.70): The sparse registry config can be removed when upgrading to Rust 1.70 +[registries.crates-io] +protocol = "sparse" diff --git a/tools/ci-build/Dockerfile b/tools/ci-build/Dockerfile index 045805f479..3e04f15c7d 100644 --- a/tools/ci-build/Dockerfile +++ b/tools/ci-build/Dockerfile @@ -6,8 +6,8 @@ # This is the base Docker build image used by CI ARG base_image=public.ecr.aws/amazonlinux/amazonlinux:2 -ARG rust_stable_version=1.67.1 -ARG rust_nightly_version=nightly-2022-11-16 +ARG rust_stable_version=1.68.2 +ARG rust_nightly_version=nightly-2023-05-31 FROM ${base_image} AS bare_base_image RUN yum -y updateinfo @@ -80,53 +80,46 @@ RUN set -eux; \ cd smithy-rs; \ git checkout ${smithy_rs_commit_hash}; \ fi; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/publisher; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/changelogger; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/crate-hasher; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/difftags; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/sdk-lints; \ - cargo +${rust_nightly_version} -Z sparse-registry install --locked --path tools/ci-build/sdk-versioner; \ + cargo install --locked --path tools/ci-build/publisher; \ + cargo install --locked --path tools/ci-build/changelogger; \ + cargo install --locked --path tools/ci-build/crate-hasher; \ + cargo install --locked --path tools/ci-build/difftags; \ + cargo install --locked --path tools/ci-build/sdk-lints; \ + cargo install --locked --path tools/ci-build/sdk-versioner; \ chmod g+rw -R /opt/cargo/registry FROM install_rust AS cargo_deny ARG cargo_deny_version=0.13.5 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-deny --locked --version ${cargo_deny_version} +RUN cargo install cargo-deny --locked --version ${cargo_deny_version} FROM install_rust AS cargo_udeps ARG cargo_udeps_version=0.1.35 ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-udeps --locked --version ${cargo_udeps_version} +RUN cargo +${rust_nightly_version} install cargo-udeps --locked --version ${cargo_udeps_version} FROM install_rust AS cargo_hack ARG cargo_hack_version=0.5.23 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-hack --locked --version ${cargo_hack_version} +RUN cargo install cargo-hack --locked --version ${cargo_hack_version} FROM install_rust AS cargo_minimal_versions ARG cargo_minimal_versions_version=0.1.8 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-minimal-versions --locked --version ${cargo_minimal_versions_version} +RUN cargo install cargo-minimal-versions --locked --version ${cargo_minimal_versions_version} FROM install_rust AS cargo_check_external_types -ARG cargo_check_external_types_version=0.1.6 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-check-external-types --locked --version ${cargo_check_external_types_version} +ARG cargo_check_external_types_version=0.1.7 +RUN cargo install cargo-check-external-types --locked --version ${cargo_check_external_types_version} FROM install_rust AS maturin ARG maturin_version=0.14.1 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install maturin --locked --version ${maturin_version} +RUN cargo install maturin --locked --version ${maturin_version} FROM install_rust AS wasm_pack ARG wasm_pack_version=0.11.0 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install wasm-pack --locked --version ${wasm_pack_version} +RUN cargo install wasm-pack --locked --version ${wasm_pack_version} FROM install_rust AS wasmtime ARG wasmtime_precompiled_url=https://github.com/bytecodealliance/wasmtime/releases/download/v7.0.0/wasmtime-v7.0.0-x86_64-linux.tar.xz ARG wasmtime_precompiled_sha256=b8a1c97f9107c885ea73a5c38677d0d340a7c26879d366e8a5f3dce84cffec99 -ARG rust_nightly_version RUN set -eux; \ curl "${wasmtime_precompiled_url}" -L -o wasmtime.xz; \ echo "${wasmtime_precompiled_sha256} wasmtime.xz" | sha256sum --check; \ @@ -135,8 +128,7 @@ RUN set -eux; \ FROM install_rust AS cargo_wasi ARG cargo_wasi_version=0.1.27 -ARG rust_nightly_version -RUN cargo +${rust_nightly_version} -Z sparse-registry install cargo-wasi --locked --version ${cargo_wasi_version} +RUN cargo install cargo-wasi --locked --version ${cargo_wasi_version} FROM install_rust AS cargo_semver_checks ARG cargo_semver_checks_version=0.20.0 @@ -194,6 +186,7 @@ COPY --chown=build:build --from=cargo_mdbook /opt/cargo/bin/mdbook /opt/cargo/bi COPY --chown=build:build --from=cargo_mdbook_mermaid /opt/cargo/bin/mdbook-mermaid /opt/cargo/bin/mdbook-mermaid COPY --chown=build:build --from=musl_toolchain /usr/local/musl/ /usr/local/musl/ ENV PATH=$PATH:/usr/local/musl/bin/ +# TODO(Rust 1.70): The sparse registry config (`CARGO_REGISTRIES_CRATES_IO_PROTOCOL`) can be removed when upgrading to Rust 1.70 ENV PATH=/opt/cargo/bin:$PATH \ CARGO_HOME=/opt/cargo \ RUSTUP_HOME=/opt/rustup \ @@ -201,6 +194,7 @@ ENV PATH=/opt/cargo/bin:$PATH \ GRADLE_USER_HOME=/home/build/.gradle \ RUST_STABLE_VERSION=${rust_stable_version} \ RUST_NIGHTLY_VERSION=${rust_nightly_version} \ + CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse \ CARGO_INCREMENTAL=0 \ RUSTDOCFLAGS="-D warnings" \ RUSTFLAGS="-D warnings" \ diff --git a/tools/ci-build/smithy-rs-tool-common/src/changelog.rs b/tools/ci-build/smithy-rs-tool-common/src/changelog.rs index 4944c7aa4c..acdee013ae 100644 --- a/tools/ci-build/smithy-rs-tool-common/src/changelog.rs +++ b/tools/ci-build/smithy-rs-tool-common/src/changelog.rs @@ -11,22 +11,17 @@ use std::fmt::{self, Display}; use std::path::Path; use std::str::FromStr; -#[derive(Copy, Clone, Debug, Serialize, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Default, Serialize, PartialEq, Eq)] pub enum SdkAffected { #[serde(rename = "client")] Client, #[serde(rename = "server")] Server, #[serde(rename = "all")] + #[default] All, } -impl Default for SdkAffected { - fn default() -> Self { - SdkAffected::All - } -} - impl Display for SdkAffected { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -209,7 +204,6 @@ impl Changelog { // Remove comments from the top let value = value .split('\n') - .into_iter() .filter(|line| !line.trim().starts_with('#')) .collect::>() .join("\n"); diff --git a/tools/ci-build/smithy-rs-tool-common/src/git.rs b/tools/ci-build/smithy-rs-tool-common/src/git.rs index c370529c91..da57a95f5a 100644 --- a/tools/ci-build/smithy-rs-tool-common/src/git.rs +++ b/tools/ci-build/smithy-rs-tool-common/src/git.rs @@ -295,7 +295,6 @@ impl Git for GitCLI { let (stdout, _) = output_text(&output); Ok(stdout .split_ascii_whitespace() - .into_iter() .map(CommitHash::from) .collect()) } From 8e37d42f3cc01da7b3d8efd9ff3433d564d4debf Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 13 Jun 2023 08:58:27 -0700 Subject: [PATCH 149/253] Unpin the `arbitrary` and `derive-arbitrary` dependencies (#2765) These no longer need to be pinned since the current MSRV is compatible with their latest versions. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-config/Cargo.toml | 2 +- rust-runtime/aws-smithy-eventstream/Cargo.toml | 4 ++-- rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index cd4bc4d110..54add78bb8 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -55,7 +55,7 @@ tracing-subscriber = { version = "0.3.16", features = ["fmt", "json"] } tokio = { version = "1.23.1", features = ["full", "test-util"] } # used for fuzzing profile parsing -arbitrary = "=1.1.3" # 1.1.4 requires Rust 1.63 to compile +arbitrary = "1.3" # used for test case deserialization serde = { version = "1", features = ["derive"] } diff --git a/rust-runtime/aws-smithy-eventstream/Cargo.toml b/rust-runtime/aws-smithy-eventstream/Cargo.toml index e3c0e0316e..c6a3e6782f 100644 --- a/rust-runtime/aws-smithy-eventstream/Cargo.toml +++ b/rust-runtime/aws-smithy-eventstream/Cargo.toml @@ -11,11 +11,11 @@ repository = "https://github.com/awslabs/smithy-rs" derive-arbitrary = ["arbitrary", "derive_arbitrary"] [dependencies] -arbitrary = { version = "=1.1.3", optional = true } # 1.1.4 requires Rust 1.63 to compile +arbitrary = { version = "1.3", optional = true } aws-smithy-types = { path = "../aws-smithy-types" } bytes = "1" crc32fast = "1.3" -derive_arbitrary = { version = "=1.1.6", optional = true } # 1.2.0 requires Rust 1.63 to compile +derive_arbitrary = { version = "1.3", optional = true } [dev-dependencies] bytes-utils = "0.1" diff --git a/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml b/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml index 0743de3809..f95d493835 100644 --- a/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml +++ b/rust-runtime/aws-smithy-eventstream/fuzz/Cargo.toml @@ -9,11 +9,11 @@ edition = "2021" cargo-fuzz = true [dependencies] -arbitrary = "=1.1.3" # 1.1.4 requires Rust 1.63 to compile +arbitrary = "1.3" aws-smithy-types = { path = "../../aws-smithy-types" } bytes = "1" crc32fast = "1" -derive_arbitrary = "=1.1.6" # 1.2.0 requires Rust 1.63 to compile +derive_arbitrary = "1.3" libfuzzer-sys = "0.4" [dependencies.aws-smithy-eventstream] From 5473192d3ff5a866c4d489a718459c35ee679f4a Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 13 Jun 2023 15:43:03 -0400 Subject: [PATCH 150/253] Update runtime plugin trait (#2754) ## Motivation and Context Update the RuntimePlugin trait based on discussion: 1. Methods are infallible 2. Split out `Config` and `Interceptors` 3. `ConfigBag` now has an explicit field `interceptor_state` 4. Refactor `ConfigBagAccessors` so that we can build the core around the trait and keep the trait together with a `where Self` trick ## Description - Update the `RuntimePlugin` trait - Deal with resulting implications ## Testing - [x] CI ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti --- .../src/glacier_interceptors.rs | 7 +- .../src/presigning_interceptors.rs | 33 ++- .../aws-runtime/src/auth/sigv4.rs | 11 +- .../aws-runtime/src/invocation_id.rs | 3 +- .../aws-runtime/src/request_info.rs | 9 +- .../aws-runtime/src/user_agent.rs | 23 +- .../AwsCustomizableOperationDecorator.kt | 2 +- .../smithy/rustsdk/InvocationIdDecorator.kt | 2 +- .../rustsdk/RecursionDetectionDecorator.kt | 2 +- .../RetryInformationHeaderDecorator.kt | 2 +- .../smithy/rustsdk/SigV4AuthDecorator.kt | 4 +- .../smithy/rustsdk/UserAgentDecorator.kt | 16 +- .../apigateway/ApiGatewayDecorator.kt | 2 +- .../customize/glacier/GlacierDecorator.kt | 7 +- .../customizations/HttpAuthDecorator.kt | 2 +- .../InterceptorConfigCustomization.kt | 4 +- .../ResiliencyConfigCustomization.kt | 6 +- .../endpoint/EndpointParamsDecorator.kt | 2 +- .../EndpointParamsInterceptorGenerator.kt | 8 +- .../generators/OperationCustomization.kt | 9 +- .../OperationRuntimePluginGenerator.kt | 31 ++- .../smithy/generators/ServiceGenerator.kt | 2 +- .../ServiceRuntimePluginGenerator.kt | 48 ++-- .../config/ServiceConfigGenerator.kt | 71 +++-- .../protocols/HttpBoundProtocolGenerator.kt | 2 +- .../testutil/TestConfigCustomization.kt | 3 +- .../codegen/client/testutil/TestHelpers.kt | 3 +- .../config/ServiceConfigGeneratorTest.kt | 7 +- .../rust/codegen/core/rustlang/RustWriter.kt | 6 +- .../src/client/interceptors.rs | 3 +- .../src/client/orchestrator.rs | 249 +++++++++++------- .../src/client/runtime_plugin.rs | 55 ++-- .../src/client/orchestrator.rs | 63 ++--- .../src/client/orchestrator/auth.rs | 33 ++- .../src/client/orchestrator/endpoints.rs | 2 +- .../interceptors/service_clock_skew.rs | 2 +- .../src/client/retries/strategy/standard.rs | 11 +- .../client/runtime_plugin/anonymous_auth.rs | 29 +- .../src/client/test_util/deserializer.rs | 14 +- .../src/client/test_util/interceptors.rs | 2 +- .../src/client/test_util/serializer.rs | 13 +- .../aws-smithy-runtime/src/client/timeout.rs | 13 +- .../aws-smithy-types/src/config_bag.rs | 240 ++++++----------- .../src/config_bag/storable.rs | 108 ++++++++ 44 files changed, 659 insertions(+), 505 deletions(-) create mode 100644 rust-runtime/aws-smithy-types/src/config_bag/storable.rs diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs index e4b313da60..b581f4009e 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -14,7 +14,7 @@ use aws_smithy_runtime_api::client::interceptors::{ BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, BoxError, Interceptor, }; -use aws_smithy_runtime_api::client::orchestrator::LoadedRequestBody; +use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, LoadedRequestBody}; use aws_smithy_types::config_bag::ConfigBag; use bytes::Bytes; use http::header::{HeaderName, HeaderValue}; @@ -119,7 +119,8 @@ impl Interceptor for GlacierTreeHashHeaderInterceptor { ) -> Result<(), BoxError> { // Request the request body to be loaded into memory immediately after serialization // so that it can be checksummed before signing and transmit - cfg.put(LoadedRequestBody::Requested); + cfg.interceptor_state() + .set_loaded_request_body(LoadedRequestBody::Requested); Ok(()) } @@ -139,7 +140,7 @@ impl Interceptor for GlacierTreeHashHeaderInterceptor { .clone(); signing_config.signing_options.payload_override = Some(SignableBody::Precomputed(content_sha256)); - cfg.put(signing_config); + cfg.interceptor_state().put(signing_config); } else { return Err( "the request body wasn't loaded into memory before the retry loop, \ diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs index 18b94723a2..55642794a7 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs @@ -19,7 +19,7 @@ use aws_smithy_runtime_api::client::interceptors::{ }; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; /// Interceptor that tells the SigV4 signer to add the signature to query params, /// and sets the request expiration time from the presigning config. @@ -40,14 +40,15 @@ impl Interceptor for SigV4PresigningInterceptor { _context: &mut BeforeSerializationInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - cfg.put::( + cfg.interceptor_state().put::( HeaderSerializationSettings::new() .omit_default_content_length() .omit_default_content_type(), ); - cfg.set_request_time(SharedTimeSource::new(StaticTimeSource::new( - self.config.start_time(), - ))); + cfg.interceptor_state() + .set_request_time(SharedTimeSource::new(StaticTimeSource::new( + self.config.start_time(), + ))); Ok(()) } @@ -61,7 +62,8 @@ impl Interceptor for SigV4PresigningInterceptor { config.signing_options.signature_type = HttpSignatureType::HttpRequestQueryParams; config.signing_options.payload_override = Some(aws_sigv4::http_request::SignableBody::UnsignedPayload); - cfg.put::(config); + cfg.interceptor_state() + .put::(config); Ok(()) } else { Err( @@ -87,18 +89,15 @@ impl SigV4PresigningRuntimePlugin { } impl RuntimePlugin for SigV4PresigningRuntimePlugin { - fn configure( - &self, - cfg: &mut ConfigBag, - interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { - // Disable some SDK interceptors that shouldn't run for presigning - cfg.put(disable_interceptor::("presigning")); - cfg.put(disable_interceptor::("presigning")); - cfg.put(disable_interceptor::("presigning")); + fn config(&self) -> Option { + let mut layer = Layer::new("Presigning"); + layer.put(disable_interceptor::("presigning")); + layer.put(disable_interceptor::("presigning")); + layer.put(disable_interceptor::("presigning")); + Some(layer.freeze()) + } - // Register the presigning interceptor + fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { interceptors.register(self.interceptor.clone()); - Ok(()) } } diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs index 8dc9fb44f8..1ac3f800de 100644 --- a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -506,6 +506,7 @@ mod tests { use super::*; use aws_credential_types::Credentials; use aws_sigv4::http_request::SigningSettings; + use aws_smithy_types::config_bag::Layer; use aws_types::region::SigningRegion; use aws_types::SigningService; use std::collections::HashMap; @@ -556,8 +557,8 @@ mod tests { #[test] fn endpoint_config_overrides_region_and_service() { - let mut cfg = ConfigBag::base(); - cfg.put(SigV4OperationSigningConfig { + let mut layer = Layer::new("test"); + layer.put(SigV4OperationSigningConfig { region: Some(SigningRegion::from(Region::new("override-this-region"))), service: Some(SigningService::from_static("override-this-service")), signing_options: Default::default(), @@ -577,6 +578,7 @@ mod tests { }); let config = AuthSchemeEndpointConfig::new(Some(&config)); + let cfg = ConfigBag::of_layers(vec![layer]); let result = SigV4HttpRequestSigner::extract_operation_config(config, &cfg).expect("success"); @@ -593,12 +595,13 @@ mod tests { #[test] fn endpoint_config_supports_fallback_when_region_or_service_are_unset() { - let mut cfg = ConfigBag::base(); - cfg.put(SigV4OperationSigningConfig { + let mut layer = Layer::new("test"); + layer.put(SigV4OperationSigningConfig { region: Some(SigningRegion::from(Region::new("us-east-1"))), service: Some(SigningService::from_static("qldb")), signing_options: Default::default(), }); + let cfg = ConfigBag::of_layers(vec![layer]); let config = AuthSchemeEndpointConfig::empty(); let result = diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 420cedb73d..cd4bd4a438 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -48,7 +48,8 @@ impl Interceptor for InvocationIdInterceptor { .map(|gen| gen.generate()) .transpose()? .flatten(); - cfg.put::(id.unwrap_or_default()); + cfg.interceptor_state() + .put::(id.unwrap_or_default()); Ok(()) } diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index 0b41310536..07c2d0bf7a 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -166,7 +166,7 @@ mod tests { use crate::request_info::RequestPairs; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; - use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; use aws_smithy_types::type_erasure::TypeErasedBox; @@ -190,13 +190,14 @@ mod tests { context.enter_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); - let mut config = ConfigBag::base(); - config.put(RetryConfig::standard()); - config.put( + let mut layer = Layer::new("test"); + layer.put(RetryConfig::standard()); + layer.put( TimeoutConfig::builder() .read_timeout(Duration::from_secs(30)) .build(), ); + let mut config = ConfigBag::of_layers(vec![layer]); let _ = context.take_input(); context.enter_before_transmit_phase(); diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 52c6cdb4da..7275490432 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -110,7 +110,7 @@ mod tests { use super::*; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; - use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::type_erasure::TypeErasedBox; @@ -138,14 +138,15 @@ mod tests { fn test_overridden_ua() { let mut context = context(); - let mut config = ConfigBag::base(); - config.put(AwsUserAgent::for_tests()); - config.put(ApiMetadata::new("unused", "unused")); + let mut layer = Layer::new("test"); + layer.put(AwsUserAgent::for_tests()); + layer.put(ApiMetadata::new("unused", "unused")); + let mut cfg = ConfigBag::of_layers(vec![layer]); let interceptor = UserAgentInterceptor::new(); let mut ctx = Into::into(&mut context); interceptor - .modify_before_signing(&mut ctx, &mut config) + .modify_before_signing(&mut ctx, &mut cfg) .unwrap(); let header = expect_header(&context, "user-agent"); @@ -163,8 +164,9 @@ mod tests { let mut context = context(); let api_metadata = ApiMetadata::new("some-service", "some-version"); - let mut config = ConfigBag::base(); - config.put(api_metadata.clone()); + let mut layer = Layer::new("test"); + layer.put(api_metadata.clone()); + let mut config = ConfigBag::of_layers(vec![layer]); let interceptor = UserAgentInterceptor::new(); let mut ctx = Into::into(&mut context); @@ -192,9 +194,10 @@ mod tests { let mut context = context(); let api_metadata = ApiMetadata::new("some-service", "some-version"); - let mut config = ConfigBag::base(); - config.put(api_metadata); - config.put(AppName::new("my_awesome_app").unwrap()); + let mut layer = Layer::new("test"); + layer.put(api_metadata); + layer.put(AppName::new("my_awesome_app").unwrap()); + let mut config = ConfigBag::of_layers(vec![layer]); let interceptor = UserAgentInterceptor::new(); let mut ctx = Into::into(&mut context); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index 1b4243c65c..3e363d9565 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -47,7 +47,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self { use #{ConfigBagAccessors}; let interceptor = #{TestParamsSetterInterceptor}::new(move |_: &mut #{BeforeTransmitInterceptorContextMut}<'_>, cfg: &mut #{ConfigBag}| { - cfg.set_request_time(#{SharedTimeSource}::new(request_time)); + cfg.interceptor_state().set_request_time(#{SharedTimeSource}::new(request_time)); }); self.interceptors.push(#{SharedInterceptor}::new(interceptor)); self diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt index e26ddbec76..71c9d9ff62 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt @@ -36,7 +36,7 @@ private class InvocationIdRuntimePluginCustomization( ) override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.AdditionalConfig) { + if (section is ServiceRuntimePluginSection.RegisterInterceptor) { section.registerInterceptor(codegenContext.runtimeConfig, this) { rustTemplate("#{InvocationIdInterceptor}::new()", *codegenScope) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt index 4685acad75..509be8d295 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt @@ -31,7 +31,7 @@ private class RecursionDetectionRuntimePluginCustomization( private val codegenContext: ClientCodegenContext, ) : ServiceRuntimePluginCustomization() { override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.AdditionalConfig) { + if (section is ServiceRuntimePluginSection.RegisterInterceptor) { section.registerInterceptor(codegenContext.runtimeConfig, this) { rust( "#T::new()", diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt index 69cf7f9b15..77185e0667 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt @@ -35,7 +35,7 @@ private class AddRetryInformationHeaderInterceptors(codegenContext: ClientCodege private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.AdditionalConfig) { + if (section is ServiceRuntimePluginSection.RegisterInterceptor) { // Track the latency between client and server. section.registerInterceptor(runtimeConfig, this) { rust( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 336e17472f..3ea33df814 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -138,7 +138,7 @@ private class AuthOperationCustomization(private val codegenContext: ClientCodeg signing_options.signing_optional = $signingOptional; signing_options.payload_override = #{payload_override}; - ${section.configBagName}.put(#{SigV4OperationSigningConfig} { + ${section.newLayerName}.put(#{SigV4OperationSigningConfig} { region: None, service: None, signing_options, @@ -147,7 +147,7 @@ private class AuthOperationCustomization(private val codegenContext: ClientCodeg let auth_option_resolver = #{StaticAuthOptionResolver}::new( vec![#{SIGV4_SCHEME_ID}] ); - ${section.configBagName}.set_auth_option_resolver(auth_option_resolver); + ${section.newLayerName}.set_auth_option_resolver(auth_option_resolver); """, *codegenScope, "payload_override" to writable { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index 7ead51d18b..7eb227ba6a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -103,13 +103,19 @@ class UserAgentDecorator : ClientCodegenDecorator { private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.AdditionalConfig) { - section.putConfigValue(this) { - rust("#T.clone()", ClientRustModule.Meta.toType().resolve("API_METADATA")) + when (section) { + is ServiceRuntimePluginSection.AdditionalConfig -> { + section.putConfigValue(this) { + rust("#T.clone()", ClientRustModule.Meta.toType().resolve("API_METADATA")) + } } - section.registerInterceptor(runtimeConfig, this) { - rust("#T::new()", awsRuntime.resolve("user_agent::UserAgentInterceptor")) + + is ServiceRuntimePluginSection.RegisterInterceptor -> { + section.registerInterceptor(runtimeConfig, this) { + rust("#T::new()", awsRuntime.resolve("user_agent::UserAgentInterceptor")) + } } + else -> emptySection } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt index ad600dac26..e6c29d23e0 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt @@ -66,7 +66,7 @@ private class ApiGatewayAddAcceptHeader : OperationCustomization() { private class ApiGatewayAcceptHeaderInterceptorCustomization(private val codegenContext: ClientCodegenContext) : ServiceRuntimePluginCustomization() { override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.AdditionalConfig) { + if (section is ServiceRuntimePluginSection.RegisterInterceptor) { section.registerInterceptor(codegenContext.runtimeConfig, this) { rustTemplate( "#{Interceptor}::default()", diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt index 053665c48c..d8a38db110 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt @@ -97,11 +97,14 @@ private class GlacierAccountIdCustomization(private val codegenContext: ClientCo } } +// TODO(enableNewSmithyRuntime): Install the glacier customizations as a single additional runtime plugin instead +// of wiring up the interceptors individually + /** Adds the `x-amz-glacier-version` header to all requests */ private class GlacierApiVersionCustomization(private val codegenContext: ClientCodegenContext) : ServiceRuntimePluginCustomization() { override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.AdditionalConfig) { + if (section is ServiceRuntimePluginSection.RegisterInterceptor) { val apiVersion = codegenContext.serviceShape.version section.registerInterceptor(codegenContext.runtimeConfig, this) { rustTemplate( @@ -122,7 +125,7 @@ private class GlacierApiVersionCustomization(private val codegenContext: ClientC private class GlacierOperationInterceptorsCustomization(private val codegenContext: ClientCodegenContext) : OperationCustomization() { override fun section(section: OperationSection): Writable = writable { - if (section is OperationSection.AdditionalRuntimePluginConfig) { + if (section is OperationSection.AdditionalInterceptors) { val inputShape = codegenContext.model.expectShape(section.operationShape.inputShape) as StructureShape val inlineModule = inlineModule(codegenContext.runtimeConfig) if (inputShape.inputWithAccountId()) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index dd19433315..b7e2f17e11 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -224,7 +224,7 @@ private class HttpAuthOperationCustomization(codegenContext: ClientCodegenContex } // TODO(enableNewSmithyRuntime): Make auth options additive in the config bag so that multiple codegen decorators can register them - rustTemplate("${section.configBagName}.set_auth_option_resolver(auth_option_resolver);", *codegenScope) + rustTemplate("${section.newLayerName}.set_auth_option_resolver(auth_option_resolver);", *codegenScope) } else -> emptySection diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt index 967bea65ac..967dbbc004 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt @@ -177,9 +177,9 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus """, ) - ServiceConfig.ToRuntimePlugin -> rust( + is ServiceConfig.RuntimePluginInterceptors -> rust( """ - interceptors.extend(self.interceptors.iter().cloned()); + ${section.interceptors}.extend(self.interceptors.iter().cloned()); """, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 90a03bd348..509fdcba7e 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -271,12 +271,12 @@ class ResiliencyServiceRuntimePluginCustomization : ServiceRuntimePluginCustomiz rust( """ if let Some(sleep_impl) = self.handle.conf.sleep_impl() { - ${section.configBagName}.put(sleep_impl); + ${section.newLayerName}.put(sleep_impl); } if let Some(timeout_config) = self.handle.conf.timeout_config() { - ${section.configBagName}.put(timeout_config.clone()); + ${section.newLayerName}.put(timeout_config.clone()); } - ${section.configBagName}.put(self.handle.conf.time_source.clone()); + ${section.newLayerName}.put(self.handle.conf.time_source.clone()); """, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt index c6d7281de7..c3d136659d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsDecorator.kt @@ -43,7 +43,7 @@ private class EndpointParametersCustomization( override fun section(section: OperationSection): Writable = writable { val symbolProvider = codegenContext.symbolProvider val operationName = symbolProvider.toSymbol(operation).name - if (section is OperationSection.AdditionalRuntimePluginConfig) { + if (section is OperationSection.AdditionalInterceptors) { section.registerInterceptor(codegenContext.runtimeConfig, this) { rust("${operationName}EndpointParamsInterceptor") } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 1dc2bb16dd..4a3b1d0d0a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -24,6 +24,7 @@ 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.withBlockTemplate 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.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -44,6 +45,8 @@ class EndpointParamsInterceptorGenerator( arrayOf( "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), + "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(rc) + .resolve("client::orchestrator::ConfigBagAccessors"), "ContextAttachedError" to interceptors.resolve("error::ContextAttachedError"), "EndpointResolverParams" to orchestrator.resolve("EndpointResolverParams"), "HttpRequest" to orchestrator.resolve("HttpRequest"), @@ -74,6 +77,7 @@ class EndpointParamsInterceptorGenerator( context: &#{BeforeSerializationInterceptorContextRef}<'_, #{Input}, #{Output}, #{Error}>, cfg: &mut #{ConfigBag}, ) -> Result<(), #{BoxError}> { + use #{ConfigBagAccessors}; let _input = context.input() .downcast_ref::<${operationInput.name}>() .ok_or("failed to downcast to ${operationInput.name}")?; @@ -89,7 +93,7 @@ class EndpointParamsInterceptorGenerator( #{param_setters} .build() .map_err(|err| #{ContextAttachedError}::new("endpoint params could not be built", err))?; - cfg.put(#{EndpointResolverParams}::new(params)); + cfg.interceptor_state().set_endpoint_resolver_params(#{EndpointResolverParams}::new(params)); Ok(()) } } @@ -167,7 +171,7 @@ class EndpointParamsInterceptorGenerator( codegenContext.smithyRuntimeMode, ) } - rust("cfg.put(endpoint_prefix);") + rust("cfg.interceptor_state().put(endpoint_prefix);") } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt index fd4f71ec84..d826a44ead 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt @@ -101,10 +101,15 @@ sealed class OperationSection(name: String) : Section(name) { */ data class AdditionalRuntimePluginConfig( override val customizations: List, - val configBagName: String, + val newLayerName: String, + val operationShape: OperationShape, + ) : OperationSection("AdditionalRuntimePluginConfig") + + data class AdditionalInterceptors( + override val customizations: List, val interceptorRegistrarName: String, val operationShape: OperationShape, - ) : OperationSection("AdditionalConfig") { + ) : OperationSection("AdditionalInterceptors") { fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) writer.rustTemplate( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index 4d786421f1..4a3d6d5530 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -11,7 +11,9 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations +import software.amazon.smithy.rust.codegen.core.util.dq /** * Generates operation-level runtime plugins @@ -25,6 +27,8 @@ class OperationRuntimePluginGenerator( arrayOf( "AuthOptionResolverParams" to runtimeApi.resolve("client::auth::AuthOptionResolverParams"), "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), + "Layer" to smithyTypes.resolve("config_bag::Layer"), + "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), @@ -43,7 +47,8 @@ class OperationRuntimePluginGenerator( writer.rustTemplate( """ impl #{RuntimePlugin} for $operationStructName { - fn configure(&self, cfg: &mut #{ConfigBag}, _interceptors: &mut #{InterceptorRegistrar}) -> Result<(), #{BoxError}> { + fn config(&self) -> #{Option}<#{FrozenLayer}> { + let mut cfg = #{Layer}::new(${operationShape.id.name.dq()}); use #{ConfigBagAccessors} as _; cfg.set_request_serializer(${operationStructName}RequestSerializer); cfg.set_response_deserializer(${operationStructName}ResponseDeserializer); @@ -57,21 +62,33 @@ class OperationRuntimePluginGenerator( cfg.set_retry_classifiers(retry_classifiers); #{additional_config} - Ok(()) + Some(cfg.freeze()) + } + + fn interceptors(&self, _interceptors: &mut #{InterceptorRegistrar}) { + #{interceptors} } } #{runtime_plugin_supporting_types} """, *codegenScope, + *preludeScope, "additional_config" to writable { writeCustomizations( customizations, - OperationSection.AdditionalRuntimePluginConfig(customizations, "cfg", "_interceptors", operationShape), + OperationSection.AdditionalRuntimePluginConfig( + customizations, + newLayerName = "cfg", + operationShape, + ), ) }, "retry_classifier_customizations" to writable { - writeCustomizations(customizations, OperationSection.RetryClassifier(customizations, "cfg", operationShape)) + writeCustomizations( + customizations, + OperationSection.RetryClassifier(customizations, "cfg", operationShape), + ) }, "runtime_plugin_supporting_types" to writable { writeCustomizations( @@ -79,6 +96,12 @@ class OperationRuntimePluginGenerator( OperationSection.RuntimePluginSupportingTypes(customizations, "cfg", operationShape), ) }, + "interceptors" to writable { + writeCustomizations( + customizations, + OperationSection.AdditionalInterceptors(customizations, "_interceptors", operationShape), + ) + }, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index 050e124376..40fa4859a0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -54,7 +54,7 @@ class ServiceGenerator( ServiceRuntimePluginGenerator(codegenContext) .render(this, decorator.serviceRuntimePluginCustomizations(codegenContext, emptyList())) - serviceConfigGenerator.renderRuntimePluginImplForBuilder(this, codegenContext) + serviceConfigGenerator.renderRuntimePluginImplForBuilder(this) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 0d6b924584..478da7fb9a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -18,6 +18,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.pre import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations +import software.amazon.smithy.rust.codegen.core.util.dq sealed class ServiceRuntimePluginSection(name: String) : Section(name) { /** @@ -42,12 +43,14 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { /** * Hook for adding additional things to config inside service runtime plugins. */ - data class AdditionalConfig(val configBagName: String, val interceptorRegistrarName: String) : ServiceRuntimePluginSection("AdditionalConfig") { + data class AdditionalConfig(val newLayerName: String) : ServiceRuntimePluginSection("AdditionalConfig") { /** Adds a value to the config bag */ fun putConfigValue(writer: RustWriter, value: Writable) { - writer.rust("$configBagName.put(#T);", value) + writer.rust("$newLayerName.put(#T);", value) } + } + data class RegisterInterceptor(val interceptorRegistrarName: String) : ServiceRuntimePluginSection("RegisterInterceptor") { /** Generates the code to register an interceptor */ fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) @@ -67,7 +70,7 @@ typealias ServiceRuntimePluginCustomization = NamedCustomization @@ -82,6 +85,8 @@ class ServiceRuntimePluginGenerator( "AnonymousIdentityResolver" to runtimeApi.resolve("client::identity::AnonymousIdentityResolver"), "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), + "Layer" to smithyTypes.resolve("config_bag::Layer"), + "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), "Connection" to runtimeApi.resolve("client::orchestrator::Connection"), "ConnectorSettings" to RuntimeType.smithyClient(rc).resolve("http_connector::ConnectorSettings"), @@ -118,8 +123,9 @@ class ServiceRuntimePluginGenerator( } impl #{RuntimePlugin} for ServiceRuntimePlugin { - fn configure(&self, cfg: &mut #{ConfigBag}, _interceptors: &mut #{InterceptorRegistrar}) -> #{Result}<(), #{BoxError}> { + fn config(&self) -> #{Option}<#{FrozenLayer}> { use #{ConfigBagAccessors}; + let mut cfg = #{Layer}::new(${codegenContext.serviceShape.id.name.dq()}); // HACK: Put the handle into the config bag to work around config not being fully implemented yet cfg.put(self.handle.clone()); @@ -147,27 +153,26 @@ class ServiceRuntimePluginGenerator( if let Some(retry_config) = retry_config { cfg.set_retry_strategy(#{StandardRetryStrategy}::new(retry_config)); - } else if cfg.retry_strategy().is_none() { - cfg.set_retry_strategy(#{NeverRetryStrategy}::new()); } let connector_settings = timeout_config.map(#{ConnectorSettings}::from_timeout_config).unwrap_or_default(); - let connection: #{Box} = #{Box}::new(#{DynConnectorAdapter}::new( - // TODO(enableNewSmithyRuntime): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation - #{require_connector}( - self.handle.conf.http_connector() - .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) - .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) - )? - )) as _; - cfg.set_connection(connection); - + if let Some(connection) = self.handle.conf.http_connector() + .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) + .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) { + let connection: #{Box} = #{Box}::new(#{DynConnectorAdapter}::new( + // TODO(enableNewSmithyRuntime): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation + connection + )) as _; + cfg.set_connection(connection); + } #{additional_config} - // Client-level Interceptors are registered after default Interceptors. - _interceptors.extend(self.handle.conf.interceptors.iter().cloned()); + Some(cfg.freeze()) + } - Ok(()) + fn interceptors(&self, interceptors: &mut #{InterceptorRegistrar}) { + interceptors.extend(self.handle.conf.interceptors.iter().cloned()); + #{additional_interceptors} } } """, @@ -179,7 +184,10 @@ class ServiceRuntimePluginGenerator( writeCustomizations(customizations, ServiceRuntimePluginSection.RetryClassifier("cfg")) }, "additional_config" to writable { - writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg", "_interceptors")) + writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg")) + }, + "additional_interceptors" to writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.RegisterInterceptor("interceptors")) }, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index a43d4f9e10..67fd7f911a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -20,13 +20,14 @@ import software.amazon.smithy.rust.codegen.core.rustlang.docsOrFallback import software.amazon.smithy.rust.codegen.core.rustlang.raw import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock -import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.CodegenContext 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.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section +import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.letIf @@ -91,7 +92,9 @@ sealed class ServiceConfig(name: String) : Section(name) { /** * A section for setting up a field to be used by RuntimePlugin */ - object ToRuntimePlugin : ServiceConfig("ToRuntimePlugin") + data class RuntimePluginConfig(val cfg: String) : ServiceConfig("ToRuntimePlugin") + + data class RuntimePluginInterceptors(val interceptors: String) : ServiceConfig("ToRuntimePluginInterceptors") /** * A section for extra functionality that needs to be defined with the config module @@ -163,7 +166,7 @@ fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : Conf rust("${param.name}: self.${param.name}$default,") } - ServiceConfig.ToRuntimePlugin -> emptySection + is ServiceConfig.RuntimePluginConfig -> emptySection else -> emptySection } @@ -196,7 +199,10 @@ typealias ConfigCustomization = NamedCustomization * // builder implementation * } */ -class ServiceConfigGenerator(private val customizations: List = listOf()) { +class ServiceConfigGenerator( + private val codegenContext: CodegenContext, + private val customizations: List = listOf(), +) { companion object { fun withBaseBehavior( @@ -207,10 +213,22 @@ class ServiceConfigGenerator(private val customizations: List Result<(), #{BoxError}> - """, - "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), - "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), - "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), - ) { - rust("// TODO(enableNewSmithyRuntime): Put into `cfg` the fields in `self.config_override` that are not `None`") - - customizations.forEach { - it.section(ServiceConfig.ToRuntimePlugin)(writer) + fun renderRuntimePluginImplForBuilder(writer: RustWriter) { + writer.rustTemplate( + """ + impl #{RuntimePlugin} for Builder { + fn config(&self) -> #{Option}<#{FrozenLayer}> { + // TODO(enableNewSmithyRuntime): Put into `cfg` the fields in `self.config_override` that are not `None` + ##[allow(unused_mut)] + let mut cfg = #{Layer}::new("service config"); + #{config} + Some(cfg.freeze()) } - rust("Ok(())") + fn interceptors(&self, _interceptors: &mut #{InterceptorRegistrar}) { + #{interceptors} + } } - } + + """, + *codegenScope, + "config" to writable { writeCustomizations(customizations, ServiceConfig.RuntimePluginConfig("cfg")) }, + "interceptors" to writable { + writeCustomizations(customizations, ServiceConfig.RuntimePluginInterceptors("_interceptors")) + }, + ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index 44961039c8..f494c10971 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -87,7 +87,7 @@ class ClientHttpBoundProtocolPayloadGenerator( if (propertyBagAvailable) { rust("properties.acquire_mut().insert(signer_sender);") } else { - rust("_cfg.put(signer_sender);") + rust("_cfg.interceptor_state().put(signer_sender);") } }, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt index cb06688225..1f19a46f2b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable 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.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest @@ -73,7 +74,7 @@ fun validateConfigCustomizations( fun stubConfigProject(customization: ConfigCustomization, project: TestWriterDelegator): TestWriterDelegator { val customizations = listOf(stubConfigCustomization("a")) + customization + stubConfigCustomization("b") - val generator = ServiceConfigGenerator(customizations = customizations.toList()) + val generator = ServiceConfigGenerator(testClientCodegenContext("namespace test".asSmithyModel()), customizations = customizations.toList()) project.withModule(ClientRustModule.Config) { generator.render(this) unitTest( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt index 1d2384b4a0..472325b486 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt @@ -23,6 +23,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProviderConfig import software.amazon.smithy.rust.codegen.core.testutil.TestModuleDocProvider import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.TestWriterDelegator +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel fun testClientRustSettings( service: ShapeId = ShapeId.from("notrelevant#notrelevant"), @@ -72,7 +73,7 @@ fun testSymbolProvider(model: Model, serviceShape: ServiceShape? = null): RustSy ) fun testClientCodegenContext( - model: Model, + model: Model = "namespace empty".asSmithyModel(), symbolProvider: RustSymbolProvider? = null, serviceShape: ServiceShape? = null, settings: ClientRustSettings = testClientRustSettings(), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt index 2aaa3e21dd..d55c615413 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt @@ -9,7 +9,7 @@ import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule -import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable @@ -101,8 +101,9 @@ internal class ServiceConfigGeneratorTest { } } } - val sut = ServiceConfigGenerator(listOf(ServiceCustomizer())) - val symbolProvider = testSymbolProvider("namespace empty".asSmithyModel()) + val ctx = testClientCodegenContext() + val sut = ServiceConfigGenerator(ctx, listOf(ServiceCustomizer())) + val symbolProvider = ctx.symbolProvider val project = TestWorkspace.testProject(symbolProvider) project.withModule(ClientRustModule.Config) { sut.render(this) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt index a29d12f7b7..9dece19f8b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt @@ -180,7 +180,11 @@ fun RustWriter.rustInline( /* rewrite #{foo} to #{foo:T} (the smithy template format) */ private fun transformTemplate(template: String, scope: Array>, trim: Boolean = true): String { - check(scope.distinctBy { it.first.lowercase() }.size == scope.size) { "Duplicate cased keys not supported" } + check( + scope.distinctBy { + it.first.lowercase() + }.size == scope.distinctBy { it.first }.size, + ) { "Duplicate cased keys not supported" } val output = template.replace(Regex("""#\{([a-zA-Z_0-9]+)(:\w)?\}""")) { matchResult -> val keyName = matchResult.groupValues[1] val templateType = matchResult.groupValues[2].ifEmpty { ":T" } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index fe1f7e535d..3e8af28db7 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -959,7 +959,8 @@ mod tests { interceptors .read_before_transmit(&mut InterceptorContext::new(Input::new(5)), &mut cfg) .expect_err("interceptor returns error"); - cfg.put(disable_interceptor::("test")); + cfg.interceptor_state() + .put(disable_interceptor::("test")); assert_eq!( interceptors .interceptors() diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 2a82860593..54e37f559f 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -12,11 +12,12 @@ use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_http::body::SdkBody; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::endpoint::Endpoint; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use bytes::Bytes; use std::fmt; +use std::fmt::Debug; use std::future::Future as StdFuture; use std::pin::Pin; use std::sync::Arc; @@ -98,196 +99,252 @@ pub enum LoadedRequestBody { Loaded(Bytes), } -pub trait ConfigBagAccessors { - fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams; - fn set_auth_option_resolver_params( - &mut self, - auth_option_resolver_params: AuthOptionResolverParams, - ); - - fn auth_option_resolver(&self) -> &dyn AuthOptionResolver; - fn set_auth_option_resolver(&mut self, auth_option_resolver: impl AuthOptionResolver + 'static); - - fn endpoint_resolver_params(&self) -> &EndpointResolverParams; - fn set_endpoint_resolver_params(&mut self, endpoint_resolver_params: EndpointResolverParams); - - fn endpoint_resolver(&self) -> &dyn EndpointResolver; - fn set_endpoint_resolver(&mut self, endpoint_resolver: impl EndpointResolver + 'static); - - fn identity_resolvers(&self) -> &IdentityResolvers; - fn set_identity_resolvers(&mut self, identity_resolvers: IdentityResolvers); - - fn connection(&self) -> &dyn Connection; - fn set_connection(&mut self, connection: impl Connection + 'static); - - fn http_auth_schemes(&self) -> &HttpAuthSchemes; - fn set_http_auth_schemes(&mut self, http_auth_schemes: HttpAuthSchemes); - - fn request_serializer(&self) -> Arc; - fn set_request_serializer(&mut self, request_serializer: impl RequestSerializer + 'static); - - fn response_deserializer(&self) -> &dyn ResponseDeserializer; - fn set_response_deserializer( - &mut self, - response_serializer: impl ResponseDeserializer + 'static, - ); - - fn retry_classifiers(&self) -> &RetryClassifiers; - fn set_retry_classifiers(&mut self, retry_classifier: RetryClassifiers); - - fn retry_strategy(&self) -> Option<&dyn RetryStrategy>; - fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static); - - fn request_time(&self) -> Option; - fn set_request_time(&mut self, time_source: impl TimeSource + 'static); +pub trait Settable { + fn layer(&mut self) -> &mut Layer; + fn put(&mut self, value: T) { + self.layer().put(value); + } +} - fn sleep_impl(&self) -> Option; - fn set_sleep_impl(&mut self, async_sleep: Option); +pub trait Gettable { + fn config_bag(&self) -> &ConfigBag; + fn get(&self) -> Option<&T> { + self.config_bag().get::() + } +} - fn loaded_request_body(&self) -> &LoadedRequestBody; - fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody); +impl Settable for Layer { + fn layer(&mut self) -> &mut Layer { + self + } } -const NOT_NEEDED: LoadedRequestBody = LoadedRequestBody::NotNeeded; +impl Gettable for ConfigBag { + fn config_bag(&self) -> &ConfigBag { + self + } +} -impl ConfigBagAccessors for ConfigBag { - fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams { - self.get::() +pub trait ConfigBagAccessors { + fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams + where + Self: Gettable, + { + self.config_bag() + .get::() .expect("auth option resolver params must be set") } - fn set_auth_option_resolver_params( &mut self, auth_option_resolver_params: AuthOptionResolverParams, - ) { + ) where + Self: Settable, + { self.put::(auth_option_resolver_params); } - fn auth_option_resolver(&self) -> &dyn AuthOptionResolver { + fn auth_option_resolver(&self) -> &dyn AuthOptionResolver + where + Self: Gettable, + { &**self + .config_bag() .get::>() .expect("an auth option resolver must be set") } - fn set_auth_option_resolver( - &mut self, - auth_option_resolver: impl AuthOptionResolver + 'static, - ) { + fn set_auth_option_resolver(&mut self, auth_option_resolver: impl AuthOptionResolver + 'static) + where + Self: Settable, + { self.put::>(Box::new(auth_option_resolver)); } - fn endpoint_resolver_params(&self) -> &EndpointResolverParams { - self.get::() + fn endpoint_resolver_params(&self) -> &EndpointResolverParams + where + Self: Gettable, + { + self.config_bag() + .get::() .expect("endpoint resolver params must be set") } - fn set_endpoint_resolver_params(&mut self, endpoint_resolver_params: EndpointResolverParams) { + fn set_endpoint_resolver_params(&mut self, endpoint_resolver_params: EndpointResolverParams) + where + Self: Settable, + { self.put::(endpoint_resolver_params); } - fn endpoint_resolver(&self) -> &dyn EndpointResolver { + fn endpoint_resolver(&self) -> &dyn EndpointResolver + where + Self: Gettable, + { &**self + .config_bag() .get::>() .expect("an endpoint resolver must be set") } - fn set_endpoint_resolver(&mut self, endpoint_resolver: impl EndpointResolver + 'static) { + fn set_endpoint_resolver(&mut self, endpoint_resolver: impl EndpointResolver + 'static) + where + Self: Settable, + { self.put::>(Box::new(endpoint_resolver)); } - fn identity_resolvers(&self) -> &IdentityResolvers { - self.get::() + fn identity_resolvers(&self) -> &IdentityResolvers + where + Self: Gettable, + { + self.config_bag() + .get::() .expect("identity resolvers must be configured") } - fn set_identity_resolvers(&mut self, identity_resolvers: IdentityResolvers) { + fn set_identity_resolvers(&mut self, identity_resolvers: IdentityResolvers) + where + Self: Settable, + { self.put::(identity_resolvers); } - fn connection(&self) -> &dyn Connection { + fn connection(&self) -> &dyn Connection + where + Self: Gettable, + { &**self + .config_bag() .get::>() .expect("missing connector") } - fn set_connection(&mut self, connection: impl Connection + 'static) { + fn set_connection(&mut self, connection: impl Connection + 'static) + where + Self: Settable, + { self.put::>(Box::new(connection)); } - fn http_auth_schemes(&self) -> &HttpAuthSchemes { - self.get::() + fn http_auth_schemes(&self) -> &HttpAuthSchemes + where + Self: Gettable, + { + self.config_bag() + .get::() .expect("auth schemes must be set") } - - fn set_http_auth_schemes(&mut self, http_auth_schemes: HttpAuthSchemes) { + fn set_http_auth_schemes(&mut self, http_auth_schemes: HttpAuthSchemes) + where + Self: Settable, + { self.put::(http_auth_schemes); } - fn request_serializer(&self) -> Arc { + fn request_serializer(&self) -> Arc + where + Self: Gettable, + { self.get::>() .expect("missing request serializer") .clone() } - - fn set_request_serializer(&mut self, request_serializer: impl RequestSerializer + 'static) { + fn set_request_serializer(&mut self, request_serializer: impl RequestSerializer + 'static) + where + Self: Settable, + { self.put::>(Arc::new(request_serializer)); } - fn response_deserializer(&self) -> &dyn ResponseDeserializer { + fn response_deserializer(&self) -> &dyn ResponseDeserializer + where + Self: Gettable, + { &**self .get::>() .expect("missing response deserializer") } - fn set_response_deserializer( &mut self, response_deserializer: impl ResponseDeserializer + 'static, - ) { + ) where + Self: Settable, + { self.put::>(Box::new(response_deserializer)); } - fn retry_classifiers(&self) -> &RetryClassifiers { + fn retry_classifiers(&self) -> &RetryClassifiers + where + Self: Gettable, + { self.get::() .expect("retry classifiers must be set") } - - fn set_retry_classifiers(&mut self, retry_classifiers: RetryClassifiers) { + fn set_retry_classifiers(&mut self, retry_classifiers: RetryClassifiers) + where + Self: Settable, + { self.put::(retry_classifiers); } - fn retry_strategy(&self) -> Option<&dyn RetryStrategy> { + fn retry_strategy(&self) -> Option<&dyn RetryStrategy> + where + Self: Gettable, + { self.get::>().map(|rs| &**rs) } - - fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static) { + fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static) + where + Self: Settable, + { self.put::>(Box::new(retry_strategy)); } - fn request_time(&self) -> Option { + fn request_time(&self) -> Option + where + Self: Gettable, + { self.get::().cloned() } - - fn set_request_time(&mut self, request_time: impl TimeSource + 'static) { - self.put::(SharedTimeSource::new(request_time)); + fn set_request_time(&mut self, time_source: impl TimeSource + 'static) + where + Self: Settable, + { + self.put::(SharedTimeSource::new(time_source)); } - fn sleep_impl(&self) -> Option { + fn sleep_impl(&self) -> Option + where + Self: Gettable, + { self.get::().cloned() } - - fn set_sleep_impl(&mut self, sleep_impl: Option) { - if let Some(sleep_impl) = sleep_impl { + fn set_sleep_impl(&mut self, async_sleep: Option) + where + Self: Settable, + { + if let Some(sleep_impl) = async_sleep { self.put::(sleep_impl); } else { - self.unset::(); + self.layer().unset::(); } } - fn loaded_request_body(&self) -> &LoadedRequestBody { + fn loaded_request_body(&self) -> &LoadedRequestBody + where + Self: Gettable, + { self.get::().unwrap_or(&NOT_NEEDED) } - - fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody) { + fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody) + where + Self: Settable, + { self.put::(loaded_request_body); } } + +const NOT_NEEDED: LoadedRequestBody = LoadedRequestBody::NotNeeded; + +impl ConfigBagAccessors for ConfigBag {} +impl ConfigBagAccessors for Layer {} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index 80c708a5a5..34fd6f7985 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -4,27 +4,34 @@ */ use crate::client::interceptors::InterceptorRegistrar; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer}; use std::fmt::Debug; pub type BoxError = Box; pub type BoxRuntimePlugin = Box; +/// RuntimePlugin Trait +/// +/// A RuntimePlugin is the unit of configuration for augmenting the SDK with new behavior +/// +/// Runtime plugins can set configuration and register interceptors. pub trait RuntimePlugin: Debug { - fn configure( - &self, - cfg: &mut ConfigBag, - interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError>; + fn config(&self) -> Option { + None + } + + fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { + let _ = interceptors; + } } impl RuntimePlugin for BoxRuntimePlugin { - fn configure( - &self, - cfg: &mut ConfigBag, - interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { - self.as_ref().configure(cfg, interceptors) + fn config(&self) -> Option { + self.as_ref().config() + } + + fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { + self.as_ref().interceptors(interceptors) } } @@ -61,7 +68,10 @@ impl RuntimePlugins { interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { for plugin in self.client_plugins.iter() { - plugin.configure(cfg, interceptors)?; + if let Some(layer) = plugin.config() { + cfg.push_shared_layer(layer); + } + plugin.interceptors(interceptors); } Ok(()) @@ -73,7 +83,10 @@ impl RuntimePlugins { interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { for plugin in self.operation_plugins.iter() { - plugin.configure(cfg, interceptors)?; + if let Some(layer) = plugin.config() { + cfg.push_shared_layer(layer); + } + plugin.interceptors(interceptors); } Ok(()) @@ -82,22 +95,12 @@ impl RuntimePlugins { #[cfg(test)] mod tests { - use super::{BoxError, RuntimePlugin, RuntimePlugins}; - use crate::client::interceptors::InterceptorRegistrar; - use aws_smithy_types::config_bag::ConfigBag; + use super::{RuntimePlugin, RuntimePlugins}; #[derive(Debug)] struct SomeStruct; - impl RuntimePlugin for SomeStruct { - fn configure( - &self, - _cfg: &mut ConfigBag, - _inters: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { - todo!() - } - } + impl RuntimePlugin for SomeStruct {} #[test] fn can_add_runtime_plugin_implementors_to_runtime_plugins() { diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 0c6fab72bf..8246e2e9bd 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -115,7 +115,7 @@ fn apply_configuration( runtime_plugins: &RuntimePlugins, ) -> Result<(), BoxError> { runtime_plugins.apply_client_configuration(cfg, interceptors.client_interceptors_mut())?; - continue_on_err!([ctx] =>interceptors.client_read_before_execution(ctx, cfg)); + continue_on_err!([ctx] => interceptors.client_read_before_execution(ctx, cfg)); runtime_plugins .apply_operation_configuration(cfg, interceptors.operation_interceptors_mut())?; continue_on_err!([ctx] => interceptors.operation_read_before_execution(ctx, cfg)); @@ -150,7 +150,8 @@ async fn try_op( let loaded_body = halt_on_err!([ctx] => ByteStream::new(body).collect().await).into_bytes(); *ctx.request_mut().as_mut().expect("set above").body_mut() = SdkBody::from(loaded_body.clone()); - cfg.set_loaded_request_body(LoadedRequestBody::Loaded(loaded_body)); + cfg.interceptor_state() + .set_loaded_request_body(LoadedRequestBody::Loaded(loaded_body)); } // Before transmit @@ -191,7 +192,7 @@ async fn try_op( break; } // Track which attempt we're currently on. - cfg.put::(i.into()); + cfg.interceptor_state().put::(i.into()); let attempt_timeout_config = cfg.maybe_timeout_config(TimeoutKind::OperationAttempt); let maybe_timeout = async { try_attempt(ctx, cfg, interceptors, stop_point).await; @@ -338,7 +339,7 @@ mod tests { }; use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, OrchestratorError}; use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin, RuntimePlugins}; - use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use http::StatusCode; use std::sync::atomic::{AtomicBool, Ordering}; @@ -367,11 +368,8 @@ mod tests { struct TestOperationRuntimePlugin; impl RuntimePlugin for TestOperationRuntimePlugin { - fn configure( - &self, - cfg: &mut ConfigBag, - _interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { + fn config(&self) -> Option { + let mut cfg = Layer::new("test operation"); cfg.set_request_serializer(new_request_serializer()); cfg.set_response_deserializer(new_response_deserializer()); cfg.set_retry_strategy(NeverRetryStrategy::new()); @@ -379,7 +377,7 @@ mod tests { cfg.set_endpoint_resolver_params(StaticUriEndpointResolverParams::new().into()); cfg.set_connection(OkConnector::new()); - Ok(()) + Some(cfg.freeze()) } } @@ -416,14 +414,8 @@ mod tests { struct FailingInterceptorsClientRuntimePlugin; impl RuntimePlugin for FailingInterceptorsClientRuntimePlugin { - fn configure( - &self, - _cfg: &mut ConfigBag, - interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { + fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { interceptors.register(SharedInterceptor::new(FailingInterceptorA)); - - Ok(()) } } @@ -431,15 +423,9 @@ mod tests { struct FailingInterceptorsOperationRuntimePlugin; impl RuntimePlugin for FailingInterceptorsOperationRuntimePlugin { - fn configure( - &self, - _cfg: &mut ConfigBag, - interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { + fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { interceptors.register(SharedInterceptor::new(FailingInterceptorB)); interceptors.register(SharedInterceptor::new(FailingInterceptorC)); - - Ok(()) } } @@ -447,7 +433,7 @@ mod tests { let runtime_plugins = RuntimePlugins::new() .with_client_plugin(FailingInterceptorsClientRuntimePlugin) .with_operation_plugin(TestOperationRuntimePlugin) - .with_operation_plugin(AnonymousAuthRuntimePlugin) + .with_operation_plugin(AnonymousAuthRuntimePlugin::new()) .with_operation_plugin(FailingInterceptorsOperationRuntimePlugin); let actual = invoke(input, &runtime_plugins) .await @@ -702,22 +688,16 @@ mod tests { struct InterceptorsTestOperationRuntimePlugin; impl RuntimePlugin for InterceptorsTestOperationRuntimePlugin { - fn configure( - &self, - _cfg: &mut ConfigBag, - interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { + fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { interceptors.register(SharedInterceptor::new(OriginInterceptor)); interceptors.register(SharedInterceptor::new(DestinationInterceptor)); - - Ok(()) } } let input = TypeErasedBox::new(Box::new(())); let runtime_plugins = RuntimePlugins::new() .with_operation_plugin(TestOperationRuntimePlugin) - .with_operation_plugin(AnonymousAuthRuntimePlugin) + .with_operation_plugin(AnonymousAuthRuntimePlugin::new()) .with_operation_plugin(InterceptorsTestOperationRuntimePlugin); let actual = invoke(input, &runtime_plugins) .await @@ -965,7 +945,7 @@ mod tests { let runtime_plugins = || { RuntimePlugins::new() .with_operation_plugin(TestOperationRuntimePlugin) - .with_operation_plugin(AnonymousAuthRuntimePlugin) + .with_operation_plugin(AnonymousAuthRuntimePlugin::new()) }; // StopPoint::None should result in a response getting set since orchestration doesn't stop @@ -1043,15 +1023,14 @@ mod tests { interceptor: TestInterceptor, } impl RuntimePlugin for TestInterceptorRuntimePlugin { - fn configure( - &self, - cfg: &mut ConfigBag, - interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { - cfg.put(self.interceptor.clone()); + fn config(&self) -> Option { + let mut layer = Layer::new("test"); + layer.put(self.interceptor.clone()); + Some(layer.freeze()) + } + fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { interceptors.register(SharedInterceptor::new(self.interceptor.clone())); - Ok(()) } } @@ -1059,7 +1038,7 @@ mod tests { let runtime_plugins = || { RuntimePlugins::new() .with_operation_plugin(TestOperationRuntimePlugin) - .with_operation_plugin(AnonymousAuthRuntimePlugin) + .with_operation_plugin(AnonymousAuthRuntimePlugin::new()) .with_operation_plugin(TestInterceptorRuntimePlugin { interceptor: interceptor.clone(), }) diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 005828aa67..3ab18caf35 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -140,6 +140,7 @@ mod tests { use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; + use aws_smithy_types::config_bag::Layer; use aws_smithy_types::type_erasure::TypedBox; use std::collections::HashMap; @@ -200,21 +201,23 @@ mod tests { let _ = ctx.take_input(); ctx.enter_before_transmit_phase(); - let mut cfg = ConfigBag::base(); - cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); - cfg.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![TEST_SCHEME_ID])); - cfg.set_identity_resolvers( + let mut layer = Layer::new("test"); + layer.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); + layer.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![TEST_SCHEME_ID])); + layer.set_identity_resolvers( IdentityResolvers::builder() .identity_resolver(TEST_SCHEME_ID, TestIdentityResolver) .build(), ); - cfg.set_http_auth_schemes( + layer.set_http_auth_schemes( HttpAuthSchemes::builder() .auth_scheme(TEST_SCHEME_ID, TestAuthScheme { signer: TestSigner }) .build(), ); - cfg.put(Endpoint::builder().url("dontcare").build()); + layer.put(Endpoint::builder().url("dontcare").build()); + let mut cfg = ConfigBag::base(); + cfg.push_layer(layer); orchestrate_auth(&mut ctx, &cfg).await.expect("success"); assert_eq!( @@ -242,26 +245,27 @@ mod tests { let _ = ctx.take_input(); ctx.enter_before_transmit_phase(); - let mut cfg = ConfigBag::base(); - cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); - cfg.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![ + let mut layer = Layer::new("test"); + layer.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); + layer.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![ HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID, ])); - cfg.set_http_auth_schemes( + layer.set_http_auth_schemes( HttpAuthSchemes::builder() .auth_scheme(HTTP_BASIC_AUTH_SCHEME_ID, BasicAuthScheme::new()) .auth_scheme(HTTP_BEARER_AUTH_SCHEME_ID, BearerAuthScheme::new()) .build(), ); - cfg.put(Endpoint::builder().url("dontcare").build()); + layer.put(Endpoint::builder().url("dontcare").build()); // First, test the presence of a basic auth login and absence of a bearer token - cfg.set_identity_resolvers( + layer.set_identity_resolvers( IdentityResolvers::builder() .identity_resolver(HTTP_BASIC_AUTH_SCHEME_ID, Login::new("a", "b", None)) .build(), ); + let mut cfg = ConfigBag::of_layers(vec![layer]); orchestrate_auth(&mut ctx, &cfg).await.expect("success"); assert_eq!( @@ -274,12 +278,15 @@ mod tests { .unwrap() ); + let mut additional_resolver = Layer::new("extra"); + // Next, test the presence of a bearer token and absence of basic auth - cfg.set_identity_resolvers( + additional_resolver.set_identity_resolvers( IdentityResolvers::builder() .identity_resolver(HTTP_BEARER_AUTH_SCHEME_ID, Token::new("t", None)) .build(), ); + cfg.push_layer(additional_resolver); let mut ctx = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); ctx.enter_serialization_phase(); diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 0d16f18704..aa7fefa9cb 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -100,7 +100,7 @@ pub(super) fn orchestrate_endpoint( apply_endpoint(request, &endpoint, endpoint_prefix)?; // Make the endpoint config available to interceptors - cfg.put(endpoint); + cfg.interceptor_state().put(endpoint); Ok(()) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs index 61bab15478..01f0ebfdb1 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs @@ -78,7 +78,7 @@ impl Interceptor for ServiceClockSkewInterceptor { } }; let skew = ServiceClockSkew::new(calculate_skew(time_sent, time_received)); - cfg.put(skew); + cfg.interceptor_state().put(skew); Ok(()) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index d19cddf9d2..6893313163 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -144,7 +144,7 @@ mod tests { use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, OrchestratorError}; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{AlwaysRetry, RetryClassifiers, RetryStrategy}; - use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::retry::ErrorKind; use aws_smithy_types::type_erasure::TypeErasedBox; use std::time::Duration; @@ -167,9 +167,12 @@ mod tests { ) -> (InterceptorContext, ConfigBag) { let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); ctx.set_output_or_error(Err(OrchestratorError::other("doesn't matter"))); - let mut cfg = ConfigBag::base(); - cfg.set_retry_classifiers(RetryClassifiers::new().with_classifier(AlwaysRetry(error_kind))); - cfg.put(RequestAttempts::new(current_request_attempts)); + let mut layer = Layer::new("test"); + layer.set_retry_classifiers( + RetryClassifiers::new().with_classifier(AlwaysRetry(error_kind)), + ); + layer.put(RequestAttempts::new(current_request_attempts)); + let cfg = ConfigBag::of_layers(vec![layer]); (ctx, cfg) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs index ad207dfad7..37ddc9c3d9 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs @@ -13,10 +13,9 @@ use aws_smithy_runtime_api::client::auth::{ AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpAuthSchemes, HttpRequestSigner, }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; -use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpRequest}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; const ANONYMOUS_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("anonymous"); @@ -30,21 +29,18 @@ const ANONYMOUS_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("anonymous"); /// - You only need to make anonymous requests, such as when interacting with [Open Data](https://aws.amazon.com/opendata/). /// - You're writing orchestrator tests and don't care about authentication. #[non_exhaustive] -#[derive(Debug, Default)] -pub struct AnonymousAuthRuntimePlugin; +#[derive(Debug)] +pub struct AnonymousAuthRuntimePlugin(FrozenLayer); -impl AnonymousAuthRuntimePlugin { - pub fn new() -> Self { - Self +impl Default for AnonymousAuthRuntimePlugin { + fn default() -> Self { + Self::new() } } -impl RuntimePlugin for AnonymousAuthRuntimePlugin { - fn configure( - &self, - cfg: &mut ConfigBag, - _interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { +impl AnonymousAuthRuntimePlugin { + pub fn new() -> Self { + let mut cfg = Layer::new("AnonymousAuth"); cfg.set_auth_option_resolver_params(StaticAuthOptionResolverParams::new().into()); cfg.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![ ANONYMOUS_AUTH_SCHEME_ID, @@ -59,8 +55,13 @@ impl RuntimePlugin for AnonymousAuthRuntimePlugin { .auth_scheme(ANONYMOUS_AUTH_SCHEME_ID, AnonymousAuthScheme::new()) .build(), ); + Self(cfg.freeze()) + } +} - Ok(()) +impl RuntimePlugin for AnonymousAuthRuntimePlugin { + fn config(&self) -> Option { + Some(self.0.clone()) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs index 292227acd0..e88ea351dc 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs @@ -4,12 +4,11 @@ */ use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; -use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; use aws_smithy_runtime_api::client::orchestrator::{ ConfigBagAccessors, HttpResponse, OrchestratorError, ResponseDeserializer, }; -use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin}; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::config_bag::{FrozenLayer, Layer}; use std::sync::Mutex; #[derive(Default, Debug)] @@ -44,15 +43,12 @@ impl ResponseDeserializer for CannedResponseDeserializer { } impl RuntimePlugin for CannedResponseDeserializer { - fn configure( - &self, - cfg: &mut ConfigBag, - _interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { + fn config(&self) -> Option { + let mut cfg = Layer::new("CannedResponse"); cfg.set_response_deserializer(Self { inner: Mutex::new(self.take()), }); - Ok(()) + Some(cfg.freeze()) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs index 07681ba274..2383b0fb22 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs @@ -63,7 +63,7 @@ mod tests { let request_time = UNIX_EPOCH + Duration::from_secs(1624036048); let interceptor = TestParamsSetterInterceptor::new( move |_: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag| { - cfg.set_request_time(request_time); + cfg.interceptor_state().set_request_time(request_time); }, ); interceptor diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs index 705324deca..faa2bbac3c 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -4,12 +4,11 @@ */ use aws_smithy_runtime_api::client::interceptors::context::Input; -use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; use aws_smithy_runtime_api::client::orchestrator::{ ConfigBagAccessors, HttpRequest, RequestSerializer, }; use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin}; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; use std::sync::Mutex; #[derive(Default, Debug)] @@ -50,15 +49,11 @@ impl RequestSerializer for CannedRequestSerializer { } impl RuntimePlugin for CannedRequestSerializer { - fn configure( - &self, - cfg: &mut ConfigBag, - _interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { + fn config(&self) -> Option { + let mut cfg = Layer::new("CannedRequest"); cfg.set_request_serializer(Self { inner: Mutex::new(self.take()), }); - - Ok(()) + Some(cfg.freeze()) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs index 2f663b2e60..875707a4d1 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs @@ -183,6 +183,7 @@ mod tests { use aws_smithy_async::assert_elapsed; use aws_smithy_async::future::never::Never; use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_types::config_bag::Layer; #[tokio::test] async fn test_no_timeout() { @@ -197,8 +198,10 @@ mod tests { tokio::time::pause(); let mut cfg = ConfigBag::base(); - cfg.put(TimeoutConfig::builder().build()); - cfg.set_sleep_impl(Some(sleep_impl)); + let mut timeout_config = Layer::new("timeout"); + timeout_config.put(TimeoutConfig::builder().build()); + timeout_config.set_sleep_impl(Some(sleep_impl)); + cfg.push_layer(timeout_config); underlying_future .maybe_timeout(&cfg, TimeoutKind::Operation) @@ -221,12 +224,14 @@ mod tests { tokio::time::pause(); let mut cfg = ConfigBag::base(); - cfg.put( + let mut timeout_config = Layer::new("timeout"); + timeout_config.put( TimeoutConfig::builder() .operation_timeout(Duration::from_millis(250)) .build(), ); - cfg.set_sleep_impl(Some(sleep_impl)); + timeout_config.set_sleep_impl(Some(sleep_impl)); + cfg.push_layer(timeout_config); let result = underlying_future .maybe_timeout(&cfg, TimeoutKind::Operation) diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index 6a7f3797d7..81f4e5ca64 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -9,6 +9,7 @@ //! with the following properties: //! 1. A new layer of configuration may be applied onto an existing configuration structure without modifying it or taking ownership. //! 2. No lifetime shenanigans to deal with +mod storable; mod typeid_map; use crate::config_bag::typeid_map::TypeIdMap; @@ -18,17 +19,18 @@ use std::borrow::Cow; use std::fmt::{Debug, Formatter}; use std::iter::Rev; use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; -use std::slice; +use std::ops::Deref; use std::slice::Iter; use std::sync::Arc; +pub use storable::{AppendItemIter, Storable, Store, StoreAppend, StoreReplace}; + /// Layered Configuration Structure /// /// [`ConfigBag`] is the "unlocked" form of the bag. Only the top layer of the bag may be unlocked. #[must_use] pub struct ConfigBag { - head: Layer, + interceptor_state: Layer, tail: Vec, } @@ -83,100 +85,6 @@ pub struct Layer { props: TypeIdMap, } -/// Trait defining how types can be stored and loaded from the config bag -pub trait Store: Sized + Send + Sync + 'static { - /// Denote the returned type when loaded from the config bag - type ReturnedType<'a>: Send + Sync; - /// Denote the stored type when stored into the config bag - type StoredType: Send + Sync + Debug; - - /// Create a returned type from an iterable of items - fn merge_iter(iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_>; -} - -/// Store an item in the config bag by replacing the existing value -#[non_exhaustive] -pub struct StoreReplace(PhantomData); -impl Debug for StoreReplace { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "StoreReplace") - } -} - -/// Store an item in the config bag by effectively appending it to a list -#[non_exhaustive] -pub struct StoreAppend(PhantomData); -impl Debug for StoreAppend { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "StoreAppend") - } -} - -/// Trait that marks the implementing types as able to be stored in the config bag -pub trait Storable: Send + Sync + Debug + 'static { - /// Specify how an item is stored in the config bag, e.g. [`StoreReplace`] and [`StoreAppend`] - type Storer: Store; -} - -impl Store for StoreReplace { - type ReturnedType<'a> = Option<&'a U>; - type StoredType = Value; - - fn merge_iter(mut iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_> { - iter.next().and_then(|item| match item { - Value::Set(item) => Some(item), - Value::ExplicitlyUnset(_) => None, - }) - } -} - -impl Store for StoreAppend { - type ReturnedType<'a> = AppendItemIter<'a, U>; - type StoredType = Value>; - - fn merge_iter(iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_> { - AppendItemIter { - inner: iter, - cur: None, - } - } -} - -/// Iterator of items returned by [`StoreAppend`] -pub struct AppendItemIter<'a, U> { - inner: ItemIter<'a, StoreAppend>, - cur: Option>>, -} -impl<'a, U> Debug for AppendItemIter<'a, U> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "AppendItemIter") - } -} - -impl<'a, U: 'a> Iterator for AppendItemIter<'a, U> -where - U: Send + Sync + Debug + 'static, -{ - type Item = &'a U; - - fn next(&mut self) -> Option { - if let Some(buf) = &mut self.cur { - match buf.next() { - Some(item) => return Some(item), - None => self.cur = None, - } - } - match self.inner.next() { - None => None, - Some(Value::Set(u)) => { - self.cur = Some(u.iter().rev()); - self.next() - } - Some(Value::ExplicitlyUnset(_)) => None, - } - } -} - impl Debug for Layer { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { struct Items<'a>(&'a Layer); @@ -269,20 +177,25 @@ impl Layer { /// This can only be used for types that use [`StoreAppend`] /// ``` - /// use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; - /// let mut bag = ConfigBag::base(); + /// use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreAppend, StoreReplace}; + /// let mut layer_1 = Layer::new("example"); /// #[derive(Debug, PartialEq, Eq)] /// struct Interceptor(&'static str); /// impl Storable for Interceptor { /// type Storer = StoreAppend; /// } /// - /// bag.store_append(Interceptor("123")); - /// bag.store_append(Interceptor("456")); + /// layer_1.store_append(Interceptor("321")); + /// layer_1.store_append(Interceptor("654")); + /// + /// let mut layer_2 = Layer::new("second layer"); + /// layer_2.store_append(Interceptor("987")); + /// + /// let bag = ConfigBag::of_layers(vec![layer_1, layer_2]); /// /// assert_eq!( /// bag.load::().collect::>(), - /// vec![&Interceptor("456"), &Interceptor("123")] + /// vec![&Interceptor("987"), &Interceptor("654"), &Interceptor("321")] /// ); /// ``` pub fn store_append(&mut self, item: T) -> &mut Self @@ -296,6 +209,17 @@ impl Layer { self } + /// Clears the value of type `T` from the config bag + /// + /// This internally marks the item of type `T` as cleared as opposed to wiping it out from the + /// config bag. + pub fn clear(&mut self) + where + T: Storable>, + { + self.put_directly::>(Value::ExplicitlyUnset(type_name::())); + } + /// Retrieves the value of type `T` from this layer if exists fn get(&self) -> Option<&T::StoredType> { self.props @@ -334,21 +258,6 @@ impl FrozenLayer { } } -// TODO(refactor of configbag): consider removing these Deref impls—they exist to keep existing code compiling -impl Deref for ConfigBag { - type Target = Layer; - - fn deref(&self) -> &Self::Target { - &self.head - } -} - -impl DerefMut for ConfigBag { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.head - } -} - impl ConfigBag { /// Create a new config bag "base". /// @@ -357,38 +266,34 @@ impl ConfigBag { /// of configuration may then be "frozen" (made immutable) by calling [`ConfigBag::freeze`]. pub fn base() -> Self { ConfigBag { - head: Layer { - name: Cow::Borrowed("base"), + interceptor_state: Layer { + name: Cow::Borrowed("interceptor_state"), props: Default::default(), }, tail: vec![], } } - pub fn push_layer(&mut self, layer: &FrozenLayer) -> &mut Self { - if !self.head.empty() { - self.freeze_head(); + pub fn of_layers(layers: Vec) -> Self { + let mut bag = ConfigBag::base(); + for layer in layers { + bag.push_layer(layer); } - self.tail.push(layer.clone()); + bag + } + + pub fn push_layer(&mut self, layer: Layer) -> &mut Self { + self.tail.push(layer.freeze()); self } - fn freeze_head(&mut self) { - let new_head = Layer::new("scratch"); - let old_head = std::mem::replace(&mut self.head, new_head); - self.tail.push(old_head.freeze()); + pub fn push_shared_layer(&mut self, layer: FrozenLayer) -> &mut Self { + self.tail.push(layer); + self } - /// Clears the value of type `T` from the config bag - /// - /// This internally marks the item of type `T` as cleared as opposed to wiping it out from the - /// config bag. - pub fn clear(&mut self) - where - T: Storable>, - { - self.head - .put_directly::>(Value::ExplicitlyUnset(type_name::())); + pub fn interceptor_state(&mut self) -> &mut Layer { + &mut self.interceptor_state } /// Load a value (or values) of type `T` depending on how `T` implements [`Storable`] @@ -410,19 +315,19 @@ impl ConfigBag { // this code looks weird to satisfy the borrow checker—we can't keep the result of `get_mut` // alive (even in a returned branch) and then call `store_put`. So: drop the borrow immediately // store, the value, then pull it right back - if matches!(self.head.get_mut::>(), None) { + if matches!(self.interceptor_state.get_mut::>(), None) { let new_item = match self.tail.iter().find_map(|b| b.load::()) { Some(item) => item.clone(), None => return None, }; - self.store_put(new_item); + self.interceptor_state.store_put(new_item); self.get_mut() } else if matches!( - self.head.get::>(), + self.interceptor_state.get::>(), Some(Value::ExplicitlyUnset(_)) ) { None - } else if let Some(Value::Set(t)) = self.head.get_mut::>() { + } else if let Some(Value::Set(t)) = self.interceptor_state.get_mut::>() { Some(t) } else { unreachable!() @@ -457,7 +362,7 @@ impl ConfigBag { // alive (even in a returned branch) and then call `store_put`. So: drop the borrow immediately // store, the value, then pull it right back if self.get_mut::().is_none() { - self.store_put((default)()); + self.interceptor_state.store_put((default)()); return self .get_mut() .expect("item was just stored in the top layer"); @@ -491,10 +396,13 @@ impl ConfigBag { ) -> ConfigBag { let mut new_layer = Layer::new(name); next(&mut new_layer); - let ConfigBag { head, mut tail } = self; + let ConfigBag { + interceptor_state: head, + mut tail, + } = self; tail.push(head.freeze()); ConfigBag { - head: new_layer, + interceptor_state: new_layer, tail, } } @@ -518,7 +426,7 @@ impl ConfigBag { fn layers(&self) -> BagIter<'_> { BagIter { - head: Some(&self.head), + head: Some(&self.interceptor_state), tail: self.tail.iter().rev(), } } @@ -599,7 +507,7 @@ mod test { let mut base_bag = ConfigBag::base() .with_fn("a", layer_a) .with_fn("b", layer_b); - base_bag.put(Prop3); + base_bag.interceptor_state().put(Prop3); assert!(base_bag.get::().is_some()); #[derive(Debug)] @@ -640,27 +548,29 @@ mod test { assert_eq!(operation_config.get::().unwrap().0, "s3"); let mut open_bag = operation_config.with_fn("my_custom_info", |_bag: &mut Layer| {}); - open_bag.put("foo"); + open_bag.interceptor_state().put("foo"); assert_eq!(open_bag.layers().count(), 4); } #[test] fn store_append() { - let mut bag = ConfigBag::base(); + let mut layer = Layer::new("test"); #[derive(Debug, PartialEq, Eq)] struct Interceptor(&'static str); impl Storable for Interceptor { type Storer = StoreAppend; } - bag.clear::(); + layer.clear::(); // you can only call store_append because interceptor is marked with a vec - bag.store_append(Interceptor("123")); - bag.store_append(Interceptor("456")); + layer.store_append(Interceptor("123")); + layer.store_append(Interceptor("456")); - let mut bag = bag.add_layer("next"); - bag.store_append(Interceptor("789")); + let mut second_layer = Layer::new("next"); + second_layer.store_append(Interceptor("789")); + + let mut bag = ConfigBag::of_layers(vec![layer, second_layer]); assert_eq!( bag.load::().collect::>(), @@ -671,7 +581,9 @@ mod test { ] ); - bag.clear::(); + let mut final_layer = Layer::new("final"); + final_layer.clear::(); + bag.push_layer(final_layer); assert_eq!(bag.load::().count(), 0); } @@ -684,12 +596,13 @@ mod test { } let mut expected = vec![]; let mut bag = ConfigBag::base(); - for layer in 0..100 { - bag = bag.add_layer(format!("{}", layer)); + for layer_idx in 0..100 { + let mut layer = Layer::new(format!("{}", layer_idx)); for item in 0..100 { - expected.push(TestItem(layer, item)); - bag.store_append(TestItem(layer, item)); + expected.push(TestItem(layer_idx, item)); + layer.store_append(TestItem(layer_idx, item)); } + bag.push_layer(layer); } expected.reverse(); assert_eq!( @@ -718,12 +631,17 @@ mod test { let mut bag_1 = ConfigBag::base(); let mut bag_2 = ConfigBag::base(); - bag_1.push_layer(&layer_1).push_layer(&layer_2); - bag_2.push_layer(&layer_2).push_layer(&layer_1); + bag_1 + .push_shared_layer(layer_1.clone()) + .push_shared_layer(layer_2.clone()); + bag_2.push_shared_layer(layer_2).push_shared_layer(layer_1); // bags have same layers but in different orders assert_eq!(bag_1.load::(), Some(&Foo(1))); assert_eq!(bag_2.load::(), Some(&Foo(0))); + + bag_1.interceptor_state().put(Foo(3)); + assert_eq!(bag_1.load::(), Some(&Foo(3))); } #[test] @@ -749,7 +667,7 @@ mod test { let new_ref = bag.load::().unwrap(); assert_eq!(new_ref, &Foo(2)); - bag.unset::(); + bag.interceptor_state().unset::(); // if it was unset, we can't clone the current one, that would be wrong assert_eq!(bag.get_mut::(), None); assert_eq!(bag.get_mut_or_default::(), &Foo(0)); diff --git a/rust-runtime/aws-smithy-types/src/config_bag/storable.rs b/rust-runtime/aws-smithy-types/src/config_bag/storable.rs new file mode 100644 index 0000000000..33c0d2d434 --- /dev/null +++ b/rust-runtime/aws-smithy-types/src/config_bag/storable.rs @@ -0,0 +1,108 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::config_bag::value::Value; +use crate::config_bag::ItemIter; +use std::fmt::{Debug, Formatter}; +use std::iter::Rev; +use std::marker::PhantomData; +use std::slice; + +/// Trait defining how types can be stored and loaded from the config bag +pub trait Store: Sized + Send + Sync + 'static { + /// Denote the returned type when loaded from the config bag + type ReturnedType<'a>: Send + Sync; + /// Denote the stored type when stored into the config bag + type StoredType: Send + Sync + Debug; + + /// Create a returned type from an iterable of items + fn merge_iter(iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_>; +} + +/// Store an item in the config bag by replacing the existing value +#[non_exhaustive] +pub struct StoreReplace(PhantomData); + +impl Debug for StoreReplace { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "StoreReplace") + } +} + +/// Store an item in the config bag by effectively appending it to a list +#[non_exhaustive] +pub struct StoreAppend(PhantomData); + +impl Debug for StoreAppend { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "StoreAppend") + } +} + +/// Trait that marks the implementing types as able to be stored in the config bag +pub trait Storable: Send + Sync + Debug + 'static { + /// Specify how an item is stored in the config bag, e.g. [`StoreReplace`] and [`StoreAppend`] + type Storer: Store; +} + +impl Store for StoreReplace { + type ReturnedType<'a> = Option<&'a U>; + type StoredType = Value; + + fn merge_iter(mut iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_> { + iter.next().and_then(|item| match item { + Value::Set(item) => Some(item), + Value::ExplicitlyUnset(_) => None, + }) + } +} + +impl Store for StoreAppend { + type ReturnedType<'a> = AppendItemIter<'a, U>; + type StoredType = Value>; + + fn merge_iter(iter: ItemIter<'_, Self>) -> Self::ReturnedType<'_> { + AppendItemIter { + inner: iter, + cur: None, + } + } +} + +/// Iterator of items returned by [`StoreAppend`] +pub struct AppendItemIter<'a, U> { + inner: ItemIter<'a, StoreAppend>, + cur: Option>>, +} + +impl<'a, U> Debug for AppendItemIter<'a, U> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "AppendItemIter") + } +} + +impl<'a, U: 'a> Iterator for AppendItemIter<'a, U> +where + U: Send + Sync + Debug + 'static, +{ + type Item = &'a U; + + fn next(&mut self) -> Option { + if let Some(buf) = &mut self.cur { + match buf.next() { + Some(item) => return Some(item), + None => self.cur = None, + } + } + match self.inner.next() { + None => None, + Some(Value::Set(u)) => { + self.cur = Some(u.iter().rev()); + self.next() + } + Some(Value::ExplicitlyUnset(_)) => None, + } + } +} From 312d190535b1c77625d662d18313b90af64cb448 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Tue, 13 Jun 2023 18:49:43 -0500 Subject: [PATCH 151/253] Update standard orchestrator retries with token bucket and more tests (#2764) ## Motivation and Context addresses #2743 ## Description - add more standard retry tests - add optional standard retries token bucket ## Testing tests are included ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti --- .../aws-config/src/profile/credentials.rs | 1 - .../smithy/generators/OperationGenerator.kt | 28 +- .../ServiceRuntimePluginGenerator.kt | 22 +- .../src/client/orchestrator/error.rs | 35 +- .../src/client/retries.rs | 16 +- .../src/client/retries/rate_limiting.rs | 13 - .../src/client/retries/rate_limiting/error.rs | 50 -- .../src/client/retries/rate_limiting/token.rs | 65 --- .../retries/rate_limiting/token_bucket.rs | 235 --------- .../src/client/orchestrator.rs | 35 +- .../src/client/retries/classifier.rs | 32 +- .../src/client/retries/strategy.rs | 2 +- .../src/client/retries/strategy/standard.rs | 457 +++++++++++++++++- .../src/client/runtime_plugin.rs | 2 + .../runtime_plugin/standard_token_bucket.rs | 100 ++++ rust-runtime/aws-smithy-types/src/retry.rs | 17 + 16 files changed, 655 insertions(+), 455 deletions(-) delete mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting.rs delete mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/error.rs delete mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token.rs delete mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token_bucket.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/standard_token_bucket.rs diff --git a/aws/rust-runtime/aws-config/src/profile/credentials.rs b/aws/rust-runtime/aws-config/src/profile/credentials.rs index 774d5785a6..c3d08e58d5 100644 --- a/aws/rust-runtime/aws-config/src/profile/credentials.rs +++ b/aws/rust-runtime/aws-config/src/profile/credentials.rs @@ -465,7 +465,6 @@ async fn build_provider_chain( #[cfg(test)] mod test { - use crate::profile::credentials::Builder; use crate::test_case::TestEnvironment; diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 859126d03f..5703766c55 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -146,20 +146,6 @@ open class OperationGenerator( if (codegenContext.smithyRuntimeMode.generateOrchestrator) { rustTemplate( """ - pub(crate) fn register_runtime_plugins( - runtime_plugins: #{RuntimePlugins}, - handle: #{Arc}, - config_override: #{Option}, - ) -> #{RuntimePlugins} { - #{register_default_runtime_plugins}( - runtime_plugins, - #{Box}::new(Self::new()) as _, - handle, - config_override - ) - #{additional_runtime_plugins} - } - pub(crate) async fn orchestrate( runtime_plugins: &#{RuntimePlugins}, input: #{Input}, @@ -186,6 +172,20 @@ open class OperationGenerator( let input = #{TypedBox}::new(input).erase(); #{invoke_with_stop_point}(input, runtime_plugins, stop_point).await } + + pub(crate) fn register_runtime_plugins( + runtime_plugins: #{RuntimePlugins}, + handle: #{Arc}, + config_override: #{Option}, + ) -> #{RuntimePlugins} { + #{register_default_runtime_plugins}( + runtime_plugins, + #{Box}::new(Self::new()) as _, + handle, + config_override + ) + #{additional_runtime_plugins} + } """, *codegenScope, "Error" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::Error"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 478da7fb9a..033c60b16b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -105,6 +105,8 @@ class ServiceRuntimePluginGenerator( "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "default_connector" to client.resolve("conns::default_connector"), "require_connector" to client.resolve("conns::require_connector"), + "TimeoutConfig" to smithyTypes.resolve("timeout::TimeoutConfig"), + "RetryConfig" to smithyTypes.resolve("retry::RetryConfig"), ) } @@ -142,20 +144,17 @@ class ServiceRuntimePluginGenerator( self.handle.conf.endpoint_resolver()); cfg.set_endpoint_resolver(endpoint_resolver); - // TODO(enableNewSmithyRuntime): Use the `store_append` method of ConfigBag to insert classifiers - let retry_classifiers = #{RetryClassifiers}::new() - #{retry_classifier_customizations}; - cfg.set_retry_classifiers(retry_classifiers); + // TODO(enableNewSmithyRuntime): Make it possible to set retry classifiers at the service level. + // Retry classifiers can also be set at the operation level and those should be added to the + // list of classifiers defined here, rather than replacing them. let sleep_impl = self.handle.conf.sleep_impl(); - let timeout_config = self.handle.conf.timeout_config(); - let retry_config = self.handle.conf.retry_config(); + let timeout_config = self.handle.conf.timeout_config().cloned().unwrap_or_else(|| #{TimeoutConfig}::disabled()); + let retry_config = self.handle.conf.retry_config().cloned().unwrap_or_else(|| #{RetryConfig}::disabled()); - if let Some(retry_config) = retry_config { - cfg.set_retry_strategy(#{StandardRetryStrategy}::new(retry_config)); - } + cfg.set_retry_strategy(#{StandardRetryStrategy}::new(&retry_config)); - let connector_settings = timeout_config.map(#{ConnectorSettings}::from_timeout_config).unwrap_or_default(); + let connector_settings = #{ConnectorSettings}::from_timeout_config(&timeout_config); if let Some(connection) = self.handle.conf.http_connector() .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) { @@ -180,9 +179,6 @@ class ServiceRuntimePluginGenerator( "http_auth_scheme_customizations" to writable { writeCustomizations(customizations, ServiceRuntimePluginSection.HttpAuthScheme("cfg")) }, - "retry_classifier_customizations" to writable { - writeCustomizations(customizations, ServiceRuntimePluginSection.RetryClassifier("cfg")) - }, "additional_config" to writable { writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg")) }, diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs index 152e442013..06d240f7ef 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs @@ -18,6 +18,12 @@ pub enum OrchestratorError { Interceptor { err: InterceptorError }, /// An error returned by a service. Operation { err: E }, + /// An error that occurs when a request times out. + Timeout { err: BoxError }, + /// An error that occurs when request dispatch fails. + Connector { err: ConnectorError }, + /// An error that occurs when a response can't be deserialized. + Response { err: BoxError }, /// A general orchestrator error. Other { err: BoxError }, } @@ -34,11 +40,26 @@ impl OrchestratorError { Self::Operation { err } } - /// Create a new `OrchestratorError` from an [`InterceptorError`]. + /// Create a new `OrchestratorError::Interceptor` from an [`InterceptorError`]. pub fn interceptor(err: InterceptorError) -> Self { Self::Interceptor { err } } + /// Create a new `OrchestratorError::Timeout` from a [`BoxError`]. + pub fn timeout(err: BoxError) -> Self { + Self::Timeout { err } + } + + /// Create a new `OrchestratorError::Response` from a [`BoxError`]. + pub fn response(err: BoxError) -> Self { + Self::Response { err } + } + + /// Create a new `OrchestratorError::Connector` from a [`ConnectorError`]. + pub fn connector(err: ConnectorError) -> Self { + Self::Connector { err } + } + /// Convert the `OrchestratorError` into `Some` operation specific error if it is one. Otherwise, /// return `None`. pub fn as_operation_error(&self) -> Option<&E> { @@ -72,6 +93,9 @@ impl OrchestratorError { debug_assert!(phase.is_after_deserialization(), "operation errors are a result of successfully receiving and parsing a response from the server. Therefore, we must be in the 'After Deserialization' phase."); SdkError::service_error(err, response.expect("phase has a response")) } + Self::Connector { err } => SdkError::dispatch_failure(err), + Self::Timeout { err } => SdkError::timeout_error(err), + Self::Response { err } => SdkError::response_error(err, response.unwrap()), Self::Other { err } => { use Phase::*; match phase { @@ -111,15 +135,6 @@ where } } -impl From for OrchestratorError -where - E: Debug + std::error::Error + 'static, -{ - fn from(err: BoxError) -> Self { - Self::other(err) - } -} - impl From for OrchestratorError { fn from(err: TypeErasedError) -> Self { Self::operation(err) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index ca9699075e..1a8f0f8cf2 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -20,6 +20,16 @@ pub enum ShouldAttempt { YesAfterDelay(Duration), } +#[cfg(feature = "test-util")] +impl ShouldAttempt { + pub fn expect_delay(self) -> Duration { + match self { + ShouldAttempt::YesAfterDelay(delay) => delay, + _ => panic!("Expected this to be the `YesAfterDelay` variant but it was the `{self:?}` variant instead"), + } + } +} + pub trait RetryStrategy: Send + Sync + Debug { fn should_attempt_initial_request(&self, cfg: &ConfigBag) -> Result; @@ -31,7 +41,7 @@ pub trait RetryStrategy: Send + Sync + Debug { } #[non_exhaustive] -#[derive(Eq, PartialEq, Debug)] +#[derive(Clone, Eq, PartialEq, Debug)] pub enum RetryReason { Error(ErrorKind), Explicit(Duration), @@ -72,10 +82,10 @@ impl RetryClassifiers { } impl ClassifyRetry for RetryClassifiers { - fn classify_retry(&self, error: &InterceptorContext) -> Option { + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { // return the first non-None result self.inner.iter().find_map(|cr| { - let maybe_reason = cr.classify_retry(error); + let maybe_reason = cr.classify_retry(ctx); match maybe_reason.as_ref() { Some(reason) => trace!( diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting.rs deleted file mode 100644 index fcb6085fbd..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting.rs +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! Code for rate-limiting smithy clients. - -pub mod error; -pub mod token; -pub mod token_bucket; - -pub use token::Token; -pub use token_bucket::TokenBucket; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/error.rs deleted file mode 100644 index b4f4d9821f..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/error.rs +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! Errors related to rate limiting - -use std::fmt; - -/// Errors related to a token bucket. -#[derive(Debug)] -pub struct RateLimitingError { - kind: ErrorKind, -} - -impl RateLimitingError { - /// An error that occurs when no tokens are left in the bucket. - pub fn no_tokens() -> Self { - Self { - kind: ErrorKind::NoTokens, - } - } - - /// An error that occurs due to a bug in the code. Please report bugs you encounter. - pub fn bug(s: impl ToString) -> Self { - Self { - kind: ErrorKind::Bug(s.to_string()), - } - } -} - -#[derive(Debug)] -enum ErrorKind { - /// A token was requested but there were no tokens left in the bucket. - NoTokens, - /// This error should never occur and is a bug. Please report it. - Bug(String), -} - -impl fmt::Display for RateLimitingError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use ErrorKind::*; - match &self.kind { - NoTokens => write!(f, "No more tokens are left in the bucket."), - Bug(msg) => write!(f, "you've encountered a bug that needs reporting: {}", msg), - } - } -} - -impl std::error::Error for RateLimitingError {} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token.rs deleted file mode 100644 index 70d620e790..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! Types and traits related to token buckets. Token buckets are used to limit the amount of -//! requests a client sends in order to avoid getting throttled. Token buckets can also act as a -//! form of concurrency control if a token is required to send a new request (as opposed to retry -//! requests only). - -use tokio::sync::OwnedSemaphorePermit; - -/// A trait implemented by types that represent a token dispensed from a [`TokenBucket`](super::TokenBucket). -pub trait Token { - /// Release this token back to the bucket. This should be called if the related request succeeds. - fn release(self); - - /// Forget this token, forever banishing it to the shadow realm, from whence no tokens return. - /// This should be called if the related request fails. - fn forget(self); -} - -/// The token type of [`Standard`]. -#[derive(Debug)] -pub struct Standard { - permit: Option, -} - -impl Standard { - pub(crate) fn new(permit: OwnedSemaphorePermit) -> Self { - Self { - permit: Some(permit), - } - } - - // Return an "empty" token for times when you need to return a token but there's no "cost" - // associated with an action. - pub(crate) fn empty() -> Self { - Self { permit: None } - } -} - -impl Token for Standard { - fn release(self) { - drop(self.permit) - } - - fn forget(self) { - if let Some(permit) = self.permit { - permit.forget() - } - } -} - -#[cfg(test)] -mod tests { - use super::Standard as Token; - use crate::client::retries::rate_limiting::token_bucket::Standard as TokenBucket; - - #[test] - fn token_bucket_trait_is_dyn_safe() { - let _tb: Box> = - Box::new(TokenBucket::builder().build()); - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token_bucket.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token_bucket.rs deleted file mode 100644 index 500ee723fd..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries/rate_limiting/token_bucket.rs +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! A token bucket intended for use with the standard smithy client retry policy. - -use super::error::RateLimitingError; -use super::token; -use super::Token; -use aws_smithy_types::retry::{ErrorKind, RetryKind}; -use std::sync::Arc; -use tokio::sync::Semaphore; -use tokio::sync::TryAcquireError; - -/// The default number of tokens to start with -const STANDARD_INITIAL_RETRY_TOKENS: usize = 500; -/// The amount of tokens to remove from the bucket when a timeout error occurs -const STANDARD_TIMEOUT_ERROR_RETRY_COST: u32 = 10; -/// The amount of tokens to remove from the bucket when a throttling error occurs -const STANDARD_RETRYABLE_ERROR_RETRY_COST: u32 = 5; - -/// This trait is implemented by types that act as token buckets. Token buckets are used to regulate -/// the amount of requests sent by clients. Different token buckets may apply different strategies -/// to manage the number of tokens in a bucket. -/// -/// related: [`Token`], [`RateLimitingError`] -pub trait TokenBucket { - /// The type of tokens this bucket dispenses. - type Token: Token; - - /// Attempt to acquire a token from the bucket. This will fail if the bucket has no more tokens. - fn try_acquire( - &self, - previous_response_kind: Option, - ) -> Result; - - /// Get the number of available tokens in the bucket. - fn available(&self) -> usize; - - /// Refill the bucket with the given number of tokens. - fn refill(&self, tokens: usize); -} - -/// A token bucket implementation that uses a `tokio::sync::Semaphore` to track the number of tokens. -/// -/// - Whenever a request succeeds on the first try, `` token(s) -/// are added back to the bucket. -/// - When a request fails with a timeout error, `` token(s) -/// are removed from the bucket. -/// - When a request fails with a retryable error, `` token(s) -/// are removed from the bucket. -/// -/// The number of tokens in the bucket will always be >= `0` and <= ``. -#[derive(Clone, Debug)] -pub struct Standard { - inner: Arc, - max_tokens: usize, - timeout_error_cost: u32, - retryable_error_cost: u32, -} - -impl Standard { - /// Create a new `TokenBucket` using builder methods. - pub fn builder() -> Builder { - Builder::default() - } -} - -/// A builder for `TokenBucket`s. -#[derive(Default, Debug)] -pub struct Builder { - starting_tokens: Option, - max_tokens: Option, - timeout_error_cost: Option, - retryable_error_cost: Option, -} - -impl Builder { - /// The number of tokens the bucket will start with. Defaults to 500. - pub fn starting_tokens(mut self, starting_tokens: usize) -> Self { - self.starting_tokens = Some(starting_tokens); - self - } - - /// The maximum number of tokens that the bucket can hold. - /// Defaults to the value of `starting_tokens`. - pub fn max_tokens(mut self, max_tokens: usize) -> Self { - self.max_tokens = Some(max_tokens); - self - } - - /// How many tokens to remove from the bucket when a request fails due to a timeout error. - /// Defaults to 10. - pub fn timeout_error_cost(mut self, timeout_error_cost: u32) -> Self { - self.timeout_error_cost = Some(timeout_error_cost); - self - } - - /// How many tokens to remove from the bucket when a request fails due to a retryable error that - /// isn't timeout-related. Defaults to 5. - pub fn retryable_error_cost(mut self, retryable_error_cost: u32) -> Self { - self.retryable_error_cost = Some(retryable_error_cost); - self - } - - /// Build this builder. Unset fields will be set to their default values. - pub fn build(self) -> Standard { - let starting_tokens = self - .starting_tokens - .unwrap_or(STANDARD_INITIAL_RETRY_TOKENS); - let max_tokens = self.max_tokens.unwrap_or(starting_tokens); - let timeout_error_cost = self - .timeout_error_cost - .unwrap_or(STANDARD_TIMEOUT_ERROR_RETRY_COST); - let retryable_error_cost = self - .retryable_error_cost - .unwrap_or(STANDARD_RETRYABLE_ERROR_RETRY_COST); - - Standard { - inner: Arc::new(Semaphore::new(starting_tokens)), - max_tokens, - timeout_error_cost, - retryable_error_cost, - } - } -} - -impl TokenBucket for Standard { - type Token = token::Standard; - - fn try_acquire( - &self, - previous_response_kind: Option, - ) -> Result { - let number_of_tokens_to_acquire = match previous_response_kind { - None => { - // Return an empty token because the quota layer lifecycle expects a for each - // request even though the standard token bucket only requires tokens for retry - // attempts. - return Ok(token::Standard::empty()); - } - - Some(retry_kind) => match retry_kind { - RetryKind::Unnecessary => { - unreachable!("BUG: asked for a token to retry a successful request") - } - RetryKind::UnretryableFailure => { - unreachable!("BUG: asked for a token to retry an un-retryable request") - } - RetryKind::Explicit(_) => self.retryable_error_cost, - RetryKind::Error(error_kind) => match error_kind { - ErrorKind::ThrottlingError | ErrorKind::TransientError => { - self.timeout_error_cost - } - ErrorKind::ServerError => self.retryable_error_cost, - ErrorKind::ClientError => unreachable!( - "BUG: asked for a token to retry a request that failed due to user error" - ), - _ => unreachable!( - "A new variant '{:?}' was added to ErrorKind, please handle it", - error_kind - ), - }, - _ => unreachable!( - "A new variant '{:?}' was added to RetryKind, please handle it", - retry_kind - ), - }, - }; - - match self - .inner - .clone() - .try_acquire_many_owned(number_of_tokens_to_acquire) - { - Ok(permit) => Ok(token::Standard::new(permit)), - Err(TryAcquireError::NoPermits) => Err(RateLimitingError::no_tokens()), - Err(other) => Err(RateLimitingError::bug(other.to_string())), - } - } - - fn available(&self) -> usize { - self.inner.available_permits() - } - - fn refill(&self, tokens: usize) { - // Ensure the bucket doesn't overflow by limiting the amount of tokens to add, if necessary. - let amount_to_add = (self.available() + tokens).min(self.max_tokens) - self.available(); - if amount_to_add > 0 { - self.inner.add_permits(amount_to_add) - } - } -} - -#[cfg(test)] -mod test { - use super::{Token, TokenBucket}; - use super::{ - STANDARD_INITIAL_RETRY_TOKENS, STANDARD_RETRYABLE_ERROR_RETRY_COST, - STANDARD_TIMEOUT_ERROR_RETRY_COST, - }; - use aws_smithy_types::retry::{ErrorKind, RetryKind}; - - #[test] - fn bucket_works() { - let bucket = super::Standard::builder().build(); - assert_eq!(bucket.available(), STANDARD_INITIAL_RETRY_TOKENS); - - let token = bucket - .try_acquire(Some(RetryKind::Error(ErrorKind::ServerError))) - .unwrap(); - assert_eq!( - bucket.available(), - STANDARD_INITIAL_RETRY_TOKENS - STANDARD_RETRYABLE_ERROR_RETRY_COST as usize - ); - Box::new(token).release(); - - let token = bucket - .try_acquire(Some(RetryKind::Error(ErrorKind::TransientError))) - .unwrap(); - assert_eq!( - bucket.available(), - STANDARD_INITIAL_RETRY_TOKENS - STANDARD_TIMEOUT_ERROR_RETRY_COST as usize - ); - Box::new(token).forget(); - assert_eq!( - bucket.available(), - STANDARD_INITIAL_RETRY_TOKENS - STANDARD_TIMEOUT_ERROR_RETRY_COST as usize - ); - - bucket.refill(STANDARD_TIMEOUT_ERROR_RETRY_COST as usize); - assert_eq!(bucket.available(), STANDARD_INITIAL_RETRY_TOKENS); - } -} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 8246e2e9bd..9bbbf10b2f 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -139,7 +139,7 @@ async fn try_op( { let request_serializer = cfg.request_serializer(); let input = ctx.take_input().expect("input set at this point"); - let request = halt_on_err!([ctx] => request_serializer.serialize_input(input, cfg)); + let request = halt_on_err!([ctx] => request_serializer.serialize_input(input, cfg).map_err(OrchestratorError::other)); ctx.set_request(request); } @@ -171,10 +171,10 @@ async fn try_op( // No, this request shouldn't be sent Ok(ShouldAttempt::No) => { let err: BoxError = "the retry strategy indicates that an initial request shouldn't be made, but it didn't specify why".into(); - halt!([ctx] => err); + halt!([ctx] => OrchestratorError::other(err)); } // No, we shouldn't make a request because... - Err(err) => halt!([ctx] => err), + Err(err) => halt!([ctx] => OrchestratorError::other(err)), Ok(ShouldAttempt::YesAfterDelay(_)) => { unreachable!("Delaying the initial request is currently unsupported. If this feature is important to you, please file an issue in GitHub.") } @@ -183,7 +183,7 @@ async fn try_op( // Save a request checkpoint before we make the request. This will allow us to "rewind" // the request in the case of retry attempts. ctx.save_checkpoint(); - for i in 0usize.. { + for i in 1usize.. { debug!("beginning attempt #{i}"); // Break from the loop if we can't rewind the request's state. This will always succeed the // first time, but will fail on subsequent iterations if the request body wasn't retryable. @@ -201,19 +201,21 @@ async fn try_op( } .maybe_timeout_with_config(attempt_timeout_config) .await - .map_err(OrchestratorError::other); + .map_err(|err| OrchestratorError::timeout(err.into_source().unwrap())); // We continue when encountering a timeout error. The retry classifier will decide what to do with it. continue_on_err!([ctx] => maybe_timeout); let retry_strategy = cfg.retry_strategy(); + // If we got a retry strategy from the bag, ask it what to do. // If no strategy was set, we won't retry. - let should_attempt = halt_on_err!( - [ctx] => retry_strategy - .map(|rs| rs.should_attempt_retry(ctx, cfg)) - .unwrap_or(Ok(ShouldAttempt::No) - )); + let should_attempt = match retry_strategy { + Some(retry_strategy) => halt_on_err!( + [ctx] => retry_strategy.should_attempt_retry(ctx, cfg).map_err(OrchestratorError::other) + ), + None => ShouldAttempt::No, + }; match should_attempt { // Yes, let's retry the request ShouldAttempt::Yes => continue, @@ -241,11 +243,11 @@ async fn try_attempt( stop_point: StopPoint, ) { halt_on_err!([ctx] => interceptors.read_before_attempt(ctx, cfg)); - halt_on_err!([ctx] => orchestrate_endpoint(ctx, cfg)); + halt_on_err!([ctx] => orchestrate_endpoint(ctx, cfg).map_err(OrchestratorError::other)); halt_on_err!([ctx] => interceptors.modify_before_signing(ctx, cfg)); halt_on_err!([ctx] => interceptors.read_before_signing(ctx, cfg)); - halt_on_err!([ctx] => orchestrate_auth(ctx, cfg).await); + halt_on_err!([ctx] => orchestrate_auth(ctx, cfg).await.map_err(OrchestratorError::other)); halt_on_err!([ctx] => interceptors.read_after_signing(ctx, cfg)); halt_on_err!([ctx] => interceptors.modify_before_transmit(ctx, cfg)); @@ -261,7 +263,12 @@ async fn try_attempt( ctx.enter_transmit_phase(); let call_result = halt_on_err!([ctx] => { let request = ctx.take_request().expect("set during serialization"); - cfg.connection().call(request).await + cfg.connection().call(request).await.map_err(|err| { + match err.downcast() { + Ok(connector_error) => OrchestratorError::connector(*connector_error), + Err(box_err) => OrchestratorError::other(box_err) + } + }) }); ctx.set_response(call_result); ctx.enter_before_deserialization_phase(); @@ -279,7 +286,7 @@ async fn try_attempt( None => read_body(response) .instrument(debug_span!("read_body")) .await - .map_err(OrchestratorError::other) + .map_err(OrchestratorError::response) .and_then(|_| response_deserializer.deserialize_nonstreaming(response)), } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs index fd9754cff2..0139e9b31f 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; use std::borrow::Cow; @@ -76,17 +76,17 @@ where Ok(_) => return None, Err(err) => err, }; - // Check that the error is an operation error - let error = error.as_operation_error()?; - // Downcast the error - let error = error.downcast_ref::>()?; + match error { - SdkError::TimeoutError(_) => Some(RetryReason::Error(ErrorKind::TransientError)), - SdkError::ResponseError { .. } => Some(RetryReason::Error(ErrorKind::TransientError)), - SdkError::DispatchFailure(err) if (err.is_timeout() || err.is_io()) => { + OrchestratorError::Response { .. } | OrchestratorError::Timeout { .. } => { Some(RetryReason::Error(ErrorKind::TransientError)) } - SdkError::DispatchFailure(err) => err.is_other().map(RetryReason::Error), + OrchestratorError::Connector { err } if err.is_timeout() || err.is_io() => { + Some(RetryReason::Error(ErrorKind::TransientError)) + } + OrchestratorError::Connector { err } if err.is_other().is_some() => { + err.is_other().map(RetryReason::Error) + } _ => None, } } @@ -152,8 +152,6 @@ mod test { HttpStatusCodeClassifier, ModeledAsRetryableClassifier, }; use aws_smithy_http::body::SdkBody; - use aws_smithy_http::operation; - use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; @@ -242,11 +240,10 @@ mod test { #[test] fn classify_response_error() { let policy = SmithyErrorClassifier::::new(); - let test_response = http::Response::new("OK").map(SdkBody::from); - let err: SdkError = - SdkError::response_error(UnmodeledError, operation::Response::new(test_response)); let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); - ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new(err)))); + ctx.set_output_or_error(Err(OrchestratorError::response( + "I am a response error".into(), + ))); assert_eq!( policy.classify_retry(&ctx), Some(RetryReason::Error(ErrorKind::TransientError)), @@ -256,9 +253,10 @@ mod test { #[test] fn test_timeout_error() { let policy = SmithyErrorClassifier::::new(); - let err: SdkError = SdkError::timeout_error("blah"); let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); - ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new(err)))); + ctx.set_output_or_error(Err(OrchestratorError::timeout( + "I am a timeout error".into(), + ))); assert_eq!( policy.classify_retry(&ctx), Some(RetryReason::Error(ErrorKind::TransientError)), diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs index 3805834b12..c046a4d9f7 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs @@ -6,7 +6,7 @@ #[cfg(feature = "test-util")] mod fixed_delay; mod never; -mod standard; +pub(crate) mod standard; #[cfg(feature = "test-util")] pub use fixed_delay::FixedDelayRetryStrategy; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index 6893313163..468d0957b9 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -3,6 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::client::retries::strategy::standard::ReleaseResult::{ + APermitWasReleased, NoPermitWasReleased, +}; +use crate::client::runtime_plugin::standard_token_bucket::StandardTokenBucket; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors}; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; @@ -11,16 +15,21 @@ use aws_smithy_runtime_api::client::retries::{ }; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::retry::RetryConfig; +use std::sync::Mutex; use std::time::Duration; +use tokio::sync::OwnedSemaphorePermit; +// The initial attempt, plus three retries. const DEFAULT_MAX_ATTEMPTS: usize = 4; #[derive(Debug)] pub struct StandardRetryStrategy { - max_attempts: usize, + // Retry settings + base: fn() -> f64, initial_backoff: Duration, + max_attempts: usize, max_backoff: Duration, - base: fn() -> f64, + retry_permit: Mutex>, } impl StandardRetryStrategy { @@ -45,6 +54,36 @@ impl StandardRetryStrategy { self.initial_backoff = initial_backoff; self } + + pub fn with_max_backoff(mut self, max_backoff: Duration) -> Self { + self.max_backoff = max_backoff; + self + } + + fn release_retry_permit(&self) -> ReleaseResult { + let mut retry_permit = self.retry_permit.lock().unwrap(); + match retry_permit.take() { + Some(p) => { + drop(p); + APermitWasReleased + } + None => NoPermitWasReleased, + } + } + + fn set_retry_permit(&self, new_retry_permit: OwnedSemaphorePermit) { + let mut old_retry_permit = self.retry_permit.lock().unwrap(); + if let Some(p) = old_retry_permit.replace(new_retry_permit) { + // Whenever we set a new retry permit and it replaces the old one, we need to "forget" + // the old permit, removing it from the bucket forever. + p.forget() + } + } +} + +enum ReleaseResult { + APermitWasReleased, + NoPermitWasReleased, } impl Default for StandardRetryStrategy { @@ -55,13 +94,14 @@ impl Default for StandardRetryStrategy { // by default, use a random base for exponential backoff base: fastrand::f64, initial_backoff: Duration::from_secs(1), + retry_permit: Mutex::new(None), } } } impl RetryStrategy for StandardRetryStrategy { - // TODO(token-bucket) add support for optional cross-request token bucket fn should_attempt_initial_request(&self, _cfg: &ConfigBag) -> Result { + // The standard token bucket is only ever considered for retry requests. Ok(ShouldAttempt::Yes) } @@ -74,18 +114,31 @@ impl RetryStrategy for StandardRetryStrategy { let output_or_error = ctx.output_or_error().expect( "This must never be called without reaching the point where the result exists.", ); + let token_bucket = cfg.get::(); if output_or_error.is_ok() { tracing::debug!("request succeeded, no retry necessary"); + if let Some(tb) = token_bucket { + // If this retry strategy is holding any permits, release them back to the bucket. + if let NoPermitWasReleased = self.release_retry_permit() { + // In the event that there was no retry permit to release, we generate new + // permits from nothing. We do this to make up for permits we had to "forget". + // Otherwise, repeated retries would empty the bucket and nothing could fill it + // back up again. + tb.regenerate_a_token(); + } + } + return Ok(ShouldAttempt::No); } // Check if we're out of attempts - let request_attempts: &RequestAttempts = cfg - .get() - .expect("at least one request attempt is made before any retry is attempted"); - if request_attempts.attempts() >= self.max_attempts { + let request_attempts = cfg + .get::() + .expect("at least one request attempt is made before any retry is attempted") + .attempts(); + if request_attempts >= self.max_attempts { tracing::trace!( - attempts = request_attempts.attempts(), + attempts = request_attempts, max_attempts = self.max_attempts, "not retrying because we are out of attempts" ); @@ -95,9 +148,24 @@ impl RetryStrategy for StandardRetryStrategy { // Run the classifiers against the context to determine if we should retry let retry_classifiers = cfg.retry_classifiers(); let retry_reason = retry_classifiers.classify_retry(ctx); + + // Calculate the appropriate backoff time. let backoff = match retry_reason { Some(RetryReason::Explicit(dur)) => dur, - Some(RetryReason::Error(_)) => { + Some(RetryReason::Error(kind)) => { + // If a token bucket was set, and the RetryReason IS NOT explicit, attempt to acquire a retry permit. + if let Some(tb) = token_bucket { + match tb.acquire(&kind) { + Some(permit) => self.set_retry_permit(permit), + None => { + tracing::debug!( + "attempt #{request_attempts} failed with {kind:?}; However, no retry permits are available, so no retry will be attempted.", + ); + return Ok(ShouldAttempt::No); + } + } + }; + let backoff = calculate_exponential_backoff( // Generate a random base multiplier to create jitter (self.base)(), @@ -105,16 +173,14 @@ impl RetryStrategy for StandardRetryStrategy { self.initial_backoff.as_secs_f64(), // `self.local.attempts` tracks number of requests made including the initial request // The initial attempt shouldn't count towards backoff calculations so we subtract it - (request_attempts.attempts() - 1) as u32, + (request_attempts - 1) as u32, ); Duration::from_secs_f64(backoff).min(self.max_backoff) } - Some(_) => { - unreachable!("RetryReason is non-exhaustive. Therefore, we need to cover this unreachable case.") - } + Some(_) => unreachable!("RetryReason is non-exhaustive"), None => { tracing::trace!( - attempts = request_attempts.attempts(), + attempts = request_attempts, max_attempts = self.max_attempts, "encountered unretryable error" ); @@ -123,8 +189,7 @@ impl RetryStrategy for StandardRetryStrategy { }; tracing::debug!( - "attempt {} failed with {:?}; retrying after {:?}", - request_attempts.attempts(), + "attempt #{request_attempts} failed with {:?}; retrying after {:?}", retry_reason.expect("the match statement above ensures this is not None"), backoff ); @@ -139,16 +204,23 @@ fn calculate_exponential_backoff(base: f64, initial_backoff: f64, retry_attempts #[cfg(test)] mod tests { - use super::{ShouldAttempt, StandardRetryStrategy}; + use super::{calculate_exponential_backoff, ShouldAttempt, StandardRetryStrategy}; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, OrchestratorError}; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; - use aws_smithy_runtime_api::client::retries::{AlwaysRetry, RetryClassifiers, RetryStrategy}; + use aws_smithy_runtime_api::client::retries::{ + AlwaysRetry, ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, + }; use aws_smithy_types::config_bag::{ConfigBag, Layer}; - use aws_smithy_types::retry::ErrorKind; + use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; use aws_smithy_types::type_erasure::TypeErasedBox; + use std::fmt; + use std::sync::Mutex; use std::time::Duration; + #[cfg(feature = "test-util")] + use crate::client::runtime_plugin::standard_token_bucket::StandardTokenBucket; + #[test] fn no_retry_necessary_for_ok_result() { let cfg = ConfigBag::base(); @@ -221,4 +293,351 @@ mod tests { .expect("method is infallible for this use"); assert_eq!(ShouldAttempt::No, actual); } + + #[derive(Debug)] + struct ServerError; + impl fmt::Display for ServerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "OperationError") + } + } + + impl std::error::Error for ServerError {} + + impl ProvideErrorKind for ServerError { + fn retryable_error_kind(&self) -> Option { + Some(ErrorKind::ServerError) + } + + fn code(&self) -> Option<&str> { + None + } + } + + #[derive(Debug)] + struct PresetReasonRetryClassifier { + retry_reasons: Mutex>, + } + + #[cfg(feature = "test-util")] + impl PresetReasonRetryClassifier { + fn new(mut retry_reasons: Vec) -> Self { + // We'll pop the retry_reasons in reverse order so we reverse the list to fix that. + retry_reasons.reverse(); + Self { + retry_reasons: Mutex::new(retry_reasons), + } + } + } + + impl ClassifyRetry for PresetReasonRetryClassifier { + fn classify_retry(&self, ctx: &InterceptorContext) -> Option { + if ctx.output_or_error().map(|it| it.is_ok()).unwrap_or(false) { + return None; + } + + let mut retry_reasons = self.retry_reasons.lock().unwrap(); + if retry_reasons.len() == 1 { + Some(retry_reasons.first().unwrap().clone()) + } else { + retry_reasons.pop() + } + } + + fn name(&self) -> &'static str { + "Always returns a preset retry reason" + } + } + + #[cfg(feature = "test-util")] + fn setup_test(retry_reasons: Vec) -> (ConfigBag, InterceptorContext) { + let mut cfg = ConfigBag::base(); + cfg.interceptor_state().set_retry_classifiers( + RetryClassifiers::new() + .with_classifier(PresetReasonRetryClassifier::new(retry_reasons)), + ); + let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + // This type doesn't matter b/c the classifier will just return whatever we tell it to. + ctx.set_output_or_error(Err(OrchestratorError::other("doesn't matter"))); + + (cfg, ctx) + } + + #[cfg(feature = "test-util")] + #[test] + fn eventual_success() { + let (mut cfg, mut ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(5); + cfg.interceptor_state().put(StandardTokenBucket::default()); + let token_bucket = cfg.get::().unwrap().clone(); + + cfg.interceptor_state().put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 495); + + cfg.interceptor_state().put(RequestAttempts::new(2)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(2)); + assert_eq!(token_bucket.available_permits(), 490); + + ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); + + cfg.interceptor_state().put(RequestAttempts::new(3)); + let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + assert_eq!(token_bucket.available_permits(), 495); + } + + #[cfg(feature = "test-util")] + #[test] + fn no_more_attempts() { + let (mut cfg, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(3); + cfg.interceptor_state().put(StandardTokenBucket::default()); + let token_bucket = cfg.get::().unwrap().clone(); + + cfg.interceptor_state().put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 495); + + cfg.interceptor_state().put(RequestAttempts::new(2)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(2)); + assert_eq!(token_bucket.available_permits(), 490); + + cfg.interceptor_state().put(RequestAttempts::new(3)); + let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + assert_eq!(token_bucket.available_permits(), 490); + } + + #[cfg(feature = "test-util")] + #[test] + fn no_quota() { + let (mut cfg, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(5); + cfg.interceptor_state().put(StandardTokenBucket::new(5)); + let token_bucket = cfg.get::().unwrap().clone(); + + cfg.interceptor_state().put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 0); + + cfg.interceptor_state().put(RequestAttempts::new(2)); + let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + assert_eq!(token_bucket.available_permits(), 0); + } + + #[cfg(feature = "test-util")] + #[test] + fn quota_replenishes_on_success() { + let (mut cfg, mut ctx) = setup_test(vec![ + RetryReason::Error(ErrorKind::TransientError), + RetryReason::Explicit(Duration::from_secs(1)), + ]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(5); + cfg.interceptor_state().put(StandardTokenBucket::new(100)); + let token_bucket = cfg.get::().unwrap().clone(); + + cfg.interceptor_state().put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 90); + + cfg.interceptor_state().put(RequestAttempts::new(2)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 90); + + ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); + + cfg.interceptor_state().put(RequestAttempts::new(3)); + let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + + assert_eq!(token_bucket.available_permits(), 100); + } + + #[cfg(feature = "test-util")] + #[test] + fn quota_replenishes_on_first_try_success() { + const PERMIT_COUNT: usize = 20; + let (mut cfg, mut ctx) = setup_test(vec![RetryReason::Error(ErrorKind::TransientError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(usize::MAX); + cfg.interceptor_state() + .put(StandardTokenBucket::new(PERMIT_COUNT)); + let token_bucket = cfg.get::().unwrap().clone(); + + let mut attempt = 1; + + // Drain all available permits with failed attempts + while token_bucket.available_permits() > 0 { + // Draining should complete in 2 attempts + if attempt > 2 { + panic!("This test should have completed by now (drain)"); + } + + cfg.interceptor_state().put(RequestAttempts::new(attempt)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + assert!(matches!(should_retry, ShouldAttempt::YesAfterDelay(_))); + attempt += 1; + } + + // Forget the permit so that we can only refill by "success on first try". + let permit = strategy.retry_permit.lock().unwrap().take().unwrap(); + permit.forget(); + + ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); + + // Replenish permits until we get back to `PERMIT_COUNT` + while token_bucket.available_permits() < PERMIT_COUNT { + if attempt > 23 { + panic!("This test should have completed by now (fillup)"); + } + + cfg.interceptor_state().put(RequestAttempts::new(attempt)); + let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + attempt += 1; + } + + assert_eq!(attempt, 23); + assert_eq!(token_bucket.available_permits(), PERMIT_COUNT); + } + + #[cfg(feature = "test-util")] + #[test] + fn backoff_timing() { + let (mut cfg, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(5); + cfg.interceptor_state().put(StandardTokenBucket::default()); + let token_bucket = cfg.get::().unwrap().clone(); + + cfg.interceptor_state().put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 495); + + cfg.interceptor_state().put(RequestAttempts::new(2)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(2)); + assert_eq!(token_bucket.available_permits(), 490); + + cfg.interceptor_state().put(RequestAttempts::new(3)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(4)); + assert_eq!(token_bucket.available_permits(), 485); + + cfg.interceptor_state().put(RequestAttempts::new(4)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(8)); + assert_eq!(token_bucket.available_permits(), 480); + + cfg.interceptor_state().put(RequestAttempts::new(5)); + let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + assert_eq!(token_bucket.available_permits(), 480); + } + + #[cfg(feature = "test-util")] + #[test] + fn max_backoff_time() { + let (mut cfg, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let strategy = StandardRetryStrategy::default() + .with_base(|| 1.0) + .with_max_attempts(5) + .with_initial_backoff(Duration::from_secs(1)) + .with_max_backoff(Duration::from_secs(3)); + cfg.interceptor_state().put(StandardTokenBucket::default()); + let token_bucket = cfg.get::().unwrap().clone(); + + cfg.interceptor_state().put(RequestAttempts::new(1)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(1)); + assert_eq!(token_bucket.available_permits(), 495); + + cfg.interceptor_state().put(RequestAttempts::new(2)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(2)); + assert_eq!(token_bucket.available_permits(), 490); + + cfg.interceptor_state().put(RequestAttempts::new(3)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(3)); + assert_eq!(token_bucket.available_permits(), 485); + + cfg.interceptor_state().put(RequestAttempts::new(4)); + let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let dur = should_retry.expect_delay(); + assert_eq!(dur, Duration::from_secs(3)); + assert_eq!(token_bucket.available_permits(), 480); + + cfg.interceptor_state().put(RequestAttempts::new(5)); + let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + assert_eq!(no_retry, ShouldAttempt::No); + assert_eq!(token_bucket.available_permits(), 480); + } + + #[test] + fn calculate_exponential_backoff_where_initial_backoff_is_one() { + let initial_backoff = 1.0; + + for (attempt, expected_backoff) in [initial_backoff, 2.0, 4.0].into_iter().enumerate() { + let actual_backoff = + calculate_exponential_backoff(1.0, initial_backoff, attempt as u32); + assert_eq!(expected_backoff, actual_backoff); + } + } + + #[test] + fn calculate_exponential_backoff_where_initial_backoff_is_greater_than_one() { + let initial_backoff = 3.0; + + for (attempt, expected_backoff) in [initial_backoff, 6.0, 12.0].into_iter().enumerate() { + let actual_backoff = + calculate_exponential_backoff(1.0, initial_backoff, attempt as u32); + assert_eq!(expected_backoff, actual_backoff); + } + } + + #[test] + fn calculate_exponential_backoff_where_initial_backoff_is_less_than_one() { + let initial_backoff = 0.03; + + for (attempt, expected_backoff) in [initial_backoff, 0.06, 0.12].into_iter().enumerate() { + let actual_backoff = + calculate_exponential_backoff(1.0, initial_backoff, attempt as u32); + assert_eq!(expected_backoff, actual_backoff); + } + } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs index 784de5cd71..a127873bdb 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs @@ -5,3 +5,5 @@ #[cfg(feature = "anonymous-auth")] pub mod anonymous_auth; + +pub mod standard_token_bucket; diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/standard_token_bucket.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/standard_token_bucket.rs new file mode 100644 index 0000000000..548d63307f --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/standard_token_bucket.rs @@ -0,0 +1,100 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::config_bag::{FrozenLayer, Layer, Storable, StoreReplace}; +use aws_smithy_types::retry::ErrorKind; +use std::sync::Arc; +use tokio::sync::{OwnedSemaphorePermit, Semaphore}; +use tracing::trace; + +/// A [RuntimePlugin] to provide a standard token bucket, usable by the +/// [`StandardRetryStrategy`](crate::client::retries::strategy::standard::StandardRetryStrategy). +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct StandardTokenBucketRuntimePlugin { + token_bucket: StandardTokenBucket, +} + +impl StandardTokenBucketRuntimePlugin { + pub fn new(initial_tokens: usize) -> Self { + Self { + token_bucket: StandardTokenBucket::new(initial_tokens), + } + } +} + +impl RuntimePlugin for StandardTokenBucketRuntimePlugin { + fn config(&self) -> Option { + let mut cfg = Layer::new("standard token bucket"); + cfg.store_put(self.token_bucket.clone()); + + Some(cfg.freeze()) + } +} + +const DEFAULT_CAPACITY: usize = 500; +const RETRY_COST: u32 = 5; +const RETRY_TIMEOUT_COST: u32 = RETRY_COST * 2; +const PERMIT_REGENERATION_AMOUNT: usize = 1; + +#[derive(Clone, Debug)] +pub(crate) struct StandardTokenBucket { + semaphore: Arc, + max_permits: usize, + timeout_retry_cost: u32, + retry_cost: u32, +} + +impl Storable for StandardTokenBucket { + type Storer = StoreReplace; +} + +impl Default for StandardTokenBucket { + fn default() -> Self { + Self { + semaphore: Arc::new(Semaphore::new(DEFAULT_CAPACITY)), + max_permits: DEFAULT_CAPACITY, + timeout_retry_cost: RETRY_TIMEOUT_COST, + retry_cost: RETRY_COST, + } + } +} + +impl StandardTokenBucket { + pub(crate) fn new(initial_quota: usize) -> Self { + Self { + semaphore: Arc::new(Semaphore::new(initial_quota)), + max_permits: initial_quota, + retry_cost: RETRY_COST, + timeout_retry_cost: RETRY_TIMEOUT_COST, + } + } + + pub(crate) fn acquire(&self, err: &ErrorKind) -> Option { + let retry_cost = if err == &ErrorKind::TransientError { + self.timeout_retry_cost + } else { + self.retry_cost + }; + + self.semaphore + .clone() + .try_acquire_many_owned(retry_cost) + .ok() + } + + pub(crate) fn regenerate_a_token(&self) { + if self.semaphore.available_permits() < (self.max_permits) { + trace!("adding {PERMIT_REGENERATION_AMOUNT} back into the bucket"); + self.semaphore.add_permits(PERMIT_REGENERATION_AMOUNT) + } + } + + #[cfg(all(test, feature = "test-util"))] + pub(crate) fn available_permits(&self) -> usize { + self.semaphore.available_permits() + } +} diff --git a/rust-runtime/aws-smithy-types/src/retry.rs b/rust-runtime/aws-smithy-types/src/retry.rs index b96ababf06..9602d6135b 100644 --- a/rust-runtime/aws-smithy-types/src/retry.rs +++ b/rust-runtime/aws-smithy-types/src/retry.rs @@ -143,6 +143,7 @@ pub struct RetryConfigBuilder { mode: Option, max_attempts: Option, initial_backoff: Option, + max_backoff: Option, reconnect_mode: Option, } @@ -212,6 +213,18 @@ impl RetryConfigBuilder { self } + /// Set the max_backoff duration. This duration should be non-zero. + pub fn set_max_backoff(&mut self, max_backoff: Option) -> &mut Self { + self.max_backoff = max_backoff; + self + } + + /// Set the max_backoff duration. This duration should be non-zero. + pub fn max_backoff(mut self, max_backoff: Duration) -> Self { + self.set_max_backoff(Some(max_backoff)); + self + } + /// Merge two builders together. Values from `other` will only be used as a fallback for values /// from `self` Useful for merging configs from different sources together when you want to /// handle "precedence" per value instead of at the config level @@ -233,6 +246,7 @@ impl RetryConfigBuilder { mode: self.mode.or(other.mode), max_attempts: self.max_attempts.or(other.max_attempts), initial_backoff: self.initial_backoff.or(other.initial_backoff), + max_backoff: self.max_backoff.or(other.max_backoff), reconnect_mode: self.reconnect_mode.or(other.reconnect_mode), } } @@ -248,6 +262,7 @@ impl RetryConfigBuilder { reconnect_mode: self .reconnect_mode .unwrap_or(ReconnectMode::ReconnectOnTransientError), + max_backoff: self.max_backoff.unwrap_or_else(|| Duration::from_secs(20)), } } } @@ -259,6 +274,7 @@ pub struct RetryConfig { mode: RetryMode, max_attempts: u32, initial_backoff: Duration, + max_backoff: Duration, reconnect_mode: ReconnectMode, } @@ -286,6 +302,7 @@ impl RetryConfig { max_attempts: 3, initial_backoff: Duration::from_secs(1), reconnect_mode: ReconnectMode::ReconnectOnTransientError, + max_backoff: Duration::from_secs(20), } } From 988eb617fb820fea81be702b5753ff91cb3bb0c2 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Wed, 14 Jun 2023 15:17:35 +0100 Subject: [PATCH 152/253] Add `Scoped` `Plugin` (#2759) ## Motivation and Context The [`FilterByOperationName`](https://docs.rs/aws-smithy-http-server/0.55.4/aws_smithy_http_server/plugin/struct.FilterByOperationName.html) allows the customer to filter application of a plugin. However this is a _runtime_ filter. A faster and type safe alternative would be a nice option. ## Description Add `Scoped` `Plugin` and `scope` macro. --------- Co-authored-by: david-perez --- CHANGELOG.next.toml | 39 +++- .../server/smithy/ServerCodegenVisitor.kt | 3 + .../smithy/generators/ScopeMacroGenerator.kt | 182 +++++++++++++++++ design/src/server/middleware.md | 24 ++- examples/pokemon-service/src/main.rs | 17 +- .../src/plugin/filter.rs | 6 + .../aws-smithy-http-server/src/plugin/mod.rs | 3 + .../src/plugin/scoped.rs | 190 ++++++++++++++++++ 8 files changed, 453 insertions(+), 11 deletions(-) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt create mode 100644 rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 34fc2b7659..b7349422a4 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -183,6 +183,12 @@ author = "ysaito1001" [[smithy-rs]] message = """ +The middleware system has been reworked as we push for a unified, simple, and consistent API. The following changes have been made in service of this goal: + +- The `Plugin` trait has been simplified. +- The `Operation` structure has been removed. +- A `Scoped` `Plugin` has been added. + The `Plugin` trait has now been simplified and the `Operation` struct has been removed. ## Simplication of the `Plugin` trait @@ -317,7 +323,7 @@ let plugin = /* some plugin */; let layer = LayerPlugin::new::(plugin); ``` -## Remove `Operation` +## Removal of `Operation` The `aws_smithy_http_server::operation::Operation` structure has now been removed. Previously, there existed a `{operation_name}_operation` setter on the service builder, which accepted an `Operation`. This allowed users to @@ -356,7 +362,7 @@ let app = PokemonService::builder_without_plugins() .unwrap(); ``` -Applying a `tower::Layer` to a _single_ operation is now done through the `Plugin` API: +Applying a `tower::Layer` to a _subset_ of operations is should now be done through the `Plugin` API via `filter_by_operation_id` ```rust use aws_smithy_http_server::plugin::{PluginLayer, filter_by_operation_name, IdentityPlugin}; @@ -371,7 +377,34 @@ let app = PokemonService::builder_with_plugins(scoped_plugin, IdentityPlugin) .unwrap(); ``` +or the new `Scoped` `Plugin` introduced below. + +# Addition of `Scoped` + +Currently, users can selectively apply a `Plugin` via the `filter_by_operation_id` function + +```rust +use aws_smithy_http_server::plugin::filter_by_operation_id; +// Only apply `plugin` to `CheckHealth` and `GetStorage` operation +let filtered_plugin = filter_by_operation_id(plugin, |name| name == CheckHealth::ID || name == GetStorage::ID); +``` + +In addition to this, we now provide `Scoped`, which selectively applies a `Plugin` at _compiletime_. Users should prefer this to `filter_by_operation_id` when applicable. + +```rust +use aws_smithy_http_server::plugin::Scoped; +use pokemon_service_server_sdk::scoped; + +scope! { + /// Includes only the `CheckHealth` and `GetStorage` operation. + struct SomeScope { + includes: [CheckHealth, GetStorage] + } +} +let scoped_plugin = Scoped::new::(plugin); +``` + """ -references = ["smithy-rs#2740"] +references = ["smithy-rs#2740", "smithy-rs#2759"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "hlbarber" diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index e6ef816331..fdf686e38e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -63,6 +63,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.generators.ConstrainedT import software.amazon.smithy.rust.codegen.server.smithy.generators.MapConstraintViolationGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedCollectionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.PubCrateConstrainedMapGenerator +import software.amazon.smithy.rust.codegen.server.smithy.generators.ScopeMacroGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerBuilderGeneratorWithoutPublicConstrainedTypes import software.amazon.smithy.rust.codegen.server.smithy.generators.ServerEnumGenerator @@ -610,6 +611,8 @@ open class ServerCodegenVisitor( codegenContext, serverProtocol, ).render(this) + + ScopeMacroGenerator(codegenContext).render(this) } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt new file mode 100644 index 0000000000..2b016fd6cf --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt @@ -0,0 +1,182 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy.generators + +import software.amazon.smithy.model.knowledge.TopDownIndex +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +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.util.toPascalCase +import software.amazon.smithy.rust.codegen.core.util.toSnakeCase +import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency +import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext + +class ScopeMacroGenerator( + private val codegenContext: ServerCodegenContext, +) { + private val runtimeConfig = codegenContext.runtimeConfig + private val codegenScope = + arrayOf( + "SmithyHttpServer" to ServerCargoDependency.smithyHttpServer(runtimeConfig).toType(), + ) + + /** Calculate all `operationShape`s contained within the `ServiceShape`. */ + private val index = TopDownIndex.of(codegenContext.model) + private val operations = index.getContainedOperations(codegenContext.serviceShape).toSortedSet(compareBy { it.id }) + + private fun macro(): Writable = writable { + val firstOperationName = codegenContext.symbolProvider.toSymbol(operations.first()).name.toPascalCase() + val operationNames = operations.joinToString(" ") { + codegenContext.symbolProvider.toSymbol(it).name.toPascalCase() + } + + // When writing `macro_rules!` we add whitespace between `$` and the arguments to avoid Kotlin templating. + + // To acheive the desired API we need to calculate the set theoretic complement `B \ A`. + // The macro below, for rules prefixed with `@`, encodes a state machine which performs this. + // The initial state is `(A) () (B)`, where `A` and `B` are lists of elements of `A` and `B`. + // The rules, in order: + // - Terminate on pattern `() (t0, t1, ...) (b0, b1, ...)`, the complement has been calculated as + // `{ t0, t1, ..., b0, b1, ...}`. + // - Send pattern `(x, a0, a1, ...) (t0, t1, ...) (x, b0, b1, ...)` to + // `(a0, a1, ...) (t0, t1, ...) (b0, b1, ...)`, eliminating a matching `x` from `A` and `B`. + // - Send pattern `(a0, a1, ...) (t0, t1, ...) ()` to `(a0, a1, ...) () (t0, t1, ...)`, restarting the search. + // - Send pattern `(a0, a1, ...) (t0, t1, ...) (b0, b1, ...)` to `(a0, a1, ...) (b0, t0, t1, ...) (b1, ...)`, + // iterating through the `B`. + val operationBranches = operations + .map { codegenContext.symbolProvider.toSymbol(it).name.toPascalCase() }.joinToString("") { + """ + // $it match found, pop from both `member` and `not_member` + (@ $ name: ident, $ contains: ident ($it $($ member: ident)*) ($($ temp: ident)*) ($it $($ not_member: ident)*)) => { + scope! { @ $ name, $ contains ($($ member)*) ($($ temp)*) ($($ not_member)*) } + }; + // $it match not found, pop from `not_member` into `temp` stack + (@ $ name: ident, $ contains: ident ($it $($ member: ident)*) ($($ temp: ident)*) ($ other: ident $($ not_member: ident)*)) => { + scope! { @ $ name, $ contains ($it $($ member)*) ($ other $($ temp)*) ($($ not_member)*) } + }; + """ + } + val crateName = codegenContext.moduleName.toSnakeCase() + + // If we have a second operation we can perform further checks + val otherOperationName: String? = operations.toList().getOrNull(1)?.let { + codegenContext.symbolProvider.toSymbol(it).name + } + val furtherTests = if (otherOperationName != null) { + writable { + rustTemplate( + """ + /// ## let a = Plugin::<(), $otherOperationName, u64>::apply(&scoped_a, 6); + /// ## let b = Plugin::<(), $otherOperationName, u64>::apply(&scoped_b, 6); + /// ## assert_eq!(a, 6_u64); + /// ## assert_eq!(b, 3_u32); + """, + ) + } + } else { + writable {} + } + + rustTemplate( + """ + /// A macro to help with scoping [plugins](#{SmithyHttpServer}::plugin) to a subset of all operations. + /// + /// In contrast to [`aws_smithy_http_server::scope`](#{SmithyHttpServer}::scope), this macro has knowledge + /// of the service and any operations _not_ specified will be placed in the opposing group. + /// + /// ## Example + /// + /// ```rust + /// scope! { + /// /// Includes [`$firstOperationName`], excluding all other operations. + /// struct ScopeA { + /// includes: [$firstOperationName] + /// } + /// } + /// + /// scope! { + /// /// Excludes [`$firstOperationName`], excluding all other operations. + /// struct ScopeB { + /// excludes: [$firstOperationName] + /// } + /// } + /// + /// ## use #{SmithyHttpServer}::plugin::{Plugin, Scoped}; + /// ## use $crateName::scope; + /// ## struct MockPlugin; + /// ## impl Plugin for MockPlugin { type Service = u32; fn apply(&self, svc: S) -> u32 { 3 } } + /// ## let scoped_a = Scoped::new::(MockPlugin); + /// ## let scoped_b = Scoped::new::(MockPlugin); + /// ## let a = Plugin::<(), $crateName::operation_shape::$firstOperationName, u64>::apply(&scoped_a, 6); + /// ## let b = Plugin::<(), $crateName::operation_shape::$firstOperationName, u64>::apply(&scoped_b, 6); + /// ## assert_eq!(a, 3_u32); + /// ## assert_eq!(b, 6_u64); + /// ``` + ##[macro_export] + macro_rules! scope { + // Completed, render impls + (@ $ name: ident, $ contains: ident () ($($ temp: ident)*) ($($ not_member: ident)*)) => { + $( + impl #{SmithyHttpServer}::plugin::scoped::Membership<$ temp> for $ name { + type Contains = #{SmithyHttpServer}::plugin::scoped::$ contains; + } + )* + $( + impl #{SmithyHttpServer}::plugin::scoped::Membership<$ not_member> for $ name { + type Contains = #{SmithyHttpServer}::plugin::scoped::$ contains; + } + )* + }; + // All `not_member`s exhausted, move `temp` into `not_member` + (@ $ name: ident, $ contains: ident ($($ member: ident)*) ($($ temp: ident)*) ()) => { + scope! { @ $ name, $ contains ($($ member)*) () ($($ temp)*) } + }; + $operationBranches + ( + $(##[$ attrs:meta])* + $ vis:vis struct $ name:ident { + includes: [$($ include:ident),*] + } + ) => { + use $ crate::operation_shape::*; + #{SmithyHttpServer}::scope! { + $(##[$ attrs])* + $ vis struct $ name { + includes: [$($ include),*], + excludes: [] + } + } + scope! { @ $ name, False ($($ include)*) () ($operationNames) } + }; + ( + $(##[$ attrs:meta])* + $ vis:vis struct $ name:ident { + excludes: [$($ exclude:ident),*] + } + ) => { + use $ crate::operation_shape::*; + + #{SmithyHttpServer}::scope! { + $(##[$ attrs])* + $ vis struct $ name { + includes: [], + excludes: [$($ exclude),*] + } + } + scope! { @ $ name, True ($($ exclude)*) () ($operationNames) } + }; + } + """, + *codegenScope, + "FurtherTests" to furtherTests, + ) + } + + fun render(writer: RustWriter) { + macro()(writer) + } +} diff --git a/design/src/server/middleware.md b/design/src/server/middleware.md index a04ae3abec..66a373a4f6 100644 --- a/design/src/server/middleware.md +++ b/design/src/server/middleware.md @@ -208,15 +208,24 @@ A "HTTP layer" can be applied to specific operations. # extern crate aws_smithy_http_server; # use tower::{util::service_fn, Layer}; # use std::time::Duration; -# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, PokemonService, input::*, output::*, error::*}; +# use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, input::*, output::*, error::*}; # use aws_smithy_http_server::{operation::OperationShapeExt, plugin::*, operation::*}; # let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; # struct LoggingLayer; # impl LoggingLayer { pub fn new() -> Self { Self } } # impl Layer for LoggingLayer { type Service = S; fn layer(&self, svc: S) -> Self::Service { svc } } +use pokemon_service_server_sdk::{PokemonService, scope}; + +scope! { + /// Only log on `GetPokemonSpecies` and `GetStorage` + struct LoggingScope { + includes: [GetPokemonSpecies, GetStorage] + } +} + // Construct `LoggingLayer`. let logging_plugin = LayerPlugin(LoggingLayer::new()); -let logging_plugin = filter_by_operation_id(logging_plugin, |name| name == GetPokemonSpecies::ID); +let logging_plugin = Scoped::new::(logging_plugin); let http_plugins = PluginPipeline::new().push(logging_plugin); let app /* : PokemonService> */ = PokemonService::builder_with_plugins(http_plugins, IdentityPlugin) @@ -244,11 +253,18 @@ A "model layer" can be applied to specific operations. # struct BufferLayer; # impl BufferLayer { pub fn new(size: usize) -> Self { Self } } # impl Layer for BufferLayer { type Service = S; fn layer(&self, svc: S) -> Self::Service { svc } } -use pokemon_service_server_sdk::PokemonService; +use pokemon_service_server_sdk::{PokemonService, scope}; + +scope! { + /// Only buffer on `GetPokemonSpecies` and `GetStorage` + struct BufferScope { + includes: [GetPokemonSpecies, GetStorage] + } +} // Construct `BufferLayer`. let buffer_plugin = LayerPlugin(BufferLayer::new(3)); -let buffer_plugin = filter_by_operation_id(buffer_plugin, |name| name != GetPokemonSpecies::ID); +let buffer_plugin = Scoped::new::(buffer_plugin); let model_plugins = PluginPipeline::new().push(buffer_plugin); let app /* : PokemonService> */ = PokemonService::builder_with_plugins(IdentityPlugin, model_plugins) diff --git a/examples/pokemon-service/src/main.rs b/examples/pokemon-service/src/main.rs index 76a8b8e594..b3aa767f50 100644 --- a/examples/pokemon-service/src/main.rs +++ b/examples/pokemon-service/src/main.rs @@ -10,7 +10,7 @@ use std::{net::SocketAddr, sync::Arc}; use aws_smithy_http_server::{ extension::OperationExtensionExt, instrumentation::InstrumentExt, - plugin::{alb_health_check::AlbHealthCheckLayer, IdentityPlugin, PluginPipeline}, + plugin::{alb_health_check::AlbHealthCheckLayer, IdentityPlugin, PluginPipeline, Scoped}, request::request_id::ServerRequestIdProviderLayer, AddExtensionLayer, }; @@ -26,7 +26,7 @@ use pokemon_service_common::{ capture_pokemon, check_health, get_pokemon_species, get_server_statistics, setup_tracing, stream_pokemon_radio, State, }; -use pokemon_service_server_sdk::PokemonService; +use pokemon_service_server_sdk::{scope, PokemonService}; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -44,9 +44,18 @@ pub async fn main() { let args = Args::parse(); setup_tracing(); + scope! { + /// A scope containing `GetPokemonSpecies` and `GetStorage` + struct PrintScope { + includes: [GetPokemonSpecies, GetStorage] + } + } + // Scope the `PrintPlugin`, defined in `plugin.rs`, to `PrintScope` + let print_plugin = Scoped::new::(PluginPipeline::new().print()); + let plugins = PluginPipeline::new() - // Apply the `PrintPlugin` defined in `plugin.rs` - .print() + // Apply the scoped `PrintPlugin` + .push(print_plugin) // Apply the `OperationExtensionPlugin` defined in `aws_smithy_http_server::extension`. This allows other // plugins or tests to access a `aws_smithy_http_server::extension::OperationExtension` from // `Response::extensions`, or infer routing failure when it's missing. diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs index 46608483c0..402fb07b7e 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs @@ -13,6 +13,9 @@ use super::Plugin; /// Filters the application of an inner [`Plugin`] using a predicate over the /// [`OperationShape::ID`](crate::operation::OperationShape). /// +/// This contrasts with [`Scoped`](crate::plugin::Scoped) which can be used to selectively apply a [`Plugin`] to a +/// subset of operations at _compile time_. +/// /// See [`filter_by_operation_id`] for more details. pub struct FilterByOperationId { inner: Inner, @@ -22,6 +25,9 @@ pub struct FilterByOperationId { /// Filters the application of an inner [`Plugin`] using a predicate over the /// [`OperationShape::ID`](crate::operation::OperationShape). /// +/// Users should prefer [`Scoped`](crate::plugin::Scoped) and fallback to [`filter_by_operation_id`] in cases where +/// [`Plugin`] application must be decided at runtime. +/// /// # Example /// /// ```rust diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs index cda732e699..c6cb440d44 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -111,6 +111,8 @@ mod filter; mod identity; mod layer; mod pipeline; +#[doc(hidden)] +pub mod scoped; mod stack; pub use closure::{plugin_from_operation_id_fn, OperationIdFn}; @@ -119,6 +121,7 @@ pub use filter::{filter_by_operation_id, FilterByOperationId}; pub use identity::IdentityPlugin; pub use layer::{LayerPlugin, PluginLayer}; pub use pipeline::PluginPipeline; +pub use scoped::Scoped; pub use stack::PluginStack; /// A mapping from one [`Service`](tower::Service) to another. This should be viewed as a diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs b/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs new file mode 100644 index 0000000000..700d174359 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs @@ -0,0 +1,190 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::marker::PhantomData; + +use super::Plugin; + +/// Marker struct for `true`. +/// +/// Implements [`ConditionalApply`] which applies the [`Plugin`]. +pub struct True; + +/// Marker struct for `false`. +/// +/// Implements [`ConditionalApply`] which does nothing. +pub struct False; + +/// Conditionally applies a [`Plugin`] `Pl` to some service `S`. +/// +/// See [`True`] and [`False`]. +pub trait ConditionalApply { + type Service; + + fn apply(plugin: &Pl, svc: S) -> Self::Service; +} + +impl ConditionalApply for True +where + Pl: Plugin, +{ + type Service = Pl::Service; + + fn apply(plugin: &Pl, svc: S) -> Self::Service { + plugin.apply(svc) + } +} + +impl ConditionalApply for False { + type Service = S; + + fn apply(_plugin: &Pl, svc: S) -> Self::Service { + svc + } +} + +/// A [`Plugin`] which scopes the application of an inner [`Plugin`]. +/// +/// In cases where operation selection must be performed at runtime [`filter_by_operation_id`](crate::plugin::filter_by_operation_id) +/// can be used. +/// +/// Operations within the scope will have the inner [`Plugin`] applied. +/// +/// # Example +/// +/// ```rust +/// # use aws_smithy_http_server::{scope, plugin::Scoped}; +/// # struct OperationA; struct OperationB; struct OperationC; +/// # let plugin = (); +/// +/// // Define a scope over a service with 3 operations +/// scope! { +/// struct OnlyAB { +/// includes: [OperationA, OperationB], +/// excludes: [OperationC] +/// } +/// } +/// +/// // Create a scoped plugin +/// let scoped_plugin = Scoped::new::(plugin); +/// ``` +pub struct Scoped { + scope: PhantomData, + plugin: Pl, +} + +impl Scoped<(), Pl> { + /// Creates a new [`Scoped`] from a `Scope` and [`Plugin`]. + pub fn new(plugin: Pl) -> Scoped { + Scoped { + scope: PhantomData, + plugin, + } + } +} + +/// A trait marking which operations are in scope via the associated type [`Membership::Contains`]. +pub trait Membership { + type Contains; +} + +impl Plugin for Scoped +where + Scope: Membership, + Scope::Contains: ConditionalApply, + Pl: Plugin, +{ + type Service = >::Service; + + fn apply(&self, svc: S) -> Self::Service { + >::apply(&self.plugin, svc) + } +} + +/// A macro to help with scoping [plugins](crate::plugin) to a subset of all operations. +/// +/// The scope must partition _all_ operations, that is, each and every operation must be included or excluded, but not +/// both. +/// +/// The generated server SDK exports a similar `scope` macro which is aware of a services operations and can complete +/// underspecified scopes automatically. +/// +/// # Example +/// +/// For a service with three operations: `OperationA`, `OperationB`, `OperationC`. +/// +/// ```rust +/// # use aws_smithy_http_server::scope; +/// # struct OperationA; struct OperationB; struct OperationC; +/// scope! { +/// struct OnlyAB { +/// includes: [OperationA, OperationB], +/// excludes: [OperationC] +/// } +/// } +/// ``` +#[macro_export] +macro_rules! scope { + ( + $(#[$attrs:meta])* + $vis:vis struct $name:ident { + includes: [$($include:ty),*], + excludes: [$($exclude:ty),*] + } + ) => { + $(#[$attrs])* + $vis struct $name; + + $( + impl $crate::plugin::scoped::Membership<$include> for $name { + type Contains = $crate::plugin::scoped::True; + } + )* + $( + impl $crate::plugin::scoped::Membership<$exclude> for $name { + type Contains = $crate::plugin::scoped::False; + } + )* + }; +} + +#[cfg(test)] +mod tests { + use crate::plugin::Plugin; + + use super::Scoped; + + struct OperationA; + struct OperationB; + + scope! { + /// Includes A, not B. + pub struct AuthScope { + includes: [OperationA], + excludes: [OperationB] + } + } + + struct MockPlugin; + + impl Plugin for MockPlugin { + type Service = String; + + fn apply(&self, svc: u32) -> Self::Service { + svc.to_string() + } + } + + #[test] + fn scope() { + let plugin = MockPlugin; + let scoped_plugin = Scoped::new::(plugin); + + let out: String = Plugin::<(), OperationA, _>::apply(&scoped_plugin, 3_u32); + assert_eq!(out, "3".to_string()); + let out: u32 = Plugin::<(), OperationB, _>::apply(&scoped_plugin, 3_u32); + assert_eq!(out, 3); + } +} From 45f27111f88ca6e3dbeba7c72379732d0195643f Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 14 Jun 2023 09:32:07 -0700 Subject: [PATCH 153/253] Fix protocol tests against the orchestrator (#2768) This PR fixes the protocol tests in orchestrator mode, and adds `--all-targets` to the orchestrator CI checks. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Zelda Hessler --- .../rustsdk/IntegrationTestDependencies.kt | 2 ++ .../protocol/ProtocolTestGenerator.kt | 22 ++++++++++++++++--- .../check-aws-sdk-orchestrator-impl | 4 ++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt index 744b79c441..cb9e0b9ecb 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Compani import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.FuturesUtil import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.HdrHistogram import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.Hound +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.HttpBody import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.SerdeJson import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.Smol import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.TempFile @@ -122,6 +123,7 @@ class S3TestDependencies(private val codegenContext: ClientCodegenContext) : Lib addDependency(BytesUtils.toDevDependency()) addDependency(FastRand.toDevDependency()) addDependency(HdrHistogram) + addDependency(HttpBody.toDevDependency()) addDependency(Smol) addDependency(TempFile) addDependency(TracingAppender) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index 5ce3c2359a..4ed9e678a5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -321,7 +321,7 @@ class DefaultProtocolTestGenerator( writeInline("let expected_output =") instantiator.render(this, expectedShape, testCase.params) write(";") - write("let http_response = #T::new()", RuntimeType.HttpResponseBuilder) + write("let mut http_response = #T::new()", RuntimeType.HttpResponseBuilder) testCase.headers.forEach { (key, value) -> writeWithNoFormatting(".header(${key.dq()}, ${value.dq()})") } @@ -360,7 +360,9 @@ class DefaultProtocolTestGenerator( let de = #{OperationDeserializer}; let parsed = de.deserialize_streaming(&mut http_response); let parsed = parsed.unwrap_or_else(|| { - let http_response = http_response.map(|body|#{copy_from_slice}(body.bytes().unwrap())); + let http_response = http_response.map(|body| { + #{SdkBody}::from(#{copy_from_slice}(body.bytes().unwrap())) + }); de.deserialize_nonstreaming(&http_response) }); """, @@ -369,12 +371,19 @@ class DefaultProtocolTestGenerator( "copy_from_slice" to RuntimeType.Bytes.resolve("copy_from_slice"), "ResponseDeserializer" to CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig).toType() .resolve("client::orchestrator::ResponseDeserializer"), + "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), ) } if (expectedShape.hasTrait()) { val errorSymbol = codegenContext.symbolProvider.symbolForOperationError(operationShape) val errorVariant = codegenContext.symbolProvider.toSymbol(expectedShape).name rust("""let parsed = parsed.expect_err("should be error response");""") + if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + rustTemplate( + """let parsed: &#{Error} = parsed.as_operation_error().expect("operation error").downcast_ref().unwrap();""", + "Error" to codegenContext.symbolProvider.symbolForOperationError(operationShape), + ) + } rustBlock("if let #T::$errorVariant(parsed) = parsed", errorSymbol) { compareMembers(expectedShape) } @@ -382,7 +391,14 @@ class DefaultProtocolTestGenerator( rust("panic!(\"wrong variant: Got: {:?}. Expected: {:?}\", parsed, expected_output);") } } else { - rust("let parsed = parsed.unwrap();") + if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + rust("let parsed = parsed.unwrap();") + } else { + rustTemplate( + """let parsed: #{Output} = *parsed.expect("should be successful response").downcast().unwrap();""", + "Output" to codegenContext.symbolProvider.toSymbol(expectedShape), + ) + } compareMembers(outputShape) } } diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index 7bb15ba663..a02eb14ad3 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -42,14 +42,14 @@ cd aws/sdk/build/aws-sdk/sdk for service in "${services_that_compile[@]}"; do pushd "${service}" echo -e "${C_YELLOW}# Running 'cargo check --all-features' on '${service}'${C_RESET}" - RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo check --all-features + RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo check --all-features --all-targets popd done for service in "${services_that_pass_tests[@]}"; do pushd "${service}" echo -e "${C_YELLOW}# Running 'cargo test --all-features' on '${service}'${C_RESET}" - RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo test --all-features --no-fail-fast + RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo test --all-features --all-targets --no-fail-fast popd done From 1e2c03c9d1ce62daafbd9d8ec5bba8be1f250db7 Mon Sep 17 00:00:00 2001 From: Thomas Cameron Date: Thu, 15 Jun 2023 02:32:22 +0900 Subject: [PATCH 154/253] Add send_with method to fluent builders (#2652) ## Motivation and Context This is a child PR of https://github.com/awslabs/smithy-rs/pull/2615. ## Description - Adds `send_with` method to Fluent Builder. ## Prerequisite PRs You can merge this first too reduce diffs. - https://github.com/awslabs/smithy-rs/pull/2651 ## Testing NA ## Checklist NA ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti Co-authored-by: John DiSanti --- CHANGELOG.next.toml | 40 +++++++++++++++++-- .../client/FluentClientGenerator.kt | 39 ++++++++++++++++-- .../generators/client/FluentClientGenerics.kt | 22 ++++++++-- .../codegen/core/rustlang/CargoDependency.kt | 4 ++ .../rust/codegen/core/rustlang/RustType.kt | 20 ++++++++++ .../rust/codegen/core/smithy/RuntimeType.kt | 5 +++ 6 files changed, 120 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index b7349422a4..7c40272066 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -15,7 +15,7 @@ message = "Fix bug in AWS JSON 1.x routers where, if a service had more than 14 operations, the router was created without the route for the 15th operation." author = "thor-bjorgvinsson" references = ["smithy-rs#2733"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" ="server" } +meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" } [[aws-sdk-rust]] message = "Remove native-tls and add a migration guide." @@ -66,7 +66,7 @@ author = "jdisanti" [[smithy-rs]] message = "For event stream operations, the `EventStreamSender` in inputs/outputs now requires the passed in `Stream` impl to implement `Sync`." references = ["smithy-rs#2673"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all"} +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all" } author = "jdisanti" [[aws-sdk-rust]] @@ -143,7 +143,7 @@ author = "jdisanti" [[smithy-rs]] message = "Update MSRV to Rust 1.68.2" references = ["smithy-rs#2745"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all"} +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all" } author = "jdisanti" [[smithy-rs]] @@ -408,3 +408,37 @@ let scoped_plugin = Scoped::new::(plugin); references = ["smithy-rs#2740", "smithy-rs#2759"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "hlbarber" + +[[smithy-rs]] +message = "Implement unstable serde support for the `Number`, `Blob`, `Document`, `DateTime` primitives" +author = "thomas-k-cameron" +meta = { "breaking" = false, "tada" = true, "bug" = false, target = "all" } +references = [ + "smithy-rs#2647", + "smithy-rs#2645", + "smithy-rs#2646", + "smithy-rs#2616", +] + +[[aws-sdk-rust]] +message = "Implement unstable serde support for the `Number`, `Blob`, `Document`, `DateTime` primitives" +author = "thomas-k-cameron" +meta = { "breaking" = false, "tada" = true, "bug" = false } +references = [ + "smithy-rs#2647", + "smithy-rs#2645", + "smithy-rs#2646", + "smithy-rs#2616", +] + +[[smithy-rs]] +message = "Add a `send_with` function on `-Input` types for sending requests without fluent builders" +author = "thomas-k-cameron" +references = ["smithy-rs#2652"] +meta = { "breaking" = false, "tada" = true, "bug" = false, target = "client" } + +[[aws-sdk-rust]] +message = "Add a `send_with` function on `-Input` types for sending requests without fluent builders" +author = "thomas-k-cameron" +references = ["smithy-rs#2652"] +meta = { "breaking" = false, "tada" = true, "bug" = false } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 801bb1da1d..6e562b3317 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -30,6 +30,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.implBlock import software.amazon.smithy.rust.codegen.core.rustlang.normalizeHtml import software.amazon.smithy.rust.codegen.core.rustlang.qualifiedName import software.amazon.smithy.rust.codegen.core.rustlang.render @@ -315,10 +316,44 @@ class FluentClientGenerator( } private fun RustWriter.renderFluentBuilder(operation: OperationShape) { + val outputType = symbolProvider.toSymbol(operation.outputShape(model)) + val errorType = symbolProvider.symbolForOperationError(operation) val operationSymbol = symbolProvider.toSymbol(operation) + val input = operation.inputShape(model) val baseDerives = symbolProvider.toSymbol(input).expectRustMetadata().derives // Filter out any derive that isn't Clone. Then add a Debug derive + // input name + val fnName = clientOperationFnName(operation, symbolProvider) + implBlock(symbolProvider.symbolForBuilder(input)) { + rustTemplate( + """ + /// Sends a request with this input using the given client. + pub async fn send_with${generics.inst}(self, client: &crate::Client${generics.inst}) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{RawResponseType}>> + #{send_bounds:W} + #{boundsWithoutWhereClause:W} + { + let mut fluent_builder = client.$fnName(); + fluent_builder.inner = self; + fluent_builder.send().await + } + """, + *preludeScope, + "RawResponseType" to if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + RuntimeType.smithyHttp(runtimeConfig).resolve("operation::Response") + } else { + RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse") + }, + "Operation" to operationSymbol, + "OperationError" to errorType, + "OperationOutput" to outputType, + "SdkError" to RuntimeType.sdkError(runtimeConfig), + "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), + "boundsWithoutWhereClause" to generics.boundsWithoutWhereClause, + "send_bounds" to generics.sendBounds(operationSymbol, outputType, errorType, retryClassifier), + ) + } + val derives = baseDerives.filter { it == RuntimeType.Clone } + RuntimeType.Debug docs("Fluent builder constructing a request to `${operationSymbol.name}`.\n") @@ -350,9 +385,6 @@ class FluentClientGenerator( "client" to RuntimeType.smithyClient(runtimeConfig), "bounds" to generics.bounds, ) { - val outputType = symbolProvider.toSymbol(operation.outputShape(model)) - val errorType = symbolProvider.symbolForOperationError(operation) - rust("/// Creates a new `${operationSymbol.name}`.") withBlockTemplate( "pub(crate) fn new(handle: #{Arc}) -> Self {", @@ -370,6 +402,7 @@ class FluentClientGenerator( } } } + if (smithyRuntimeMode.generateMiddleware) { val middlewareScope = arrayOf( *preludeScope, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt index d37fb89057..a0b9f15c1a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt @@ -34,6 +34,9 @@ interface FluentClientGenerics { /** Convert this `FluentClientGenerics` into the more general `RustGenerics` */ fun toRustGenerics(): RustGenerics + + /** bounds without where clause. If bounds does is not prefixed with `where\n`, then it gets the same value. **/ + val boundsWithoutWhereClause: Writable } class NoClientGenerics(private val runtimeConfig: RuntimeConfig) : FluentClientGenerics { @@ -55,6 +58,8 @@ class NoClientGenerics(private val runtimeConfig: RuntimeConfig) : FluentClientG /** Trait bounds */ override val bounds = writable { } + override val boundsWithoutWhereClause = writable {} + /** Bounds for generated `send()` functions */ override fun sendBounds( operation: Symbol, @@ -94,9 +99,18 @@ data class FlexibleClientGenerics( rustTemplate( """ where - C: #{client}::bounds::SmithyConnector, - M: #{client}::bounds::SmithyMiddleware, - R: #{client}::retry::NewRequestPolicy, + #{bounds} + """, + "bounds" to boundsWithoutWhereClause, + ) + } + + override val boundsWithoutWhereClause = writable { + rustTemplate( + """ + C: #{client}::bounds::SmithyConnector, + M: #{client}::bounds::SmithyMiddleware, + R: #{client}::retry::NewRequestPolicy, """, "client" to client, ) @@ -112,7 +126,7 @@ data class FlexibleClientGenerics( #{OperationOutput}, #{OperationError}, #{RetryClassifier} - > + >, """, "client" to client, "Operation" to operation, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index 72eba06a7d..952e3bb0ae 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -16,6 +16,7 @@ import java.nio.file.Path sealed class DependencyScope { object Dev : DependencyScope() object Compile : DependencyScope() + object CfgUnstable : DependencyScope() object Build : DependencyScope() } @@ -283,5 +284,8 @@ data class CargoDependency( fun smithyRuntimeApi(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-runtime-api") fun smithyTypes(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-types") fun smithyXml(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-xml") + + // behind feature-gate + val Serde = CargoDependency("serde", CratesIo("1.0"), features = setOf("derive"), scope = DependencyScope.CfgUnstable) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index 6d010089d2..6fc9ca7adf 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -475,6 +475,23 @@ class Attribute(val inner: Writable, val isDeriveHelper: Boolean = false) { } } + // These were supposed to be a part of companion object but we decided to move it out to here to avoid NPE + // You can find the discussion here. + // https://github.com/awslabs/smithy-rs/discussions/2248 + public fun SerdeSerialize(): Attribute { + return Attribute(cfgAttr(all(writable("aws_sdk_unstable"), feature("serde-serialize")), derive(RuntimeType.SerdeSerialize))) + } + public fun SerdeDeserialize(): Attribute { + return Attribute(cfgAttr(all(writable("aws_sdk_unstable"), feature("serde-deserialize")), derive(RuntimeType.SerdeDeserialize))) + } + public fun SerdeSkip(): Attribute { + return Attribute(cfgAttr(all(writable("aws_sdk_unstable"), any(feature("serde-serialize"), feature("serde-deserialize"))), serde("skip"))) + } + + public fun SerdeSerializeOrDeserialize(): Attribute { + return Attribute(cfg(all(writable("aws_sdk_unstable"), any(feature("serde-serialize"), feature("serde-deserialize"))))) + } + companion object { val AllowClippyBoxedLocal = Attribute(allow("clippy::boxed_local")) val AllowClippyLetAndReturn = Attribute(allow("clippy::let_and_return")) @@ -504,6 +521,7 @@ class Attribute(val inner: Writable, val isDeriveHelper: Boolean = false) { val Test = Attribute("test") val TokioTest = Attribute(RuntimeType.Tokio.resolve("test").writable) + val AwsSdkUnstableAttribute = Attribute(cfg("aws_sdk_unstable")) /** * [non_exhaustive](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute) @@ -532,10 +550,12 @@ class Attribute(val inner: Writable, val isDeriveHelper: Boolean = false) { } fun all(vararg attrMacros: Writable): Writable = macroWithArgs("all", *attrMacros) + fun cfgAttr(vararg attrMacros: Writable): Writable = macroWithArgs("cfg_attr", *attrMacros) fun allow(lints: Collection): Writable = macroWithArgs("allow", *lints.toTypedArray()) fun allow(vararg lints: String): Writable = macroWithArgs("allow", *lints) fun deny(vararg lints: String): Writable = macroWithArgs("deny", *lints) + fun serde(vararg lints: String): Writable = macroWithArgs("serde", *lints) fun any(vararg attrMacros: Writable): Writable = macroWithArgs("any", *attrMacros) fun cfg(vararg attrMacros: Writable): Writable = macroWithArgs("cfg", *attrMacros) fun cfg(vararg attrMacros: String): Writable = macroWithArgs("cfg", *attrMacros) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 6e5b5ee8ff..0a25844584 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -301,6 +301,11 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) val ConstrainedTrait = RuntimeType("crate::constrained::Constrained", InlineDependency.constrained()) val MaybeConstrained = RuntimeType("crate::constrained::MaybeConstrained", InlineDependency.constrained()) + // serde types. Gated behind `CfgUnstable`. + val Serde = CargoDependency.Serde.toType() + val SerdeSerialize = Serde.resolve("Serialize") + val SerdeDeserialize = Serde.resolve("Deserialize") + // smithy runtime types fun smithyAsync(runtimeConfig: RuntimeConfig) = CargoDependency.smithyAsync(runtimeConfig).toType() fun smithyChecksums(runtimeConfig: RuntimeConfig) = CargoDependency.smithyChecksums(runtimeConfig).toType() From 9d56db1f3e3ca1b3b7d3a60a4ad12154380ffc37 Mon Sep 17 00:00:00 2001 From: Thomas Cameron Date: Thu, 15 Jun 2023 02:33:47 +0900 Subject: [PATCH 155/253] Add serde support to document type. (#2616) ## Description Implements serde support for `Document`. ## Testing Test checks whether the serialized/de-serialized data matches with the expected value ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- rust-runtime/aws-smithy-types/src/document.rs | 78 +++++++++++++++++++ .../test_data/serialize_document.json | 43 ++++++++++ 2 files changed, 121 insertions(+) create mode 100644 rust-runtime/aws-smithy-types/test_data/serialize_document.json diff --git a/rust-runtime/aws-smithy-types/src/document.rs b/rust-runtime/aws-smithy-types/src/document.rs index a7c46d04db..2f13f895f8 100644 --- a/rust-runtime/aws-smithy-types/src/document.rs +++ b/rust-runtime/aws-smithy-types/src/document.rs @@ -7,6 +7,12 @@ use crate::Number; use std::borrow::Cow; use std::collections::HashMap; +#[cfg(any( + all(aws_sdk_unstable, feature = "serde-deserialize"), + all(aws_sdk_unstable, feature = "serde-serialize") +))] +use serde; + /* ANCHOR: document */ /// Document Type @@ -16,6 +22,21 @@ use std::collections::HashMap; /// modeled using rigid types, or data that has a schema that evolves outside of the purview of a model. /// The serialization format of a document is an implementation detail of a protocol. #[derive(Clone, Debug, PartialEq)] +#[cfg_attr( + all(aws_sdk_unstable, feature = "serde-serialize"), + derive(serde::Serialize) +)] +#[cfg_attr( + all(aws_sdk_unstable, feature = "serde-deserialize"), + derive(serde::Deserialize) +)] +#[cfg_attr( + any( + all(aws_sdk_unstable, feature = "serde-deserialize"), + all(aws_sdk_unstable, feature = "serde-serialize") + ), + serde(untagged) +)] pub enum Document { /// JSON object Object(HashMap), @@ -207,3 +228,60 @@ impl From for Document { Document::Number(value) } } + +/* ANCHOR END: document */ + +#[cfg(test)] +mod test { + /// checks if a) serialization of json suceeds and b) it is compatible with serde_json + #[test] + #[cfg(all( + aws_sdk_unstable, + feature = "serde-serialize", + feature = "serde-deserialize" + ))] + fn serialize_json() { + use crate::Document; + use crate::Number; + use std::collections::HashMap; + let mut map: HashMap = HashMap::new(); + // string + map.insert("hello".into(), "world".to_string().into()); + // numbers + map.insert("pos_int".into(), Document::Number(Number::PosInt(1).into())); + map.insert( + "neg_int".into(), + Document::Number(Number::NegInt(-1).into()), + ); + map.insert( + "float".into(), + Document::Number(Number::Float(0.1 + 0.2).into()), + ); + // booleans + map.insert("true".into(), true.into()); + map.insert("false".into(), false.into()); + // check if array with different datatypes would succeed + map.insert( + "array".into(), + vec![ + map.clone().into(), + "hello-world".to_string().into(), + true.into(), + false.into(), + ] + .into(), + ); + // map + map.insert("map".into(), map.clone().into()); + // null + map.insert("null".into(), Document::Null); + let obj = Document::Object(map); + // comparing string isnt going to work since there is no gurantee for the ordering of the keys + let target_file = include_str!("../test_data/serialize_document.json"); + let json: Result = serde_json::from_str(target_file); + // serializer + assert_eq!(serde_json::to_value(&obj).unwrap(), json.unwrap()); + let doc: Result = serde_json::from_str(target_file); + assert_eq!(obj, doc.unwrap()); + } +} diff --git a/rust-runtime/aws-smithy-types/test_data/serialize_document.json b/rust-runtime/aws-smithy-types/test_data/serialize_document.json new file mode 100644 index 0000000000..dcbac2986a --- /dev/null +++ b/rust-runtime/aws-smithy-types/test_data/serialize_document.json @@ -0,0 +1,43 @@ +{ + "null": null, + "true": true, + "pos_int": 1, + "false": false, + "map": { + "array": [ + { + "pos_int": 1, + "float": 0.30000000000000004, + "neg_int": -1, + "hello": "world", + "false": false, + "true": true + }, + "hello-world", + true, + false + ], + "pos_int": 1, + "float": 0.30000000000000004, + "neg_int": -1, + "hello": "world", + "false": false, + "true": true + }, + "float": 0.30000000000000004, + "neg_int": -1, + "hello": "world", + "array": [ + { + "pos_int": 1, + "float": 0.30000000000000004, + "neg_int": -1, + "hello": "world", + "false": false, + "true": true + }, + "hello-world", + true, + false + ] +} From a694dd9dcd028d3c4ddd4a90091c1a994da9d008 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 14 Jun 2023 13:05:16 -0700 Subject: [PATCH 156/253] Fix Polly presigning in the orchestrator (#2769) This PR fixes presigning for Amazon Polly in the orchestrator implementation. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-inlineable/Cargo.toml | 2 +- .../src/presigning_interceptors.rs | 21 +++-- .../smithy/rustsdk/AwsPresigningDecorator.kt | 83 +++++++++++++++++-- .../client/smithy/ClientCodegenContext.kt | 2 + .../client/smithy/ClientCodegenVisitor.kt | 1 + .../protocol/RequestSerializerGenerator.kt | 42 ++++++---- .../src/client/retries/strategy/never.rs | 1 + .../check-aws-sdk-orchestrator-impl | 2 +- 8 files changed, 123 insertions(+), 31 deletions(-) diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml index d941f059b2..e061a335e6 100644 --- a/aws/rust-runtime/aws-inlineable/Cargo.toml +++ b/aws/rust-runtime/aws-inlineable/Cargo.toml @@ -23,6 +23,7 @@ aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client" } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } aws-smithy-http-tower = { path = "../../../rust-runtime/aws-smithy-http-tower" } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } +aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-types = { path = "../aws-types" } @@ -42,7 +43,6 @@ tracing = "0.1" aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client", features = ["test-util"] } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http", features = ["rt-tokio"] } -aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } tempfile = "3.6.0" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] } diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs index 55642794a7..8d67e3ae6a 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs @@ -11,7 +11,9 @@ use aws_runtime::auth::sigv4::{HttpSignatureType, SigV4OperationSigningConfig}; use aws_runtime::invocation_id::InvocationIdInterceptor; use aws_runtime::request_info::RequestInfoInterceptor; use aws_runtime::user_agent::UserAgentInterceptor; +use aws_sigv4::http_request::SignableBody; use aws_smithy_async::time::{SharedTimeSource, StaticTimeSource}; +use aws_smithy_runtime::client::retries::strategy::NeverRetryStrategy; use aws_smithy_runtime_api::client::interceptors::{ disable_interceptor, BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, BoxError, Interceptor, InterceptorRegistrar, @@ -26,11 +28,15 @@ use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; #[derive(Debug)] pub(crate) struct SigV4PresigningInterceptor { config: PresigningConfig, + payload_override: SignableBody<'static>, } impl SigV4PresigningInterceptor { - pub(crate) fn new(config: PresigningConfig) -> Self { - Self { config } + pub(crate) fn new(config: PresigningConfig, payload_override: SignableBody<'static>) -> Self { + Self { + config, + payload_override, + } } } @@ -60,8 +66,7 @@ impl Interceptor for SigV4PresigningInterceptor { if let Some(mut config) = cfg.get::().cloned() { config.signing_options.expires_in = Some(self.config.expires()); config.signing_options.signature_type = HttpSignatureType::HttpRequestQueryParams; - config.signing_options.payload_override = - Some(aws_sigv4::http_request::SignableBody::UnsignedPayload); + config.signing_options.payload_override = Some(self.payload_override.clone()); cfg.interceptor_state() .put::(config); Ok(()) @@ -81,9 +86,12 @@ pub(crate) struct SigV4PresigningRuntimePlugin { } impl SigV4PresigningRuntimePlugin { - pub(crate) fn new(config: PresigningConfig) -> Self { + pub(crate) fn new(config: PresigningConfig, payload_override: SignableBody<'static>) -> Self { Self { - interceptor: SharedInterceptor::new(SigV4PresigningInterceptor::new(config)), + interceptor: SharedInterceptor::new(SigV4PresigningInterceptor::new( + config, + payload_override, + )), } } } @@ -91,6 +99,7 @@ impl SigV4PresigningRuntimePlugin { impl RuntimePlugin for SigV4PresigningRuntimePlugin { fn config(&self) -> Option { let mut layer = Layer::new("Presigning"); + layer.set_retry_strategy(NeverRetryStrategy::new()); layer.put(disable_interceptor::("presigning")); layer.put(disable_interceptor::("presigning")); layer.put(disable_interceptor::("presigning")); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index bbf5f80fb5..6c85ce361c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -23,17 +23,20 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSec import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.client.FluentClientSection import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.RequestSerializerGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientHttpBoundProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs +import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock 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.contextName import software.amazon.smithy.rust.codegen.core.util.cloneOperation import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -305,14 +308,24 @@ class AwsPresignedFluentBuilderMethod( } private fun RustWriter.renderPresignedMethodBody(section: FluentClientSection.FluentBuilderImpl) { + val presignableOp = PRESIGNABLE_OPERATIONS.getValue(section.operationShape.id) + val operationShape = if (presignableOp.hasModelTransforms()) { + codegenContext.model.expectShape(syntheticShapeId(section.operationShape.id), OperationShape::class.java) + } else { + section.operationShape + } + rustTemplate( """ + #{alternate_presigning_serializer} + let runtime_plugins = #{Operation}::register_runtime_plugins( - #{RuntimePlugins}::new() - .with_client_plugin(#{SigV4PresigningRuntimePlugin}::new(presigning_config)), + #{RuntimePlugins}::new(), self.handle.clone(), - self.config_override, - ); + self.config_override + ) + .with_client_plugin(#{SigV4PresigningRuntimePlugin}::new(presigning_config, #{payload_override})) + #{alternate_presigning_serializer_registration}; let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; let mut context = #{Operation}::orchestrate_with_stop_point(&runtime_plugins, input, #{StopPoint}::BeforeTransmit) @@ -332,14 +345,74 @@ class AwsPresignedFluentBuilderMethod( "OperationError" to section.operationErrorType, "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::runtime_plugin::RuntimePlugins"), - "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors").resolve("SharedInterceptor"), + "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors") + .resolve("SharedInterceptor"), "SigV4PresigningRuntimePlugin" to AwsRuntimeType.presigningInterceptor(runtimeConfig) .resolve("SigV4PresigningRuntimePlugin"), "StopPoint" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::StopPoint"), "TypedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypedBox"), "USER_AGENT" to CargoDependency.Http.toType().resolve("header::USER_AGENT"), + "alternate_presigning_serializer" to writable { + if (presignableOp.hasModelTransforms()) { + val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig) + rustTemplate( + """ + ##[derive(Debug)] + struct AlternatePresigningSerializerRuntimePlugin; + impl #{RuntimePlugin} for AlternatePresigningSerializerRuntimePlugin { + fn config(&self) -> Option<#{FrozenLayer}> { + use #{ConfigBagAccessors}; + let mut cfg = #{Layer}::new("presigning_serializer"); + cfg.set_request_serializer(#{AlternateSerializer}); + Some(cfg.freeze()) + } + } + """, + "AlternateSerializer" to alternateSerializer(operationShape), + "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) + .resolve("client::orchestrator::ConfigBagAccessors"), + "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), + "Layer" to smithyTypes.resolve("config_bag::Layer"), + "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), + ) + } + }, + "alternate_presigning_serializer_registration" to writable { + if (presignableOp.hasModelTransforms()) { + rust(".with_operation_plugin(AlternatePresigningSerializerRuntimePlugin)") + } + }, + "payload_override" to writable { + rustTemplate( + "#{aws_sigv4}::http_request::SignableBody::" + + when (presignableOp.payloadSigningType) { + PayloadSigningType.EMPTY -> "Bytes(b\"\")" + PayloadSigningType.UNSIGNED_PAYLOAD -> "UnsignedPayload" + }, + "aws_sigv4" to AwsRuntimeType.awsSigv4(runtimeConfig), + ) + }, ) } + + private fun alternateSerializer(transformedOperationShape: OperationShape): RuntimeType = + transformedOperationShape.contextName(codegenContext.serviceShape).replaceFirstChar { + it.uppercase() + }.let { baseName -> + "${baseName}PresigningRequestSerializer".let { name -> + RuntimeType.forInlineFun(name, codegenContext.symbolProvider.moduleForShape(transformedOperationShape)) { + RequestSerializerGenerator( + codegenContext, + codegenContext.protocolImpl!!, + null, + nameOverride = name, + ).render( + this, + transformedOperationShape, + ) + } + } + } } interface PresignModelTransform { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt index 92a8a9f8ca..18db619823 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.CodegenTarget import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol /** * [ClientCodegenContext] contains code-generation context that is _specific_ to the [RustClientCodegenPlugin] plugin @@ -30,6 +31,7 @@ data class ClientCodegenContext( // Expose the `rootDecorator`, enabling customizations to compose by referencing information from the root codegen // decorator val rootDecorator: ClientCodegenDecorator, + val protocolImpl: Protocol? = null, ) : CodegenContext( model, symbolProvider, moduleDocProvider, serviceShape, protocol, settings, CodegenTarget.CLIENT, ) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index 99bb528fe9..7583bc3426 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -108,6 +108,7 @@ class ClientCodegenVisitor( codegenContext, ClientModuleDocProvider(codegenContext, service.serviceNameOrDefault("the service")), ), + protocolImpl = protocolGeneratorFactory.protocol(codegenContext), ) rustCrate = RustCrate( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt index f3b53c3606..99adee16b5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt @@ -29,7 +29,8 @@ import software.amazon.smithy.rust.codegen.core.util.inputShape class RequestSerializerGenerator( private val codegenContext: ClientCodegenContext, private val protocol: Protocol, - private val bodyGenerator: ProtocolPayloadGenerator, + private val bodyGenerator: ProtocolPayloadGenerator?, + private val nameOverride: String? = null, ) { private val httpBindingResolver = protocol.httpBindingResolver private val symbolProvider = codegenContext.symbolProvider @@ -63,11 +64,12 @@ class RequestSerializerGenerator( val inputShape = operationShape.inputShape(codegenContext.model) val operationName = symbolProvider.toSymbol(operationShape).name val inputSymbol = symbolProvider.toSymbol(inputShape) + val serializerName = nameOverride ?: "${operationName}RequestSerializer" writer.rustTemplate( """ ##[derive(Debug)] - struct ${operationName}RequestSerializer; - impl #{RequestSerializer} for ${operationName}RequestSerializer { + struct $serializerName; + impl #{RequestSerializer} for $serializerName { ##[allow(unused_mut, clippy::let_and_return, clippy::needless_borrow, clippy::useless_conversion)] fn serialize_input(&self, input: #{Input}, _cfg: &mut #{ConfigBag}) -> Result<#{HttpRequest}, #{BoxError}> { let input = #{TypedBox}::<#{ConcreteInput}>::assume_from(input).expect("correct type").unwrap(); @@ -85,22 +87,26 @@ class RequestSerializerGenerator( "ConcreteInput" to inputSymbol, "create_http_request" to createHttpRequest(operationShape), "generate_body" to writable { - val body = writable { - bodyGenerator.generatePayload( - this, - "input", - operationShape, - ClientAdditionalPayloadContext(propertyBagAvailable = false), - ) - } - val streamingMember = inputShape.findStreamingMember(codegenContext.model) - val isBlobStreaming = - streamingMember != null && codegenContext.model.expectShape(streamingMember.target) is BlobShape - if (isBlobStreaming) { - // Consume the `ByteStream` into its inner `SdkBody`. - rust("#T.into_inner()", body) + if (bodyGenerator != null) { + val body = writable { + bodyGenerator.generatePayload( + this, + "input", + operationShape, + ClientAdditionalPayloadContext(propertyBagAvailable = false), + ) + } + val streamingMember = inputShape.findStreamingMember(codegenContext.model) + val isBlobStreaming = + streamingMember != null && codegenContext.model.expectShape(streamingMember.target) is BlobShape + if (isBlobStreaming) { + // Consume the `ByteStream` into its inner `SdkBody`. + rust("#T.into_inner()", body) + } else { + rustTemplate("#{SdkBody}::from(#{body})", *codegenScope, "body" to body) + } } else { - rustTemplate("#{SdkBody}::from(#{body})", *codegenScope, "body" to body) + rustTemplate("#{SdkBody}::empty()", *codegenScope) } }, "add_content_length" to if (needsContentLength(operationShape)) { diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs index 599b6ea5af..3f0a2325ec 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs @@ -8,6 +8,7 @@ use aws_smithy_runtime_api::client::orchestrator::BoxError; use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; use aws_smithy_types::config_bag::ConfigBag; +#[non_exhaustive] #[derive(Debug, Clone, Default)] pub struct NeverRetryStrategy {} diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index a02eb14ad3..e9889a2980 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -16,7 +16,6 @@ cd smithy-rs services_that_compile=(\ "aws-config"\ "dynamodb"\ - "polly"\ "route53"\ "s3"\ "sts"\ @@ -30,6 +29,7 @@ services_that_pass_tests=(\ "iam"\ "kms"\ "lambda"\ + "polly"\ "qldbsession"\ "s3control"\ "sso"\ From 3f6f49943de4d477d404a783ecf4cd794ccebaa9 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 14 Jun 2023 18:08:25 -0700 Subject: [PATCH 157/253] Fix rustfmt on generated SDK code (#2778) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../client/FluentClientGenerator.kt | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 6e562b3317..c964df9023 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -329,13 +329,19 @@ class FluentClientGenerator( rustTemplate( """ /// Sends a request with this input using the given client. - pub async fn send_with${generics.inst}(self, client: &crate::Client${generics.inst}) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{RawResponseType}>> - #{send_bounds:W} - #{boundsWithoutWhereClause:W} - { - let mut fluent_builder = client.$fnName(); - fluent_builder.inner = self; - fluent_builder.send().await + pub async fn send_with${generics.inst}( + self, + client: &crate::Client${generics.inst} + ) -> #{Result}< + #{OperationOutput}, + #{SdkError}< + #{OperationError}, + #{RawResponseType} + > + > #{send_bounds:W} #{boundsWithoutWhereClause:W} { + let mut fluent_builder = client.$fnName(); + fluent_builder.inner = self; + fluent_builder.send().await } """, *preludeScope, From 07bd832a213c8e4366454bf7176bf639dfd5ce66 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Thu, 15 Jun 2023 14:30:54 +0100 Subject: [PATCH 158/253] Relax bounds on `Scoped` (#2779) ## Motivation and Context I accidentally added this bound, it is not only redundant but its absence is one of the key benefits of this approach. --- CHANGELOG.next.toml | 2 +- rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 7c40272066..2da07d6a16 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -405,7 +405,7 @@ let scoped_plugin = Scoped::new::(plugin); ``` """ -references = ["smithy-rs#2740", "smithy-rs#2759"] +references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "hlbarber" diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs b/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs index 700d174359..64dc17ab83 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs @@ -94,7 +94,6 @@ impl Plugin for Scoped where Scope: Membership, Scope::Contains: ConditionalApply, - Pl: Plugin, { type Service = >::Service; From 45885b115f28d43219744f04f361de4a44b1a583 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 15 Jun 2023 10:41:34 -0400 Subject: [PATCH 159/253] Make RuntimePlugin & RuntimePlugins Send/Sync (#2771) ## Motivation and Context We will eventually want to store additional runtime plugins on fluent builders, clients, etc. To make this work, the RuntimePlugin trait needs to assert Send/Sync ## Description Small tweaks + test on the RuntimePlugin trait ## Testing CI ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../smithy/generators/OperationGenerator.kt | 4 +- .../src/client/runtime_plugin.rs | 46 +++++++++++-------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 5703766c55..c120fa8209 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -51,7 +51,7 @@ open class OperationGenerator( """ pub(crate) fn register_default_runtime_plugins( runtime_plugins: #{RuntimePlugins}, - operation: #{Box}, + operation: impl #{RuntimePlugin} + 'static, handle: #{Arc}, config_override: #{Option}, ) -> #{RuntimePlugins} { @@ -180,7 +180,7 @@ open class OperationGenerator( ) -> #{RuntimePlugins} { #{register_default_runtime_plugins}( runtime_plugins, - #{Box}::new(Self::new()) as _, + Self::new(), handle, config_override ) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index 34fd6f7985..cd5845c43a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -6,16 +6,16 @@ use crate::client::interceptors::InterceptorRegistrar; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer}; use std::fmt::Debug; +use std::sync::Arc; pub type BoxError = Box; -pub type BoxRuntimePlugin = Box; /// RuntimePlugin Trait /// /// A RuntimePlugin is the unit of configuration for augmenting the SDK with new behavior /// /// Runtime plugins can set configuration and register interceptors. -pub trait RuntimePlugin: Debug { +pub trait RuntimePlugin: Debug + Send + Sync { fn config(&self) -> Option { None } @@ -25,20 +25,29 @@ pub trait RuntimePlugin: Debug { } } -impl RuntimePlugin for BoxRuntimePlugin { +#[derive(Debug, Clone)] +struct SharedRuntimePlugin(Arc); + +impl SharedRuntimePlugin { + fn new(plugin: impl RuntimePlugin + 'static) -> Self { + Self(Arc::new(plugin)) + } +} + +impl RuntimePlugin for SharedRuntimePlugin { fn config(&self) -> Option { - self.as_ref().config() + self.0.config() } fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { - self.as_ref().interceptors(interceptors) + self.0.interceptors(interceptors) } } -#[derive(Default)] +#[derive(Default, Clone, Debug)] pub struct RuntimePlugins { - client_plugins: Vec, - operation_plugins: Vec, + client_plugins: Vec, + operation_plugins: Vec, } impl RuntimePlugins { @@ -46,19 +55,14 @@ impl RuntimePlugins { Default::default() } - pub fn with_client_plugin( - mut self, - plugin: impl RuntimePlugin + Send + Sync + 'static, - ) -> Self { - self.client_plugins.push(Box::new(plugin)); + pub fn with_client_plugin(mut self, plugin: impl RuntimePlugin + 'static) -> Self { + self.client_plugins.push(SharedRuntimePlugin::new(plugin)); self } - pub fn with_operation_plugin( - mut self, - plugin: impl RuntimePlugin + Send + Sync + 'static, - ) -> Self { - self.operation_plugins.push(Box::new(plugin)); + pub fn with_operation_plugin(mut self, plugin: impl RuntimePlugin + 'static) -> Self { + self.operation_plugins + .push(SharedRuntimePlugin::new(plugin)); self } @@ -106,4 +110,10 @@ mod tests { fn can_add_runtime_plugin_implementors_to_runtime_plugins() { RuntimePlugins::new().with_client_plugin(SomeStruct); } + + #[test] + fn runtime_plugins_are_send_sync() { + fn assert_send_sync() {} + assert_send_sync::(); + } } From 3d0db5650ceced0b5ce0eb24489a32aa326fd4c1 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 15 Jun 2023 10:06:28 -0700 Subject: [PATCH 160/253] Categorize orchestrator TODO comments into `Launch` and `Cleanup` (#2774) This PR categorizes orchestrator TODO comments into `Launch` and `Cleanup`: - `enableNewSmithyRuntimeLaunch`: Comment needs to be addressed before orchestrator launch - `enableNewSmithyRuntimeCleanup`: Comment needs to be addressed after launch when removing middleware ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .github/workflows/ci.yml | 2 +- aws/rust-runtime/aws-config/src/sso.rs | 2 +- .../aws-http/src/recursion_detection.rs | 2 +- aws/rust-runtime/aws-http/src/user_agent.rs | 2 +- .../aws-inlineable/src/glacier_checksums.rs | 2 +- aws/rust-runtime/aws-runtime/src/request_info.rs | 1 - .../aws-sig-auth/src/event_stream.rs | 6 +++--- aws/rust-runtime/aws-sig-auth/src/middleware.rs | 4 ++-- .../rustsdk/AwsCustomizableOperationDecorator.kt | 2 +- .../smithy/rustsdk/AwsPresigningDecorator.kt | 2 +- .../amazon/smithy/rustsdk/AwsRuntimeType.kt | 4 ++-- .../rustsdk/HttpConnectorConfigCustomization.kt | 2 +- .../rustsdk/HttpRequestChecksumDecorator.kt | 2 +- .../rustsdk/HttpResponseChecksumDecorator.kt | 2 +- .../rustsdk/IntegrationTestDependencies.kt | 2 +- .../amazon/smithy/rustsdk/SigV4AuthDecorator.kt | 2 +- .../smithy/rustsdk/SigV4SigningDecorator.kt | 2 +- .../amazon/smithy/rustsdk/UserAgentDecorator.kt | 2 +- .../customize/apigateway/ApiGatewayDecorator.kt | 4 ++-- .../customize/glacier/AccountIdAutofill.kt | 2 +- .../customize/glacier/ApiVersionHeader.kt | 2 +- .../customize/glacier/GlacierDecorator.kt | 2 +- .../rustsdk/customize/glacier/TreeHashHeader.kt | 2 +- .../codegen/client/smithy/ClientRustSettings.kt | 2 +- .../smithy/customizations/ApiKeyAuthDecorator.kt | 2 +- .../smithy/customizations/HttpAuthDecorator.kt | 2 +- .../InterceptorConfigCustomization.kt | 8 ++++---- .../smithy/customize/RequiredCustomizations.kt | 1 - .../client/smithy/endpoint/EndpointsDecorator.kt | 2 +- .../generators/EndpointTraitBindingGenerator.kt | 2 +- .../smithy/generators/OperationCustomization.kt | 16 ++++++++-------- .../smithy/generators/OperationGenerator.kt | 6 +++--- .../generators/ServiceRuntimePluginGenerator.kt | 4 ++-- .../client/CustomizableOperationGenerator.kt | 10 +++++----- .../generators/client/FluentClientGenerator.kt | 10 +++++----- .../generators/client/FluentClientGenerics.kt | 2 +- .../generators/config/ServiceConfigGenerator.kt | 2 +- .../protocol/MakeOperationGenerator.kt | 2 +- .../protocol/ProtocolParserGenerator.kt | 8 ++++---- .../protocols/HttpBoundProtocolGenerator.kt | 6 +++--- .../smithy/generators/PaginatorGeneratorTest.kt | 2 +- .../client/testutil/TestCodegenSettings.kt | 8 ++++---- rust-runtime/aws-smithy-eventstream/src/frame.rs | 2 +- rust-runtime/aws-smithy-http-auth/src/lib.rs | 2 +- .../aws-smithy-http/src/endpoint/middleware.rs | 2 +- rust-runtime/aws-smithy-http/src/operation.rs | 6 +++--- .../src/client/interceptors/context.rs | 1 - .../src/client/connections/test_connection.rs | 1 - .../src/client/retries/strategy/standard.rs | 2 +- .../src/client/test_util/interceptors.rs | 2 +- rust-runtime/aws-smithy-types/src/lib.rs | 4 ++-- tools/ci-scripts/check-aws-sdk-adhoc-tests | 2 +- tools/ci-scripts/check-aws-sdk-orchestrator-impl | 2 +- tools/ci-scripts/codegen-diff/semver-checks.py | 2 +- 54 files changed, 87 insertions(+), 91 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fb0575046..0cd2c8877b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,7 +84,7 @@ jobs: test: - action: check-aws-sdk-adhoc-tests runner: ubuntu-latest - # TODO(enableNewSmithyRuntime): Remove `check-aws-sdk-orchestrator-impl` when cleaning up middleware + # TODO(enableNewSmithyRuntimeCleanup): Remove `check-aws-sdk-orchestrator-impl` when cleaning up middleware - action: check-aws-sdk-orchestrator-impl runner: smithy_ubuntu-latest_8-core - action: check-client-codegen-integration-tests diff --git a/aws/rust-runtime/aws-config/src/sso.rs b/aws/rust-runtime/aws-config/src/sso.rs index cd0b2df812..a4b0314b1d 100644 --- a/aws/rust-runtime/aws-config/src/sso.rs +++ b/aws/rust-runtime/aws-config/src/sso.rs @@ -217,7 +217,7 @@ async fn load_sso_credentials( .region(sso_provider_config.region.clone()) .credentials_cache(CredentialsCache::no_caching()) .build(); - // TODO(enableNewSmithyRuntime): Use `customize().config_override()` to set the region instead of creating a new client once middleware is removed + // TODO(enableNewSmithyRuntimeCleanup): Use `customize().config_override()` to set the region instead of creating a new client once middleware is removed let client = SsoClient::from_conf(config); let resp = client .get_role_credentials() diff --git a/aws/rust-runtime/aws-http/src/recursion_detection.rs b/aws/rust-runtime/aws-http/src/recursion_detection.rs index a5cc165032..3cc27615d3 100644 --- a/aws/rust-runtime/aws-http/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-http/src/recursion_detection.rs @@ -11,7 +11,7 @@ use http::HeaderValue; use percent_encoding::{percent_encode, CONTROLS}; use std::borrow::Cow; -// TODO(enableNewSmithyRuntime): Delete this module +// TODO(enableNewSmithyRuntimeCleanup): Delete this module /// Recursion Detection Middleware /// diff --git a/aws/rust-runtime/aws-http/src/user_agent.rs b/aws/rust-runtime/aws-http/src/user_agent.rs index 8d338bff70..72a2e2105d 100644 --- a/aws/rust-runtime/aws-http/src/user_agent.rs +++ b/aws/rust-runtime/aws-http/src/user_agent.rs @@ -513,7 +513,7 @@ impl fmt::Display for ExecEnvMetadata { } } -// TODO(enableNewSmithyRuntime): Delete the user agent Tower middleware and consider moving all the remaining code into aws-runtime +// TODO(enableNewSmithyRuntimeCleanup): Delete the user agent Tower middleware and consider moving all the remaining code into aws-runtime /// User agent middleware #[non_exhaustive] diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs b/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs index 9cb8cc0164..18f1d9219e 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_checksums.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO(enableNewSmithyRuntime): Delete this file when cleaning up middleware +// TODO(enableNewSmithyRuntimeCleanup): Delete this file when cleaning up middleware use aws_sig_auth::signer::SignableBody; use aws_smithy_http::body::SdkBody; diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index 07c2d0bf7a..d5776a70d3 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -54,7 +54,6 @@ impl RequestInfoInterceptor { &self, cfg: &ConfigBag, ) -> Option<(Cow<'static, str>, Cow<'static, str>)> { - // TODO(enableNewSmithyRuntime) What config will we actually store in the bag? Will it be a whole config or just the max_attempts part? if let Some(retry_config) = cfg.get::() { let max_attempts = retry_config.max_attempts().to_string(); Some((Cow::Borrowed("max"), Cow::Owned(max_attempts))) diff --git a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs index e6e06dac75..01a1dd55fa 100644 --- a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs +++ b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO(enableNewSmithyRuntime): Remove this blanket allow once the old implementations are deleted +// TODO(enableNewSmithyRuntimeCleanup): Remove this blanket allow once the old implementations are deleted #![allow(deprecated)] use crate::middleware::Signature; @@ -123,7 +123,7 @@ mod tests { } } -// TODO(enableNewSmithyRuntime): Delete this old implementation that was kept around to support patch releases. +// TODO(enableNewSmithyRuntimeCleanup): Delete this old implementation that was kept around to support patch releases. #[deprecated = "use aws_sig_auth::event_stream::SigV4MessageSigner instead (this may require upgrading the smithy-rs code generator)"] #[derive(Debug)] /// Event Stream SigV4 signing implementation. @@ -199,7 +199,7 @@ impl SignMessage for SigV4Signer { } } -// TODO(enableNewSmithyRuntime): Delete this old implementation that was kept around to support patch releases. +// TODO(enableNewSmithyRuntimeCleanup): Delete this old implementation that was kept around to support patch releases. #[cfg(test)] mod old_tests { use crate::event_stream::SigV4Signer; diff --git a/aws/rust-runtime/aws-sig-auth/src/middleware.rs b/aws/rust-runtime/aws-sig-auth/src/middleware.rs index 8dc84da5c2..fd0b1474a0 100644 --- a/aws/rust-runtime/aws-sig-auth/src/middleware.rs +++ b/aws/rust-runtime/aws-sig-auth/src/middleware.rs @@ -25,7 +25,7 @@ use crate::event_stream::SigV4MessageSigner as EventStreamSigV4Signer; #[cfg(feature = "sign-eventstream")] use aws_smithy_eventstream::frame::DeferredSignerSender; -// TODO(enableNewSmithyRuntime): Delete `Signature` when switching to the orchestrator +// TODO(enableNewSmithyRuntimeCleanup): Delete `Signature` when switching to the orchestrator /// Container for the request signature for use in the property bag. #[non_exhaustive] #[derive(Debug, Clone)] @@ -151,7 +151,7 @@ fn signing_config( request_ts: config .get::() .map(|t| t.now()) - // TODO(enableNewSmithyRuntime): Remove this fallback + // TODO(enableNewSmithyRuntimeLaunch): Remove this fallback .unwrap_or_else(|| SharedTimeSource::default().now()), region, payload_override, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index 3e363d9565..baeb8c4866 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -85,7 +85,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : *codegenScope, ) } else { - // TODO(enableNewSmithyRuntime): Delete this branch when middleware is no longer used + // TODO(enableNewSmithyRuntimeCleanup): Delete this branch when middleware is no longer used rustTemplate( """ ##[doc(hidden)] diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index 6c85ce361c..0cc280449b 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -140,7 +140,7 @@ class AwsPresigningDecorator internal constructor( } } -// TODO(enableNewSmithyRuntime): Delete this class when cleaning up middleware +// TODO(enableNewSmithyRuntimeCleanup): Delete this class when cleaning up middleware class AwsInputPresignedMethod( private val codegenContext: ClientCodegenContext, private val operationShape: OperationShape, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt index 272cb35f31..ec7e0ba3d0 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsRuntimeType.kt @@ -53,11 +53,11 @@ object AwsRuntimeType { ), ) - // TODO(enableNewSmithyRuntime): Delete the `presigning_service.rs` inlineable when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete the `presigning_service.rs` inlineable when cleaning up middleware fun presigningService(): RuntimeType = RuntimeType.forInlineDependency(InlineAwsDependency.forRustFile("presigning_service", visibility = Visibility.PUBCRATE)) - // TODO(enableNewSmithyRuntime): Delete defaultMiddleware and middleware.rs, and remove tower dependency from inlinables, when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete defaultMiddleware and middleware.rs, and remove tower dependency from inlinables, when cleaning up middleware fun RuntimeConfig.defaultMiddleware() = RuntimeType.forInlineDependency( InlineAwsDependency.forRustFile( "middleware", visibility = Visibility.PUBLIC, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt index d64ddea9b3..77ad6fad98 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt @@ -17,7 +17,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.letIf -// TODO(enableNewSmithyRuntime): Delete this decorator since it's now in `codegen-client` +// TODO(enableNewSmithyRuntimeCleanup): Delete this decorator since it's now in `codegen-client` class HttpConnectorDecorator : ClientCodegenDecorator { override val name: String = "HttpConnectorDecorator" override val order: Byte = 0 diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt index 6cbabac906..5c82527cea 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt @@ -42,7 +42,7 @@ class HttpRequestChecksumDecorator : ClientCodegenDecorator { override val name: String = "HttpRequestChecksum" override val order: Byte = 0 - // TODO(enableNewSmithyRuntime): Implement checksumming via interceptor and delete this decorator upon launching the orchestrator + // TODO(enableNewSmithyRuntimeCleanup): Delete this decorator upon launching the orchestrator private fun applies(codegenContext: ClientCodegenContext): Boolean = codegenContext.smithyRuntimeMode.generateMiddleware diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt index 4e50c4c156..af25e816dd 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt @@ -33,7 +33,7 @@ class HttpResponseChecksumDecorator : ClientCodegenDecorator { override val name: String = "HttpResponseChecksum" override val order: Byte = 0 - // TODO(enableNewSmithyRuntime): Implement checksumming via interceptor and delete this decorator + // TODO(enableNewSmithyRuntimeCleanup): Delete this decorator private fun applies(codegenContext: ClientCodegenContext, operationShape: OperationShape): Boolean = codegenContext.smithyRuntimeMode.generateMiddleware && operationShape.outputShape != ShapeId.from("com.amazonaws.s3#GetObjectOutput") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt index cb9e0b9ecb..16b3823fca 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt @@ -129,7 +129,7 @@ class S3TestDependencies(private val codegenContext: ClientCodegenContext) : Lib addDependency(TracingAppender) addDependency(TracingTest) - // TODO(enableNewSmithyRuntime): These additional dependencies may not be needed anymore when removing this flag + // TODO(enableNewSmithyRuntimeCleanup): These additional dependencies may not be needed anymore when removing this flag // depending on if the sra-test is kept around or not. if (codegenContext.smithyRuntimeMode.generateOrchestrator) { addDependency(CargoDependency.smithyRuntime(codegenContext.runtimeConfig).toDevDependency()) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 3ea33df814..5e2c7d9cbe 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -143,7 +143,7 @@ private class AuthOperationCustomization(private val codegenContext: ClientCodeg service: None, signing_options, }); - // TODO(enableNewSmithyRuntime): Make auth options additive in the config bag so that multiple codegen decorators can register them + // TODO(enableNewSmithyRuntimeLaunch): Make auth options additive in the config bag so that multiple codegen decorators can register them let auth_option_resolver = #{StaticAuthOptionResolver}::new( vec![#{SIGV4_SCHEME_ID}] ); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt index 8ca37474c7..5f5877396b 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt @@ -32,7 +32,7 @@ import software.amazon.smithy.rust.codegen.core.util.hasEventStreamOperations import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.isInputEventStream -// TODO(enableNewSmithyRuntime): Remove this decorator (superseded by SigV4AuthDecorator) +// TODO(enableNewSmithyRuntimeCleanup): Remove this decorator (superseded by SigV4AuthDecorator) /** * The SigV4SigningDecorator: * - adds a `signing_service()` method to `config` to return the default signing service diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index 7eb227ba6a..40eb360444 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -120,7 +120,7 @@ class UserAgentDecorator : ClientCodegenDecorator { } } - // TODO(enableNewSmithyRuntime): Remove this customization class + // TODO(enableNewSmithyRuntimeCleanup): Remove this customization class private class UserAgentMutateOpRequest( codegenContext: ClientCodegenContext, ) : OperationCustomization() { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt index e6c29d23e0..3f699f5973 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt @@ -25,7 +25,7 @@ class ApiGatewayDecorator : ClientCodegenDecorator { override val name: String = "ApiGateway" override val order: Byte = 0 - // TODO(enableNewSmithyRuntime): Delete when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete when cleaning up middleware override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, @@ -44,7 +44,7 @@ class ApiGatewayDecorator : ClientCodegenDecorator { } } -// TODO(enableNewSmithyRuntime): Delete when cleaning up middleware +// TODO(enableNewSmithyRuntimeCleanup): Delete when cleaning up middleware private class ApiGatewayAddAcceptHeader : OperationCustomization() { override fun section(section: OperationSection): Writable = when (section) { is OperationSection.FinalizeOperation -> emptySection diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt index 66b7f1fe49..6b18ca9116 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/AccountIdAutofill.kt @@ -14,7 +14,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.util.inputShape -// TODO(enableNewSmithyRuntime): Delete this file when cleaning up middleware. +// TODO(enableNewSmithyRuntimeCleanup): Delete this file when cleaning up middleware. class AccountIdAutofill : OperationCustomization() { override fun mutSelf(): Boolean = true diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt index 69dafc7672..093bd61061 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/ApiVersionHeader.kt @@ -13,7 +13,7 @@ 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.util.dq -// TODO(enableNewSmithyRuntime): Delete this file when cleaning up middleware. +// TODO(enableNewSmithyRuntimeCleanup): Delete this file when cleaning up middleware. class ApiVersionHeader( /** diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt index d8a38db110..5262471c09 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt @@ -97,7 +97,7 @@ private class GlacierAccountIdCustomization(private val codegenContext: ClientCo } } -// TODO(enableNewSmithyRuntime): Install the glacier customizations as a single additional runtime plugin instead +// TODO(enableNewSmithyRuntimeLaunch): Install the glacier customizations as a single additional runtime plugin instead // of wiring up the interceptors individually /** Adds the `x-amz-glacier-version` header to all requests */ diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt index 7f1f5d590e..ff03c29dda 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/TreeHashHeader.kt @@ -18,7 +18,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.generators.operationBuildError import software.amazon.smithy.rustsdk.InlineAwsDependency -// TODO(enableNewSmithyRuntime): Delete this file when cleaning up middleware. +// TODO(enableNewSmithyRuntimeCleanup): Delete this file when cleaning up middleware. val TreeHashDependencies = listOf( CargoDependency.Ring, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt index d7246cf986..f4d2fdeede 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt @@ -72,7 +72,7 @@ data class ClientRustSettings( } } -// TODO(enableNewSmithyRuntime): Remove this mode after switching to the orchestrator +// TODO(enableNewSmithyRuntimeCleanup): Remove this mode after switching to the orchestrator enum class SmithyRuntimeMode { Middleware, BothDefaultMiddleware, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt index a224915435..5152597709 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt @@ -28,7 +28,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.util.letIf -// TODO(enableNewSmithyRuntime): Delete this decorator when switching to the orchestrator +// TODO(enableNewSmithyRuntimeCleanup): Delete this decorator when switching to the orchestrator /** * Inserts a ApiKeyAuth configuration into the operation diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index b7e2f17e11..78a100ad6a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -223,7 +223,7 @@ private class HttpAuthOperationCustomization(codegenContext: ClientCodegenContex } } - // TODO(enableNewSmithyRuntime): Make auth options additive in the config bag so that multiple codegen decorators can register them + // TODO(enableNewSmithyRuntimeLaunch): Make auth options additive in the config bag so that multiple codegen decorators can register them rustTemplate("${section.newLayerName}.set_auth_option_resolver(auth_option_resolver);", *codegenScope) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt index 967dbbc004..4644bb91e2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt @@ -42,7 +42,7 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus ServiceConfig.ConfigImpl -> rustTemplate( """ - // TODO(enableNewSmithyRuntime): Remove this doc hidden upon launch + // TODO(enableNewSmithyRuntimeLaunch): Remove this doc hidden upon launch ##[doc(hidden)] /// Returns interceptors currently registered by the user. pub fn interceptors(&self) -> impl Iterator + '_ { @@ -55,7 +55,7 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus ServiceConfig.BuilderImpl -> rustTemplate( """ - // TODO(enableNewSmithyRuntime): Remove this doc hidden upon launch + // TODO(enableNewSmithyRuntimeLaunch): Remove this doc hidden upon launch ##[doc(hidden)] /// Add an [`Interceptor`](#{Interceptor}) that runs at specific stages of the request execution pipeline. /// @@ -106,7 +106,7 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus self } - // TODO(enableNewSmithyRuntime): Remove this doc hidden upon launch + // TODO(enableNewSmithyRuntimeLaunch): Remove this doc hidden upon launch ##[doc(hidden)] /// Add a [`SharedInterceptor`](#{SharedInterceptor}) that runs at specific stages of the request execution pipeline. /// @@ -160,7 +160,7 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus self } - // TODO(enableNewSmithyRuntime): Remove this doc hidden upon launch + // TODO(enableNewSmithyRuntimeLaunch): Remove this doc hidden upon launch ##[doc(hidden)] /// Set [`SharedInterceptor`](#{SharedInterceptor})s for the builder. pub fn set_interceptors(&mut self, interceptors: impl IntoIterator) -> &mut Self { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index 307866bfac..64fb43bd63 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -56,7 +56,6 @@ class RequiredCustomizations : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - // TODO(enableNewSmithyRuntime): Keep only then branch once we switch to orchestrator if (codegenContext.smithyRuntimeMode.generateOrchestrator) { baseCustomizations + ResiliencyConfigCustomization(codegenContext) + InterceptorConfigCustomization( codegenContext, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt index c2cf1c9fd3..e9176ff767 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt @@ -102,7 +102,6 @@ class EndpointsDecorator : ClientCodegenDecorator { override val name: String = "Endpoints" override val order: Byte = 0 - // TODO(enableNewSmithyRuntime): Remove `operationCustomizations` and `InjectEndpointInMakeOperation` override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, @@ -155,6 +154,7 @@ class EndpointsDecorator : ClientCodegenDecorator { * .build(); * ``` */ + // TODO(enableNewSmithyRuntimeCleanup): Delete this customization class InjectEndpointInMakeOperation( private val ctx: ClientCodegenContext, private val typesGenerator: EndpointTypesGenerator, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt index 102d16d216..1741a08035 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingGenerator.kt @@ -75,7 +75,7 @@ class EndpointTraitBindings( } if (generateValidation) { val contents = if (smithyRuntimeMode.generateOrchestrator) { - // TODO(enableNewSmithyRuntime): Remove the allow attribute once all places need .into method + // TODO(enableNewSmithyRuntimeCleanup): Remove the allow attribute once all places need .into method """ if $field.is_empty() { ##[allow(clippy::useless_conversion)] diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt index d826a44ead..833c1f6985 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt @@ -23,7 +23,7 @@ sealed class OperationSection(name: String) : Section(name) { data class OperationImplBlock(override val customizations: List) : OperationSection("OperationImplBlock") - // TODO(enableNewSmithyRuntime): Delete this customization hook when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete this customization hook when cleaning up middleware /** Write additional functions inside the Input's impl block */ @Deprecated("customization for middleware; won't be used in the orchestrator impl") data class InputImpl( @@ -33,7 +33,7 @@ sealed class OperationSection(name: String) : Section(name) { val protocol: Protocol, ) : OperationSection("InputImpl") - // TODO(enableNewSmithyRuntime): Delete this customization hook when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete this customization hook when cleaning up middleware @Deprecated("customization for middleware; won't be used in the orchestrator impl") data class MutateInput( override val customizations: List, @@ -41,7 +41,7 @@ sealed class OperationSection(name: String) : Section(name) { val config: String, ) : OperationSection("MutateInput") - // TODO(enableNewSmithyRuntime): Delete this customization hook when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete this customization hook when cleaning up middleware /** Write custom code into the block that builds an operation * * [request]: Name of the variable holding the `aws_smithy_http::Request` @@ -55,7 +55,7 @@ sealed class OperationSection(name: String) : Section(name) { val config: String, ) : OperationSection("Feature") - // TODO(enableNewSmithyRuntime): Delete this customization hook when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete this customization hook when cleaning up middleware @Deprecated("customization for middleware; won't be used in the orchestrator impl") data class FinalizeOperation( override val customizations: List, @@ -69,7 +69,7 @@ sealed class OperationSection(name: String) : Section(name) { /** Name of the response headers map (for referring to it in Rust code) */ val responseHeadersName: String, - // TODO(enableNewSmithyRuntime): Remove this flag when switching to the orchestrator + // TODO(enableNewSmithyRuntimeCleanup): Remove this flag when switching to the orchestrator /** Whether the property bag exists in this context */ val propertyBagAvailable: Boolean, ) : OperationSection("MutateOutput") @@ -165,11 +165,11 @@ sealed class OperationSection(name: String) : Section(name) { } abstract class OperationCustomization : NamedCustomization() { - // TODO(enableNewSmithyRuntime): Delete this when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete this when cleaning up middleware @Deprecated("property for middleware; won't be used in the orchestrator impl") open fun retryType(): RuntimeType? = null - // TODO(enableNewSmithyRuntime): Delete this when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete this when cleaning up middleware /** * Does `make_operation` consume the self parameter? * @@ -179,7 +179,7 @@ abstract class OperationCustomization : NamedCustomization() { @Deprecated("property for middleware; won't be used in the orchestrator impl") open fun consumesSelf(): Boolean = false - // TODO(enableNewSmithyRuntime): Delete this when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete this when cleaning up middleware /** * Does `make_operation` mutate the self parameter? */ diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index c120fa8209..442d95ff0f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -38,10 +38,10 @@ open class OperationGenerator( * Operations generate a `make_operation(&config)` method to build a `aws_smithy_http::Operation` that can be dispatched * This is the serializer side of request dispatch */ - // TODO(enableNewSmithyRuntime): Remove the `makeOperationGenerator` + // TODO(enableNewSmithyRuntimeCleanup): Remove the `makeOperationGenerator` private val makeOperationGenerator: MakeOperationGenerator, private val bodyGenerator: ProtocolPayloadGenerator, - // TODO(enableNewSmithyRuntime): Remove the `traitGenerator` + // TODO(enableNewSmithyRuntimeCleanup): Remove the `traitGenerator` private val traitGenerator: HttpBoundProtocolTraitImplGenerator, ) { companion object { @@ -82,7 +82,7 @@ open class OperationGenerator( */ fun renderOperation( operationWriter: RustWriter, - // TODO(enableNewSmithyRuntime): Remove the `inputWriter` since `make_operation` generation is going away + // TODO(enableNewSmithyRuntimeCleanup): Remove the `inputWriter` since `make_operation` generation is going away inputWriter: RustWriter, operationShape: OperationShape, codegenDecorator: ClientCodegenDecorator, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 033c60b16b..412abd6c89 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -144,7 +144,7 @@ class ServiceRuntimePluginGenerator( self.handle.conf.endpoint_resolver()); cfg.set_endpoint_resolver(endpoint_resolver); - // TODO(enableNewSmithyRuntime): Make it possible to set retry classifiers at the service level. + // TODO(enableNewSmithyRuntimeLaunch): Make it possible to set retry classifiers at the service level. // Retry classifiers can also be set at the operation level and those should be added to the // list of classifiers defined here, rather than replacing them. @@ -159,7 +159,7 @@ class ServiceRuntimePluginGenerator( .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) { let connection: #{Box} = #{Box}::new(#{DynConnectorAdapter}::new( - // TODO(enableNewSmithyRuntime): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation + // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation connection )) as _; cfg.set_connection(connection); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index 8a64e514c1..37fac72b63 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -36,8 +36,8 @@ class CustomizableOperationGenerator( fun render(crate: RustCrate) { crate.withModule(ClientRustModule.Client.customize) { rustTemplate( - // TODO(enableNewSmithyRuntime): Stop exporting `Operation` when removing middleware - // TODO(enableNewSmithyRuntime): Re-export orchestrator equivalents for retry types when removing middleware + // TODO(enableNewSmithyRuntimeCleanup): Stop exporting `Operation` when removing middleware + // TODO(enableNewSmithyRuntimeLaunch): Re-export orchestrator equivalents for retry types when defaulting to orchestrator """ pub use #{Operation}; pub use #{Request}; @@ -171,7 +171,7 @@ class CustomizableOperationGenerator( crate.withModule(customizeModule) { renderConvenienceAliases(customizeModule, this) - // TODO(enableNewSmithyRuntime): Render it directly under the customize module when CustomizableOperation + // TODO(enableNewSmithyRuntimeCleanup): Render it directly under the customize module when CustomizableOperation // in the middleware has been removed. withInlineModule( RustModule.new( @@ -332,12 +332,12 @@ fun renderCustomizableOperationSend(codegenContext: ClientCodegenContext, generi *preludeScope, "SdkSuccess" to RuntimeType.sdkSuccess(runtimeConfig), "SdkError" to RuntimeType.sdkError(runtimeConfig), - // TODO(enableNewSmithyRuntime): Delete the trait bounds when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete the trait bounds when cleaning up middleware "ParseHttpResponse" to smithyHttp.resolve("response::ParseHttpResponse"), "NewRequestPolicy" to smithyClient.resolve("retry::NewRequestPolicy"), "SmithyRetryPolicy" to smithyClient.resolve("bounds::SmithyRetryPolicy"), "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), - // TODO(enableNewSmithyRuntime): Delete the generics when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete the generics when cleaning up middleware "combined_generics_decl" to combinedGenerics.declaration(), "handle_generics_bounds" to handleGenerics.bounds(), ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index c964df9023..7a4dc7b707 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -125,7 +125,7 @@ class FluentClientGenerator( }, "RetryConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::RetryConfig"), "TimeoutConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout::TimeoutConfig"), - // TODO(enableNewSmithyRuntime): Delete the generics when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete the generics when cleaning up middleware "generics_decl" to generics.decl, "smithy_inst" to generics.smithyInst, ) @@ -224,7 +224,7 @@ class FluentClientGenerator( } ##[doc(hidden)] - // TODO(enableNewSmithyRuntime): Delete this function when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete this function when cleaning up middleware // This is currently kept around so the tests still compile in both modes /// Creates a client with the given service configuration. pub fn with_config(_client: #{client}::Client, conf: crate::Config) -> Self { @@ -234,7 +234,7 @@ class FluentClientGenerator( } ##[doc(hidden)] - // TODO(enableNewSmithyRuntime): Delete this function when cleaning up middleware + // TODO(enableNewSmithyRuntimeCleanup): Delete this function when cleaning up middleware // This is currently kept around so the tests still compile in both modes /// Returns the client's configuration. pub fn conf(&self) -> &crate::Config { @@ -516,7 +516,7 @@ class FluentClientGenerator( } ##[doc(hidden)] - // TODO(enableNewSmithyRuntime): Remove `async` once we switch to orchestrator + // TODO(enableNewSmithyRuntimeCleanup): Remove `async` once we switch to orchestrator pub async fn customize_orchestrator( self, ) -> #{CustomizableOperation}< @@ -556,7 +556,7 @@ class FluentClientGenerator( /// Consumes this builder, creating a customizable operation that can be modified before being /// sent. - // TODO(enableNewSmithyRuntime): Remove `async` and `Result` once we switch to orchestrator + // TODO(enableNewSmithyRuntimeCleanup): Remove `async` and `Result` once we switch to orchestrator pub async fn customize( self, ) -> #{Result}< diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt index a0b9f15c1a..77b4a46019 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerics.kt @@ -15,7 +15,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -// TODO(enableNewSmithyRuntime): Delete this client generics on/off switch headache +// TODO(enableNewSmithyRuntimeCleanup): Delete this client generics on/off switch headache interface FluentClientGenerics { /** Declaration with defaults set */ val decl: Writable diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 67fd7f911a..fee8326018 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -329,7 +329,7 @@ class ServiceConfigGenerator( """ impl #{RuntimePlugin} for Builder { fn config(&self) -> #{Option}<#{FrozenLayer}> { - // TODO(enableNewSmithyRuntime): Put into `cfg` the fields in `self.config_override` that are not `None` + // TODO(enableNewSmithyRuntimeLaunch): Put into `cfg` the fields in `self.config_override` that are not `None` ##[allow(unused_mut)] let mut cfg = #{Layer}::new("service config"); #{config} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt index 127c67584f..54c2e3a7eb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt @@ -35,7 +35,7 @@ import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.letIf -// TODO(enableNewSmithyRuntime): Delete this class when cleaning up `enableNewSmithyRuntime` +// TODO(enableNewSmithyRuntimeCleanup): Delete this class when cleaning up `enableNewSmithyRuntime` /** Generates the `make_operation` function on input structs */ open class MakeOperationGenerator( protected val codegenContext: CodegenContext, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt index b3cf511549..61b0c42047 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolParserGenerator.kt @@ -55,13 +55,13 @@ class ProtocolParserGenerator( "operation" to RuntimeType.operationModule(codegenContext.runtimeConfig), "Bytes" to RuntimeType.Bytes, "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), - // TODO(enableNewSmithyRuntime): Remove the `PropertyBag` below + // TODO(enableNewSmithyRuntimeCleanup): Remove the `PropertyBag` below "PropertyBag" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("property_bag::PropertyBag"), ) fun parseResponseFn( operationShape: OperationShape, - // TODO(enableNewSmithyRuntime): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator + // TODO(enableNewSmithyRuntimeCleanup): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator propertyBagAvailable: Boolean, customizations: List, ): RuntimeType { @@ -189,7 +189,7 @@ class ProtocolParserGenerator( fun parseStreamingResponseFn( operationShape: OperationShape, - // TODO(enableNewSmithyRuntime): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator + // TODO(enableNewSmithyRuntimeCleanup): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator propertyBagAvailable: Boolean, customizations: List, ): RuntimeType { @@ -241,7 +241,7 @@ class ProtocolParserGenerator( outputShape: StructureShape, bindings: List, errorSymbol: Symbol, - // TODO(enableNewSmithyRuntime): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator + // TODO(enableNewSmithyRuntimeCleanup): Remove the `propertyBagAvailable` flag as if it were always set to `false` when switching to the orchestrator propertyBagAvailable: Boolean, customizations: List, ) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index f494c10971..49e38991d8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -33,7 +33,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctio import software.amazon.smithy.rust.codegen.core.util.hasStreamingMember import software.amazon.smithy.rust.codegen.core.util.outputShape -// TODO(enableNewSmithyRuntime): Delete this class when cleaning up `enableNewSmithyRuntime` (replace with ClientProtocolGenerator) +// TODO(enableNewSmithyRuntimeCleanup): Delete this class when cleaning up `enableNewSmithyRuntime` (replace with ClientProtocolGenerator) class HttpBoundProtocolGenerator( codegenContext: ClientCodegenContext, protocol: Protocol, @@ -52,7 +52,7 @@ class HttpBoundProtocolGenerator( HttpBoundProtocolTraitImplGenerator(codegenContext, protocol), ) -// TODO(enableNewSmithyRuntime): Completely delete `AdditionalPayloadContext` when switching to the orchestrator +// TODO(enableNewSmithyRuntimeCleanup): Completely delete `AdditionalPayloadContext` when switching to the orchestrator data class ClientAdditionalPayloadContext( val propertyBagAvailable: Boolean, ) : AdditionalPayloadContext @@ -94,7 +94,7 @@ class ClientHttpBoundProtocolPayloadGenerator( }, ) -// TODO(enableNewSmithyRuntime): Delete this class when cleaning up `enableNewSmithyRuntime` +// TODO(enableNewSmithyRuntimeCleanup): Delete this class when cleaning up `enableNewSmithyRuntime` open class HttpBoundProtocolTraitImplGenerator( codegenContext: ClientCodegenContext, protocol: Protocol, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt index f7dbbdafff..574bc2e8d3 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt @@ -70,7 +70,7 @@ internal class PaginatorGeneratorTest { } """.asSmithyModel() - // TODO(enableNewSmithyRuntime): Remove this middleware test when launching + // TODO(enableNewSmithyRuntimeCleanup): Remove this middleware test when launching @Test fun `generate paginators that compile with middleware`() { clientIntegrationTest(model) { clientCodegenContext, rustCrate -> diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestCodegenSettings.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestCodegenSettings.kt index abe664bcc4..a54397ff59 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestCodegenSettings.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestCodegenSettings.kt @@ -10,7 +10,7 @@ import software.amazon.smithy.model.node.StringNode import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams object TestCodegenSettings { - // TODO(enableNewSmithyRuntime): Delete this when removing `enableNewSmithyRuntime` feature gate + // TODO(enableNewSmithyRuntimeCleanup): Delete this when removing `enableNewSmithyRuntime` feature gate fun middlewareMode(): ObjectNode = ObjectNode.objectNodeBuilder() .withMember( "codegen", @@ -19,7 +19,7 @@ object TestCodegenSettings { ) .build() - // TODO(enableNewSmithyRuntime): Delete this when removing `enableNewSmithyRuntime` feature gate + // TODO(enableNewSmithyRuntimeCleanup): Delete this when removing `enableNewSmithyRuntime` feature gate fun orchestratorMode(): ObjectNode = ObjectNode.objectNodeBuilder() .withMember( "codegen", @@ -28,11 +28,11 @@ object TestCodegenSettings { ) .build() - // TODO(enableNewSmithyRuntime): Delete this when removing `enableNewSmithyRuntime` feature gate + // TODO(enableNewSmithyRuntimeCleanup): Delete this when removing `enableNewSmithyRuntime` feature gate val middlewareModeTestParams get(): IntegrationTestParams = IntegrationTestParams(additionalSettings = middlewareMode()) - // TODO(enableNewSmithyRuntime): Delete this when removing `enableNewSmithyRuntime` feature gate + // TODO(enableNewSmithyRuntimeCleanup): Delete this when removing `enableNewSmithyRuntime` feature gate val orchestratorModeTestParams get(): IntegrationTestParams = IntegrationTestParams(additionalSettings = orchestratorMode()) } diff --git a/rust-runtime/aws-smithy-eventstream/src/frame.rs b/rust-runtime/aws-smithy-eventstream/src/frame.rs index edf48e60de..7d2d65ce64 100644 --- a/rust-runtime/aws-smithy-eventstream/src/frame.rs +++ b/rust-runtime/aws-smithy-eventstream/src/frame.rs @@ -101,7 +101,7 @@ impl DeferredSigner { .unwrap() .try_recv() .ok() - // TODO(enableNewSmithyRuntime): When the middleware implementation is removed, + // TODO(enableNewSmithyRuntimeCleanup): When the middleware implementation is removed, // this should panic rather than default to the `NoOpSigner`. The reason it defaults // is because middleware-based generic clients don't have any default middleware, // so there is no way to send a `NoOpSigner` by default when there is no other diff --git a/rust-runtime/aws-smithy-http-auth/src/lib.rs b/rust-runtime/aws-smithy-http-auth/src/lib.rs index 9b9977d2ec..8f5f956ced 100644 --- a/rust-runtime/aws-smithy-http-auth/src/lib.rs +++ b/rust-runtime/aws-smithy-http-auth/src/lib.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO(enableNewSmithyRuntime): The contents of this crate are moving into aws-smithy-runtime. +// TODO(enableNewSmithyRuntimeCleanup): The contents of this crate are moving into aws-smithy-runtime. // This crate is kept to continue sorting the middleware implementation until it is removed. // When removing the old implementation, clear out this crate and deprecate it. diff --git a/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs b/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs index fd934058d2..1fbb099b11 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint/middleware.rs @@ -13,7 +13,7 @@ use http::header::HeaderName; use http::{HeaderValue, Uri}; use std::str::FromStr; -// TODO(enableNewSmithyRuntime): Delete this module +// TODO(enableNewSmithyRuntimeCleanup): Delete this module /// Middleware to apply an HTTP endpoint to the request /// diff --git a/rust-runtime/aws-smithy-http/src/operation.rs b/rust-runtime/aws-smithy-http/src/operation.rs index d5b62898ae..dadc22b282 100644 --- a/rust-runtime/aws-smithy-http/src/operation.rs +++ b/rust-runtime/aws-smithy-http/src/operation.rs @@ -60,7 +60,7 @@ pub struct Parts { pub metadata: Option, } -// TODO(enableNewSmithyRuntime): Delete `operation::Operation` when cleaning up middleware +// TODO(enableNewSmithyRuntimeCleanup): Delete `operation::Operation` when cleaning up middleware /// An [`Operation`] is a request paired with a response handler, retry classifier, /// and metadata that identifies the API being called. /// @@ -160,7 +160,7 @@ impl Operation { } } -// TODO(enableNewSmithyRuntime): Delete `operation::Request` when cleaning up middleware +// TODO(enableNewSmithyRuntimeCleanup): Delete `operation::Request` when cleaning up middleware /// Operation request type that associates a property bag with an underlying HTTP request. /// This type represents the request in the Tower `Service` in middleware so that middleware /// can share information with each other via the properties. @@ -252,7 +252,7 @@ impl Request { } } -// TODO(enableNewSmithyRuntime): Delete `operation::Response` when cleaning up middleware +// TODO(enableNewSmithyRuntimeCleanup): Delete `operation::Response` when cleaning up middleware /// Operation response type that associates a property bag with an underlying HTTP response. /// This type represents the response in the Tower `Service` in middleware so that middleware /// can share information with each other via the properties. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 03cb367556..f3b50e281e 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -294,7 +294,6 @@ impl InterceptorContext { } // Otherwise, rewind to the saved request checkpoint - // TODO(enableNewSmithyRuntime): Also rewind the ConfigBag self.phase = Phase::BeforeTransmit; self.request = try_clone(self.request_checkpoint.as_ref().expect("checked above")); assert!( diff --git a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs b/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs index 2ca2db43b1..8c00beae6c 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs @@ -225,7 +225,6 @@ impl TestConnection { impl Connection for TestConnection { fn call(&self, request: HttpRequest) -> BoxFuture { - // TODO(enableNewSmithyRuntime) Validate request let (res, simulated_latency) = if let Some(event) = self.data.lock().unwrap().pop() { self.requests.lock().unwrap().push(ValidateRequest { expected: event.req, diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index 468d0957b9..bf85fc5855 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -34,7 +34,7 @@ pub struct StandardRetryStrategy { impl StandardRetryStrategy { pub fn new(retry_config: &RetryConfig) -> Self { - // TODO(enableNewSmithyRuntime) add support for `retry_config.reconnect_mode()` here or in the orchestrator flow. + // TODO(enableNewSmithyRuntimeLaunch) add support for `retry_config.reconnect_mode()` here or in the orchestrator flow. Self::default() .with_max_attempts(retry_config.max_attempts() as usize) .with_initial_backoff(retry_config.initial_backoff()) diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs index 2383b0fb22..357894f82f 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO(enableNewSmithyRuntime): Delete this file once test helpers on `CustomizableOperation` have been removed +// TODO(enableNewSmithyRuntimeCleanup): Delete this file once test helpers on `CustomizableOperation` have been removed use aws_smithy_runtime_api::client::interceptors::{ BeforeTransmitInterceptorContextMut, BoxError, Interceptor, diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index 3df626f1f9..1d756d5802 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -14,7 +14,7 @@ unreachable_pub )] pub mod base64; -//TODO(enableNewSmithyRuntime): Unhide this module when switching to the orchestrator +//TODO(enableNewSmithyRuntimeLaunch): Unhide this module when switching to the orchestrator #[doc(hidden)] /// A typemap for storing configuration. pub mod config_bag; @@ -25,7 +25,7 @@ pub mod primitive; pub mod retry; pub mod timeout; -//TODO(enableNewSmithyRuntime): Unhide this module when switching to the orchestrator +//TODO(enableNewSmithyRuntimeLaunch): Unhide this module when switching to the orchestrator #[doc(hidden)] /// Utilities for type erasure. pub mod type_erasure; diff --git a/tools/ci-scripts/check-aws-sdk-adhoc-tests b/tools/ci-scripts/check-aws-sdk-adhoc-tests index 71e11c66ba..911d11f998 100755 --- a/tools/ci-scripts/check-aws-sdk-adhoc-tests +++ b/tools/ci-scripts/check-aws-sdk-adhoc-tests @@ -10,7 +10,7 @@ C_RESET='\033[0m' set -eu cd smithy-rs -# TODO(enableNewSmithyRuntime): Remove the middleware test run when cleaning up middleware +# TODO(enableNewSmithyRuntimeCleanup): Remove the middleware test run when cleaning up middleware echo -e "## ${C_YELLOW}Running SDK adhoc tests against the middleware implementation...${C_RESET}" ./gradlew aws:sdk-adhoc-test:clean ./gradlew aws:sdk-adhoc-test:check -Psmithy.runtime.mode=middleware diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index e9889a2980..226cc8a386 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -12,7 +12,7 @@ C_RESET='\033[0m' set -eu cd smithy-rs -# TODO(enableNewSmithyRuntime): Move these into `services_that_pass_tests` as more progress is made +# TODO(enableNewSmithyRuntimeLaunch): Move these into `services_that_pass_tests` as more progress is made services_that_compile=(\ "aws-config"\ "dynamodb"\ diff --git a/tools/ci-scripts/codegen-diff/semver-checks.py b/tools/ci-scripts/codegen-diff/semver-checks.py index 054315fb56..3210e564e9 100755 --- a/tools/ci-scripts/codegen-diff/semver-checks.py +++ b/tools/ci-scripts/codegen-diff/semver-checks.py @@ -34,7 +34,7 @@ def main(skip_generation=False): os.chdir(sdk_directory) failed = False - # TODO(enableNewSmithyRuntime): Remove the deny list below + # TODO(enableNewSmithyRuntimeLaunch): Remove the deny list below deny_list = [ "aws-runtime", "aws-runtime-api", From b2bdcba57ae04b1efca3398333f0151d2b4e2749 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Thu, 15 Jun 2023 22:37:24 +0100 Subject: [PATCH 161/253] Parameterize `Plugin` by service rather than protocol (#2772) ## Motivation and Context Closes https://github.com/awslabs/smithy-rs/issues/1839 Currently, `Plugin` is parameterized by protocol and operation. To improve symmetry, extensibility and uniformity we switch this to be parameterized by service instead. The protocol can still be recovered via the `type Protocol` associated type on `ServiceShape`. ## Description - Add `ServiceShape` trait, encoding the properties of a Smithy service. - Change `Plugin` to `Plugin`. - Add `FilterByOperation` and `filter_by_operation` `Plugin`s. --- CHANGELOG.next.toml | 126 ++++++++++++++++-- .../smithy/generators/ScopeMacroGenerator.kt | 2 +- .../generators/ServerServiceGenerator.kt | 115 +++++++++++++--- design/src/server/anatomy.md | 34 ++--- design/src/server/middleware.md | 63 +++------ .../tests/plugins_execution_order.rs | 6 +- examples/pokemon-service/src/plugin.rs | 23 +++- .../aws-smithy-http-server/src/extension.rs | 6 +- .../src/instrumentation/plugin.rs | 8 +- .../aws-smithy-http-server/src/lib.rs | 1 + .../src/operation/upgrade.rs | 9 +- .../src/plugin/closure.rs | 71 +++++----- .../src/plugin/either.rs | 14 +- .../src/plugin/filter.rs | 77 ++++++----- .../src/plugin/identity.rs | 4 +- .../src/plugin/layer.rs | 20 +-- .../aws-smithy-http-server/src/plugin/mod.rs | 96 +++++++++---- .../src/plugin/pipeline.rs | 36 ++--- .../src/plugin/scoped.rs | 38 +++--- .../src/plugin/stack.rs | 12 +- .../aws-smithy-http-server/src/service.rs | 89 +++++++++++++ .../aws-smithy-http-server/src/shape_id.rs | 19 ++- 22 files changed, 593 insertions(+), 276 deletions(-) create mode 100644 rust-runtime/aws-smithy-http-server/src/service.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 2da07d6a16..dbc42020a6 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -182,15 +182,64 @@ meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } author = "ysaito1001" [[smithy-rs]] -message = """ -The middleware system has been reworked as we push for a unified, simple, and consistent API. The following changes have been made in service of this goal: +message = """The middleware system has been reworked as we push for a unified, simple, and consistent API. The following changes have been made in service of this goal: +- A `ServiceShape` trait has been added. - The `Plugin` trait has been simplified. - The `Operation` structure has been removed. - A `Scoped` `Plugin` has been added. The `Plugin` trait has now been simplified and the `Operation` struct has been removed. +## Addition of `ServiceShape` + +Since the [0.52 release](https://github.com/awslabs/smithy-rs/releases/tag/release-2022-12-12) the `OperationShape` has existed. + +```rust +/// Models the [Smithy Operation shape]. +/// +/// [Smithy Operation shape]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#operation +pub trait OperationShape { + /// The ID of the operation. + const ID: ShapeId; + + /// The operation input. + type Input; + /// The operation output. + type Output; + /// The operation error. [`Infallible`](std::convert::Infallible) in the case where no error + /// exists. + type Error; +} +``` + +This allowed `Plugin` authors to access these associated types and constants. See the [`PrintPlugin`](https://github.com/awslabs/smithy-rs/blob/main/examples/pokemon-service/src/plugin.rs) as an example. + +We continue with this approach and introduce the following trait: + +```rust +/// Models the [Smithy Service shape]. +/// +/// [Smithy Service shape]: https://smithy.io/2.0/spec/service-types.html +pub trait ServiceShape { + /// The [`ShapeId`] of the service. + const ID: ShapeId; + + /// The version of the service. + const VERSION: Option<&'static str>; + + /// The [Protocol] applied to this service. + /// + /// [Protocol]: https://smithy.io/2.0/spec/protocol-traits.html + type Protocol; + + /// An enumeration of all operations contained in this service. + type Operations; +} +``` + +With the changes to `Plugin`, described below, middleware authors now have access to this information at compile time. + ## Simplication of the `Plugin` trait Previously, @@ -209,14 +258,16 @@ modified an `Operation`. Now, ```rust -trait Plugin { - type Service; +trait Plugin { + type Output; - fn apply(&self, svc: S) -> Self::Service; + fn apply(&self, input: T) -> Self::Output; } ``` -maps a `tower::Service` to a `tower::Service`. This is equivalent to `tower::Layer` with two extra type parameters: `Protocol` and `Operation`. +maps a `tower::Service` to a `tower::Service`. This is equivalent to `tower::Layer` with two extra type parameters: `Service` and `Operation`, which implement `ServiceShape` and `OperationShape` respectively. + +Having both `Service` and `Operation` as type parameters also provides an even surface for advanced users to extend the codegenerator in a structured way. See [this issue](https://github.com/awslabs/smithy-rs/issues/2777) for more context. The following middleware setup @@ -286,18 +337,33 @@ where pub struct PrintPlugin; -impl Plugin for PrintPlugin +impl Plugin for PrintPlugin where Op: OperationShape, { - type Service = PrintService; + type Output = PrintService; - fn apply(&self, svc: S) -> Self::Service { + fn apply(&self, inner: T) -> Self::Output { PrintService { inner, name: Op::ID.name() } } } ``` +Alternatively, using the new `ServiceShape`, implemented on `Ser`: + +```rust +impl Plugin for PrintPlugin +where + Ser: ServiceShape, +{ + type Service = PrintService; + + fn apply(&self, inner: T) -> Self::Service { + PrintService { inner, name: Ser::ID.name() } + } +} +``` + A single `Plugin` can no longer apply a `tower::Layer` on HTTP requests/responses _and_ modelled structures at the same time (see middleware positions [C](https://awslabs.github.io/smithy-rs/design/server/middleware.html#c-operation-specific-http-middleware) and [D](https://awslabs.github.io/smithy-rs/design/server/middleware.html#d-operation-specific-model-middleware). Instead one `Plugin` must be specified for each and passed to the service builder constructor separately: ```rust @@ -442,3 +508,45 @@ message = "Add a `send_with` function on `-Input` types for sending requests wit author = "thomas-k-cameron" references = ["smithy-rs#2652"] meta = { "breaking" = false, "tada" = true, "bug" = false } + +[[smithy-rs]] +message = """Remove `filter_by_operation_id` and `plugin_from_operation_id_fn` in favour of `filter_by_operation` and `plugin_from_operation_fn`. + +Previously, we provided `filter_by_operation_id` which filtered `Plugin` application via a predicate over the Shape ID. + +```rust +use aws_smithy_http_server::plugin::filter_by_operation_id; +use pokemon_service_server_sdk::operation_shape::CheckHealth; + +let filtered = filter_by_operation_id(plugin, |name| name != CheckHealth::NAME); +``` + +This had the problem that the user is unable to exhaustively match over a `&'static str`. To remedy this we have switched to `filter_by_operation` which is a predicate over an enum containing all operations contained in the service. + +```rust +use aws_smithy_http_server::plugin::filter_by_operation_id; +use pokemon_service_server_sdk::service::Operation; + +let filtered = filter_by_operation(plugin, |op: Operation| op != Operation::CheckHealth); +``` + +Similarly, `plugin_from_operation_fn` now allows for + +```rust +use aws_smithy_http_server::plugin::plugin_from_operation_fn; +use pokemon_service_server_sdk::service::Operation; + +fn map(op: Operation, inner: S) -> PrintService { + match op { + Operation::CheckHealth => PrintService { name: op.shape_id().name(), inner }, + Operation::GetPokemonSpecies => PrintService { name: "hello world", inner }, + _ => todo!() + } +} + +let plugin = plugin_from_operation_fn(map); +``` +""" +references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "hlbarber" diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt index 2b016fd6cf..bb31a55d26 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ScopeMacroGenerator.kt @@ -108,7 +108,7 @@ class ScopeMacroGenerator( /// ## use #{SmithyHttpServer}::plugin::{Plugin, Scoped}; /// ## use $crateName::scope; /// ## struct MockPlugin; - /// ## impl Plugin for MockPlugin { type Service = u32; fn apply(&self, svc: S) -> u32 { 3 } } + /// ## impl Plugin for MockPlugin { type Output = u32; fn apply(&self, input: T) -> u32 { 3 } } /// ## let scoped_a = Scoped::new::(MockPlugin); /// ## let scoped_b = Scoped::new::(MockPlugin); /// ## let a = Plugin::<(), $crateName::operation_shape::$firstOperationName, u64>::apply(&scoped_a, 6); diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt index 57dd3c82b4..2852734b3f 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt @@ -50,7 +50,8 @@ class ServerServiceGenerator( private val crateName = codegenContext.moduleUseName() private val service = codegenContext.serviceShape - private val serviceName = service.id.name.toPascalCase() + private val serviceId = service.id + private val serviceName = serviceId.name.toPascalCase() private val builderName = "${serviceName}Builder" private val builderBodyGenericTypeName = "Body" @@ -136,30 +137,30 @@ class ServerServiceGenerator( HandlerType: #{SmithyHttpServer}::operation::Handler, ModelPlugin: #{SmithyHttpServer}::plugin::Plugin< - #{Protocol}, + $serviceName, crate::operation_shape::$structName, #{SmithyHttpServer}::operation::IntoService >, #{SmithyHttpServer}::operation::UpgradePlugin::: #{SmithyHttpServer}::plugin::Plugin< - #{Protocol}, + $serviceName, crate::operation_shape::$structName, - ModelPlugin::Service + ModelPlugin::Output >, HttpPlugin: #{SmithyHttpServer}::plugin::Plugin< - #{Protocol}, + $serviceName, crate::operation_shape::$structName, < #{SmithyHttpServer}::operation::UpgradePlugin:: as #{SmithyHttpServer}::plugin::Plugin< - #{Protocol}, + $serviceName, crate::operation_shape::$structName, - ModelPlugin::Service + ModelPlugin::Output > - >::Service + >::Output >, - HttpPlugin::Service: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, - >>::Future: Send + 'static, + HttpPlugin::Output: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, + >>::Future: Send + 'static, { use #{SmithyHttpServer}::operation::OperationShapeExt; @@ -199,30 +200,30 @@ class ServerServiceGenerator( S: #{SmithyHttpServer}::operation::OperationService, ModelPlugin: #{SmithyHttpServer}::plugin::Plugin< - #{Protocol}, + $serviceName, crate::operation_shape::$structName, #{SmithyHttpServer}::operation::Normalize >, #{SmithyHttpServer}::operation::UpgradePlugin::: #{SmithyHttpServer}::plugin::Plugin< - #{Protocol}, + $serviceName, crate::operation_shape::$structName, - ModelPlugin::Service + ModelPlugin::Output >, HttpPlugin: #{SmithyHttpServer}::plugin::Plugin< - #{Protocol}, + $serviceName, crate::operation_shape::$structName, < #{SmithyHttpServer}::operation::UpgradePlugin:: as #{SmithyHttpServer}::plugin::Plugin< - #{Protocol}, + $serviceName, crate::operation_shape::$structName, - ModelPlugin::Service + ModelPlugin::Output > - >::Service + >::Output >, - HttpPlugin::Service: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, - >>::Future: Send + 'static, + HttpPlugin::Output: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, + >>::Future: Send + 'static, { use #{SmithyHttpServer}::operation::OperationShapeExt; @@ -353,7 +354,6 @@ class ServerServiceGenerator( for (operationShape in operations) { val fieldName = builderFieldNames[operationShape]!! val (specBuilderFunctionName, _) = requestSpecMap.getValue(operationShape) - val operationZstTypeName = operationStructNames[operationShape]!! rustTemplate( """ ( @@ -590,6 +590,75 @@ class ServerServiceGenerator( ) } + private fun serviceShapeImpl(): Writable = writable { + val namespace = serviceId.namespace + val name = serviceId.name + val absolute = serviceId.toString().replace("#", "##") + val version = codegenContext.serviceShape.version?.let { "Some(\"$it\")" } ?: "None" + rustTemplate( + """ + impl #{SmithyHttpServer}::service::ServiceShape for $serviceName { + const ID: #{SmithyHttpServer}::shape_id::ShapeId = #{SmithyHttpServer}::shape_id::ShapeId::new("$absolute", "$namespace", "$name"); + + const VERSION: Option<&'static str> = $version; + + type Protocol = #{Protocol}; + + type Operations = Operation; + } + """, + "Protocol" to protocol.markerStruct(), + *codegenScope, + ) + } + + private fun operationEnum(): Writable = writable { + val operations = operationStructNames.values.joinToString(",") + val matchArms: Writable = operationStructNames.map { + (shape, name) -> + writable { + val absolute = shape.id.toString().replace("#", "##") + rustTemplate( + """ + Operation::$name => #{SmithyHttpServer}::shape_id::ShapeId::new("$absolute", "${shape.id.namespace}", "${shape.id.name}") + """, + *codegenScope, + ) + } + }.join(",") + rustTemplate( + """ + /// An enumeration of all [operations](https://smithy.io/2.0/spec/service-types.html##operation) in $serviceName. + ##[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub enum Operation { + $operations + } + + impl Operation { + /// Returns the [operations](https://smithy.io/2.0/spec/service-types.html##operation) [`ShapeId`](#{SmithyHttpServer}::shape_id::ShapeId). + pub fn shape_id(&self) -> #{SmithyHttpServer}::shape_id::ShapeId { + match self { + #{Arms} + } + } + } + """, + *codegenScope, + "Arms" to matchArms, + ) + + for ((_, value) in operationStructNames) { + rustTemplate( + """ + impl #{SmithyHttpServer}::service::ContainsOperation for $serviceName { + const VALUE: Operation = Operation::$value; + } + """, + *codegenScope, + ) + } + } + fun render(writer: RustWriter) { writer.rustTemplate( """ @@ -600,11 +669,17 @@ class ServerServiceGenerator( #{RequestSpecs:W} #{Struct:W} + + #{Operations} + + #{ServiceImpl} """, "Builder" to builder(), "MissingOperationsError" to missingOperationsError(), "RequestSpecs" to requestSpecsModule(), "Struct" to serviceStruct(), + "Operations" to operationEnum(), + "ServiceImpl" to serviceShapeImpl(), *codegenScope, ) } diff --git a/design/src/server/anatomy.md b/design/src/server/anatomy.md index 6ed01d0f50..7089acdfa6 100644 --- a/design/src/server/anatomy.md +++ b/design/src/server/anatomy.md @@ -425,20 +425,20 @@ state in <> A [`Plugin`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/plugin/trait.Plugin.html) is a -[`tower::Layer`] with two extra type parameters, `Protocol` and `Operation`. This allows the middleware to be +[`tower::Layer`] with two extra type parameters, `Service` and `Operation`, corresponding to [Smithy Service](https://awslabs.github.io/smithy/2.0/spec/service-types.html#service) and [Smithy Operation](https://awslabs.github.io/smithy/2.0/spec/service-types.html#operation). This allows the middleware to be parameterized them and change behavior depending on the context in which it's applied. ```rust # extern crate aws_smithy_http_server; -pub trait Plugin { - type Service; +pub trait Plugin { + type Output; - fn apply(&self, svc: S) -> Self::Service; + fn apply(&self, input: T) -> Self::Output; } # use aws_smithy_http_server::plugin::Plugin as Pl; -# impl> Plugin for T { -# type Service = >::Service; -# fn apply(&self, svc: S) -> Self::Service { >::apply(self, svc) } +# impl> Plugin for U { +# type Output = >::Output; +# fn apply(&self, input: T) -> Self::Output { >::apply(self, input) } # } ``` @@ -538,19 +538,19 @@ The builder has two setter methods for each [Smithy Operation](https://awslabs.g HandlerType:Handler, ModelPlugin: Plugin< - RestJson1, + PokemonService, GetPokemonSpecies, IntoService >, UpgradePlugin::: Plugin< - RestJson1, + PokemonService, GetPokemonSpecies, - ModelPlugin::Service + ModelPlugin::Output >, HttpPlugin: Plugin< - RestJson1, + PokemonService, GetPokemonSpecies, - UpgradePlugin::::Service + UpgradePlugin::::Output >, { let svc = GetPokemonSpecies::from_handler(handler); @@ -566,19 +566,19 @@ The builder has two setter methods for each [Smithy Operation](https://awslabs.g S: OperationService, ModelPlugin: Plugin< - RestJson1, + PokemonService, GetPokemonSpecies, Normalize >, UpgradePlugin::: Plugin< - RestJson1, + PokemonService, GetPokemonSpecies, - ModelPlugin::Service + ModelPlugin::Output >, HttpPlugin: Plugin< - RestJson1, + PokemonService, GetPokemonSpecies, - UpgradePlugin::::Service + UpgradePlugin::::Output >, { let svc = GetPokemonSpecies::from_service(service); diff --git a/design/src/server/middleware.md b/design/src/server/middleware.md index 66a373a4f6..409c8700fa 100644 --- a/design/src/server/middleware.md +++ b/design/src/server/middleware.md @@ -284,14 +284,17 @@ Suppose we want to apply a different `Layer` to every operation. In this case, p Consider the following middleware: ```rust +# extern crate aws_smithy_http_server; # extern crate tower; +use aws_smithy_http_server::shape_id::ShapeId; use std::task::{Context, Poll}; use tower::Service; /// A [`Service`] that adds a print log. pub struct PrintService { inner: S, - name: &'static str, + operation_id: ShapeId, + service_id: ShapeId } impl Service for PrintService @@ -307,7 +310,7 @@ where } fn call(&mut self, req: R) -> Self::Future { - println!("Hi {}", self.name); + println!("Hi {} in {}", self.operation_id.name(), self.service_id.name()); self.inner.call(req) } } @@ -319,57 +322,27 @@ An example of a `PrintPlugin` which prints the operation name: ```rust # extern crate aws_smithy_http_server; -# pub struct PrintService { inner: S, name: &'static str } -use aws_smithy_http_server::{plugin::Plugin, operation::OperationShape}; +# use aws_smithy_http_server::shape_id::ShapeId; +# pub struct PrintService { inner: S, operation_id: ShapeId, service_id: ShapeId } +use aws_smithy_http_server::{plugin::Plugin, operation::OperationShape, service::ServiceShape}; /// A [`Plugin`] for a service builder to add a [`PrintService`] over operations. #[derive(Debug)] pub struct PrintPlugin; -impl Plugin for PrintPlugin +impl Plugin for PrintPlugin where + Ser: ServiceShape, Op: OperationShape, { - type Service = PrintService; - - fn apply(&self, inner: S) -> Self::Service { - PrintService { name: Op::ID.name(), inner } - } -} -``` - -An alternative example which prints the protocol name: + type Output = PrintService; -```rust -# extern crate aws_smithy_http_server; -# pub struct PrintService { name: &'static str, inner: S} -use aws_smithy_http_server::{ - plugin::Plugin, - proto::{ - aws_json_10::AwsJson1_0, - rest_xml::RestXml, - } -}; - -/// A [`Plugin`] for a service builder to add a [`PrintService`] over operations. -#[derive(Debug)] -pub struct PrintPlugin; - -impl Plugin for PrintPlugin -{ - type Service = PrintService; - - fn apply(&self, inner: S) -> Self::Service { - PrintService { name: "AWS JSON 1.0", inner } - } -} - -impl Plugin for PrintPlugin -{ - type Service = PrintService; - - fn apply(&self, inner: S) -> Self::Service { - PrintService { name: "AWS REST XML", inner } + fn apply(&self, inner: T) -> Self::Output { + PrintService { + inner, + operation_id: Op::ID, + service_id: Ser::ID, + } } } ``` @@ -403,7 +376,7 @@ This allows for: # extern crate aws_smithy_http_server; # use aws_smithy_http_server::plugin::{PluginStack, Plugin}; # struct PrintPlugin; -# impl Plugin for PrintPlugin { type Service = S; fn apply(&self, svc: S) -> Self::Service { svc }} +# impl Plugin for PrintPlugin { type Output = T; fn apply(&self, svc: T) -> Self::Output { svc }} # trait PrintExt { fn print(self) -> PluginPipeline>; } # impl PrintExt for PluginPipeline { fn print(self) -> PluginPipeline> { self.push(PrintPlugin) }} # use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, input::*, output::*, error::*}; diff --git a/examples/pokemon-service-common/tests/plugins_execution_order.rs b/examples/pokemon-service-common/tests/plugins_execution_order.rs index 901ed89fc1..de9f2632f1 100644 --- a/examples/pokemon-service-common/tests/plugins_execution_order.rs +++ b/examples/pokemon-service-common/tests/plugins_execution_order.rs @@ -65,10 +65,10 @@ impl SentinelPlugin { } } -impl Plugin for SentinelPlugin { - type Service = SentinelService; +impl Plugin for SentinelPlugin { + type Output = SentinelService; - fn apply(&self, inner: S) -> Self::Service { + fn apply(&self, inner: T) -> Self::Output { SentinelService { inner, name: self.name, diff --git a/examples/pokemon-service/src/plugin.rs b/examples/pokemon-service/src/plugin.rs index e423663164..e030d3a472 100644 --- a/examples/pokemon-service/src/plugin.rs +++ b/examples/pokemon-service/src/plugin.rs @@ -8,6 +8,7 @@ use aws_smithy_http_server::{ operation::OperationShape, plugin::{Plugin, PluginPipeline, PluginStack}, + service::ServiceShape, shape_id::ShapeId, }; use tower::Service; @@ -18,7 +19,8 @@ use std::task::{Context, Poll}; #[derive(Clone, Debug)] pub struct PrintService { inner: S, - id: ShapeId, + operation_id: ShapeId, + service_id: ShapeId, } impl Service for PrintService @@ -34,7 +36,11 @@ where } fn call(&mut self, req: R) -> Self::Future { - println!("Hi {}", self.id.absolute()); + println!( + "Hi {} in {}", + self.operation_id.absolute(), + self.service_id.absolute() + ); self.inner.call(req) } } @@ -42,14 +48,19 @@ where #[derive(Debug)] pub struct PrintPlugin; -impl Plugin for PrintPlugin +impl Plugin for PrintPlugin where + Ser: ServiceShape, Op: OperationShape, { - type Service = PrintService; + type Output = PrintService; - fn apply(&self, inner: S) -> Self::Service { - PrintService { inner, id: Op::ID } + fn apply(&self, inner: T) -> Self::Output { + PrintService { + inner, + operation_id: Op::ID, + service_id: Ser::ID, + } } } /// This provides a [`print`](PrintExt::print) method on [`PluginPipeline`]. diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index bd25a3f5f9..be04b9c760 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -114,13 +114,13 @@ impl fmt::Debug for OperationExtensionPlugin { } } -impl Plugin for OperationExtensionPlugin +impl Plugin for OperationExtensionPlugin where Op: OperationShape, { - type Service = OperationExtensionService; + type Output = OperationExtensionService; - fn apply(&self, inner: S) -> Self::Service { + fn apply(&self, inner: T) -> Self::Output { OperationExtensionService { inner, operation_extension: OperationExtension(Op::ID), diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs index ba7104801d..ee88555d73 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs @@ -13,15 +13,15 @@ use super::InstrumentOperation; #[derive(Debug)] pub struct InstrumentPlugin; -impl Plugin for InstrumentPlugin +impl Plugin for InstrumentPlugin where Op: OperationShape, Op: Sensitivity, { - type Service = InstrumentOperation; + type Output = InstrumentOperation; - fn apply(&self, svc: S) -> Self::Service { - InstrumentOperation::new(svc, Op::ID) + fn apply(&self, input: T) -> Self::Output { + InstrumentOperation::new(input, Op::ID) .request_fmt(Op::request_fmt()) .response_fmt(Op::response_fmt()) } diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index c6d1175c37..139f30f1ca 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -28,6 +28,7 @@ pub mod response; pub mod routing; #[doc(hidden)] pub mod runtime_error; +pub mod service; pub mod shape_id; #[doc(inline)] diff --git a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs index f0ed2a0604..15e87ebc19 100644 --- a/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs +++ b/rust-runtime/aws-smithy-http-server/src/operation/upgrade.rs @@ -18,7 +18,7 @@ use tracing::error; use crate::{ body::BoxBody, plugin::Plugin, request::FromRequest, response::IntoResponse, - runtime_error::InternalFailureException, + runtime_error::InternalFailureException, service::ServiceShape, }; use super::OperationShape; @@ -47,13 +47,14 @@ impl UpgradePlugin { } } -impl Plugin for UpgradePlugin +impl Plugin for UpgradePlugin where + Ser: ServiceShape, Op: OperationShape, { - type Service = Upgrade; + type Output = Upgrade; - fn apply(&self, inner: S) -> Self::Service { + fn apply(&self, inner: T) -> Self::Output { Upgrade { _protocol: PhantomData, _input: PhantomData, diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs index 2429cc25f1..d78ba4c937 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/closure.rs @@ -3,58 +3,69 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::operation::OperationShape; -use crate::shape_id::ShapeId; +use crate::service::ContainsOperation; use super::Plugin; -/// An adapter to convert a `Fn(ShapeId) -> Layer` closure into a [`Plugin`]. See [`plugin_from_operation_id_fn`] for more details. -pub struct OperationIdFn { +/// An adapter to convert a `Fn(ShapeId, T) -> Service` closure into a [`Plugin`]. See [`plugin_from_operation_fn`] for more details. +pub struct OperationFn { f: F, } -impl Plugin for OperationIdFn +impl Plugin for OperationFn where - F: Fn(ShapeId, S) -> NewService, - Op: OperationShape, + Ser: ContainsOperation, + F: Fn(Ser::Operations, T) -> NewService, { - type Service = NewService; + type Output = NewService; - fn apply(&self, svc: S) -> Self::Service { - (self.f)(Op::ID, svc) + fn apply(&self, input: T) -> Self::Output { + (self.f)(Ser::VALUE, input) } } -/// Constructs a [`Plugin`] using a closure over the operation name `F: Fn(ShapeId) -> L` where `L` is a HTTP -/// [`Layer`](tower::Layer). +/// Constructs a [`Plugin`] using a closure over the [`ServiceShape::] `F: Fn(ShapeId, T) -> Service`. /// /// # Example /// /// ```rust -/// use aws_smithy_http_server::plugin::plugin_from_operation_id_fn; -/// use aws_smithy_http_server::shape_id::ShapeId; +/// # use aws_smithy_http_server::{service::*, operation::OperationShape, plugin::Plugin, shape_id::ShapeId}; +/// # pub enum Operation { CheckHealth, GetPokemonSpecies } +/// # impl Operation { fn shape_id(&self) -> ShapeId { ShapeId::new("", "", "") }} +/// # pub struct CheckHealth; +/// # pub struct GetPokemonSpecies; +/// # pub struct PokemonService; +/// # impl ServiceShape for PokemonService { +/// # const ID: ShapeId = ShapeId::new("", "", ""); +/// # const VERSION: Option<&'static str> = None; +/// # type Protocol = (); +/// # type Operations = Operation; +/// # } +/// # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +/// # impl OperationShape for GetPokemonSpecies { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +/// # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::CheckHealth; } +/// # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::GetPokemonSpecies; } +/// use aws_smithy_http_server::plugin::plugin_from_operation_fn; /// use tower::layer::layer_fn; /// -/// // A `Service` which prints the operation name before calling `S`. -/// struct PrintService { -/// operation_name: ShapeId, +/// struct FooService { +/// info: String, /// inner: S /// } /// -/// // A `Layer` applying `PrintService`. -/// struct PrintLayer { -/// operation_name: ShapeId +/// fn map(op: Operation, inner: S) -> FooService { +/// match op { +/// Operation::CheckHealth => FooService { info: op.shape_id().name().to_string(), inner }, +/// Operation::GetPokemonSpecies => FooService { info: "bar".to_string(), inner }, +/// _ => todo!() +/// } /// } /// -/// // Defines a closure taking the operation name to `PrintLayer`. -/// let f = |operation_name| PrintLayer { operation_name }; -/// -/// // This plugin applies the `PrintService` middleware around every operation. -/// let plugin = plugin_from_operation_id_fn(f); +/// // This plugin applies the `FooService` middleware around every operation. +/// let plugin = plugin_from_operation_fn(map); +/// # let _ = Plugin::::apply(&plugin, ()); +/// # let _ = Plugin::::apply(&plugin, ()); /// ``` -pub fn plugin_from_operation_id_fn(f: F) -> OperationIdFn -where - F: Fn(ShapeId) -> NewService, -{ - OperationIdFn { f } +pub fn plugin_from_operation_fn(f: F) -> OperationFn { + OperationFn { f } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/either.rs b/rust-runtime/aws-smithy-http-server/src/plugin/either.rs index 2054e906e8..b2da9cc5f0 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/either.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/either.rs @@ -102,20 +102,20 @@ where } } -impl Plugin for Either +impl Plugin for Either where - Le: Plugin, - Ri: Plugin, + Le: Plugin, + Ri: Plugin, { - type Service = Either; + type Output = Either; - fn apply(&self, svc: S) -> Self::Service { + fn apply(&self, input: T) -> Self::Output { match self { Either::Left { value } => Either::Left { - value: value.apply(svc), + value: value.apply(input), }, Either::Right { value } => Either::Right { - value: value.apply(svc), + value: value.apply(input), }, } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs index 402fb07b7e..0e4a195e6d 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs @@ -6,71 +6,70 @@ use super::{either::Either, IdentityPlugin}; use crate::operation::OperationShape; -use crate::shape_id::ShapeId; +use crate::service::ContainsOperation; use super::Plugin; /// Filters the application of an inner [`Plugin`] using a predicate over the -/// [`OperationShape::ID`](crate::operation::OperationShape). +/// [`ServiceShape::Operations`](crate::service::ServiceShape::Operations). /// /// This contrasts with [`Scoped`](crate::plugin::Scoped) which can be used to selectively apply a [`Plugin`] to a /// subset of operations at _compile time_. /// -/// See [`filter_by_operation_id`] for more details. -pub struct FilterByOperationId { +/// See [`filter_by_operation`] for more details. +pub struct FilterByOperation { inner: Inner, predicate: F, } +impl Plugin for FilterByOperation +where + Ser: ContainsOperation, + F: Fn(Ser::Operations) -> bool, + Inner: Plugin, + Op: OperationShape, +{ + type Output = Either; + + fn apply(&self, input: T) -> Self::Output { + let either_plugin = if (self.predicate)(>::VALUE) { + Either::Left { value: &self.inner } + } else { + Either::Right { value: IdentityPlugin } + }; + either_plugin.apply(input) + } +} + /// Filters the application of an inner [`Plugin`] using a predicate over the -/// [`OperationShape::ID`](crate::operation::OperationShape). +/// [`ServiceShape::Operations`](crate::service::ServiceShape::Operations). /// -/// Users should prefer [`Scoped`](crate::plugin::Scoped) and fallback to [`filter_by_operation_id`] in cases where +/// Users should prefer [`Scoped`](crate::plugin::Scoped) and fallback to [`filter_by_operation`] in cases where /// [`Plugin`] application must be decided at runtime. /// /// # Example /// /// ```rust -/// use aws_smithy_http_server::plugin::filter_by_operation_id; -/// # use aws_smithy_http_server::{plugin::Plugin, operation::OperationShape, shape_id::ShapeId}; +/// use aws_smithy_http_server::plugin::filter_by_operation; +/// # use aws_smithy_http_server::{plugin::Plugin, operation::OperationShape, shape_id::ShapeId, service::{ServiceShape, ContainsOperation}}; /// # struct Pl; +/// # struct PokemonService; +/// # #[derive(PartialEq, Eq)] +/// # enum Operation { CheckHealth } +/// # impl ServiceShape for PokemonService { const VERSION: Option<&'static str> = None; const ID: ShapeId = ShapeId::new("", "", ""); type Operations = Operation; type Protocol = (); } +/// # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::CheckHealth; } /// # struct CheckHealth; /// # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } -/// # impl Plugin<(), CheckHealth, ()> for Pl { type Service = (); fn apply(&self, input: ()) -> Self::Service { input }} +/// # impl Plugin for Pl { type Output = (); fn apply(&self, input: ()) -> Self::Output { input }} /// # let plugin = Pl; /// # let svc = (); /// // Prevents `plugin` from being applied to the `CheckHealth` operation. -/// let filtered_plugin = filter_by_operation_id(plugin, |name| name != CheckHealth::ID); +/// let filtered_plugin = filter_by_operation(plugin, |name| name != Operation::CheckHealth); /// let new_operation = filtered_plugin.apply(svc); /// ``` -pub fn filter_by_operation_id(plugins: Inner, predicate: F) -> FilterByOperationId -where - F: Fn(ShapeId) -> bool, -{ - FilterByOperationId::new(plugins, predicate) -} - -impl FilterByOperationId { - /// Creates a new [`FilterByOperationId`]. - fn new(inner: Inner, predicate: F) -> Self { - Self { inner, predicate } - } -} - -impl Plugin for FilterByOperationId -where - F: Fn(ShapeId) -> bool, - Inner: Plugin, - Op: OperationShape, -{ - type Service = Either; - - fn apply(&self, svc: S) -> Self::Service { - let either_plugin = if (self.predicate)(Op::ID) { - Either::Left { value: &self.inner } - } else { - Either::Right { value: IdentityPlugin } - }; - either_plugin.apply(svc) +pub fn filter_by_operation(plugins: Inner, predicate: F) -> FilterByOperation { + FilterByOperation { + inner: plugins, + predicate, } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs b/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs index 52cd7e6965..affbd9f6b9 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs @@ -8,8 +8,8 @@ use super::Plugin; /// A [`Plugin`] that maps a service to itself. pub struct IdentityPlugin; -impl Plugin for IdentityPlugin { - type Service = S; +impl Plugin for IdentityPlugin { + type Output = S; fn apply(&self, svc: S) -> S { svc diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs b/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs index 6d5e9188f6..b1a025b4cb 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs @@ -12,29 +12,29 @@ use super::Plugin; /// A [`Plugin`] which acts as a [`Layer`] `L`. pub struct LayerPlugin(pub L); -impl Plugin for LayerPlugin +impl Plugin for LayerPlugin where L: Layer, { - type Service = L::Service; + type Output = L::Service; - fn apply(&self, svc: S) -> Self::Service { + fn apply(&self, svc: S) -> Self::Output { self.0.layer(svc) } } /// A [`Layer`] which acts as a [`Plugin`] `Pl` for specific protocol `P` and operation `Op`. -pub struct PluginLayer { +pub struct PluginLayer { plugin: Pl, - _protocol: PhantomData

, + _ser: PhantomData, _op: PhantomData, } -impl Layer for PluginLayer +impl Layer for PluginLayer where - Pl: Plugin, + Pl: Plugin, { - type Service = Pl::Service; + type Service = Pl::Output; fn layer(&self, inner: S) -> Self::Service { self.plugin.apply(inner) @@ -42,10 +42,10 @@ where } impl PluginLayer<(), (), Pl> { - pub fn new(plugin: Pl) -> PluginLayer { + pub fn new(plugin: Pl) -> PluginLayer { PluginLayer { plugin, - _protocol: PhantomData, + _ser: PhantomData, _op: PhantomData, } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs index c6cb440d44..106dc9200c 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -14,27 +14,56 @@ //! # use aws_smithy_http_server::plugin::*; //! # use aws_smithy_http_server::shape_id::ShapeId; //! # let layer = (); +//! # #[derive(PartialEq)] +//! # enum Operation { GetPokemonSpecies } //! # struct GetPokemonSpecies; //! # impl GetPokemonSpecies { const ID: ShapeId = ShapeId::new("namespace#name", "namespace", "name"); }; //! // Create a `Plugin` from a HTTP `Layer` //! let plugin = LayerPlugin(layer); //! //! // Only apply the layer to operations with name "GetPokemonSpecies" -//! let plugin = filter_by_operation_id(plugin, |id| id.name() == GetPokemonSpecies::ID.name()); +//! let plugin = filter_by_operation(plugin, |operation: Operation| operation == Operation::GetPokemonSpecies); //! ``` //! //! # Construct a [`Plugin`] from a closure that takes as input the operation name //! -//! ``` -//! # use aws_smithy_http_server::plugin::*; -//! # use aws_smithy_http_server::shape_id::ShapeId; -//! // A `tower::Layer` which requires the operation name -//! struct PrintLayer { -//! name: ShapeId, +//! ```rust +//! # use aws_smithy_http_server::{service::*, operation::OperationShape, plugin::Plugin, shape_id::ShapeId}; +//! # pub enum Operation { CheckHealth, GetPokemonSpecies } +//! # impl Operation { fn shape_id(&self) -> ShapeId { ShapeId::new("", "", "") }} +//! # pub struct CheckHealth; +//! # pub struct GetPokemonSpecies; +//! # pub struct PokemonService; +//! # impl ServiceShape for PokemonService { +//! # const ID: ShapeId = ShapeId::new("", "", ""); +//! # const VERSION: Option<&'static str> = None; +//! # type Protocol = (); +//! # type Operations = Operation; +//! # } +//! # impl OperationShape for CheckHealth { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +//! # impl OperationShape for GetPokemonSpecies { const ID: ShapeId = ShapeId::new("", "", ""); type Input = (); type Output = (); type Error = (); } +//! # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::CheckHealth; } +//! # impl ContainsOperation for PokemonService { const VALUE: Operation = Operation::GetPokemonSpecies; } +//! use aws_smithy_http_server::plugin::plugin_from_operation_fn; +//! use tower::layer::layer_fn; +//! +//! struct FooService { +//! info: String, +//! inner: S //! } //! -//! // Create a `Plugin` using `PrintLayer` -//! let plugin = plugin_from_operation_id_fn(|name| PrintLayer { name }); +//! fn map(op: Operation, inner: S) -> FooService { +//! match op { +//! Operation::CheckHealth => FooService { info: op.shape_id().name().to_string(), inner }, +//! Operation::GetPokemonSpecies => FooService { info: "bar".to_string(), inner }, +//! _ => todo!() +//! } +//! } +//! +//! // This plugin applies the `FooService` middleware around every operation. +//! let plugin = plugin_from_operation_fn(map); +//! # let _ = Plugin::::apply(&plugin, ()); +//! # let _ = Plugin::::apply(&plugin, ()); //! ``` //! //! # Combine [`Plugin`]s @@ -55,7 +84,8 @@ //! //! ```rust //! use aws_smithy_http_server::{ -//! operation::{OperationShape}, +//! operation::OperationShape, +//! service::ServiceShape, //! plugin::{Plugin, PluginPipeline, PluginStack}, //! shape_id::ShapeId, //! }; @@ -66,7 +96,8 @@ //! #[derive(Clone, Debug)] //! pub struct PrintService { //! inner: S, -//! id: ShapeId, +//! service_id: ShapeId, +//! operation_id: ShapeId //! } //! //! impl Service for PrintService @@ -82,7 +113,7 @@ //! } //! //! fn call(&mut self, req: R) -> Self::Future { -//! println!("Hi {}", self.id.absolute()); +//! println!("Hi {} in {}", self.operation_id.absolute(), self.service_id.absolute()); //! self.inner.call(req) //! } //! } @@ -91,14 +122,19 @@ //! #[derive(Debug)] //! pub struct PrintPlugin; //! -//! impl Plugin for PrintPlugin +//! impl Plugin for PrintPlugin //! where +//! Ser: ServiceShape, //! Op: OperationShape, //! { -//! type Service = PrintService; -//! -//! fn apply(&self, inner: S) -> Self::Service { -//! PrintService { inner, id: Op::ID } +//! type Output = PrintService; +//! +//! fn apply(&self, inner: T) -> Self::Output { +//! PrintService { +//! inner, +//! service_id: Op::ID, +//! operation_id: Ser::ID, +//! } //! } //! } //! ``` @@ -115,9 +151,9 @@ mod pipeline; pub mod scoped; mod stack; -pub use closure::{plugin_from_operation_id_fn, OperationIdFn}; +pub use closure::{plugin_from_operation_fn, OperationFn}; pub use either::Either; -pub use filter::{filter_by_operation_id, FilterByOperationId}; +pub use filter::{filter_by_operation, FilterByOperation}; pub use identity::IdentityPlugin; pub use layer::{LayerPlugin, PluginLayer}; pub use pipeline::PluginPipeline; @@ -127,24 +163,28 @@ pub use stack::PluginStack; /// A mapping from one [`Service`](tower::Service) to another. This should be viewed as a /// [`Layer`](tower::Layer) parameterized by the protocol and operation. /// -/// The generics `Protocol` and `Op` allow the behavior to be parameterized. +/// The generics `Ser` and `Op` allow the behavior to be parameterized by the [Smithy service] and +/// [operation] it's applied to. /// /// See [module](crate::plugin) documentation for more information. -pub trait Plugin { +/// +/// [Smithy service]: https://smithy.io/2.0/spec/service-types.html#service +/// [operation]: https://smithy.io/2.0/spec/service-types.html#operation +pub trait Plugin { /// The type of the new [`Service`](tower::Service). - type Service; + type Output; /// Maps a [`Service`](tower::Service) to another. - fn apply(&self, svc: S) -> Self::Service; + fn apply(&self, input: T) -> Self::Output; } -impl<'a, P, Op, S, Pl> Plugin for &'a Pl +impl<'a, Ser, Op, T, Pl> Plugin for &'a Pl where - Pl: Plugin, + Pl: Plugin, { - type Service = Pl::Service; + type Output = Pl::Output; - fn apply(&self, inner: S) -> Self::Service { - >::apply(self, inner) + fn apply(&self, inner: T) -> Self::Output { + >::apply(self, inner) } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs index 2fde128511..acac792c3b 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs @@ -34,27 +34,27 @@ use super::LayerPlugin; /// /// `PluginPipeline` is itself a [`Plugin`]: you can apply any transformation that expects a /// [`Plugin`] to an entire pipeline. In this case, we want to use -/// [`filter_by_operation_id`](crate::plugin::filter_by_operation_id) to limit the scope of +/// [`filter_by_operation`](crate::plugin::filter_by_operation) to limit the scope of /// the logging and metrics plugins to the `CheckHealth` operation: /// /// ```rust -/// use aws_smithy_http_server::plugin::{filter_by_operation_id, PluginPipeline}; +/// use aws_smithy_http_server::plugin::{filter_by_operation, PluginPipeline}; /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; /// use aws_smithy_http_server::shape_id::ShapeId; +/// # #[derive(PartialEq)] +/// # enum Operation { CheckHealth } /// # struct CheckHealth; /// # impl CheckHealth { const ID: ShapeId = ShapeId::new("namespace#MyName", "namespace", "MyName"); } /// /// // The logging and metrics plugins will only be applied to the `CheckHealth` operation. -/// let operation_specific_pipeline = filter_by_operation_id( -/// PluginPipeline::new() +/// let plugin = PluginPipeline::new() /// .push(LoggingPlugin) -/// .push(MetricsPlugin), -/// |name| name == CheckHealth::ID -/// ); +/// .push(MetricsPlugin); +/// let filtered_plugin = filter_by_operation(plugin, |operation: Operation| operation == Operation::CheckHealth); /// let pipeline = PluginPipeline::new() -/// .push(operation_specific_pipeline) +/// .push(filtered_plugin) /// // The auth plugin will be applied to all operations /// .push(AuthPlugin); /// ``` @@ -150,12 +150,16 @@ impl

PluginPipeline

{ /// #[derive(Debug)] /// pub struct PrintPlugin; /// - /// impl Plugin for PrintPlugin + /// impl Plugin for PrintPlugin /// // [...] /// { /// // [...] - /// fn apply(&self, inner: S) -> Self::Service { - /// PrintService { inner, name: Op::ID } + /// fn apply(&self, inner: T) -> Self::Service { + /// PrintService { + /// inner, + /// service_id: Ser::ID, + /// operation_id: Op::ID + /// } /// } /// } /// ``` @@ -170,13 +174,13 @@ impl

PluginPipeline

{ } } -impl Plugin for PluginPipeline +impl Plugin for PluginPipeline where - InnerPlugin: Plugin, + InnerPlugin: Plugin, { - type Service = InnerPlugin::Service; + type Output = InnerPlugin::Output; - fn apply(&self, svc: S) -> Self::Service { - self.0.apply(svc) + fn apply(&self, input: T) -> Self::Output { + self.0.apply(input) } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs b/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs index 64dc17ab83..a3761b2b1a 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs @@ -20,34 +20,34 @@ pub struct False; /// Conditionally applies a [`Plugin`] `Pl` to some service `S`. /// /// See [`True`] and [`False`]. -pub trait ConditionalApply { +pub trait ConditionalApply { type Service; - fn apply(plugin: &Pl, svc: S) -> Self::Service; + fn apply(plugin: &Pl, svc: T) -> Self::Service; } -impl ConditionalApply for True +impl ConditionalApply for True where - Pl: Plugin, + Pl: Plugin, { - type Service = Pl::Service; + type Service = Pl::Output; - fn apply(plugin: &Pl, svc: S) -> Self::Service { - plugin.apply(svc) + fn apply(plugin: &Pl, input: T) -> Self::Service { + plugin.apply(input) } } -impl ConditionalApply for False { - type Service = S; +impl ConditionalApply for False { + type Service = T; - fn apply(_plugin: &Pl, svc: S) -> Self::Service { - svc + fn apply(_plugin: &Pl, input: T) -> Self::Service { + input } } /// A [`Plugin`] which scopes the application of an inner [`Plugin`]. /// -/// In cases where operation selection must be performed at runtime [`filter_by_operation_id`](crate::plugin::filter_by_operation_id) +/// In cases where operation selection must be performed at runtime [`filter_by_operation`](crate::plugin::filter_by_operation) /// can be used. /// /// Operations within the scope will have the inner [`Plugin`] applied. @@ -90,15 +90,15 @@ pub trait Membership { type Contains; } -impl Plugin for Scoped +impl Plugin for Scoped where Scope: Membership, - Scope::Contains: ConditionalApply, + Scope::Contains: ConditionalApply, { - type Service = >::Service; + type Output = >::Service; - fn apply(&self, svc: S) -> Self::Service { - >::apply(&self.plugin, svc) + fn apply(&self, input: T) -> Self::Output { + >::apply(&self.plugin, input) } } @@ -169,9 +169,9 @@ mod tests { struct MockPlugin; impl Plugin for MockPlugin { - type Service = String; + type Output = String; - fn apply(&self, svc: u32) -> Self::Service { + fn apply(&self, svc: u32) -> Self::Output { svc.to_string() } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs b/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs index e5aa54d219..63f8e44865 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs @@ -22,15 +22,15 @@ impl PluginStack { } } -impl Plugin for PluginStack +impl Plugin for PluginStack where - Inner: Plugin, - Outer: Plugin, + Inner: Plugin, + Outer: Plugin, { - type Service = Outer::Service; + type Output = Outer::Output; - fn apply(&self, svc: S) -> Self::Service { - let svc = self.inner.apply(svc); + fn apply(&self, input: T) -> Self::Output { + let svc = self.inner.apply(input); self.outer.apply(svc) } } diff --git a/rust-runtime/aws-smithy-http-server/src/service.rs b/rust-runtime/aws-smithy-http-server/src/service.rs new file mode 100644 index 0000000000..8ad7a026fd --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/service.rs @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! The shape of a [Smithy service] is modelled by the [`ServiceShape`] trait. Its associated types +//! [`ServiceShape::ID`], [`ServiceShape::VERSION`], [`ServiceShape::Protocol`], and [`ServiceShape::Operations`] map +//! to the services [Shape ID](https://smithy.io/2.0/spec/model.html#shape-id), the version field, the applied +//! [protocol trait](https://smithy.io/2.0/aws/protocols/index.html) (see [`proto`](crate::proto) module), and the +//! operations field. +//! +//! We generate an implementation on this for every service struct (exported from the root of the generated crate). +//! +//! As stated in the [operation module documentation](crate::operation) we also generate marker structs for +//! [`OperationShape`](crate::operation::OperationShape), these are coupled to the `S: ServiceShape` via the [`ContainsOperation`] trait. +//! +//! The model +//! +//! ```smithy +//! @restJson1 +//! service Shopping { +//! version: "1.0", +//! operations: [ +//! GetShopping, +//! PutShopping +//! ] +//! } +//! ``` +//! +//! is identified with the implementation +//! +//! ```rust,no_run +//! # use aws_smithy_http_server::shape_id::ShapeId; +//! # use aws_smithy_http_server::service::{ServiceShape, ContainsOperation}; +//! # use aws_smithy_http_server::proto::rest_json_1::RestJson1; +//! # pub struct Shopping; +//! // For more information on these marker structs see `OperationShape` +//! struct GetShopping; +//! struct PutShopping; +//! +//! // This is a generated enumeration of all operations. +//! #[derive(PartialEq, Eq, Clone, Copy)] +//! pub enum Operation { +//! GetShopping, +//! PutShopping +//! } +//! +//! impl ServiceShape for Shopping { +//! const ID: ShapeId = ShapeId::new("namespace#Shopping", "namespace", "Shopping"); +//! const VERSION: Option<&'static str> = Some("1.0"); +//! type Protocol = RestJson1; +//! type Operations = Operation; +//! } +//! +//! impl ContainsOperation for Shopping { +//! const VALUE: Operation = Operation::GetShopping; +//! } +//! +//! impl ContainsOperation for Shopping { +//! const VALUE: Operation = Operation::PutShopping; +//! } +//! ``` +//! +//! [Smithy service]: https://smithy.io/2.0/spec/service-types.html#service + +use crate::shape_id::ShapeId; + +/// Models the [Smithy Service shape]. +/// +/// [Smithy Service shape]: https://smithy.io/2.0/spec/service-types.html#service +pub trait ServiceShape { + /// The [`ShapeId`] of the service. + const ID: ShapeId; + + /// The version of the service. + const VERSION: Option<&'static str>; + + /// The [Protocol] applied to this service. + /// + /// [Protocol]: https://smithy.io/2.0/spec/protocol-traits.html + type Protocol; + + /// An enumeration of all operations contained in this service. + type Operations; +} + +pub trait ContainsOperation: ServiceShape { + const VALUE: Self::Operations; +} diff --git a/rust-runtime/aws-smithy-http-server/src/shape_id.rs b/rust-runtime/aws-smithy-http-server/src/shape_id.rs index 1cb13f0921..cdc73c8412 100644 --- a/rust-runtime/aws-smithy-http-server/src/shape_id.rs +++ b/rust-runtime/aws-smithy-http-server/src/shape_id.rs @@ -3,13 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Extension types. -//! -//! Shape ID is a type that describes a Smithy shape. +//! A [`ShapeId`] represents a [Smithy Shape ID](https://smithy.io/2.0/spec/model.html#shape-id). //! //! # Example //! //! In the following model: +//! //! ```smithy //! namespace smithy.example //! @@ -22,7 +21,7 @@ pub use crate::request::extension::{Extension, MissingExtension}; -/// Shape ID for a modelled Smithy shape. +/// Represents a [Smithy Shape ID](https://smithy.io/2.0/spec/model.html#shape-id). #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ShapeId { absolute: &'static str, @@ -42,17 +41,23 @@ impl ShapeId { } } - /// Returns the Smithy operation namespace. + /// Returns the namespace. + /// + /// See [Shape ID](https://smithy.io/2.0/spec/model.html#shape-id) for a breakdown of the syntax. pub fn namespace(&self) -> &'static str { self.namespace } - /// Returns the Smithy operation name. + /// Returns the member name. + /// + /// See [Shape ID](https://smithy.io/2.0/spec/model.html#shape-id) for a breakdown of the syntax. pub fn name(&self) -> &'static str { self.name } - /// Returns the absolute operation shape ID. + /// Returns the absolute shape ID. + /// + /// See [Shape ID](https://smithy.io/2.0/spec/model.html#shape-id) for a breakdown of the syntax. pub fn absolute(&self) -> &'static str { self.absolute } From 2e472d068efef60d79f0f792e7bdcf20d3e0d1b3 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Thu, 15 Jun 2023 18:46:24 -0500 Subject: [PATCH 162/253] Make service config just contain a `FrozenLayer` in the orchestrator mode (#2762) ## Motivation and Context Service config structs now only contain a `aws_smithy_types::config_bag::FrozenLayer` in the orchestrator mode (no changes for the middleware mode). ## Description This PR reduces the individual fields of service configs to contain just `FrozenLayer`. This makes service configs work more seamlessly with runtime plugins in the orchestrator mode. Note that service config _builder_ s still contain individual fields. We're planning to make them just contain `aws_smithy_types::config_bag::Layer`. To do that, though, we need to make `Layer` cloneable and that will be handled in a separate PR. For builders, the only change you will in the PR is that their `build` method will put fields into a `Layer`, freeze it, and pass it to service configs. This PR is marked as a breaking change because it's based on [another PR](https://github.com/awslabs/smithy-rs/pull/2728) that's also breaking change. ## Testing - [x] Passed tests in CI ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito Co-authored-by: Zelda Hessler --- .../aws-credential-types/external-types.toml | 3 + .../aws-credential-types/src/cache.rs | 9 + .../aws-credential-types/src/provider.rs | 5 + .../aws-types/external-types.toml | 5 +- aws/rust-runtime/aws-types/src/app_name.rs | 5 + .../aws-types/src/endpoint_config.rs | 31 ++++ aws/rust-runtime/aws-types/src/lib.rs | 1 + aws/rust-runtime/aws-types/src/region.rs | 5 + .../amazon/smithy/rustsdk/CredentialCaches.kt | 134 ++++++++++----- .../rustsdk/EndpointBuiltInsDecorator.kt | 55 +++++-- .../HttpConnectorConfigCustomization.kt | 44 +++-- .../amazon/smithy/rustsdk/RegionDecorator.kt | 57 +++++-- .../smithy/rustsdk/UserAgentDecorator.kt | 62 ++++--- .../timestream/TimestreamDecorator.kt | 6 +- .../rustsdk/CredentialProviderConfigTest.kt | 14 +- .../HttpConnectorConfigCustomizationTest.kt | 15 +- .../rustsdk/RegionProviderConfigTest.kt | 15 +- .../rustsdk/SigV4SigningDecoratorTest.kt | 20 ++- .../customizations/ApiKeyAuthDecorator.kt | 52 ++++-- .../customizations/HttpAuthDecorator.kt | 9 +- .../HttpConnectorConfigDecorator.kt | 44 +++-- .../InterceptorConfigCustomization.kt | 16 +- .../ResiliencyConfigCustomization.kt | 125 +++++++++----- .../customizations/TimeSourceCustomization.kt | 127 ++++++++++++++ .../customize/RequiredCustomizations.kt | 8 +- .../ClientContextConfigCustomization.kt | 17 +- .../endpoint/EndpointConfigCustomization.kt | 100 +++++++---- .../EndpointParamsInterceptorGenerator.kt | 4 +- .../ServiceRuntimePluginGenerator.kt | 2 +- .../IdempotencyTokenProviderCustomization.kt | 112 +++++++------ .../config/ServiceConfigGenerator.kt | 155 +++++++++++++++--- .../generators/config/TimeSourceConfig.kt | 40 ----- .../testutil/TestConfigCustomization.kt | 78 ++++++--- .../codegen/client/testutil/TestHelpers.kt | 4 + .../ResiliencyConfigCustomizationTest.kt | 2 +- .../ClientContextConfigCustomizationTest.kt | 62 +++++-- ...empotencyTokenProviderCustomizationTest.kt | 20 ++- .../config/ServiceConfigGeneratorTest.kt | 108 +++++++++--- .../codegen/core/rustlang/CargoDependency.kt | 11 +- .../rust/codegen/core/smithy/RuntimeType.kt | 4 +- .../core/rustlang/InlineDependencyTest.kt | 16 +- rust-runtime/aws-smithy-async/Cargo.toml | 1 + .../aws-smithy-async/external-types.toml | 4 + rust-runtime/aws-smithy-async/src/rt/sleep.rs | 5 + .../aws-smithy-async/src/test_util.rs | 2 +- rust-runtime/aws-smithy-async/src/time.rs | 5 + .../aws-smithy-client/src/http_connector.rs | 5 + rust-runtime/aws-smithy-http/src/endpoint.rs | 5 + .../src/client/identity.rs | 6 +- .../src/client/interceptors.rs | 6 +- rust-runtime/aws-smithy-types/src/retry.rs | 5 + rust-runtime/aws-smithy-types/src/timeout.rs | 5 + .../inlineable/src/idempotency_token.rs | 5 + 53 files changed, 1203 insertions(+), 453 deletions(-) create mode 100644 aws/rust-runtime/aws-types/src/endpoint_config.rs create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt delete mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt diff --git a/aws/rust-runtime/aws-credential-types/external-types.toml b/aws/rust-runtime/aws-credential-types/external-types.toml index 20337b394d..e65c743b10 100644 --- a/aws/rust-runtime/aws-credential-types/external-types.toml +++ b/aws/rust-runtime/aws-credential-types/external-types.toml @@ -1,3 +1,6 @@ allowed_external_types = [ "aws_smithy_async::rt::sleep::SharedAsyncSleep", + "aws_smithy_types::config_bag::storable::Storable", + "aws_smithy_types::config_bag::storable::StoreReplace", + "aws_smithy_types::config_bag::storable::Storer", ] diff --git a/aws/rust-runtime/aws-credential-types/src/cache.rs b/aws/rust-runtime/aws-credential-types/src/cache.rs index 4d4810efab..a1351d0f9c 100644 --- a/aws/rust-runtime/aws-credential-types/src/cache.rs +++ b/aws/rust-runtime/aws-credential-types/src/cache.rs @@ -14,6 +14,7 @@ pub use lazy_caching::Builder as LazyBuilder; use no_caching::NoCredentialsCache; use crate::provider::{future, SharedCredentialsProvider}; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::sync::Arc; /// Asynchronous Cached Credentials Provider @@ -62,6 +63,10 @@ impl ProvideCachedCredentials for SharedCredentialsCache { } } +impl Storable for SharedCredentialsCache { + type Storer = StoreReplace; +} + #[derive(Clone, Debug)] pub(crate) enum Inner { Lazy(lazy_caching::Builder), @@ -122,3 +127,7 @@ impl CredentialsCache { } } } + +impl Storable for CredentialsCache { + type Storer = StoreReplace; +} diff --git a/aws/rust-runtime/aws-credential-types/src/provider.rs b/aws/rust-runtime/aws-credential-types/src/provider.rs index d9a43ab36d..9be88b590f 100644 --- a/aws/rust-runtime/aws-credential-types/src/provider.rs +++ b/aws/rust-runtime/aws-credential-types/src/provider.rs @@ -72,6 +72,7 @@ construct credentials from hardcoded values. //! ``` use crate::Credentials; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::sync::Arc; /// Credentials provider errors @@ -350,3 +351,7 @@ impl ProvideCredentials for SharedCredentialsProvider { self.0.provide_credentials() } } + +impl Storable for SharedCredentialsProvider { + type Storer = StoreReplace; +} diff --git a/aws/rust-runtime/aws-types/external-types.toml b/aws/rust-runtime/aws-types/external-types.toml index 830795ad27..e43510d69e 100644 --- a/aws/rust-runtime/aws-types/external-types.toml +++ b/aws/rust-runtime/aws-types/external-types.toml @@ -2,13 +2,16 @@ allowed_external_types = [ "aws_credential_types::cache::CredentialsCache", "aws_credential_types::provider::SharedCredentialsProvider", "aws_smithy_async::rt::sleep::SharedAsyncSleep", - "aws_smithy_async::time::TimeSource", "aws_smithy_async::time::SharedTimeSource", + "aws_smithy_async::time::TimeSource", "aws_smithy_client::http_connector", "aws_smithy_client::http_connector::HttpConnector", "aws_smithy_http::endpoint::Endpoint", "aws_smithy_http::endpoint::EndpointPrefix", "aws_smithy_http::endpoint::error::InvalidEndpointError", + "aws_smithy_types::config_bag::storable::Storable", + "aws_smithy_types::config_bag::storable::StoreReplace", + "aws_smithy_types::config_bag::storable::Storer", "aws_smithy_types::retry::RetryConfig", "aws_smithy_types::timeout::TimeoutConfig", "http::uri::Uri", diff --git a/aws/rust-runtime/aws-types/src/app_name.rs b/aws/rust-runtime/aws-types/src/app_name.rs index 70843e19bd..25555eab64 100644 --- a/aws/rust-runtime/aws-types/src/app_name.rs +++ b/aws/rust-runtime/aws-types/src/app_name.rs @@ -5,6 +5,7 @@ //! New-type for a configurable app name. +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::borrow::Cow; use std::error::Error; use std::fmt; @@ -38,6 +39,10 @@ impl fmt::Display for AppName { } } +impl Storable for AppName { + type Storer = StoreReplace; +} + impl AppName { /// Creates a new app name. /// diff --git a/aws/rust-runtime/aws-types/src/endpoint_config.rs b/aws/rust-runtime/aws-types/src/endpoint_config.rs new file mode 100644 index 0000000000..562051f530 --- /dev/null +++ b/aws/rust-runtime/aws-types/src/endpoint_config.rs @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Newtypes for endpoint-related parameters +//! +//! Parameters require newtypes so they have distinct types when stored in layers in config bag. + +use aws_smithy_types::config_bag::{Storable, StoreReplace}; + +/// Newtype for `use_fips` +#[derive(Clone, Debug)] +pub struct UseFips(pub bool); +impl Storable for UseFips { + type Storer = StoreReplace; +} + +/// Newtype for `use_dual_stack` +#[derive(Clone, Debug)] +pub struct UseDualStack(pub bool); +impl Storable for UseDualStack { + type Storer = StoreReplace; +} + +/// Newtype for `endpoint_url` +#[derive(Clone, Debug)] +pub struct EndpointUrl(pub String); +impl Storable for EndpointUrl { + type Storer = StoreReplace; +} diff --git a/aws/rust-runtime/aws-types/src/lib.rs b/aws/rust-runtime/aws-types/src/lib.rs index 27860df1be..de5bfa269e 100644 --- a/aws/rust-runtime/aws-types/src/lib.rs +++ b/aws/rust-runtime/aws-types/src/lib.rs @@ -16,6 +16,7 @@ pub mod app_name; pub mod build_metadata; +pub mod endpoint_config; #[doc(hidden)] pub mod os_shim_internal; pub mod region; diff --git a/aws/rust-runtime/aws-types/src/region.rs b/aws/rust-runtime/aws-types/src/region.rs index 3f053d0acb..be09aac6da 100644 --- a/aws/rust-runtime/aws-types/src/region.rs +++ b/aws/rust-runtime/aws-types/src/region.rs @@ -5,6 +5,7 @@ //! Region type for determining the endpoint to send requests to. +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::borrow::Cow; use std::fmt::{Display, Formatter}; @@ -35,6 +36,10 @@ impl Display for Region { } } +impl Storable for Region { + type Storer = StoreReplace; +} + impl Region { /// Creates a new `Region` from the given string. pub fn new(region: impl Into>) -> Self { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt index ae3cde9cc0..8bf443d8f9 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt @@ -27,7 +27,7 @@ class CredentialsCacheDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List { - return baseCustomizations + CredentialCacheConfig(codegenContext.runtimeConfig) + return baseCustomizations + CredentialCacheConfig(codegenContext) } override fun operationCustomizations( @@ -49,44 +49,65 @@ class CredentialsCacheDecorator : ClientCodegenDecorator { /** * Add a `.credentials_cache` field and builder to the `Config` for a given service */ -class CredentialCacheConfig(runtimeConfig: RuntimeConfig) : ConfigCustomization() { +class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode private val codegenScope = arrayOf( - "cache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("cache"), - "provider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider"), + "CredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("cache::CredentialsCache"), "DefaultProvider" to defaultProvider(), + "SharedCredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("cache::SharedCredentialsCache"), + "SharedCredentialsProvider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider::SharedCredentialsProvider"), ) override fun section(section: ServiceConfig) = writable { when (section) { - ServiceConfig.ConfigStruct -> rustTemplate( - """pub(crate) credentials_cache: #{cache}::SharedCredentialsCache,""", - *codegenScope, - ) - - ServiceConfig.ConfigImpl -> rustTemplate( - """ - /// Returns the credentials cache. - pub fn credentials_cache(&self) -> #{cache}::SharedCredentialsCache { - self.credentials_cache.clone() + ServiceConfig.ConfigStruct -> { + if (runtimeMode.defaultToMiddleware) { + rustTemplate( + """pub(crate) credentials_cache: #{SharedCredentialsCache},""", + *codegenScope, + ) } - """, - *codegenScope, - ) + } + + ServiceConfig.ConfigImpl -> { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Returns the credentials cache. + pub fn credentials_cache(&self) -> #{SharedCredentialsCache} { + self.inner.load::<#{SharedCredentialsCache}>().expect("credentials cache should be set").clone() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns the credentials cache. + pub fn credentials_cache(&self) -> #{SharedCredentialsCache} { + self.credentials_cache.clone() + } + """, + *codegenScope, + ) + } + } ServiceConfig.BuilderStruct -> - rustTemplate("credentials_cache: Option<#{cache}::CredentialsCache>,", *codegenScope) + rustTemplate("credentials_cache: Option<#{CredentialsCache}>,", *codegenScope) ServiceConfig.BuilderImpl -> { rustTemplate( """ /// Sets the credentials cache for this service - pub fn credentials_cache(mut self, credentials_cache: #{cache}::CredentialsCache) -> Self { + pub fn credentials_cache(mut self, credentials_cache: #{CredentialsCache}) -> Self { self.set_credentials_cache(Some(credentials_cache)); self } /// Sets the credentials cache for this service - pub fn set_credentials_cache(&mut self, credentials_cache: Option<#{cache}::CredentialsCache>) -> &mut Self { + pub fn set_credentials_cache(&mut self, credentials_cache: Option<#{CredentialsCache}>) -> &mut Self { self.credentials_cache = credentials_cache; self } @@ -95,29 +116,56 @@ class CredentialCacheConfig(runtimeConfig: RuntimeConfig) : ConfigCustomization( ) } - ServiceConfig.BuilderBuild -> rustTemplate( - """ - credentials_cache: self - .credentials_cache - .unwrap_or_else({ - let sleep = self.sleep_impl.clone(); - || match sleep { - Some(sleep) => { - #{cache}::CredentialsCache::lazy_builder() - .sleep(sleep) - .into_credentials_cache() - } - None => #{cache}::CredentialsCache::lazy(), - } - }) - .create_cache( - self.credentials_provider.unwrap_or_else(|| { - #{provider}::SharedCredentialsProvider::new(#{DefaultProvider}) - }) - ), - """, - *codegenScope, - ) + ServiceConfig.BuilderBuild -> { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + layer.store_put( + self.credentials_cache + .unwrap_or_else({ + let sleep = self.sleep_impl.clone(); + || match sleep { + Some(sleep) => { + #{CredentialsCache}::lazy_builder() + .sleep(sleep) + .into_credentials_cache() + } + None => #{CredentialsCache}::lazy(), + } + }) + .create_cache(self.credentials_provider.unwrap_or_else(|| { + #{SharedCredentialsProvider}::new(#{DefaultProvider}) + })), + ); + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + credentials_cache: self + .credentials_cache + .unwrap_or_else({ + let sleep = self.sleep_impl.clone(); + || match sleep { + Some(sleep) => { + #{CredentialsCache}::lazy_builder() + .sleep(sleep) + .into_credentials_cache() + } + None => #{CredentialsCache}::lazy(), + } + }) + .create_cache( + self.credentials_provider.unwrap_or_else(|| { + #{SharedCredentialsProvider}::new(#{DefaultProvider}) + }) + ), + """, + *codegenScope, + ) + } + } else -> emptySection } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt index 1ac83c10ca..9eec481e0c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt @@ -22,18 +22,21 @@ import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointRulese import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigParam +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype import software.amazon.smithy.rust.codegen.client.smithy.generators.config.standardConfigParam import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs 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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.extendIf import software.amazon.smithy.rust.codegen.core.util.orNull +import software.amazon.smithy.rust.codegen.core.util.toPascalCase import java.util.Optional /** load a builtIn parameter from a ruleset by name */ @@ -48,14 +51,27 @@ fun ClientCodegenContext.getBuiltIn(builtIn: String): Parameter? { return rules.getBuiltIn(builtIn) } -private fun toConfigParam(parameter: Parameter): ConfigParam = ConfigParam( - parameter.name.rustName(), - when (parameter.type!!) { - ParameterType.STRING -> RuntimeType.String.toSymbol() - ParameterType.BOOLEAN -> RuntimeType.Bool.toSymbol() - }, - parameter.documentation.orNull()?.let { writable { docs(it) } }, -) +private fun promotedBuiltins(parameter: Parameter) = + parameter == Builtins.FIPS || parameter == Builtins.DUALSTACK || parameter == Builtins.SDK_ENDPOINT + +private fun ConfigParam.Builder.toConfigParam(parameter: Parameter, runtimeConfig: RuntimeConfig): ConfigParam = + this.name(this.name ?: parameter.name.rustName()) + .type( + when (parameter.type!!) { + ParameterType.STRING -> RuntimeType.String.toSymbol() + ParameterType.BOOLEAN -> RuntimeType.Bool.toSymbol() + }, + ) + .newtype( + when (promotedBuiltins(parameter)) { + true -> AwsRuntimeType.awsTypes(runtimeConfig) + .resolve("endpoint_config::${this.name!!.toPascalCase()}") + + false -> configParamNewtype(this.name!!.toPascalCase(), this.type!!, runtimeConfig) + }, + ) + .setterDocs(this.setterDocs ?: parameter.documentation.orNull()?.let { writable { docs(it) } }) + .build() fun Model.loadBuiltIn(serviceId: ShapeId, builtInSrc: Parameter): Parameter? { val model = this @@ -82,14 +98,14 @@ fun Model.sdkConfigSetter( } /** - * Create a client codegen decorator that creates bindings for a builtIn parameter. Optionally, you can provide [clientParam] - * which allows control over the config parameter that will be generated. + * Create a client codegen decorator that creates bindings for a builtIn parameter. Optionally, you can provide + * [clientParam.Builder] which allows control over the config parameter that will be generated. */ fun decoratorForBuiltIn( builtIn: Parameter, - clientParam: ConfigParam? = null, + clientParamBuilder: ConfigParam.Builder? = null, ): ClientCodegenDecorator { - val nameOverride = clientParam?.name + val nameOverride = clientParamBuilder?.name val name = nameOverride ?: builtIn.name.rustName() return object : ClientCodegenDecorator { override val name: String = "Auto${builtIn.builtIn.get()}" @@ -100,7 +116,7 @@ fun decoratorForBuiltIn( override fun extraSections(codegenContext: ClientCodegenContext): List { return listOfNotNull( - codegenContext.model.sdkConfigSetter(codegenContext.serviceShape.id, builtIn, clientParam?.name), + codegenContext.model.sdkConfigSetter(codegenContext.serviceShape.id, builtIn, clientParamBuilder?.name), ) } @@ -110,7 +126,9 @@ fun decoratorForBuiltIn( ): List { return baseCustomizations.extendIf(rulesetContainsBuiltIn(codegenContext)) { standardConfigParam( - clientParam ?: toConfigParam(builtIn), + clientParamBuilder?.toConfigParam(builtIn, codegenContext.runtimeConfig) ?: ConfigParam.Builder() + .toConfigParam(builtIn, codegenContext.runtimeConfig), + codegenContext, ) } } @@ -120,11 +138,16 @@ fun decoratorForBuiltIn( override fun loadBuiltInFromServiceConfig(parameter: Parameter, configRef: String): Writable? = when (parameter.builtIn) { builtIn.builtIn -> writable { - rust("$configRef.$name") + if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + rust("$configRef.$name()") + } else { + rust("$configRef.$name") + } if (parameter.type == ParameterType.STRING) { rust(".clone()") } } + else -> null } @@ -173,6 +196,6 @@ val PromotedBuiltInsDecorators = decoratorForBuiltIn(Builtins.DUALSTACK), decoratorForBuiltIn( Builtins.SDK_ENDPOINT, - ConfigParam("endpoint_url", RuntimeType.String.toSymbol(), endpointUrlDocs), + ConfigParam.Builder().name("endpoint_url").type(RuntimeType.String.toSymbol()).setterDocs(endpointUrlDocs), ), ).toTypedArray() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt index 77ad6fad98..e33e7ec8d1 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt @@ -13,7 +13,6 @@ 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.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.letIf @@ -32,9 +31,10 @@ class HttpConnectorDecorator : ClientCodegenDecorator { } class HttpConnectorConfigCustomization( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, ) : ConfigCustomization() { private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), @@ -43,18 +43,32 @@ class HttpConnectorConfigCustomization( override fun section(section: ServiceConfig): Writable { return when (section) { is ServiceConfig.ConfigStruct -> writable { - rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + if (runtimeMode.defaultToMiddleware) { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } } is ServiceConfig.ConfigImpl -> writable { - rustTemplate( - """ - /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. - pub fn http_connector(&self) -> Option<&#{HttpConnector}> { - self.http_connector.as_ref() - } - """, - *codegenScope, - ) + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. + pub fn http_connector(&self) -> Option<&#{HttpConnector}> { + self.inner.load::<#{HttpConnector}>() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. + pub fn http_connector(&self) -> Option<&#{HttpConnector}> { + self.http_connector.as_ref() + } + """, + *codegenScope, + ) + } } is ServiceConfig.BuilderStruct -> writable { rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) @@ -145,7 +159,11 @@ class HttpConnectorConfigCustomization( ) } is ServiceConfig.BuilderBuild -> writable { - rust("http_connector: self.http_connector,") + if (runtimeMode.defaultToOrchestrator) { + rust("layer.store_or_unset(self.http_connector);") + } else { + rust("http_connector: self.http_connector,") + } } else -> emptySection } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt index 61373f5d24..ae3f8b248e 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt @@ -21,7 +21,6 @@ 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.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization @@ -131,7 +130,11 @@ class RegionDecorator : ClientCodegenDecorator { override fun loadBuiltInFromServiceConfig(parameter: Parameter, configRef: String): Writable? { return when (parameter.builtIn) { Builtins.REGION.builtIn -> writable { - rust("$configRef.region.as_ref().map(|r|r.as_ref().to_owned())") + if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + rust("$configRef.region().as_ref().map(|r|r.as_ref().to_owned())") + } else { + rust("$configRef.region.as_ref().map(|r|r.as_ref().to_owned())") + } } else -> null } @@ -153,22 +156,41 @@ class RegionDecorator : ClientCodegenDecorator { } } -class RegionProviderConfig(codegenContext: CodegenContext) : ConfigCustomization() { +class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomization() { private val region = region(codegenContext.runtimeConfig) private val moduleUseName = codegenContext.moduleUseName() + private val runtimeMode = codegenContext.smithyRuntimeMode private val codegenScope = arrayOf("Region" to region.resolve("Region")) override fun section(section: ServiceConfig) = writable { when (section) { - ServiceConfig.ConfigStruct -> rustTemplate("pub(crate) region: Option<#{Region}>,", *codegenScope) - ServiceConfig.ConfigImpl -> rustTemplate( - """ - /// Returns the AWS region, if it was provided. - pub fn region(&self) -> Option<&#{Region}> { - self.region.as_ref() + ServiceConfig.ConfigStruct -> { + if (runtimeMode.defaultToMiddleware) { + rustTemplate("pub(crate) region: Option<#{Region}>,", *codegenScope) } - """, - *codegenScope, - ) + } + ServiceConfig.ConfigImpl -> { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Returns the AWS region, if it was provided. + pub fn region(&self) -> Option<&#{Region}> { + self.inner.load::<#{Region}>() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns the AWS region, if it was provided. + pub fn region(&self) -> Option<&#{Region}> { + self.region.as_ref() + } + """, + *codegenScope, + ) + } + } ServiceConfig.BuilderStruct -> rustTemplate("pub(crate) region: Option<#{Region}>,", *codegenScope) @@ -201,10 +223,13 @@ class RegionProviderConfig(codegenContext: CodegenContext) : ConfigCustomization *codegenScope, ) - ServiceConfig.BuilderBuild -> rustTemplate( - """region: self.region,""", - *codegenScope, - ) + ServiceConfig.BuilderBuild -> { + if (runtimeMode.defaultToOrchestrator) { + rust("layer.store_or_unset(self.region);") + } else { + rust("region: self.region,") + } + } else -> emptySection } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index 40eb360444..2e9a751686 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -20,7 +20,7 @@ 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.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersionCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization @@ -40,7 +40,7 @@ class UserAgentDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List { - return baseCustomizations + AppNameCustomization(codegenContext.runtimeConfig) + return baseCustomizations + AppNameCustomization(codegenContext) } override fun operationCustomizations( @@ -149,15 +149,18 @@ class UserAgentDecorator : ClientCodegenDecorator { } } - private class AppNameCustomization(runtimeConfig: RuntimeConfig) : ConfigCustomization() { + private class AppNameCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode private val codegenScope = arrayOf( + *preludeScope, "AppName" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("app_name::AppName"), ) override fun section(section: ServiceConfig): Writable = when (section) { is ServiceConfig.BuilderStruct -> writable { - rustTemplate("app_name: Option<#{AppName}>,", *codegenScope) + rustTemplate("app_name: #{Option}<#{AppName}>,", *codegenScope) } is ServiceConfig.BuilderImpl -> writable { @@ -176,7 +179,7 @@ class UserAgentDecorator : ClientCodegenDecorator { /// /// This _optional_ name is used to identify the application in the user agent that /// gets sent along with requests. - pub fn set_app_name(&mut self, app_name: Option<#{AppName}>) -> &mut Self { + pub fn set_app_name(&mut self, app_name: #{Option}<#{AppName}>) -> &mut Self { self.app_name = app_name; self } @@ -186,26 +189,47 @@ class UserAgentDecorator : ClientCodegenDecorator { } is ServiceConfig.BuilderBuild -> writable { - rust("app_name: self.app_name,") + if (runtimeMode.defaultToOrchestrator) { + rust("layer.store_or_unset(self.app_name);") + } else { + rust("app_name: self.app_name,") + } } is ServiceConfig.ConfigStruct -> writable { - rustTemplate("app_name: Option<#{AppName}>,", *codegenScope) + if (runtimeMode.defaultToMiddleware) { + rustTemplate("app_name: #{Option}<#{AppName}>,", *codegenScope) + } } is ServiceConfig.ConfigImpl -> writable { - rustTemplate( - """ - /// Returns the name of the app that is using the client, if it was provided. - /// - /// This _optional_ name is used to identify the application in the user agent that - /// gets sent along with requests. - pub fn app_name(&self) -> Option<&#{AppName}> { - self.app_name.as_ref() - } - """, - *codegenScope, - ) + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Returns the name of the app that is using the client, if it was provided. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn app_name(&self) -> #{Option}<&#{AppName}> { + self.inner.load::<#{AppName}>() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns the name of the app that is using the client, if it was provided. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn app_name(&self) -> #{Option}<&#{AppName}> { + self.app_name.as_ref() + } + """, + *codegenScope, + ) + } } else -> emptySection diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt index 695110e590..c066562ac3 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt @@ -52,6 +52,7 @@ class TimestreamDecorator : ClientCodegenDecorator { Visibility.PUBLIC, CargoDependency.Tokio.copy(scope = DependencyScope.Compile, features = setOf("sync")), ) + val runtimeMode = codegenContext.smithyRuntimeMode rustCrate.lib { // helper function to resolve an endpoint given a base client rustTemplate( @@ -62,7 +63,7 @@ class TimestreamDecorator : ClientCodegenDecorator { #{ResolveEndpointError}::from_source("failed to call describe_endpoints", e) })?; let endpoint = describe_endpoints.endpoints().unwrap().get(0).unwrap(); - let expiry = client.conf().time_source.now() + #{Duration}::from_secs(endpoint.cache_period_in_minutes() as u64 * 60); + let expiry = client.conf().time_source().now() + #{Duration}::from_secs(endpoint.cache_period_in_minutes() as u64 * 60); Ok(( #{Endpoint}::builder() .url(format!("https://{}", endpoint.address().unwrap())) @@ -78,7 +79,7 @@ class TimestreamDecorator : ClientCodegenDecorator { pub async fn enable_endpoint_discovery(self) -> #{Result}<(Self, #{endpoint_discovery}::ReloadEndpoint), #{ResolveEndpointError}> { let mut new_conf = self.conf().clone(); let sleep = self.conf().sleep_impl().expect("sleep impl must be provided"); - let time = self.conf().time_source.clone(); + let time = self.conf().time_source(); let (resolver, reloader) = #{endpoint_discovery}::create_cache( move || { let client = self.clone(); @@ -92,7 +93,6 @@ class TimestreamDecorator : ClientCodegenDecorator { Ok((Self::from_conf(new_conf), reloader)) } } - """, "endpoint_discovery" to endpointDiscovery.toType(), "SystemTime" to RuntimeType.std.resolve("time::SystemTime"), diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt index df26efdfec..df19f6f16b 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt @@ -5,12 +5,18 @@ package software.amazon.smithy.rustsdk -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode internal class CredentialProviderConfigTest { - @Test - fun `generates a valid config`() { - validateConfigCustomizations(CredentialProviderConfig(AwsTestRuntimeConfig)) + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generates a valid config`(smithyRuntimeModeStr: String) { + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val codegenContext = awsTestCodegenContext().withSmithyRuntimeMode(smithyRuntimeMode) + validateConfigCustomizations(codegenContext, CredentialProviderConfig(codegenContext.runtimeConfig)) } } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomizationTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomizationTest.kt index b97952e002..ca5e087b96 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomizationTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomizationTest.kt @@ -5,15 +5,20 @@ package software.amazon.smithy.rustsdk -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace class HttpConnectorConfigCustomizationTest { - @Test - fun `generates a valid config`() { + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generates a valid config`(smithyRuntimeModeStr: String) { val project = TestWorkspace.testProject() - val codegenContext = awsTestCodegenContext() - validateConfigCustomizations(HttpConnectorConfigCustomization(codegenContext), project) + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val codegenContext = awsTestCodegenContext().withSmithyRuntimeMode(smithyRuntimeMode) + validateConfigCustomizations(codegenContext, HttpConnectorConfigCustomization(codegenContext), project) } } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/RegionProviderConfigTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/RegionProviderConfigTest.kt index 9d2e865a64..1160f322ef 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/RegionProviderConfigTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/RegionProviderConfigTest.kt @@ -5,22 +5,27 @@ package software.amazon.smithy.rustsdk -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.rustSettings internal class RegionProviderConfigTest { - @Test - fun `generates a valid config`() { + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generates a valid config`(smithyRuntimeModeStr: String) { val project = TestWorkspace.testProject() + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) val codegenContext = awsTestCodegenContext( settings = testClientRustSettings( moduleName = project.rustSettings().moduleName, runtimeConfig = AwsTestRuntimeConfig, ), - ) - validateConfigCustomizations(RegionProviderConfig(codegenContext), project) + ).withSmithyRuntimeMode(smithyRuntimeMode) + validateConfigCustomizations(codegenContext, RegionProviderConfig(codegenContext), project) } } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt index 71bd5eaf6c..68a6610180 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt @@ -5,19 +5,31 @@ package software.amazon.smithy.rustsdk -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource import software.amazon.smithy.aws.traits.auth.SigV4Trait +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.testutil.stubConfigProject +import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest internal class SigV4SigningDecoratorTest { - @Test - fun `generates a valid config`() { + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generates a valid config`(smithyRuntimeModeStr: String) { + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val codegenContext = awsTestCodegenContext( + settings = testClientRustSettings( + runtimeConfig = AwsTestRuntimeConfig, + ), + ).withSmithyRuntimeMode(smithyRuntimeMode) val project = stubConfigProject( + codegenContext, SigV4SigningConfig( - AwsTestRuntimeConfig, + codegenContext.runtimeConfig, true, SigV4Trait.builder().name("test-service").build(), ), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt index 5152597709..a0b6a4fabf 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt @@ -25,6 +25,7 @@ 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.RuntimeConfig 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.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.util.letIf @@ -46,7 +47,7 @@ class ApiKeyAuthDecorator : ClientCodegenDecorator { baseCustomizations: List, ): List { return baseCustomizations.letIf(applies(codegenContext)) { customizations -> - customizations + ApiKeyConfigCustomization(codegenContext.runtimeConfig) + customizations + ApiKeyConfigCustomization(codegenContext) } } @@ -156,15 +157,18 @@ private class ApiKeyOperationCustomization(private val runtimeConfig: RuntimeCon } } -private class ApiKeyConfigCustomization(runtimeConfig: RuntimeConfig) : ConfigCustomization() { +private class ApiKeyConfigCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { + val runtimeMode = codegenContext.smithyRuntimeMode + val runtimeConfig = codegenContext.runtimeConfig private val codegenScope = arrayOf( + *preludeScope, "ApiKey" to apiKey(runtimeConfig), ) override fun section(section: ServiceConfig): Writable = when (section) { is ServiceConfig.BuilderStruct -> writable { - rustTemplate("api_key: Option<#{ApiKey}>,", *codegenScope) + rustTemplate("api_key: #{Option}<#{ApiKey}>,", *codegenScope) } is ServiceConfig.BuilderImpl -> writable { rustTemplate( @@ -176,7 +180,7 @@ private class ApiKeyConfigCustomization(runtimeConfig: RuntimeConfig) : ConfigCu } /// Sets the API key that will be used by the client. - pub fn set_api_key(&mut self, api_key: Option<#{ApiKey}>) -> &mut Self { + pub fn set_api_key(&mut self, api_key: #{Option}<#{ApiKey}>) -> &mut Self { self.api_key = api_key; self } @@ -185,21 +189,39 @@ private class ApiKeyConfigCustomization(runtimeConfig: RuntimeConfig) : ConfigCu ) } is ServiceConfig.BuilderBuild -> writable { - rust("api_key: self.api_key,") + if (runtimeMode.defaultToOrchestrator) { + rust("layer.store_or_unset(self.api_key);") + } else { + rust("api_key: self.api_key,") + } } is ServiceConfig.ConfigStruct -> writable { - rustTemplate("api_key: Option<#{ApiKey}>,", *codegenScope) + if (runtimeMode.defaultToMiddleware) { + rustTemplate("api_key: #{Option}<#{ApiKey}>,", *codegenScope) + } } is ServiceConfig.ConfigImpl -> writable { - rustTemplate( - """ - /// Returns API key used by the client, if it was provided. - pub fn api_key(&self) -> Option<&#{ApiKey}> { - self.api_key.as_ref() - } - """, - *codegenScope, - ) + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Returns API key used by the client, if it was provided. + pub fn api_key(&self) -> #{Option}<&#{ApiKey}> { + self.inner.load::<#{ApiKey}>() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns API key used by the client, if it was provided. + pub fn api_key(&self) -> #{Option}<&#{ApiKey}> { + self.api_key.as_ref() + } + """, + *codegenScope, + ) + } } else -> emptySection } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index 78a100ad6a..c985d7fd47 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -237,6 +237,7 @@ private class HttpAuthConfigCustomization( private val authSchemes: HttpAuthSchemes, ) : ConfigCustomization() { private val codegenScope = codegenScope(codegenContext.runtimeConfig) + private val runtimeMode = codegenContext.smithyRuntimeMode override fun section(section: ServiceConfig): Writable = writable { when (section) { @@ -324,7 +325,9 @@ private class HttpAuthConfigCustomization( } is ServiceConfig.BuilderBuild -> { - rust("identity_resolvers: self.identity_resolvers,") + if (runtimeMode.defaultToMiddleware) { + rust("identity_resolvers: self.identity_resolvers,") + } } is ServiceConfig.ConfigStruct -> { @@ -343,6 +346,10 @@ private class HttpAuthConfigCustomization( ) } + is ServiceConfig.BuilderBuildExtras -> { + rust("identity_resolvers: self.identity_resolvers,") + } + else -> {} } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index ba9c68f5f7..3fe3c56737 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -13,7 +13,6 @@ 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.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.letIf @@ -31,9 +30,10 @@ class HttpConnectorConfigDecorator : ClientCodegenDecorator { } private class HttpConnectorConfigCustomization( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, ) : ConfigCustomization() { private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), @@ -42,19 +42,33 @@ private class HttpConnectorConfigCustomization( override fun section(section: ServiceConfig): Writable { return when (section) { is ServiceConfig.ConfigStruct -> writable { - rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + if (runtimeMode.defaultToMiddleware) { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } } is ServiceConfig.ConfigImpl -> writable { - rustTemplate( - """ - /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. - pub fn http_connector(&self) -> Option<&#{HttpConnector}> { - self.http_connector.as_ref() - } - """, - *codegenScope, - ) + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. + pub fn http_connector(&self) -> Option<&#{HttpConnector}> { + self.inner.load::<#{HttpConnector}>() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. + pub fn http_connector(&self) -> Option<&#{HttpConnector}> { + self.http_connector.as_ref() + } + """, + *codegenScope, + ) + } } is ServiceConfig.BuilderStruct -> writable { @@ -147,7 +161,11 @@ private class HttpConnectorConfigCustomization( } is ServiceConfig.BuilderBuild -> writable { - rust("http_connector: self.http_connector,") + if (runtimeMode.defaultToOrchestrator) { + rust("self.http_connector.map(|c| layer.store_put(c));") + } else { + rust("http_connector: self.http_connector,") + } } else -> emptySection diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt index 4644bb91e2..7bca1950d3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt @@ -26,17 +26,13 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus writable { when (section) { ServiceConfig.ConfigStruct -> rustTemplate( - """ - pub(crate) interceptors: Vec<#{SharedInterceptor}>, - """, + "pub(crate) interceptors: Vec<#{SharedInterceptor}>,", *codegenScope, ) ServiceConfig.BuilderStruct -> rustTemplate( - """ - interceptors: Vec<#{SharedInterceptor}>, - """, + "interceptors: Vec<#{SharedInterceptor}>,", *codegenScope, ) @@ -171,18 +167,14 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus *codegenScope, ) - ServiceConfig.BuilderBuild -> rust( - """ - interceptors: self.interceptors, - """, - ) - is ServiceConfig.RuntimePluginInterceptors -> rust( """ ${section.interceptors}.extend(self.interceptors.iter().cloned()); """, ) + is ServiceConfig.BuilderBuildExtras -> rust("interceptors: self.interceptors,") + else -> emptySection } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 509fdcba7e..44c91aa1de 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations +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.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection @@ -14,13 +15,13 @@ 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.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.RustCrate -class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCustomization() { +class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode private val retryConfig = RuntimeType.smithyTypes(runtimeConfig).resolve("retry") private val sleepModule = RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep") private val timeoutModule = RuntimeType.smithyTypes(runtimeConfig).resolve("timeout") @@ -35,38 +36,64 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust override fun section(section: ServiceConfig) = writable { when (section) { - is ServiceConfig.ConfigStruct -> rustTemplate( - """ - retry_config: Option<#{RetryConfig}>, - sleep_impl: Option<#{SharedAsyncSleep}>, - timeout_config: Option<#{TimeoutConfig}>, - """, - *codegenScope, - ) + is ServiceConfig.ConfigStruct -> { + if (runtimeMode.defaultToMiddleware) { + rustTemplate( + """ + retry_config: Option<#{RetryConfig}>, + sleep_impl: Option<#{SharedAsyncSleep}>, + timeout_config: Option<#{TimeoutConfig}>, + """, + *codegenScope, + ) + } + } is ServiceConfig.ConfigImpl -> { - rustTemplate( - """ - /// Return a reference to the retry configuration contained in this config, if any. - pub fn retry_config(&self) -> Option<&#{RetryConfig}> { - self.retry_config.as_ref() - } + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Return a reference to the retry configuration contained in this config, if any. + pub fn retry_config(&self) -> Option<&#{RetryConfig}> { + self.inner.load::<#{RetryConfig}>() + } - /// Return a cloned shared async sleep implementation from this config, if any. - pub fn sleep_impl(&self) -> Option<#{SharedAsyncSleep}> { - self.sleep_impl.clone() - } + /// Return a cloned shared async sleep implementation from this config, if any. + pub fn sleep_impl(&self) -> Option<#{SharedAsyncSleep}> { + self.inner.load::<#{SharedAsyncSleep}>().cloned() + } - /// Return a reference to the timeout configuration contained in this config, if any. - pub fn timeout_config(&self) -> Option<&#{TimeoutConfig}> { - self.timeout_config.as_ref() - } - """, - *codegenScope, - ) + /// Return a reference to the timeout configuration contained in this config, if any. + pub fn timeout_config(&self) -> Option<&#{TimeoutConfig}> { + self.inner.load::<#{TimeoutConfig}>() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Return a reference to the retry configuration contained in this config, if any. + pub fn retry_config(&self) -> Option<&#{RetryConfig}> { + self.retry_config.as_ref() + } + + /// Return a cloned shared async sleep implementation from this config, if any. + pub fn sleep_impl(&self) -> Option<#{SharedAsyncSleep}> { + self.sleep_impl.clone() + } + + /// Return a reference to the timeout configuration contained in this config, if any. + pub fn timeout_config(&self) -> Option<&#{TimeoutConfig}> { + self.timeout_config.as_ref() + } + """, + *codegenScope, + ) + } } - is ServiceConfig.BuilderStruct -> + is ServiceConfig.BuilderStruct -> { rustTemplate( """ retry_config: Option<#{RetryConfig}>, @@ -75,6 +102,7 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust """, *codegenScope, ) + } ServiceConfig.BuilderImpl -> rustTemplate( @@ -216,21 +244,30 @@ class ResiliencyConfigCustomization(codegenContext: CodegenContext) : ConfigCust *codegenScope, ) - ServiceConfig.BuilderBuild -> rustTemplate( - // We call clone on sleep_impl because the field is used by - // initializing the credentials_cache field later in the build - // method of a Config builder. - // We could rearrange the order of decorators so that AwsCodegenDecorator - // runs before RequiredCustomizations, which in turns renders - // CredentialsCacheDecorator before this class, but that is a bigger - // change than adding a call to the clone method on sleep_impl. - """ - retry_config: self.retry_config, - sleep_impl: self.sleep_impl.clone(), - timeout_config: self.timeout_config, - """, - *codegenScope, - ) + ServiceConfig.BuilderBuild -> { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + self.retry_config.map(|r| layer.store_put(r)); + self.sleep_impl.clone().map(|s| layer.store_put(s)); + self.timeout_config.map(|t| layer.store_put(t)); + """, + *codegenScope, + ) + } else { + rustTemplate( + // We call clone on sleep_impl because the field is used by + // initializing the credentials_cache field later in the build + // method of a Config builder. + """ + retry_config: self.retry_config, + sleep_impl: self.sleep_impl.clone(), + timeout_config: self.timeout_config, + """, + *codegenScope, + ) + } + } else -> emptySection } @@ -276,7 +313,7 @@ class ResiliencyServiceRuntimePluginCustomization : ServiceRuntimePluginCustomiz if let Some(timeout_config) = self.handle.conf.timeout_config() { ${section.newLayerName}.put(timeout_config.clone()); } - ${section.newLayerName}.put(self.handle.conf.time_source.clone()); + ${section.newLayerName}.put(self.handle.conf.time_source().clone()); """, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt new file mode 100644 index 0000000000..e27531814e --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt @@ -0,0 +1,127 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +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.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate +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.RuntimeType.Companion.preludeScope + +class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { + private val runtimeMode = codegenContext.smithyRuntimeMode + private val codegenScope = arrayOf( + *preludeScope, + "SharedTimeSource" to RuntimeType.smithyAsync(codegenContext.runtimeConfig).resolve("time::SharedTimeSource"), + ) + + override fun section(section: ServiceConfig) = + writable { + when (section) { + is ServiceConfig.ConfigStruct -> { + if (runtimeMode.defaultToMiddleware) { + rustTemplate( + """ + pub(crate) time_source: #{SharedTimeSource}, + """, + *codegenScope, + ) + } + } + + is ServiceConfig.ConfigImpl -> { + rust("/// Return time source used for this service.") + rustBlockTemplate( + "pub fn time_source(&self) -> #{SharedTimeSource}", + *codegenScope, + ) { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """self.inner.load::<#{SharedTimeSource}>().expect("time source should be set").clone()""", + *codegenScope, + ) + } else { + rust("self.time_source.clone()") + } + } + } + + is ServiceConfig.BuilderStruct -> + rustTemplate( + """ + time_source: #{Option}<#{SharedTimeSource}>, + """, + *codegenScope, + ) + + ServiceConfig.BuilderImpl -> + rustTemplate( + """ + /// Sets the time source used for this service + pub fn time_source( + mut self, + time_source: impl #{Into}<#{SharedTimeSource}>, + ) -> Self { + self.time_source = Some(time_source.into()); + self + } + /// Sets the time source used for this service + pub fn set_time_source( + &mut self, + time_source: #{Option}<#{SharedTimeSource}>, + ) -> &mut Self { + self.time_source = time_source; + self + } + """, + *codegenScope, + ) + + ServiceConfig.BuilderBuild -> { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + layer.store_put(self.time_source.unwrap_or_default()); + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + time_source: self.time_source.unwrap_or_default(), + """, + *codegenScope, + ) + } + } + + else -> emptySection + } + } +} + +class TimeSourceOperationCustomization : OperationCustomization() { + override fun section(section: OperationSection): Writable { + return when (section) { + is OperationSection.MutateRequest -> writable { + rust( + """ + ${section.request}.properties_mut().insert(${section.config}.time_source.clone()); + """, + ) + } + + else -> emptySection + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index 64fb43bd63..ea24e78b3d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -16,11 +16,11 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.Intercep import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.customizations.TimeSourceCustomization +import software.amazon.smithy.rust.codegen.client.smithy.customizations.TimeSourceOperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.TimeSourceOperationCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.config.timeSourceCustomization import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customizations.AllowLintsCustomization @@ -59,9 +59,9 @@ class RequiredCustomizations : ClientCodegenDecorator { if (codegenContext.smithyRuntimeMode.generateOrchestrator) { baseCustomizations + ResiliencyConfigCustomization(codegenContext) + InterceptorConfigCustomization( codegenContext, - ) + timeSourceCustomization(codegenContext) + ) + TimeSourceCustomization(codegenContext) } else { - baseCustomizations + ResiliencyConfigCustomization(codegenContext) + timeSourceCustomization(codegenContext) + baseCustomizations + ResiliencyConfigCustomization(codegenContext) + TimeSourceCustomization(codegenContext) } override fun libRsCustomizations( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt index 05be47f0f5..db842def8d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomization.kt @@ -11,19 +11,22 @@ import software.amazon.smithy.model.shapes.ShapeType import software.amazon.smithy.model.shapes.StringShape import software.amazon.smithy.rulesengine.traits.ClientContextParamDefinition import software.amazon.smithy.rulesengine.traits.ClientContextParamsTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigParam import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype import software.amazon.smithy.rust.codegen.client.smithy.generators.config.standardConfigParam import software.amazon.smithy.rust.codegen.core.rustlang.RustReservedWords import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.join import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.orNull +import software.amazon.smithy.rust.codegen.core.util.toPascalCase import software.amazon.smithy.rust.codegen.core.util.toSnakeCase /** @@ -32,10 +35,11 @@ import software.amazon.smithy.rust.codegen.core.util.toSnakeCase * This handles injecting parameters like `s3::Accelerate` or `s3::ForcePathStyle`. The resulting parameters become * setters on the config builder object. */ -class ClientContextConfigCustomization(ctx: CodegenContext) : ConfigCustomization() { +class ClientContextConfigCustomization(ctx: ClientCodegenContext) : ConfigCustomization() { + private val runtimeConfig = ctx.runtimeConfig private val configParams = ctx.serviceShape.getTrait()?.parameters.orEmpty().toList() - .map { (key, value) -> fromClientParam(key, value, ctx.symbolProvider) } - private val decorators = configParams.map { standardConfigParam(it) } + .map { (key, value) -> fromClientParam(key, value, ctx.symbolProvider, runtimeConfig) } + private val decorators = configParams.map { standardConfigParam(it, ctx) } companion object { fun toSymbol(shapeType: ShapeType, symbolProvider: RustSymbolProvider): Symbol = @@ -51,10 +55,13 @@ class ClientContextConfigCustomization(ctx: CodegenContext) : ConfigCustomizatio name: String, definition: ClientContextParamDefinition, symbolProvider: RustSymbolProvider, + runtimeConfig: RuntimeConfig, ): ConfigParam { + val inner = toSymbol(definition.type, symbolProvider) return ConfigParam( RustReservedWords.escapeIfNeeded(name.toSnakeCase()), - toSymbol(definition.type, symbolProvider), + inner, + configParamNewtype(RustReservedWords.escapeIfNeeded(name.toPascalCase()), inner, runtimeConfig), definition.documentation.orNull()?.let { writable { docs(it) } }, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index 6d74f1a7dc..a3308ce4da 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -25,6 +25,7 @@ internal class EndpointConfigCustomization( ConfigCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val moduleUseName = codegenContext.moduleUseName() + private val runtimeMode = codegenContext.smithyRuntimeMode private val types = Types(runtimeConfig) override fun section(section: ServiceConfig): Writable { @@ -38,21 +39,38 @@ internal class EndpointConfigCustomization( "Params" to typesGenerator.paramsStruct(), ) when (section) { - is ServiceConfig.ConfigStruct -> rustTemplate( - "pub (crate) endpoint_resolver: $sharedEndpointResolver,", - *codegenScope, - ) + is ServiceConfig.ConfigStruct -> { + if (runtimeMode.defaultToMiddleware) { + rustTemplate( + "pub (crate) endpoint_resolver: $sharedEndpointResolver,", + *codegenScope, + ) + } + } - is ServiceConfig.ConfigImpl -> - rustTemplate( - """ - /// Returns the endpoint resolver. - pub fn endpoint_resolver(&self) -> $sharedEndpointResolver { - self.endpoint_resolver.clone() - } - """, - *codegenScope, - ) + is ServiceConfig.ConfigImpl -> { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Returns the endpoint resolver. + pub fn endpoint_resolver(&self) -> $sharedEndpointResolver { + self.inner.load::<$sharedEndpointResolver>().expect("endpoint resolver should be set").clone() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns the endpoint resolver. + pub fn endpoint_resolver(&self) -> $sharedEndpointResolver { + self.endpoint_resolver.clone() + } + """, + *codegenScope, + ) + } + } is ServiceConfig.BuilderStruct -> rustTemplate( @@ -123,15 +141,27 @@ internal class EndpointConfigCustomization( ServiceConfig.BuilderBuild -> { val defaultResolver = typesGenerator.defaultResolver() if (defaultResolver != null) { - rustTemplate( - """ - endpoint_resolver: self.endpoint_resolver.unwrap_or_else(|| - #{SharedEndpointResolver}::new(#{DefaultResolver}::new()) - ), - """, - *codegenScope, - "DefaultResolver" to defaultResolver, - ) + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + layer.store_put(self.endpoint_resolver.unwrap_or_else(|| + #{SharedEndpointResolver}::new(#{DefaultResolver}::new()) + )); + """, + *codegenScope, + "DefaultResolver" to defaultResolver, + ) + } else { + rustTemplate( + """ + endpoint_resolver: self.endpoint_resolver.unwrap_or_else(|| + #{SharedEndpointResolver}::new(#{DefaultResolver}::new()) + ), + """, + *codegenScope, + "DefaultResolver" to defaultResolver, + ) + } } else { val alwaysFailsResolver = RuntimeType.forInlineFun("MissingResolver", ClientRustModule.Endpoint) { @@ -152,13 +182,23 @@ internal class EndpointConfigCustomization( } // To keep this diff under control, rather than `.expect` here, insert a resolver that will // always fail. In the future, this will be changed to an `expect()` - rustTemplate( - """ - endpoint_resolver: self.endpoint_resolver.unwrap_or_else(||#{SharedEndpointResolver}::new(#{FailingResolver})), - """, - *codegenScope, - "FailingResolver" to alwaysFailsResolver, - ) + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + layer.store_put(self.endpoint_resolver.unwrap_or_else(||#{SharedEndpointResolver}::new(#{FailingResolver}))); + """, + *codegenScope, + "FailingResolver" to alwaysFailsResolver, + ) + } else { + rustTemplate( + """ + endpoint_resolver: self.endpoint_resolver.unwrap_or_else(||#{SharedEndpointResolver}::new(#{FailingResolver})), + """, + *codegenScope, + "FailingResolver" to alwaysFailsResolver, + ) + } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 4a3b1d0d0a..ff15b8f659 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -119,9 +119,9 @@ class EndpointParamsInterceptorGenerator( val paramName = EndpointParamsGenerator.memberName(name) val setterName = EndpointParamsGenerator.setterName(name) if (param.type == ShapeType.BOOLEAN) { - rust(".$setterName(_config.$paramName)") + rust(".$setterName(_config.$paramName())") } else { - rust(".$setterName(_config.$paramName.clone())") + rust(".$setterName(_config.$paramName().clone())") } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 412abd6c89..aeac4f1c58 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -170,7 +170,7 @@ class ServiceRuntimePluginGenerator( } fn interceptors(&self, interceptors: &mut #{InterceptorRegistrar}) { - interceptors.extend(self.handle.conf.interceptors.iter().cloned()); + interceptors.extend(self.handle.conf.interceptors().cloned()); #{additional_interceptors} } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt index 75b075dc4d..f7c0b3789f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt @@ -5,62 +5,98 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.config +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext 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.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization /** * Add a `make_token` field to Service config. See below for the resulting generated code. */ -class IdempotencyTokenProviderCustomization : NamedCustomization() { +class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext) : NamedCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode + private val codegenScope = arrayOf( + *preludeScope, + "default_provider" to RuntimeType.idempotencyToken(runtimeConfig).resolve("default_provider"), + "IdempotencyTokenProvider" to RuntimeType.idempotencyToken(runtimeConfig).resolve("IdempotencyTokenProvider"), + ) + override fun section(section: ServiceConfig): Writable { return when (section) { is ServiceConfig.ConfigStruct -> writable { - rust("pub (crate) make_token: #T::IdempotencyTokenProvider,", RuntimeType.IdempotencyToken) + if (runtimeMode.defaultToMiddleware) { + rustTemplate("pub (crate) make_token: #{IdempotencyTokenProvider},", *codegenScope) + } } ServiceConfig.ConfigImpl -> writable { - rust( - """ - /// Returns a copy of the idempotency token provider. - /// If a random token provider was configured, - /// a newly-randomized token provider will be returned. - pub fn make_token(&self) -> #T::IdempotencyTokenProvider { - self.make_token.clone() - } - """, - RuntimeType.IdempotencyToken, - ) + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Returns a copy of the idempotency token provider. + /// If a random token provider was configured, + /// a newly-randomized token provider will be returned. + pub fn make_token(&self) -> #{IdempotencyTokenProvider} { + self.inner.load::<#{IdempotencyTokenProvider}>().expect("the idempotency provider should be set").clone() + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Returns a copy of the idempotency token provider. + /// If a random token provider was configured, + /// a newly-randomized token provider will be returned. + pub fn make_token(&self) -> #{IdempotencyTokenProvider} { + self.make_token.clone() + } + """, + *codegenScope, + ) + } } ServiceConfig.BuilderStruct -> writable { - rust("make_token: Option<#T::IdempotencyTokenProvider>,", RuntimeType.IdempotencyToken) + rustTemplate("make_token: #{Option}<#{IdempotencyTokenProvider}>,", *codegenScope) } ServiceConfig.BuilderImpl -> writable { rustTemplate( """ /// Sets the idempotency token provider to use for service calls that require tokens. - pub fn make_token(mut self, make_token: impl Into<#{TokenProvider}>) -> Self { - self.set_make_token(Some(make_token.into())); + pub fn make_token(mut self, make_token: impl #{Into}<#{IdempotencyTokenProvider}>) -> Self { + self.set_make_token(#{Some}(make_token.into())); self } /// Sets the idempotency token provider to use for service calls that require tokens. - pub fn set_make_token(&mut self, make_token: Option<#{TokenProvider}>) -> &mut Self { + pub fn set_make_token(&mut self, make_token: #{Option}<#{IdempotencyTokenProvider}>) -> &mut Self { self.make_token = make_token; self } """, - "TokenProvider" to RuntimeType.IdempotencyToken.resolve("IdempotencyTokenProvider"), + *codegenScope, ) } ServiceConfig.BuilderBuild -> writable { - rust("make_token: self.make_token.unwrap_or_else(#T::default_provider),", RuntimeType.IdempotencyToken) + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + "layer.store_put(self.make_token.unwrap_or_else(#{default_provider}));", + *codegenScope, + ) + } else { + rustTemplate( + "make_token: self.make_token.unwrap_or_else(#{default_provider}),", + *codegenScope, + ) + } } is ServiceConfig.DefaultForTests -> writable { @@ -71,41 +107,3 @@ class IdempotencyTokenProviderCustomization : NamedCustomization( } } } - -/* Generated Code -pub struct Config { - pub(crate) make_token: Box, -} -impl Config { - pub fn builder() -> Builder { - Builder::default() - } -} -#[derive(Default)] -pub struct Builder { - #[allow(dead_code)] - make_token: Option>, -} -impl Builder { - pub fn new() -> Self { - Self::default() - } - - /// Sets the idempotency token provider to use for service calls that require tokens. - pub fn make_token( - mut self, - make_token: impl crate::idempotency_token::MakeIdempotencyToken + 'static, - ) -> Self { - self.make_token = Some(Box::new(make_token)); - self - } - - pub fn build(self) -> Config { - Config { - make_token: self - .make_token - .unwrap_or_else(|| Box::new(crate::idempotency_token::default_provider())), - } - } -} - */ diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index fee8326018..f533740ce5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -11,6 +11,9 @@ import software.amazon.smithy.model.knowledge.OperationIndex import software.amazon.smithy.model.knowledge.TopDownIndex import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.traits.IdempotencyTokenTrait +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.customizations.codegenScope import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -22,7 +25,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock 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.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig 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.smithy.customize.NamedCustomization @@ -89,6 +92,14 @@ sealed class ServiceConfig(name: String) : Section(name) { */ object BuilderBuild : ServiceConfig("BuilderBuild") + // TODO(enableNewSmithyRuntime): This is temporary until config builder is backed by a CloneableLayer. + // It is needed because certain config fields appear explicitly regardless of the smithy runtime mode, e.g. + // interceptors. The [BuilderBuild] section is bifurcated depending on the runtime mode (in the orchestrator mode, + // storing a field into a frozen layer and in the middleware moving it into a corresponding service config field) + // so we need a different temporary section to always move a field from a builder to service config within the + // build method. + object BuilderBuildExtras : ServiceConfig("BuilderBuildExtras") + /** * A section for setting up a field to be used by RuntimePlugin */ @@ -110,10 +121,53 @@ sealed class ServiceConfig(name: String) : Section(name) { data class ConfigParam( val name: String, val type: Symbol, + val newtype: RuntimeType?, val setterDocs: Writable?, val getterDocs: Writable? = null, val optional: Boolean = true, -) +) { + + data class Builder( + var name: String? = null, + var type: Symbol? = null, + var newtype: RuntimeType? = null, + var setterDocs: Writable? = null, + var getterDocs: Writable? = null, + var optional: Boolean = true, + ) { + fun name(name: String) = apply { this.name = name } + fun type(type: Symbol) = apply { this.type = type } + fun newtype(newtype: RuntimeType) = apply { this.newtype = newtype } + fun setterDocs(setterDocs: Writable?) = apply { this.setterDocs = setterDocs } + fun getterDocs(getterDocs: Writable?) = apply { this.getterDocs = getterDocs } + fun optional(optional: Boolean) = apply { this.optional = optional } + fun build() = ConfigParam(name!!, type!!, newtype, setterDocs, getterDocs, optional) + } +} + +/** + * Generate a [RuntimeType] for a newtype whose name is [newtypeName] that wraps [inner]. + * + * When config parameters are stored in a config map in Rust, stored parameters are keyed by type. + * Therefore, primitive types, such as bool and String, need to be wrapped in newtypes to make them distinct. + */ +fun configParamNewtype(newtypeName: String, inner: Symbol, runtimeConfig: RuntimeConfig) = + RuntimeType.forInlineFun(newtypeName, ClientRustModule.Config) { + val codegenScope = arrayOf( + "Storable" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::Storable"), + "StoreReplace" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::StoreReplace"), + ) + rustTemplate( + """ + ##[derive(Debug, Clone)] + pub(crate) struct $newtypeName($inner); + impl #{Storable} for $newtypeName { + type Storer = #{StoreReplace}<$newtypeName>; + } + """, + *codegenScope, + ) + } /** * Config customization for a config param with no special behavior: @@ -121,19 +175,47 @@ data class ConfigParam( * 2. convenience setter (non-optional) * 3. standard setter (&mut self) */ -fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : ConfigCustomization() { +fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext): ConfigCustomization = object : ConfigCustomization() { + private val runtimeMode = codegenContext.smithyRuntimeMode + override fun section(section: ServiceConfig): Writable { return when (section) { - is ServiceConfig.ConfigStruct -> writable { - docsOrFallback(param.getterDocs) - val t = when (param.optional) { - true -> param.type.makeOptional() - false -> param.type + ServiceConfig.ConfigStruct -> writable { + if (runtimeMode.defaultToMiddleware) { + docsOrFallback(param.getterDocs) + val t = when (param.optional) { + true -> param.type.makeOptional() + false -> param.type + } + rust("pub (crate) ${param.name}: #T,", t) + } + } + + ServiceConfig.ConfigImpl -> writable { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + pub(crate) fn ${param.name}(&self) -> #{output} { + self.inner.load::<#{newtype}>().map(#{f}) + } + """, + "f" to writable { + if (param.type.name == "bool") { + rust("|ty| ty.0") + } else { + rust("|ty| ty.0.clone()") + } + }, + "newtype" to param.newtype!!, + "output" to if (param.optional) { + param.type.makeOptional() + } else { + param.type + }, + ) } - rust("pub (crate) ${param.name}: #T,", t) } - ServiceConfig.ConfigImpl -> emptySection ServiceConfig.BuilderStruct -> writable { rust("${param.name}: #T,", param.type.makeOptional()) } @@ -162,8 +244,15 @@ fun standardConfigParam(param: ConfigParam): ConfigCustomization = object : Conf } ServiceConfig.BuilderBuild -> writable { - val default = "".letIf(!param.optional) { ".unwrap_or_default() " } - rust("${param.name}: self.${param.name}$default,") + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + "layer.store_or_unset(self.${param.name}.map(#{newtype}));", + "newtype" to param.newtype!!, + ) + } else { + val default = "".letIf(!param.optional) { ".unwrap_or_default() " } + rust("${param.name}: self.${param.name}$default,") + } } is ServiceConfig.RuntimePluginConfig -> emptySection @@ -199,19 +288,19 @@ typealias ConfigCustomization = NamedCustomization * // builder implementation * } */ + class ServiceConfigGenerator( - private val codegenContext: CodegenContext, + private val codegenContext: ClientCodegenContext, private val customizations: List = listOf(), ) { - companion object { fun withBaseBehavior( - codegenContext: CodegenContext, + codegenContext: ClientCodegenContext, extraCustomizations: List, ): ServiceConfigGenerator { val baseFeatures = mutableListOf() if (codegenContext.serviceShape.needsIdempotencyToken(codegenContext.model)) { - baseFeatures.add(IdempotencyTokenProviderCustomization()) + baseFeatures.add(IdempotencyTokenProviderCustomization(codegenContext)) } return ServiceConfigGenerator(codegenContext, baseFeatures + extraCustomizations) } @@ -228,6 +317,8 @@ class ServiceConfigGenerator( "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), *preludeScope, ) + private val moduleUseName = codegenContext.moduleUseName() + private val runtimeMode = codegenContext.smithyRuntimeMode fun render(writer: RustWriter) { writer.docs("Service config.\n") @@ -236,6 +327,12 @@ class ServiceConfigGenerator( } Attribute(Attribute.derive(RuntimeType.Clone)).render(writer) writer.rustBlock("pub struct Config") { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + "inner: #{FrozenLayer},", + *codegenScope, + ) + } customizations.forEach { it.section(ServiceConfig.ConfigStruct)(this) } @@ -287,7 +384,7 @@ class ServiceConfigGenerator( writer.rustBlock("impl Builder") { writer.docs("Constructs a config builder.") - writer.rustTemplate("pub fn new() -> Self { Self::default() }") + writer.rust("pub fn new() -> Self { Self::default() }") customizations.forEach { it.section(ServiceConfig.BuilderImpl)(this) } @@ -312,15 +409,31 @@ class ServiceConfigGenerator( docs("Builds a [`Config`].") rustBlock("pub fn build(self) -> Config") { - rustBlock("Config") { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """let mut layer = #{Layer}::new("$moduleUseName::Config");""", + *codegenScope, + ) customizations.forEach { it.section(ServiceConfig.BuilderBuild)(this) } + rustBlock("Config") { + customizations.forEach { + it.section(ServiceConfig.BuilderBuildExtras)(this) + } + rust("inner: layer.freeze(),") + } + } else { + rustBlock("Config") { + customizations.forEach { + it.section(ServiceConfig.BuilderBuild)(this) + } + } } } - } - customizations.forEach { - it.section(ServiceConfig.Extras)(writer) + customizations.forEach { + it.section(ServiceConfig.Extras)(writer) + } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt deleted file mode 100644 index ea5285aa70..0000000000 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/TimeSourceConfig.kt +++ /dev/null @@ -1,40 +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 software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection -import software.amazon.smithy.rust.codegen.core.rustlang.Writable -import software.amazon.smithy.rust.codegen.core.rustlang.docs -import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType - -fun timeSourceCustomization(codegenContext: ClientCodegenContext) = standardConfigParam( - ConfigParam( - "time_source", - RuntimeType.smithyAsync(codegenContext.runtimeConfig).resolve("time::SharedTimeSource").toSymbol(), - setterDocs = writable { docs("""Sets the time source used for this service""") }, - optional = false, - ), -) - -class TimeSourceOperationCustomization : OperationCustomization() { - override fun section(section: OperationSection): Writable { - return when (section) { - is OperationSection.MutateRequest -> writable { - rust( - """ - ${section.request}.properties_mut().insert(${section.config}.time_source.clone()); - """, - ) - } - - else -> emptySection - } - } -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt index 1f19a46f2b..d45a2300ee 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt @@ -5,36 +5,61 @@ 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.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.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): ConfigCustomization { +fun stubConfigCustomization(name: String, codegenContext: ClientCodegenContext): ConfigCustomization { return object : ConfigCustomization() { override fun section(section: ServiceConfig): Writable = writable { when (section) { - ServiceConfig.ConfigStruct -> rust("_$name: u64,") - ServiceConfig.ConfigImpl -> rust( - """ - ##[allow(missing_docs)] - pub fn $name(&self) -> u64 { - self._$name + ServiceConfig.ConfigStruct -> { + if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + rust("_$name: u64,") } - """, - ) + } + ServiceConfig.ConfigImpl -> { + if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + rustTemplate( + """ + ##[allow(missing_docs)] + pub fn $name(&self) -> u64 { + self.inner.load::<#{T}>().map(|u| u.0).unwrap() + } + """, + "T" to configParamNewtype( + "_$name".toPascalCase(), RuntimeType.U64.toSymbol(), + codegenContext.runtimeConfig, + ), + ) + } else { + rust( + """ + ##[allow(missing_docs)] + pub fn $name(&self) -> u64 { + self._$name + } + """, + ) + } + } ServiceConfig.BuilderStruct -> rust("_$name: Option,") ServiceConfig.BuilderImpl -> rust( """ @@ -45,11 +70,25 @@ fun stubConfigCustomization(name: String): ConfigCustomization { } """, ) - ServiceConfig.BuilderBuild -> rust( - """ - _$name: self._$name.unwrap_or(123), - """, - ) + ServiceConfig.BuilderBuild -> { + if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + rustTemplate( + """ + layer.store_or_unset(self._$name.map(#{T})); + """, + "T" to configParamNewtype( + "_$name".toPascalCase(), RuntimeType.U64.toSymbol(), + codegenContext.runtimeConfig, + ), + ) + } else { + rust( + """ + _$name: self._$name.unwrap_or(123), + """, + ) + } + } else -> emptySection } } @@ -63,18 +102,19 @@ fun stubConfigCustomization(name: String): ConfigCustomization { * */ @Suppress("NAME_SHADOWING") fun validateConfigCustomizations( + codegenContext: ClientCodegenContext, customization: ConfigCustomization, project: TestWriterDelegator? = null, ): TestWriterDelegator { val project = project ?: TestWorkspace.testProject() - stubConfigProject(customization, project) + stubConfigProject(codegenContext, customization, project) project.compileAndTest() return project } -fun stubConfigProject(customization: ConfigCustomization, project: TestWriterDelegator): TestWriterDelegator { - val customizations = listOf(stubConfigCustomization("a")) + customization + stubConfigCustomization("b") - val generator = ServiceConfigGenerator(testClientCodegenContext("namespace test".asSmithyModel()), customizations = customizations.toList()) +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( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt index 472325b486..97464760e9 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientModuleProvider import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.RustClientCodegenPlugin +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig @@ -90,6 +91,9 @@ fun testClientCodegenContext( rootDecorator ?: CombinedClientCodegenDecorator(emptyList()), ) +fun ClientCodegenContext.withSmithyRuntimeMode(smithyRuntimeMode: SmithyRuntimeMode): ClientCodegenContext = + copy(settings = settings.copy(codegenConfig = settings.codegenConfig.copy(enableNewSmithyRuntime = smithyRuntimeMode))) + fun TestWriterDelegator.clientRustSettings() = testClientRustSettings( service = ShapeId.from("fake#Fake"), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt index 6ebc6ad759..4f9c1e23e3 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt @@ -39,7 +39,7 @@ internal class ResiliencyConfigCustomizationTest { val project = TestWorkspace.testProject(model, ClientCodegenConfig()) val codegenContext = testClientCodegenContext(model, settings = project.clientRustSettings()) - stubConfigProject(ResiliencyConfigCustomization(codegenContext), project) + stubConfigProject(codegenContext, ResiliencyConfigCustomization(codegenContext), project) ResiliencyReExportCustomization(codegenContext.runtimeConfig).extras(project) project.compileAndTest() } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt index 32cbbddeb4..509a86e845 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt @@ -5,9 +5,12 @@ package software.amazon.smithy.rust.codegen.client.smithy.endpoint -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode 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.withSmithyRuntimeMode import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel @@ -29,28 +32,51 @@ class ClientContextConfigCustomizationTest { service TestService { operations: [] } """.asSmithyModel() - @Test - fun `client params generate a valid customization`() { + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `client params generate a valid customization`(smithyRuntimeModeStr: String) { val project = TestWorkspace.testProject() + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) project.unitTest { - rust( - """ - let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build(); - assert_eq!(conf.a_string_param.unwrap(), "hello!"); - assert_eq!(conf.a_bool_param, Some(true)); - """, - ) + if (smithyRuntimeMode.defaultToOrchestrator) { + rust( + """ + let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build(); + assert_eq!(conf.a_string_param().unwrap(), "hello!"); + assert_eq!(conf.a_bool_param(), Some(true)); + """, + ) + } else { + rust( + """ + let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build(); + assert_eq!(conf.a_string_param.unwrap(), "hello!"); + assert_eq!(conf.a_bool_param, Some(true)); + """, + ) + } } // unset fields project.unitTest { - rust( - """ - let conf = crate::Config::builder().a_string_param("hello!").build(); - assert_eq!(conf.a_string_param.unwrap(), "hello!"); - assert_eq!(conf.a_bool_param, None); - """, - ) + if (smithyRuntimeMode.defaultToOrchestrator) { + rust( + """ + let conf = crate::Config::builder().a_string_param("hello!").build(); + assert_eq!(conf.a_string_param().unwrap(), "hello!"); + assert_eq!(conf.a_bool_param(), None); + """, + ) + } else { + rust( + """ + let conf = crate::Config::builder().a_string_param("hello!").build(); + assert_eq!(conf.a_string_param.unwrap(), "hello!"); + assert_eq!(conf.a_bool_param, None); + """, + ) + } } - validateConfigCustomizations(ClientContextConfigCustomization(testClientCodegenContext(model)), project) + val context = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode) + validateConfigCustomizations(context, ClientContextConfigCustomization(context), project) } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomizationTest.kt index cf65b897f4..2ae469608e 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomizationTest.kt @@ -5,12 +5,24 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.config -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode +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.withSmithyRuntimeMode +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel class IdempotencyTokenProviderCustomizationTest { - @Test - fun `generates a valid config`() { - validateConfigCustomizations(IdempotencyTokenProviderCustomization()) + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generates a valid config`(smithyRuntimeModeStr: String) { + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val model = "namespace test".asSmithyModel() + val codegenContext = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode) + validateConfigCustomizations( + codegenContext, + IdempotencyTokenProviderCustomization(codegenContext), + ) } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt index d55c615413..f5b3a39c27 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt @@ -7,18 +7,26 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.config import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource 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.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext +import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode 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.smithy.customize.NamedCustomization 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.core.testutil.unitTest import software.amazon.smithy.rust.codegen.core.util.lookup +import software.amazon.smithy.rust.codegen.core.util.toPascalCase internal class ServiceConfigGeneratorTest { @Test @@ -76,46 +84,98 @@ internal class ServiceConfigGeneratorTest { model.lookup("com.example#ResourceService").needsIdempotencyToken(model) shouldBe true } - @Test - fun `generate customizations as specified`() { - class ServiceCustomizer : NamedCustomization() { + @ParameterizedTest + @ValueSource(strings = ["middleware", "orchestrator"]) + fun `generate customizations as specified`(smithyRuntimeModeStr: String) { + class ServiceCustomizer(private val codegenContext: ClientCodegenContext) : + NamedCustomization() { + private val runtimeMode = codegenContext.smithyRuntimeMode + override fun section(section: ServiceConfig): Writable { return when (section) { ServiceConfig.ConfigStructAdditionalDocs -> emptySection - ServiceConfig.ConfigStruct -> writable { rust("config_field: u64,") } + ServiceConfig.ConfigStruct -> writable { + if (runtimeMode.defaultToMiddleware) { + rust("config_field: u64,") + } + } + ServiceConfig.ConfigImpl -> writable { - rust( - """ - pub fn config_field(&self) -> u64 { - self.config_field - } - """, - ) + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + ##[allow(missing_docs)] + pub fn config_field(&self) -> u64 { + self.inner.load::<#{T}>().map(|u| u.0).unwrap() + } + """, + "T" to configParamNewtype( + "config_field".toPascalCase(), RuntimeType.U64.toSymbol(), + codegenContext.runtimeConfig, + ), + ) + } else { + rust( + """ + ##[allow(missing_docs)] + pub fn config_field(&self) -> u64 { + self.config_field + } + """, + ) + } } + ServiceConfig.BuilderStruct -> writable { rust("config_field: Option") } ServiceConfig.BuilderImpl -> emptySection ServiceConfig.BuilderBuild -> writable { - rust("config_field: self.config_field.unwrap_or_default(),") + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + "layer.store_or_unset(self.config_field.map(#{T}));", + "T" to configParamNewtype( + "config_field".toPascalCase(), RuntimeType.U64.toSymbol(), + codegenContext.runtimeConfig, + ), + ) + } else { + rust("config_field: self.config_field.unwrap_or_default(),") + } } + else -> emptySection } } } - val ctx = testClientCodegenContext() - val sut = ServiceConfigGenerator(ctx, listOf(ServiceCustomizer())) - val symbolProvider = ctx.symbolProvider + + val model = "namespace empty".asSmithyModel() + val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val codegenContext = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode) + 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 mut builder = Config::builder(); - builder.config_field = Some(99); - let config = builder.build(); - assert_eq!(config.config_field, 99); - """, - ) + if (smithyRuntimeMode.defaultToOrchestrator) { + unitTest( + "set_config_fields", + """ + let mut builder = Config::builder(); + builder.config_field = Some(99); + let config = builder.build(); + assert_eq!(config.config_field(), 99); + """, + ) + } else { + unitTest( + "set_config_fields", + """ + let mut builder = Config::builder(); + builder.config_field = Some(99); + let config = builder.build(); + assert_eq!(config.config_field, 99); + """, + ) + } } project.compileAndTest() } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index 952e3bb0ae..ee097cdc45 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -104,8 +104,12 @@ class InlineDependency( CargoDependency.Http, ) - fun idempotencyToken() = - forInlineableRustFile("idempotency_token", CargoDependency.FastRand) + fun idempotencyToken(runtimeConfig: RuntimeConfig) = + forInlineableRustFile( + "idempotency_token", + CargoDependency.FastRand, + CargoDependency.smithyTypes(runtimeConfig), + ) fun ec2QueryErrors(runtimeConfig: RuntimeConfig): InlineDependency = forInlineableRustFile("ec2_query_errors", CargoDependency.smithyXml(runtimeConfig)) @@ -232,7 +236,8 @@ data class CargoDependency( val AsyncStream: CargoDependency = CargoDependency("async-stream", CratesIo("0.3.0"), DependencyScope.Dev) val Criterion: CargoDependency = CargoDependency("criterion", CratesIo("0.4.0"), DependencyScope.Dev) val FuturesCore: CargoDependency = CargoDependency("futures-core", CratesIo("0.3.25"), DependencyScope.Dev) - val FuturesUtil: CargoDependency = CargoDependency("futures-util", CratesIo("0.3.25"), DependencyScope.Dev, defaultFeatures = false) + val FuturesUtil: CargoDependency = + CargoDependency("futures-util", CratesIo("0.3.25"), DependencyScope.Dev, defaultFeatures = false) val HdrHistogram: CargoDependency = CargoDependency("hdrhistogram", CratesIo("7.5.2"), DependencyScope.Dev) val Hound: CargoDependency = CargoDependency("hound", CratesIo("3.4.0"), DependencyScope.Dev) val PrettyAssertions: CargoDependency = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 0a25844584..5a8f8a0020 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -274,6 +274,7 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) val String = std.resolve("string::String") val Sync = std.resolve("marker::Sync") val TryFrom = stdConvert.resolve("TryFrom") + val U64 = std.resolve("primitive::u64") val Vec = std.resolve("vec::Vec") // external cargo dependency types @@ -431,7 +432,8 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun unwrappedXmlErrors(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.unwrappedXmlErrors(runtimeConfig)) - val IdempotencyToken by lazy { forInlineDependency(InlineDependency.idempotencyToken()) } + fun idempotencyToken(runtimeConfig: RuntimeConfig) = + forInlineDependency(InlineDependency.idempotencyToken(runtimeConfig)) fun runtimePlugin(runtimeConfig: RuntimeConfig) = RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugin") diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/InlineDependencyTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/InlineDependencyTest.kt index 9d613f558f..4e341c769f 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/InlineDependencyTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/InlineDependencyTest.kt @@ -10,6 +10,7 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import org.junit.jupiter.api.Test import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest @@ -32,20 +33,23 @@ internal class InlineDependencyTest { @Test fun `locate dependencies from the inlineable module`() { - val dep = InlineDependency.idempotencyToken() + val runtimeConfig = TestRuntimeConfig + val dep = InlineDependency.serializationSettings(runtimeConfig) val testProject = TestWorkspace.testProject() testProject.lib { rustTemplate( """ + ##[test] - fn idempotency_works() { - use #{idempotency}::uuid_v4; - let res = uuid_v4(0); - assert_eq!(res, "00000000-0000-4000-8000-000000000000"); + fn header_serialization_settings_can_be_constructed() { + use #{serialization_settings}::HeaderSerializationSettings; + use #{aws_smithy_http}::header::set_request_header_if_absent; + let _settings = HeaderSerializationSettings::default(); } """, - "idempotency" to dep.toType(), + "serialization_settings" to dep.toType(), + "aws_smithy_http" to RuntimeType.smithyHttp(runtimeConfig), ) } testProject.compileAndTest() diff --git a/rust-runtime/aws-smithy-async/Cargo.toml b/rust-runtime/aws-smithy-async/Cargo.toml index c95862d9ff..ca703aaf65 100644 --- a/rust-runtime/aws-smithy-async/Cargo.toml +++ b/rust-runtime/aws-smithy-async/Cargo.toml @@ -12,6 +12,7 @@ rt-tokio = ["tokio/time"] test-util = [] [dependencies] +aws-smithy-types = { path = "../aws-smithy-types" } pin-project-lite = "0.2" tokio = { version = "1.23.1", features = ["sync"] } tokio-stream = { version = "0.1.5", default-features = false } diff --git a/rust-runtime/aws-smithy-async/external-types.toml b/rust-runtime/aws-smithy-async/external-types.toml index 613ae21ffb..dee6e8bb30 100644 --- a/rust-runtime/aws-smithy-async/external-types.toml +++ b/rust-runtime/aws-smithy-async/external-types.toml @@ -1,4 +1,8 @@ allowed_external_types = [ + "aws_smithy_types::config_bag::storable::Storable", + "aws_smithy_types::config_bag::storable::StoreReplace", + "aws_smithy_types::config_bag::storable::Storer", + # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Switch to AsyncIterator once standardized "futures_core::stream::Stream", diff --git a/rust-runtime/aws-smithy-async/src/rt/sleep.rs b/rust-runtime/aws-smithy-async/src/rt/sleep.rs index 076db95c97..6776790ee0 100644 --- a/rust-runtime/aws-smithy-async/src/rt/sleep.rs +++ b/rust-runtime/aws-smithy-async/src/rt/sleep.rs @@ -6,6 +6,7 @@ //! Provides an [`AsyncSleep`] trait that returns a future that sleeps for a given duration, //! and implementations of `AsyncSleep` for different async runtimes. +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::fmt::{Debug, Formatter}; use std::future::Future; use std::pin::Pin; @@ -68,6 +69,10 @@ impl AsyncSleep for SharedAsyncSleep { } } +impl Storable for SharedAsyncSleep { + type Storer = StoreReplace; +} + #[cfg(feature = "rt-tokio")] /// Returns a default sleep implementation based on the features enabled pub fn default_async_sleep() -> Option { diff --git a/rust-runtime/aws-smithy-async/src/test_util.rs b/rust-runtime/aws-smithy-async/src/test_util.rs index 365db3b504..0aec83070d 100644 --- a/rust-runtime/aws-smithy-async/src/test_util.rs +++ b/rust-runtime/aws-smithy-async/src/test_util.rs @@ -24,7 +24,7 @@ pub struct ManualTimeSource { impl TimeSource for ManualTimeSource { fn now(&self) -> SystemTime { - self.start_time + self.log.lock().unwrap().iter().sum() + self.start_time + self.log.lock().unwrap().iter().sum::() } } diff --git a/rust-runtime/aws-smithy-async/src/time.rs b/rust-runtime/aws-smithy-async/src/time.rs index 2abe332c88..0b43a0af74 100644 --- a/rust-runtime/aws-smithy-async/src/time.rs +++ b/rust-runtime/aws-smithy-async/src/time.rs @@ -4,6 +4,7 @@ */ //! Time source abstraction to support WASM and testing +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::fmt::Debug; use std::sync::Arc; use std::time::SystemTime; @@ -86,3 +87,7 @@ impl TimeSource for SharedTimeSource { self.0.now() } } + +impl Storable for SharedTimeSource { + type Storer = StoreReplace; +} diff --git a/rust-runtime/aws-smithy-client/src/http_connector.rs b/rust-runtime/aws-smithy-client/src/http_connector.rs index 37366604ee..eaac5805c6 100644 --- a/rust-runtime/aws-smithy-client/src/http_connector.rs +++ b/rust-runtime/aws-smithy-client/src/http_connector.rs @@ -8,6 +8,7 @@ use crate::erase::DynConnector; use aws_smithy_async::rt::sleep::SharedAsyncSleep; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use aws_smithy_types::timeout::TimeoutConfig; use std::time::Duration; use std::{fmt::Debug, sync::Arc}; @@ -41,6 +42,10 @@ impl Debug for HttpConnector { } } +impl Storable for HttpConnector { + type Storer = StoreReplace; +} + impl HttpConnector { /// If `HttpConnector` is `Prebuilt`, return a clone of that connector. /// If `HttpConnector` is `ConnectorFn`, generate a new connector from settings and return it. diff --git a/rust-runtime/aws-smithy-http/src/endpoint.rs b/rust-runtime/aws-smithy-http/src/endpoint.rs index dcc1db18d7..47c1e1af00 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint.rs @@ -7,6 +7,7 @@ use crate::endpoint::error::InvalidEndpointError; use crate::operation::error::BuildError; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use http::uri::{Authority, Uri}; use std::borrow::Cow; use std::fmt::{Debug, Formatter}; @@ -66,6 +67,10 @@ impl From>> for SharedEndpointResolver { } } +impl Storable for SharedEndpointResolver { + type Storer = StoreReplace>; +} + impl ResolveEndpoint for SharedEndpointResolver { fn resolve_endpoint(&self, params: &T) -> Result { self.0.resolve_endpoint(params) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index 341567d191..ad8188342d 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -5,7 +5,7 @@ use crate::client::auth::AuthSchemeId; use crate::client::orchestrator::Future; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use std::any::Any; use std::fmt::Debug; use std::sync::Arc; @@ -23,6 +23,10 @@ pub struct IdentityResolvers { identity_resolvers: Vec<(AuthSchemeId, Arc)>, } +impl Storable for IdentityResolvers { + type Storer = StoreReplace; +} + impl IdentityResolvers { pub fn builder() -> builders::IdentityResolversBuilder { builders::IdentityResolversBuilder::new() diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index 3e8af28db7..0f13473ce0 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -9,7 +9,7 @@ pub mod error; use crate::client::interceptors::context::wrappers::{ FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, }; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend}; use aws_smithy_types::error::display::DisplayErrorContext; pub use context::{ wrappers::{ @@ -635,6 +635,10 @@ impl Deref for SharedInterceptor { } } +impl Storable for SharedInterceptor { + type Storer = StoreAppend; +} + /// Collection of [`SharedInterceptor`] that allows for only registration #[derive(Debug, Clone, Default)] pub struct InterceptorRegistrar { diff --git a/rust-runtime/aws-smithy-types/src/retry.rs b/rust-runtime/aws-smithy-types/src/retry.rs index 9602d6135b..1db024a411 100644 --- a/rust-runtime/aws-smithy-types/src/retry.rs +++ b/rust-runtime/aws-smithy-types/src/retry.rs @@ -5,6 +5,7 @@ //! This module defines types that describe when to retry given a response. +use crate::config_bag::{Storable, StoreReplace}; use std::fmt; use std::str::FromStr; use std::time::Duration; @@ -278,6 +279,10 @@ pub struct RetryConfig { reconnect_mode: ReconnectMode, } +impl Storable for RetryConfig { + type Storer = StoreReplace; +} + /// Mode for connection re-establishment /// /// By default, when a transient error is encountered, the connection in use will be poisoned. This diff --git a/rust-runtime/aws-smithy-types/src/timeout.rs b/rust-runtime/aws-smithy-types/src/timeout.rs index 04e92650a9..160895ceb1 100644 --- a/rust-runtime/aws-smithy-types/src/timeout.rs +++ b/rust-runtime/aws-smithy-types/src/timeout.rs @@ -6,6 +6,7 @@ //! This module defines types that describe timeouts that can be applied to various stages of the //! Smithy networking stack. +use crate::config_bag::{Storable, StoreReplace}; use std::time::Duration; /// Builder for [`TimeoutConfig`]. @@ -208,6 +209,10 @@ pub struct TimeoutConfig { operation_attempt_timeout: Option, } +impl Storable for TimeoutConfig { + type Storer = StoreReplace; +} + impl TimeoutConfig { /// Returns a builder to create a `TimeoutConfig`. pub fn builder() -> TimeoutConfigBuilder { diff --git a/rust-runtime/inlineable/src/idempotency_token.rs b/rust-runtime/inlineable/src/idempotency_token.rs index ac71874bc1..12921f02af 100644 --- a/rust-runtime/inlineable/src/idempotency_token.rs +++ b/rust-runtime/inlineable/src/idempotency_token.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::sync::Mutex; pub(crate) fn uuid_v4(input: u128) -> String { @@ -58,6 +59,10 @@ impl From<&'static str> for IdempotencyTokenProvider { } } +impl Storable for IdempotencyTokenProvider { + type Storer = StoreReplace; +} + impl IdempotencyTokenProvider { pub fn make_idempotency_token(&self) -> String { match &self.inner { From 963089276570c8335a1e5a686aa1e9ca298e810c Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 16 Jun 2023 12:48:00 -0500 Subject: [PATCH 163/253] Rename `make_token` to `idempotency_token_provider` (#2783) ## Motivation and Context Incorporates suggestion made in https://github.com/awslabs/smithy-rs/pull/2762#discussion_r1231462878 ## Description This PR renames `make_token` to `idempotency_token_provider` for clarity wherever it is referred to in the fields and API in service configs and their builders. ## Testing Existing tests in CI ## Checklist - [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._ --------- Co-authored-by: Yuki Saito --- CHANGELOG.next.toml | 12 +++++++++ .../timestreamquery/tests/endpoint_disco.rs | 2 +- .../IdempotencyTokenGenerator.kt | 2 +- .../IdempotencyTokenProviderCustomization.kt | 26 +++++++++---------- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index dbc42020a6..e7157b365e 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -550,3 +550,15 @@ let plugin = plugin_from_operation_fn(map); references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "hlbarber" + +[[smithy-rs]] +message = "The naming `make_token` for fields and the API of `IdempotencyTokenProvider` in service configs and their builders has now been updated to `idempotency_token_provider`." +references = ["smithy-rs#2783"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } +author = "ysaito1001" + +[[aws-sdk-rust]] +message = "The naming `make_token` for fields and the API of `IdempotencyTokenProvider` in service configs and their builders has now been updated to `idempotency_token_provider`." +references = ["smithy-rs#2783"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "ysaito1001" diff --git a/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs index 9d4557b0c3..d15244825f 100644 --- a/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs +++ b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs @@ -29,7 +29,7 @@ async fn do_endpoint_discovery() { .time_source(SharedTimeSource::new(ts.clone())) .build(); let conf = query::config::Builder::from(&config) - .make_token("0000-0000-0000") + .idempotency_token_provider("0000-0000-0000") .build(); let (client, reloader) = query::Client::from_conf(conf) .enable_endpoint_discovery() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt index b9e36bd30c..1c6ef7eb42 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt @@ -32,7 +32,7 @@ class IdempotencyTokenGenerator(codegenContext: CodegenContext, operationShape: rustTemplate( """ if ${section.input}.$memberName.is_none() { - ${section.input}.$memberName = #{Some}(${section.config}.make_token.make_idempotency_token()); + ${section.input}.$memberName = #{Some}(${section.config}.idempotency_token_provider.make_idempotency_token()); } """, *preludeScope, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt index f7c0b3789f..d2e779f1ff 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt @@ -15,7 +15,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.pre import software.amazon.smithy.rust.codegen.core.smithy.customize.NamedCustomization /** - * Add a `make_token` field to Service config. See below for the resulting generated code. + * Add an `idempotency_token_provider` field to Service config. */ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext) : NamedCustomization() { private val runtimeConfig = codegenContext.runtimeConfig @@ -30,7 +30,7 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext return when (section) { is ServiceConfig.ConfigStruct -> writable { if (runtimeMode.defaultToMiddleware) { - rustTemplate("pub (crate) make_token: #{IdempotencyTokenProvider},", *codegenScope) + rustTemplate("pub (crate) idempotency_token_provider: #{IdempotencyTokenProvider},", *codegenScope) } } @@ -41,7 +41,7 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext /// Returns a copy of the idempotency token provider. /// If a random token provider was configured, /// a newly-randomized token provider will be returned. - pub fn make_token(&self) -> #{IdempotencyTokenProvider} { + pub fn idempotency_token_provider(&self) -> #{IdempotencyTokenProvider} { self.inner.load::<#{IdempotencyTokenProvider}>().expect("the idempotency provider should be set").clone() } """, @@ -53,8 +53,8 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext /// Returns a copy of the idempotency token provider. /// If a random token provider was configured, /// a newly-randomized token provider will be returned. - pub fn make_token(&self) -> #{IdempotencyTokenProvider} { - self.make_token.clone() + pub fn idempotency_token_provider(&self) -> #{IdempotencyTokenProvider} { + self.idempotency_token_provider.clone() } """, *codegenScope, @@ -63,21 +63,21 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext } ServiceConfig.BuilderStruct -> writable { - rustTemplate("make_token: #{Option}<#{IdempotencyTokenProvider}>,", *codegenScope) + rustTemplate("idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>,", *codegenScope) } ServiceConfig.BuilderImpl -> writable { rustTemplate( """ /// Sets the idempotency token provider to use for service calls that require tokens. - pub fn make_token(mut self, make_token: impl #{Into}<#{IdempotencyTokenProvider}>) -> Self { - self.set_make_token(#{Some}(make_token.into())); + pub fn idempotency_token_provider(mut self, idempotency_token_provider: impl #{Into}<#{IdempotencyTokenProvider}>) -> Self { + self.set_idempotency_token_provider(#{Some}(idempotency_token_provider.into())); self } /// Sets the idempotency token provider to use for service calls that require tokens. - pub fn set_make_token(&mut self, make_token: #{Option}<#{IdempotencyTokenProvider}>) -> &mut Self { - self.make_token = make_token; + pub fn set_idempotency_token_provider(&mut self, idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>) -> &mut Self { + self.idempotency_token_provider = idempotency_token_provider; self } """, @@ -88,19 +88,19 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext ServiceConfig.BuilderBuild -> writable { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - "layer.store_put(self.make_token.unwrap_or_else(#{default_provider}));", + "layer.store_put(self.idempotency_token_provider.unwrap_or_else(#{default_provider}));", *codegenScope, ) } else { rustTemplate( - "make_token: self.make_token.unwrap_or_else(#{default_provider}),", + "idempotency_token_provider: self.idempotency_token_provider.unwrap_or_else(#{default_provider}),", *codegenScope, ) } } is ServiceConfig.DefaultForTests -> writable { - rust("""${section.configBuilderRef}.set_make_token(Some("00000000-0000-4000-8000-000000000000".into()));""") + rust("""${section.configBuilderRef}.set_idempotency_token_provider(Some("00000000-0000-4000-8000-000000000000".into()));""") } else -> writable { } From 7dc67c819287af0ab37addcba617c7c4ba413d37 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 19 Jun 2023 14:34:57 -0700 Subject: [PATCH 164/253] Fix `@httpChecksum` in the orchestrator implementation (#2785) This PR fixes codegen for the Smithy `@httpChecksum` trait when generating in orchestrator mode, and also fixes an issue in the unit tests where the wrong body was being tested to be retryable. The request body should be retryable rather than the response body. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- ...um.rs => http_body_checksum_middleware.rs} | 3 +- .../src/http_request_checksum.rs | 303 ++++++++++++++++++ .../src/http_response_checksum.rs | 267 +++++++++++++++ aws/rust-runtime/aws-inlineable/src/lib.rs | 6 +- .../rustsdk/HttpRequestChecksumDecorator.kt | 131 +++++--- .../rustsdk/HttpResponseChecksumDecorator.kt | 152 ++++++--- .../customizations/EndpointPrefixGenerator.kt | 2 + .../HttpVersionListCustomization.kt | 2 + .../client/interceptors/context/wrappers.rs | 5 +- .../src/client/orchestrator.rs | 3 + 10 files changed, 770 insertions(+), 104 deletions(-) rename aws/rust-runtime/aws-inlineable/src/{http_body_checksum.rs => http_body_checksum_middleware.rs} (99%) create mode 100644 aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs create mode 100644 aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs diff --git a/aws/rust-runtime/aws-inlineable/src/http_body_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_body_checksum_middleware.rs similarity index 99% rename from aws/rust-runtime/aws-inlineable/src/http_body_checksum.rs rename to aws/rust-runtime/aws-inlineable/src/http_body_checksum_middleware.rs index 01ecd2f37b..f71e8708e8 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_body_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_body_checksum_middleware.rs @@ -248,8 +248,7 @@ fn is_part_level_checksum(checksum: &str) -> bool { #[cfg(test)] mod tests { - use super::wrap_body_with_checksum_validator; - use crate::http_body_checksum::is_part_level_checksum; + use super::{is_part_level_checksum, wrap_body_with_checksum_validator}; use aws_smithy_checksums::ChecksumAlgorithm; use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream::ByteStream; diff --git a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs new file mode 100644 index 0000000000..f64bde8173 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs @@ -0,0 +1,303 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +//! Interceptor for handling Smithy `@httpChecksum` request checksumming with AWS SigV4 + +use aws_http::content_encoding::{AwsChunkedBody, AwsChunkedBodyOptions}; +use aws_runtime::auth::sigv4::SigV4OperationSigningConfig; +use aws_sigv4::http_request::SignableBody; +use aws_smithy_checksums::ChecksumAlgorithm; +use aws_smithy_checksums::{body::calculate, http::HttpChecksum}; +use aws_smithy_http::body::{BoxBody, SdkBody}; +use aws_smithy_http::operation::error::BuildError; +use aws_smithy_runtime_api::client::interceptors::context::Input; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, BoxError, + Interceptor, +}; +use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; +use http::HeaderValue; +use http_body::Body; +use std::{fmt, mem}; + +/// Errors related to constructing checksum-validated HTTP requests +#[derive(Debug)] +pub(crate) enum Error { + /// Only request bodies with a known size can be checksum validated + UnsizedRequestBody, + ChecksumHeadersAreUnsupportedForStreamingBody, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::UnsizedRequestBody => write!( + f, + "Only request bodies with a known size can be checksum validated." + ), + Self::ChecksumHeadersAreUnsupportedForStreamingBody => write!( + f, + "Checksum header insertion is only supported for non-streaming HTTP bodies. \ + To checksum validate a streaming body, the checksums must be sent as trailers." + ), + } + } +} + +impl std::error::Error for Error {} + +#[derive(Debug)] +struct RequestChecksumInterceptorState { + checksum_algorithm: Option, +} +impl Storable for RequestChecksumInterceptorState { + type Storer = StoreReplace; +} + +pub(crate) struct RequestChecksumInterceptor { + algorithm_provider: AP, +} + +impl fmt::Debug for RequestChecksumInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RequestChecksumInterceptor").finish() + } +} + +impl RequestChecksumInterceptor { + pub(crate) fn new(algorithm_provider: AP) -> Self { + Self { algorithm_provider } + } +} + +impl Interceptor for RequestChecksumInterceptor +where + AP: Fn(&Input) -> Result, BoxError>, +{ + fn read_before_serialization( + &self, + context: &BeforeSerializationInterceptorContextRef<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let checksum_algorithm = (self.algorithm_provider)(context.input())?; + + let mut layer = Layer::new("RequestChecksumInterceptor"); + layer.store_put(RequestChecksumInterceptorState { checksum_algorithm }); + cfg.push_layer(layer); + + Ok(()) + } + + /// Calculate a checksum and modify the request to include the checksum as a header + /// (for in-memory request bodies) or a trailer (for streaming request bodies). + /// Streaming bodies must be sized or this will return an error. + fn modify_before_retry_loop( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let state = cfg + .load::() + .expect("set in `read_before_serialization`"); + + if let Some(checksum_algorithm) = state.checksum_algorithm { + let request = context.request_mut(); + add_checksum_for_request_body(request, checksum_algorithm, cfg)?; + } + + Ok(()) + } +} + +fn add_checksum_for_request_body( + request: &mut http::request::Request, + checksum_algorithm: ChecksumAlgorithm, + cfg: &mut ConfigBag, +) -> Result<(), BoxError> { + match request.body().bytes() { + // Body is in-memory: read it and insert the checksum as a header. + Some(data) => { + tracing::debug!("applying {checksum_algorithm:?} of the request body as a header"); + let mut checksum = checksum_algorithm.into_impl(); + checksum.update(data); + + request + .headers_mut() + .insert(checksum.header_name(), checksum.header_value()); + } + // Body is streaming: wrap the body so it will emit a checksum as a trailer. + None => { + tracing::debug!("applying {checksum_algorithm:?} of the request body as a trailer"); + if let Some(mut signing_config) = cfg.get::().cloned() { + signing_config.signing_options.payload_override = + Some(SignableBody::StreamingUnsignedPayloadTrailer); + + let mut layer = Layer::new("http_body_checksum_sigv4_payload_override"); + layer.put(signing_config); + cfg.push_layer(layer); + } + wrap_streaming_request_body_in_checksum_calculating_body(request, checksum_algorithm)?; + } + } + Ok(()) +} + +fn wrap_streaming_request_body_in_checksum_calculating_body( + request: &mut http::request::Request, + checksum_algorithm: ChecksumAlgorithm, +) -> Result<(), BuildError> { + let original_body_size = request + .body() + .size_hint() + .exact() + .ok_or_else(|| BuildError::other(Error::UnsizedRequestBody))?; + + let mut body = { + let body = mem::replace(request.body_mut(), SdkBody::taken()); + + body.map(move |body| { + let checksum = checksum_algorithm.into_impl(); + let trailer_len = HttpChecksum::size(checksum.as_ref()); + let body = calculate::ChecksumBody::new(body, checksum); + let aws_chunked_body_options = + AwsChunkedBodyOptions::new(original_body_size, vec![trailer_len]); + + let body = AwsChunkedBody::new(body, aws_chunked_body_options); + + SdkBody::from_dyn(BoxBody::new(body)) + }) + }; + + let encoded_content_length = body + .size_hint() + .exact() + .ok_or_else(|| BuildError::other(Error::UnsizedRequestBody))?; + + let headers = request.headers_mut(); + + headers.insert( + http::header::HeaderName::from_static("x-amz-trailer"), + // Convert into a `HeaderName` and then into a `HeaderValue` + http::header::HeaderName::from(checksum_algorithm).into(), + ); + + headers.insert( + http::header::CONTENT_LENGTH, + HeaderValue::from(encoded_content_length), + ); + headers.insert( + http::header::HeaderName::from_static("x-amz-decoded-content-length"), + HeaderValue::from(original_body_size), + ); + headers.insert( + http::header::CONTENT_ENCODING, + HeaderValue::from_str(aws_http::content_encoding::header_value::AWS_CHUNKED) + .map_err(BuildError::other) + .expect("\"aws-chunked\" will always be a valid HeaderValue"), + ); + + mem::swap(request.body_mut(), &mut body); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use crate::http_request_checksum::wrap_streaming_request_body_in_checksum_calculating_body; + use aws_smithy_checksums::ChecksumAlgorithm; + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::byte_stream::ByteStream; + use aws_smithy_types::base64; + use bytes::BytesMut; + use http_body::Body; + use tempfile::NamedTempFile; + + #[tokio::test] + async fn test_checksum_body_is_retryable() { + let input_text = "Hello world"; + let chunk_len_hex = format!("{:X}", input_text.len()); + let mut request = http::Request::builder() + .body(SdkBody::retryable(move || SdkBody::from(input_text))) + .unwrap(); + + // ensure original SdkBody is retryable + assert!(request.body().try_clone().is_some()); + + let checksum_algorithm: ChecksumAlgorithm = "crc32".parse().unwrap(); + wrap_streaming_request_body_in_checksum_calculating_body(&mut request, checksum_algorithm) + .unwrap(); + + // ensure wrapped SdkBody is retryable + let mut body = request.body().try_clone().expect("body is retryable"); + + let mut body_data = BytesMut::new(); + loop { + match body.data().await { + Some(data) => body_data.extend_from_slice(&data.unwrap()), + None => break, + } + } + let body = std::str::from_utf8(&body_data).unwrap(); + assert_eq!( + format!( + "{chunk_len_hex}\r\n{input_text}\r\n0\r\nx-amz-checksum-crc32:i9aeUg==\r\n\r\n" + ), + body + ); + } + + #[tokio::test] + async fn test_checksum_body_from_file_is_retryable() { + use std::io::Write; + let mut file = NamedTempFile::new().unwrap(); + let checksum_algorithm: ChecksumAlgorithm = "crc32c".parse().unwrap(); + + let mut crc32c_checksum = checksum_algorithm.into_impl(); + for i in 0..10000 { + let line = format!("This is a large file created for testing purposes {}", i); + file.as_file_mut().write_all(line.as_bytes()).unwrap(); + crc32c_checksum.update(line.as_bytes()); + } + let crc32c_checksum = crc32c_checksum.finalize(); + + let mut request = http::Request::builder() + .body( + ByteStream::read_from() + .path(&file) + .buffer_size(1024) + .build() + .await + .unwrap() + .into_inner(), + ) + .unwrap(); + + // ensure original SdkBody is retryable + assert!(request.body().try_clone().is_some()); + + wrap_streaming_request_body_in_checksum_calculating_body(&mut request, checksum_algorithm) + .unwrap(); + + // ensure wrapped SdkBody is retryable + let mut body = request.body().try_clone().expect("body is retryable"); + + let mut body_data = BytesMut::new(); + loop { + match body.data().await { + Some(data) => body_data.extend_from_slice(&data.unwrap()), + None => break, + } + } + let body = std::str::from_utf8(&body_data).unwrap(); + let expected_checksum = base64::encode(&crc32c_checksum); + let expected = format!("This is a large file created for testing purposes 9999\r\n0\r\nx-amz-checksum-crc32c:{expected_checksum}\r\n\r\n"); + assert!( + body.ends_with(&expected), + "expected {body} to end with '{expected}'" + ); + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs new file mode 100644 index 0000000000..c6d504edd8 --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs @@ -0,0 +1,267 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#![allow(dead_code)] + +//! Interceptor for handling Smithy `@httpChecksum` response checksumming + +use aws_smithy_checksums::ChecksumAlgorithm; +use aws_smithy_http::body::{BoxBody, SdkBody}; +use aws_smithy_runtime_api::client::interceptors::context::Input; +use aws_smithy_runtime_api::client::interceptors::{ + BeforeDeserializationInterceptorContextMut, BeforeSerializationInterceptorContextRef, BoxError, + Interceptor, +}; +use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; +use http::HeaderValue; +use std::{fmt, mem}; + +#[derive(Debug)] +struct ResponseChecksumInterceptorState { + validation_enabled: bool, +} +impl Storable for ResponseChecksumInterceptorState { + type Storer = StoreReplace; +} + +pub(crate) struct ResponseChecksumInterceptor { + response_algorithms: &'static [&'static str], + validation_enabled: VE, +} + +impl fmt::Debug for ResponseChecksumInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ResponseChecksumInterceptor") + .field("response_algorithms", &self.response_algorithms) + .finish() + } +} + +impl ResponseChecksumInterceptor { + pub(crate) fn new( + response_algorithms: &'static [&'static str], + validation_enabled: VE, + ) -> Self { + Self { + response_algorithms, + validation_enabled, + } + } +} + +impl Interceptor for ResponseChecksumInterceptor +where + VE: Fn(&Input) -> bool, +{ + fn read_before_serialization( + &self, + context: &BeforeSerializationInterceptorContextRef<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let validation_enabled = (self.validation_enabled)(context.input()); + + let mut layer = Layer::new("ResponseChecksumInterceptor"); + layer.store_put(ResponseChecksumInterceptorState { validation_enabled }); + cfg.push_layer(layer); + + Ok(()) + } + + fn modify_before_deserialization( + &self, + context: &mut BeforeDeserializationInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let state = cfg + .load::() + .expect("set in `read_before_serialization`"); + + if state.validation_enabled { + let response = context.response_mut(); + let maybe_checksum_headers = check_headers_for_precalculated_checksum( + response.headers(), + self.response_algorithms, + ); + if let Some((checksum_algorithm, precalculated_checksum)) = maybe_checksum_headers { + let mut body = SdkBody::taken(); + mem::swap(&mut body, response.body_mut()); + + let mut body = wrap_body_with_checksum_validator( + body, + checksum_algorithm, + precalculated_checksum, + ); + mem::swap(&mut body, response.body_mut()); + } + } + + Ok(()) + } +} + +/// Given an `SdkBody`, a `aws_smithy_checksums::ChecksumAlgorithm`, and a pre-calculated checksum, +/// return an `SdkBody` where the body will processed with the checksum algorithm and checked +/// against the pre-calculated checksum. +pub(crate) fn wrap_body_with_checksum_validator( + body: SdkBody, + checksum_algorithm: ChecksumAlgorithm, + precalculated_checksum: bytes::Bytes, +) -> SdkBody { + use aws_smithy_checksums::body::validate; + + body.map(move |body| { + SdkBody::from_dyn(BoxBody::new(validate::ChecksumBody::new( + body, + checksum_algorithm.into_impl(), + precalculated_checksum.clone(), + ))) + }) +} + +/// Given a `HeaderMap`, extract any checksum included in the headers as `Some(Bytes)`. +/// If no checksum header is set, return `None`. If multiple checksum headers are set, the one that +/// is fastest to compute will be chosen. +pub(crate) fn check_headers_for_precalculated_checksum( + headers: &http::HeaderMap, + response_algorithms: &[&str], +) -> Option<(ChecksumAlgorithm, bytes::Bytes)> { + let checksum_algorithms_to_check = + aws_smithy_checksums::http::CHECKSUM_ALGORITHMS_IN_PRIORITY_ORDER + .into_iter() + // Process list of algorithms, from fastest to slowest, that may have been used to checksum + // the response body, ignoring any that aren't marked as supported algorithms by the model. + .flat_map(|algo| { + // For loop is necessary b/c the compiler doesn't infer the correct lifetimes for iter().find() + for res_algo in response_algorithms { + if algo.eq_ignore_ascii_case(res_algo) { + return Some(algo); + } + } + + None + }); + + for checksum_algorithm in checksum_algorithms_to_check { + let checksum_algorithm: ChecksumAlgorithm = checksum_algorithm.parse().expect( + "CHECKSUM_ALGORITHMS_IN_PRIORITY_ORDER only contains valid checksum algorithm names", + ); + if let Some(precalculated_checksum) = + headers.get(http::HeaderName::from(checksum_algorithm)) + { + let base64_encoded_precalculated_checksum = precalculated_checksum + .to_str() + .expect("base64 uses ASCII characters"); + + // S3 needs special handling for checksums of objects uploaded with `MultiPartUpload`. + if is_part_level_checksum(base64_encoded_precalculated_checksum) { + tracing::warn!( + more_info = "See https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#large-object-checksums for more information.", + "This checksum is a part-level checksum which can't be validated by the Rust SDK. Disable checksum validation for this request to fix this warning.", + ); + + return None; + } + + let precalculated_checksum = match aws_smithy_types::base64::decode( + base64_encoded_precalculated_checksum, + ) { + Ok(decoded_checksum) => decoded_checksum.into(), + Err(_) => { + tracing::error!("Checksum received from server could not be base64 decoded. No checksum validation will be performed."); + return None; + } + }; + + return Some((checksum_algorithm, precalculated_checksum)); + } + } + + None +} + +fn is_part_level_checksum(checksum: &str) -> bool { + let mut found_number = false; + let mut found_dash = false; + + for ch in checksum.chars().rev() { + // this could be bad + if ch.is_ascii_digit() { + found_number = true; + continue; + } + + // Yup, it's a part-level checksum + if ch == '-' { + if found_dash { + // Found a second dash?? This isn't a part-level checksum. + return false; + } + + found_dash = true; + continue; + } + + break; + } + + found_number && found_dash +} + +#[cfg(test)] +mod tests { + use super::{is_part_level_checksum, wrap_body_with_checksum_validator}; + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::byte_stream::ByteStream; + use aws_smithy_types::error::display::DisplayErrorContext; + use bytes::Bytes; + + #[tokio::test] + async fn test_build_checksum_validated_body_works() { + let checksum_algorithm = "crc32".parse().unwrap(); + let input_text = "Hello world"; + let precalculated_checksum = Bytes::from_static(&[0x8b, 0xd6, 0x9e, 0x52]); + let body = ByteStream::new(SdkBody::from(input_text)); + + let body = body.map(move |sdk_body| { + wrap_body_with_checksum_validator( + sdk_body, + checksum_algorithm, + precalculated_checksum.clone(), + ) + }); + + let mut validated_body = Vec::new(); + if let Err(e) = tokio::io::copy(&mut body.into_async_read(), &mut validated_body).await { + tracing::error!("{}", DisplayErrorContext(&e)); + panic!("checksum validation has failed"); + }; + let body = std::str::from_utf8(&validated_body).unwrap(); + + assert_eq!(input_text, body); + } + + #[test] + fn test_is_multipart_object_checksum() { + // These ARE NOT part-level checksums + assert!(!is_part_level_checksum("abcd")); + assert!(!is_part_level_checksum("abcd=")); + assert!(!is_part_level_checksum("abcd==")); + assert!(!is_part_level_checksum("1234")); + assert!(!is_part_level_checksum("1234=")); + assert!(!is_part_level_checksum("1234==")); + // These ARE part-level checksums + assert!(is_part_level_checksum("abcd-1")); + assert!(is_part_level_checksum("abcd=-12")); + assert!(is_part_level_checksum("abcd12-134")); + assert!(is_part_level_checksum("abcd==-10000")); + // These are gibberish and shouldn't be regarded as a part-level checksum + assert!(!is_part_level_checksum("")); + assert!(!is_part_level_checksum("Spaces? In my header values?")); + assert!(!is_part_level_checksum("abcd==-134!#{!#")); + assert!(!is_part_level_checksum("abcd==-")); + assert!(!is_part_level_checksum("abcd==--11")); + assert!(!is_part_level_checksum("abcd==-AA")); + } +} diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index 5ade5bb680..8f2476ace8 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -49,8 +49,12 @@ pub mod middleware; /// Strip prefixes from IDs returned by Route53 operations when those IDs are used to construct requests pub mod route53_resource_id_preprocessor; +pub mod http_request_checksum; +pub mod http_response_checksum; + +// TODO(enableNewSmithyRuntimeCleanup): Delete this module /// Convert a streaming `SdkBody` into an aws-chunked streaming body with checksum trailers -pub mod http_body_checksum; +pub mod http_body_checksum_middleware; #[allow(dead_code)] pub mod endpoint_discovery; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt index 5c82527cea..b6bca07f39 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt @@ -16,18 +16,34 @@ 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.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.RuntimeConfig 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.smithy.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.util.expectMember import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape -import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.orNull -fun RuntimeConfig.awsInlineableBodyWithChecksum() = RuntimeType.forInlineDependency( +private fun RuntimeConfig.awsInlineableHttpRequestChecksum() = RuntimeType.forInlineDependency( InlineAwsDependency.forRustFile( - "http_body_checksum", visibility = Visibility.PUBCRATE, + "http_request_checksum", visibility = Visibility.PUBCRATE, + CargoDependency.Bytes, + CargoDependency.Http, + CargoDependency.HttpBody, + CargoDependency.Tracing, + CargoDependency.smithyChecksums(this), + CargoDependency.smithyHttp(this), + CargoDependency.smithyRuntimeApi(this), + CargoDependency.smithyTypes(this), + ), +) + +// TODO(enableNewSmithyRuntimeCleanup): Delete this method +fun RuntimeConfig.awsInlineableBodyWithChecksumMiddleware() = RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "http_body_checksum_middleware", visibility = Visibility.PUBCRATE, CargoDependency.Http, CargoDependency.HttpBody, CargoDependency.smithyHttp(this), @@ -42,17 +58,12 @@ class HttpRequestChecksumDecorator : ClientCodegenDecorator { override val name: String = "HttpRequestChecksum" override val order: Byte = 0 - // TODO(enableNewSmithyRuntimeCleanup): Delete this decorator upon launching the orchestrator - private fun applies(codegenContext: ClientCodegenContext): Boolean = - codegenContext.smithyRuntimeMode.generateMiddleware - override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = baseCustomizations.letIf(applies(codegenContext)) { - it + HttpRequestChecksumCustomization(codegenContext, operation) - } + ): List = + baseCustomizations + HttpRequestChecksumCustomization(codegenContext, operation) } private fun HttpChecksumTrait.requestAlgorithmMember( @@ -69,6 +80,8 @@ private fun HttpChecksumTrait.requestAlgorithmMember( private fun HttpChecksumTrait.checksumAlgorithmToStr( codegenContext: ClientCodegenContext, operationShape: OperationShape, + // TODO(enableNewSmithyRuntimeCleanup): Remove `algorithmWasCloned` (it should always be false) + algorithmWasCloned: Boolean, ): Writable { val runtimeConfig = codegenContext.runtimeConfig val requestAlgorithmMember = this.requestAlgorithmMember(codegenContext, operationShape) @@ -76,8 +89,10 @@ private fun HttpChecksumTrait.checksumAlgorithmToStr( return { if (requestAlgorithmMember != null) { - // User may set checksum for requests, and we need to call as_ref before we can convert the algorithm to a &str - rust("let checksum_algorithm = $requestAlgorithmMember.as_ref();") + if (algorithmWasCloned) { + // User may set checksum for requests, and we need to call as_ref before we can convert the algorithm to a &str + rust("let checksum_algorithm = $requestAlgorithmMember.as_ref();") + } if (isRequestChecksumRequired) { // Checksums are required, fall back to MD5 @@ -118,54 +133,76 @@ class HttpRequestChecksumCustomization( ) : OperationCustomization() { private val runtimeConfig = codegenContext.runtimeConfig - override fun section(section: OperationSection): Writable { + override fun section(section: OperationSection): Writable = writable { // Get the `HttpChecksumTrait`, returning early if this `OperationShape` doesn't have one - val checksumTrait = operationShape.getTrait() ?: return emptySection - val checksumAlgorithm = checksumTrait.requestAlgorithmMember(codegenContext, operationShape) - - return when (section) { - is OperationSection.MutateInput -> { - // Various other things will consume the input struct before we can get at the checksum algorithm - // field within it. This ensures that we preserve a copy of it. It's an enum so cloning is cheap. - if (checksumAlgorithm != null) { - return { - rust("let $checksumAlgorithm = self.$checksumAlgorithm().cloned();") - } - } else { - emptySection - } - } - is OperationSection.MutateRequest -> { - // Return early if no request checksum can be set nor is it required - if (!checksumTrait.isRequestChecksumRequired && checksumAlgorithm == null) { - return emptySection - } else { - // `add_checksum_calculation_to_request` handles both streaming and in-memory request bodies. - return { + val checksumTrait = operationShape.getTrait() ?: return@writable + val requestAlgorithmMember = checksumTrait.requestAlgorithmMember(codegenContext, operationShape) + val inputShape = codegenContext.model.expectShape(operationShape.inputShape) + + when (section) { + is OperationSection.AdditionalInterceptors -> { + if (requestAlgorithmMember != null) { + section.registerInterceptor(runtimeConfig, this) { + val runtimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) rustTemplate( """ - ${section.request} = ${section.request}.augment(|mut req, properties| { - #{checksum_algorithm_to_str:W} - if let Some(checksum_algorithm) = checksum_algorithm { - #{add_checksum_calculation_to_request}(&mut req, properties, checksum_algorithm)?; - } - Result::<_, #{BuildError}>::Ok(req) - })?; + #{RequestChecksumInterceptor}::new(|input: &#{Input}| { + let input: &#{OperationInput} = input.downcast_ref().expect("correct type"); + let checksum_algorithm = input.$requestAlgorithmMember(); + #{checksum_algorithm_to_str} + #{Result}::<_, #{BoxError}>::Ok(checksum_algorithm) + }) """, + *preludeScope, + "BoxError" to runtimeApi.resolve("client::interceptors::error::BoxError"), + "Input" to runtimeApi.resolve("client::interceptors::context::Input"), + "OperationInput" to codegenContext.symbolProvider.toSymbol(inputShape), + "RequestChecksumInterceptor" to runtimeConfig.awsInlineableHttpRequestChecksum() + .resolve("RequestChecksumInterceptor"), "checksum_algorithm_to_str" to checksumTrait.checksumAlgorithmToStr( codegenContext, operationShape, + algorithmWasCloned = false, ), - "add_checksum_calculation_to_request" to runtimeConfig.awsInlineableBodyWithChecksum() - .resolve("add_checksum_calculation_to_request"), - "BuildError" to runtimeConfig.operationBuildError(), ) } } } - else -> { - return emptySection + // TODO(enableNewSmithyRuntimeCleanup): Delete `is OperationSection.MutateInput` + is OperationSection.MutateInput -> { + // Various other things will consume the input struct before we can get at the checksum algorithm + // field within it. This ensures that we preserve a copy of it. It's an enum so cloning is cheap. + if (requestAlgorithmMember != null) { + rust("let $requestAlgorithmMember = self.$requestAlgorithmMember().cloned();") + } + } + // TODO(enableNewSmithyRuntimeCleanup): Delete `is OperationSection.MutateRequest` + is OperationSection.MutateRequest -> { + // Return early if no request checksum can be set nor is it required + if (checksumTrait.isRequestChecksumRequired || requestAlgorithmMember != null) { + // `add_checksum_calculation_to_request` handles both streaming and in-memory request bodies. + rustTemplate( + """ + ${section.request} = ${section.request}.augment(|mut req, properties| { + #{checksum_algorithm_to_str:W} + if let Some(checksum_algorithm) = checksum_algorithm { + #{add_checksum_calculation_to_request}(&mut req, properties, checksum_algorithm)?; + } + Result::<_, #{BuildError}>::Ok(req) + })?; + """, + "checksum_algorithm_to_str" to checksumTrait.checksumAlgorithmToStr( + codegenContext, + operationShape, + algorithmWasCloned = true, + ), + "add_checksum_calculation_to_request" to runtimeConfig.awsInlineableBodyWithChecksumMiddleware() + .resolve("add_checksum_calculation_to_request"), + "BuildError" to runtimeConfig.operationBuildError(), + ) + } } + else -> { } } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt index af25e816dd..114644daaf 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpResponseChecksumDecorator.kt @@ -13,14 +13,34 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +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.rustTemplate +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig +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.util.expectMember import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.orNull +private fun RuntimeConfig.awsInlineableHttpResponseChecksum() = RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile( + "http_response_checksum", visibility = Visibility.PUBCRATE, + CargoDependency.Bytes, + CargoDependency.Http, + CargoDependency.HttpBody, + CargoDependency.Tracing, + CargoDependency.smithyChecksums(this), + CargoDependency.smithyHttp(this), + CargoDependency.smithyRuntimeApi(this), + CargoDependency.smithyTypes(this), + ), +) + fun HttpChecksumTrait.requestValidationModeMember( codegenContext: ClientCodegenContext, operationShape: OperationShape, @@ -33,15 +53,14 @@ class HttpResponseChecksumDecorator : ClientCodegenDecorator { override val name: String = "HttpResponseChecksum" override val order: Byte = 0 - // TODO(enableNewSmithyRuntimeCleanup): Delete this decorator - private fun applies(codegenContext: ClientCodegenContext, operationShape: OperationShape): Boolean = - codegenContext.smithyRuntimeMode.generateMiddleware && operationShape.outputShape != ShapeId.from("com.amazonaws.s3#GetObjectOutput") + private fun applies(operationShape: OperationShape): Boolean = + operationShape.outputShape != ShapeId.from("com.amazonaws.s3#GetObjectOutput") override fun operationCustomizations( codegenContext: ClientCodegenContext, operation: OperationShape, baseCustomizations: List, - ): List = baseCustomizations.letIf(applies(codegenContext, operation)) { + ): List = baseCustomizations.letIf(applies(operation)) { it + HttpResponseChecksumCustomization(codegenContext, operation) } } @@ -52,79 +71,110 @@ class HttpResponseChecksumCustomization( private val codegenContext: ClientCodegenContext, private val operationShape: OperationShape, ) : OperationCustomization() { - override fun section(section: OperationSection): Writable { - val checksumTrait = operationShape.getTrait() ?: return emptySection + override fun section(section: OperationSection): Writable = writable { + val checksumTrait = operationShape.getTrait() ?: return@writable val requestValidationModeMember = - checksumTrait.requestValidationModeMember(codegenContext, operationShape) ?: return emptySection + checksumTrait.requestValidationModeMember(codegenContext, operationShape) ?: return@writable val requestValidationModeMemberInner = if (requestValidationModeMember.isOptional) { codegenContext.model.expectShape(requestValidationModeMember.target) } else { requestValidationModeMember } val validationModeName = codegenContext.symbolProvider.toMemberName(requestValidationModeMember) + val inputShape = codegenContext.model.expectShape(operationShape.inputShape) when (section) { - is OperationSection.MutateRequest -> { - // Otherwise, we need to set a property that the `MutateOutput` section handler will read to know if it - // should checksum validate the response. - return { + is OperationSection.AdditionalInterceptors -> { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + // CRC32, CRC32C, SHA256, SHA1 -> "crc32", "crc32c", "sha256", "sha1" + val responseAlgorithms = checksumTrait.responseAlgorithms + .map { algorithm -> algorithm.lowercase() }.joinToString(", ") { algorithm -> "\"$algorithm\"" } + val runtimeApi = RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) rustTemplate( """ - if let Some($validationModeName) = self.$validationModeName.as_ref() { - let $validationModeName = $validationModeName.clone(); - // Place #{ValidationModeShape} in the property bag so we can check - // it during response deserialization to see if we need to checksum validate - // the response body. - let _ = request.properties_mut().insert($validationModeName); - } + #{ResponseChecksumInterceptor}::new( + [$responseAlgorithms].as_slice(), + |input: &#{Input}| { + ${""/* + Per [the spec](https://smithy.io/2.0/aws/aws-core.html#http-response-checksums), + we check to see if it's the `ENABLED` variant + */} + let input: &#{OperationInput} = input.downcast_ref().expect("correct type"); + matches!(input.$validationModeName(), #{Some}(#{ValidationModeShape}::Enabled)) + } + ) """, + *preludeScope, + "ResponseChecksumInterceptor" to codegenContext.runtimeConfig.awsInlineableHttpResponseChecksum() + .resolve("ResponseChecksumInterceptor"), + "Input" to runtimeApi.resolve("client::interceptors::context::Input"), + "OperationInput" to codegenContext.symbolProvider.toSymbol(inputShape), "ValidationModeShape" to codegenContext.symbolProvider.toSymbol(requestValidationModeMemberInner), ) } } + // TODO(enableNewSmithyRuntimeCleanup): Delete `is OperationSection.MutateRequest` + is OperationSection.MutateRequest -> { + // Otherwise, we need to set a property that the `MutateOutput` section handler will read to know if it + // should checksum validate the response. + rustTemplate( + """ + if let Some($validationModeName) = self.$validationModeName.as_ref() { + let $validationModeName = $validationModeName.clone(); + // Place #{ValidationModeShape} in the property bag so we can check + // it during response deserialization to see if we need to checksum validate + // the response body. + let _ = request.properties_mut().insert($validationModeName); + } + """, + "ValidationModeShape" to codegenContext.symbolProvider.toSymbol(requestValidationModeMemberInner), + ) + } + // TODO(enableNewSmithyRuntimeCleanup): Delete `is OperationSection.MutateOutput` is OperationSection.MutateOutput -> { if (!section.propertyBagAvailable) { - return emptySection + return@writable } // CRC32, CRC32C, SHA256, SHA1 -> "crc32", "crc32c", "sha256", "sha1" val responseAlgorithms = checksumTrait.responseAlgorithms .map { algorithm -> algorithm.lowercase() }.joinToString(", ") { algorithm -> "\"$algorithm\"" } - return { - rustTemplate( - """ - let response_algorithms = [$responseAlgorithms].as_slice(); - let $validationModeName = properties.get::<#{ValidationModeShape}>(); - // Per [the spec](https://awslabs.github.io/smithy/1.0/spec/aws/aws-core.html##http-response-checksums), - // we check to see if it's the `ENABLED` variant - if matches!($validationModeName, Some(&#{ValidationModeShape}::Enabled)) { - if let Some((checksum_algorithm, precalculated_checksum)) = - #{check_headers_for_precalculated_checksum}( - response.headers(), - response_algorithms, - ) - { - let bytestream = output.body.take().map(|bytestream| { - bytestream.map(move |sdk_body| { - #{wrap_body_with_checksum_validator}( - sdk_body, - checksum_algorithm, - precalculated_checksum.clone(), - ) - }) - }); - output = output.set_body(bytestream); - } + rustTemplate( + """ + let response_algorithms = [$responseAlgorithms].as_slice(); + let $validationModeName = properties.get::<#{ValidationModeShape}>(); + // Per [the spec](https://smithy.io/2.0/aws/aws-core.html##http-response-checksums), + // we check to see if it's the `ENABLED` variant + if matches!($validationModeName, Some(&#{ValidationModeShape}::Enabled)) { + if let Some((checksum_algorithm, precalculated_checksum)) = + #{check_headers_for_precalculated_checksum}( + response.headers(), + response_algorithms, + ) + { + let bytestream = output.body.take().map(|bytestream| { + bytestream.map(move |sdk_body| { + #{wrap_body_with_checksum_validator}( + sdk_body, + checksum_algorithm, + precalculated_checksum.clone(), + ) + }) + }); + output = output.set_body(bytestream); } - """, - "ValidationModeShape" to codegenContext.symbolProvider.toSymbol(requestValidationModeMemberInner), - "wrap_body_with_checksum_validator" to codegenContext.runtimeConfig.awsInlineableBodyWithChecksum().resolve("wrap_body_with_checksum_validator"), - "check_headers_for_precalculated_checksum" to codegenContext.runtimeConfig.awsInlineableBodyWithChecksum().resolve("check_headers_for_precalculated_checksum"), - ) - } + } + """, + "ValidationModeShape" to codegenContext.symbolProvider.toSymbol(requestValidationModeMemberInner), + "wrap_body_with_checksum_validator" to codegenContext.runtimeConfig.awsInlineableBodyWithChecksumMiddleware() + .resolve("wrap_body_with_checksum_validator"), + "check_headers_for_precalculated_checksum" to codegenContext.runtimeConfig.awsInlineableBodyWithChecksumMiddleware() + .resolve("check_headers_for_precalculated_checksum"), + ) } - else -> return emptySection + + else -> {} } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt index 2183506ca5..60ac6c631a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/EndpointPrefixGenerator.kt @@ -17,6 +17,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.withBlock import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.util.orNull +// TODO(enableNewSmithyRuntimeCleanup): Delete this file + class EndpointPrefixGenerator(private val codegenContext: ClientCodegenContext, private val shape: OperationShape) : OperationCustomization() { companion object { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt index 54ef873a9d..43dab5ba8c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListCustomization.kt @@ -17,6 +17,8 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.isEventStream +// TODO(enableNewSmithyRuntimeCleanup): Delete this file + class HttpVersionListCustomization( private val codegenContext: CodegenContext, private val operationShape: OperationShape, diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs index 7754478a2d..810598f9a0 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs @@ -45,13 +45,13 @@ macro_rules! declare_method { (&mut $name:ident, $doc:literal, $($tt:tt)+) => { #[doc=$doc] pub fn $name(&mut self) -> output!(&mut $($tt)+) { - self.inner.$name().expect("wrapper type cannot be created unless this is set") + self.inner.$name().expect(concat!("`", stringify!($name), "` wasn't set in the underlying interceptor context. This is a bug.")) } }; (&$name:ident, $doc:literal, $($tt:tt)+) => { #[doc=$doc] pub fn $name(&self) -> output!(&$($tt)+) { - self.inner.$name().expect("wrapper type cannot be created unless this is set") + self.inner.$name().expect(concat!("`", stringify!($name), "` wasn't set in the underlying interceptor context. This is a bug.")) } }; } @@ -145,7 +145,6 @@ declare_wrapper!( declare_wrapper!( (BeforeTransmitInterceptorContextRef BeforeTransmitInterceptorContextMut) - (input: I) (request: Request) ); diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 9bbbf10b2f..f86679e093 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +// TODO(msrvUpgrade): This can be removed once we upgrade the MSRV to Rust 1.69 +#![allow(unknown_lints)] + use self::auth::orchestrate_auth; use crate::client::orchestrator::endpoints::orchestrate_endpoint; use crate::client::orchestrator::http::read_body; From 2987cbdd9104c7350f2c26b10673b9af180264f8 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Tue, 20 Jun 2023 12:10:57 +0300 Subject: [PATCH 165/253] Optimize `AggregatedBytes::to_vec` (#2786) ## Motivation and Context Avoid intermediate vec allocations in `AggregatedBytes::to_vec`. ## Description Calling `slice::to_vec` on every segment is wasteful, the segments can be just flattened into an iterator over `u8`s and collected directly. Also makes the code a bit simpler. ## Testing ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --------- Co-authored-by: 82marbag <69267416+82marbag@users.noreply.github.com> --- CHANGELOG.next.toml | 6 ++++++ rust-runtime/aws-smithy-http/src/byte_stream.rs | 6 +----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index e7157b365e..aec1605b37 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -11,6 +11,12 @@ # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} # author = "rcoh" +[[smithy-rs]] +message = "Avoid intermediate vec allocations in AggregatedBytes::to_vec." +author = "yotamofek" +references = ["smithy-rs#2786"] +meta = { "breaking" = false, "tada" = false, "bug" = false } + [[smithy-rs]] message = "Fix bug in AWS JSON 1.x routers where, if a service had more than 14 operations, the router was created without the route for the 15th operation." author = "thor-bjorgvinsson" diff --git a/rust-runtime/aws-smithy-http/src/byte_stream.rs b/rust-runtime/aws-smithy-http/src/byte_stream.rs index fcd697c7b4..e067018a9d 100644 --- a/rust-runtime/aws-smithy-http/src/byte_stream.rs +++ b/rust-runtime/aws-smithy-http/src/byte_stream.rs @@ -484,11 +484,7 @@ impl AggregatedBytes { /// Convert this buffer into a `Vec` pub fn to_vec(self) -> Vec { - self.0 - .into_inner() - .into_iter() - .flat_map(|b| b.to_vec()) - .collect() + self.0.into_inner().into_iter().flatten().collect() } } From 472adcfb01469367c4c08113bcfb4808e6f8e15f Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Tue, 20 Jun 2023 11:03:32 +0100 Subject: [PATCH 166/253] Invert `seen_${var}` to `${var}_seen` (#2789) ## Motivation and Context When `var` is a reserved word, `seen_${var}` becomes `seen_r#var` which is invalid Rust. ## Description Invert `seen_${var}` to `${var}_seen`. This is a least effort fix. --------- Signed-off-by: Daniele Ahmed Co-authored-by: 82marbag <69267416+82marbag@users.noreply.github.com> --- .../smithy/protocols/ServerHttpBoundProtocolGenerator.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index a88036b0ed..b10cee3994 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -971,7 +971,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( val (queryBindingsTargetingCollection, queryBindingsTargetingSimple) = queryBindings.partition { model.expectShape(it.member.target) is CollectionShape } queryBindingsTargetingSimple.forEach { - rust("let mut seen_${symbolProvider.toMemberName(it.member)} = false;") + rust("let mut ${symbolProvider.toMemberName(it.member)}_seen = false;") } queryBindingsTargetingCollection.forEach { rust("let mut ${symbolProvider.toMemberName(it.member)} = Vec::new();") @@ -983,11 +983,11 @@ class ServerHttpBoundProtocolTraitImplGenerator( val memberName = symbolProvider.toMemberName(it.member) rustTemplate( """ - if !seen_$memberName && k == "${it.locationName}" { + if !${memberName}_seen && k == "${it.locationName}" { input = input.${it.member.setterName()}( #{deserializer}(&v)? ); - seen_$memberName = true; + ${memberName}_seen = true; } """.trimIndent(), "deserializer" to deserializer, From 934129cf4c2b55ebf408a494a3e6a4ab0db7d131 Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Tue, 20 Jun 2023 13:27:25 +0100 Subject: [PATCH 167/253] Consolidate `proto` and `protocols` modules into single `protocol` module (#2780) ## Motivation and Context We have two modules `proto` and `protocols` in `aws_smithy_http_server`, these can be consolidated. --- .../server/smithy/ServerRuntimeType.kt | 2 +- .../ServerRuntimeTypesReExportsGenerator.kt | 2 +- .../generators/protocol/ServerProtocol.kt | 18 +++++----- .../ServerHttpBoundProtocolGenerator.kt | 8 ++--- design/src/server/anatomy.md | 4 +-- .../src/error.rs | 2 +- .../src/middleware/pytests/layer.rs | 2 +- .../aws-smithy-http-server/src/extension.rs | 2 +- .../aws-smithy-http-server/src/lib.rs | 5 +-- .../aws-smithy-http-server/src/proto/mod.rs | 35 ------------------- .../src/{proto => protocol}/aws_json/mod.rs | 0 .../{proto => protocol}/aws_json/rejection.rs | 0 .../{proto => protocol}/aws_json/router.rs | 2 +- .../aws_json/runtime_error.rs | 4 +-- .../{proto => protocol}/aws_json_10/mod.rs | 0 .../{proto => protocol}/aws_json_10/router.rs | 2 +- .../{proto => protocol}/aws_json_11/mod.rs | 0 .../{proto => protocol}/aws_json_11/router.rs | 2 +- .../src/{protocols.rs => protocol/mod.rs} | 32 ++++++++++++++++- .../src/{proto => protocol}/rest/mod.rs | 0 .../src/{proto => protocol}/rest/router.rs | 2 +- .../{proto => protocol}/rest_json_1/mod.rs | 0 .../rest_json_1/rejection.rs | 8 ++--- .../{proto => protocol}/rest_json_1/router.rs | 2 +- .../rest_json_1/runtime_error.rs | 8 ++--- .../src/{proto => protocol}/rest_xml/mod.rs | 0 .../{proto => protocol}/rest_xml/rejection.rs | 4 +-- .../{proto => protocol}/rest_xml/router.rs | 2 +- .../rest_xml/runtime_error.rs | 2 +- .../src/routing/request_spec.rs | 2 +- .../src/runtime_error.rs | 4 +-- .../aws-smithy-http-server/src/service.rs | 4 +-- 32 files changed, 76 insertions(+), 84 deletions(-) delete mode 100644 rust-runtime/aws-smithy-http-server/src/proto/mod.rs rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/aws_json/mod.rs (100%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/aws_json/rejection.rs (100%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/aws_json/router.rs (98%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/aws_json/runtime_error.rs (96%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/aws_json_10/mod.rs (100%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/aws_json_10/router.rs (95%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/aws_json_11/mod.rs (100%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/aws_json_11/router.rs (95%) rename rust-runtime/aws-smithy-http-server/src/{protocols.rs => protocol/mod.rs} (90%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/rest/mod.rs (100%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/rest/router.rs (99%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/rest_json_1/mod.rs (100%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/rest_json_1/rejection.rs (97%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/rest_json_1/router.rs (96%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/rest_json_1/runtime_error.rs (92%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/rest_xml/mod.rs (100%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/rest_xml/rejection.rs (93%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/rest_xml/router.rs (96%) rename rust-runtime/aws-smithy-http-server/src/{proto => protocol}/rest_xml/runtime_error.rs (98%) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt index 2f4ac7425b..46b18face5 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerRuntimeType.kt @@ -18,7 +18,7 @@ object ServerRuntimeType { ServerCargoDependency.smithyHttpServer(runtimeConfig).toType().resolve("routing::Router") fun protocol(name: String, path: String, runtimeConfig: RuntimeConfig) = - ServerCargoDependency.smithyHttpServer(runtimeConfig).toType().resolve("proto::$path::$name") + ServerCargoDependency.smithyHttpServer(runtimeConfig).toType().resolve("protocol::$path::$name") fun protocol(runtimeConfig: RuntimeConfig) = protocol("Protocol", "", runtimeConfig) } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt index 6318c0483c..85a9507648 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt @@ -50,7 +50,7 @@ class ServerRuntimeTypesReExportsGenerator( } pub use #{SmithyHttpServer}::instrumentation; - pub use #{SmithyHttpServer}::proto; + pub use #{SmithyHttpServer}::protocol; pub use #{SmithyHttpServer}::Extension; """, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt index 3ee16c233f..77ba711fd2 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocol.kt @@ -39,7 +39,7 @@ import software.amazon.smithy.rust.codegen.server.smithy.protocols.ServerRestJso import software.amazon.smithy.rust.codegen.server.smithy.targetCanReachConstrainedShape interface ServerProtocol : Protocol { - /** The path such that `aws_smithy_http_server::proto::$path` points to the protocol's module. */ + /** The path such that `aws_smithy_http_server::protocol::$path` points to the protocol's module. */ val protocolModulePath: String /** Returns the Rust marker struct enjoying `OperationShape`. */ @@ -79,17 +79,17 @@ interface ServerProtocol : Protocol { /** The protocol-specific `RequestRejection` type. **/ fun requestRejection(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::$protocolModulePath::rejection::RequestRejection") + .toType().resolve("protocol::$protocolModulePath::rejection::RequestRejection") /** The protocol-specific `ResponseRejection` type. **/ fun responseRejection(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::$protocolModulePath::rejection::ResponseRejection") + .toType().resolve("protocol::$protocolModulePath::rejection::ResponseRejection") /** The protocol-specific `RuntimeError` type. **/ fun runtimeError(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::$protocolModulePath::runtime_error::RuntimeError") + .toType().resolve("protocol::$protocolModulePath::runtime_error::RuntimeError") } fun returnSymbolToParseFn(codegenContext: ServerCodegenContext): (Shape) -> ReturnSymbolToParse { @@ -145,7 +145,7 @@ class ServerAwsJsonProtocol( } override fun routerType() = ServerCargoDependency.smithyHttpServer(runtimeConfig).toType() - .resolve("proto::aws_json::router::AwsJsonRouter") + .resolve("protocol::aws_json::router::AwsJsonRouter") /** * Returns the operation name as required by the awsJson1.x protocols. @@ -170,18 +170,18 @@ class ServerAwsJsonProtocol( override fun requestRejection(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::aws_json::rejection::RequestRejection") + .toType().resolve("protocol::aws_json::rejection::RequestRejection") override fun responseRejection(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::aws_json::rejection::ResponseRejection") + .toType().resolve("protocol::aws_json::rejection::ResponseRejection") override fun runtimeError(runtimeConfig: RuntimeConfig): RuntimeType = ServerCargoDependency.smithyHttpServer(runtimeConfig) - .toType().resolve("proto::aws_json::runtime_error::RuntimeError") + .toType().resolve("protocol::aws_json::runtime_error::RuntimeError") } private fun restRouterType(runtimeConfig: RuntimeConfig) = ServerCargoDependency.smithyHttpServer(runtimeConfig).toType() - .resolve("proto::rest::router::RestRouter") + .resolve("protocol::rest::router::RestRouter") class ServerRestJsonProtocol( private val serverCodegenContext: ServerCodegenContext, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt index b10cee3994..6e22828a1c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerHttpBoundProtocolGenerator.kt @@ -210,7 +210,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( httpBindingResolver.responseContentType(operationShape)?.also { contentType -> rustTemplate( """ - if !#{SmithyHttpServer}::protocols::accept_header_classifier(request.headers(), &$staticContentType) { + if !#{SmithyHttpServer}::protocol::accept_header_classifier(request.headers(), &$staticContentType) { return Err(#{RequestRejection}::NotAcceptable); } """, @@ -246,7 +246,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( ?.let { "Some(${it.dq()})" } ?: "None" rustTemplate( """ - #{SmithyHttpServer}::protocols::content_type_header_classifier(request.headers(), $expectedRequestContentType)?; + #{SmithyHttpServer}::protocol::content_type_header_classifier(request.headers(), $expectedRequestContentType)?; """, *codegenScope, ) @@ -700,7 +700,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( if (protocol is RestJson) { rustTemplate( """ - #{SmithyHttpServer}::protocols::content_type_header_classifier(&parts.headers, Some("application/json"))?; + #{SmithyHttpServer}::protocol::content_type_header_classifier(&parts.headers, Some("application/json"))?; """, *codegenScope, ) @@ -742,7 +742,7 @@ class ServerHttpBoundProtocolTraitImplGenerator( conditionalBlock("if body.is_empty() {", "}", conditional = parser != null) { rustTemplate( """ - #{SmithyHttpServer}::protocols::content_type_header_empty_body_no_modeled_input(&parts.headers)?; + #{SmithyHttpServer}::protocol::content_type_header_empty_body_no_modeled_input(&parts.headers)?; """, *codegenScope, ) diff --git a/design/src/server/anatomy.md b/design/src/server/anatomy.md index 7089acdfa6..bc7a550c95 100644 --- a/design/src/server/anatomy.md +++ b/design/src/server/anatomy.md @@ -265,7 +265,7 @@ Note that both traits are parameterized by `Protocol`. These [protocols](https:/ ```rust # extern crate aws_smithy_http_server; -# use aws_smithy_http_server::proto::{ +# use aws_smithy_http_server::protocol::{ # aws_json_10::AwsJson1_0 as _, # aws_json_11::AwsJson1_1 as _, # rest_json_1::RestJson1 as _, @@ -615,7 +615,7 @@ The final outcome, an instance of `PokemonService`, looks roughly like this: ```rust # extern crate aws_smithy_http_server; -# use aws_smithy_http_server::{routing::RoutingService, proto::rest_json_1::{router::RestRouter, RestJson1}}; +# use aws_smithy_http_server::{routing::RoutingService, protocol::rest_json_1::{router::RestRouter, RestJson1}}; /// The Pokémon Service allows you to retrieve information about Pokémon species. #[derive(Clone)] pub struct PokemonService { diff --git a/rust-runtime/aws-smithy-http-server-python/src/error.rs b/rust-runtime/aws-smithy-http-server-python/src/error.rs index 01596be507..2cc308fea5 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/error.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/error.rs @@ -7,7 +7,7 @@ use aws_smithy_http_server::{ body::{to_boxed, BoxBody}, - proto::{ + protocol::{ aws_json_10::AwsJson1_0, aws_json_11::AwsJson1_1, rest_json_1::RestJson1, rest_xml::RestXml, }, response::IntoResponse, diff --git a/rust-runtime/aws-smithy-http-server-python/src/middleware/pytests/layer.rs b/rust-runtime/aws-smithy-http-server-python/src/middleware/pytests/layer.rs index 506e5a6c20..cf6cb2d095 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/middleware/pytests/layer.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/middleware/pytests/layer.rs @@ -7,7 +7,7 @@ use std::convert::Infallible; use aws_smithy_http_server::{ body::{to_boxed, Body, BoxBody}, - proto::rest_json_1::RestJson1, + protocol::rest_json_1::RestJson1, }; use aws_smithy_http_server_python::{ middleware::{PyMiddlewareHandler, PyMiddlewareLayer}, diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index be04b9c760..a2a54affb2 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -186,7 +186,7 @@ impl Deref for RuntimeErrorExtension { mod tests { use tower::{service_fn, Layer, ServiceExt}; - use crate::{plugin::PluginLayer, proto::rest_json_1::RestJson1}; + use crate::{plugin::PluginLayer, protocol::rest_json_1::RestJson1}; use super::*; diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 139f30f1ca..7b32c50736 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -19,7 +19,7 @@ pub mod instrumentation; pub mod operation; pub mod plugin; #[doc(hidden)] -pub mod protocols; +pub mod protocol; #[doc(hidden)] pub mod rejection; pub mod request; @@ -40,6 +40,3 @@ pub use tower_http::add_extension::{AddExtension, AddExtensionLayer}; #[cfg(test)] mod test_helpers; - -#[doc(hidden)] -pub mod proto; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/mod.rs b/rust-runtime/aws-smithy-http-server/src/proto/mod.rs deleted file mode 100644 index 26fb17d893..0000000000 --- a/rust-runtime/aws-smithy-http-server/src/proto/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -pub mod aws_json; -pub mod aws_json_10; -pub mod aws_json_11; -pub mod rest; -pub mod rest_json_1; -pub mod rest_xml; - -#[cfg(test)] -pub mod test_helpers { - use http::{HeaderMap, Method, Request}; - - /// Helper function to build a `Request`. Used in other test modules. - pub fn req(method: &Method, uri: &str, headers: Option) -> Request<()> { - let mut r = Request::builder().method(method).uri(uri).body(()).unwrap(); - if let Some(headers) = headers { - *r.headers_mut() = headers - } - r - } - - // Returns a `Response`'s body as a `String`, without consuming the response. - pub async fn get_body_as_string(body: B) -> String - where - B: http_body::Body + std::marker::Unpin, - B::Error: std::fmt::Debug, - { - let body_bytes = hyper::body::to_bytes(body).await.unwrap(); - String::from(std::str::from_utf8(&body_bytes).unwrap()) - } -} diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/rejection.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json/rejection.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json/rejection.rs diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/router.rs similarity index 98% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json/router.rs index 4474dfb600..2cd48f8e61 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/router.rs @@ -121,7 +121,7 @@ impl FromIterator<(String, S)> for AwsJsonRouter { #[cfg(test)] mod tests { use super::*; - use crate::{proto::test_helpers::req, routing::Router}; + use crate::{protocol::test_helpers::req, routing::Router}; use http::{HeaderMap, HeaderValue, Method}; use pretty_assertions::assert_eq; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/runtime_error.rs similarity index 96% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json/runtime_error.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json/runtime_error.rs index 2e92befa81..8970d16f30 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json/runtime_error.rs @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::proto::aws_json_11::AwsJson1_1; +use crate::protocol::aws_json_11::AwsJson1_1; use crate::response::IntoResponse; use crate::runtime_error::{InternalFailureException, INVALID_HTTP_RESPONSE_FOR_RUNTIME_ERROR_PANIC_MESSAGE}; -use crate::{extension::RuntimeErrorExtension, proto::aws_json_10::AwsJson1_0}; +use crate::{extension::RuntimeErrorExtension, protocol::aws_json_10::AwsJson1_0}; use http::StatusCode; use super::rejection::{RequestRejection, ResponseRejection}; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/router.rs similarity index 95% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/router.rs index 7b130b24a7..f3093685e2 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_10/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_10/router.rs @@ -10,7 +10,7 @@ use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::AwsJson1_0; -pub use crate::proto::aws_json::router::*; +pub use crate::protocol::aws_json::router::*; impl IntoResponse for Error { fn into_response(self) -> http::Response { diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/router.rs similarity index 95% rename from rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/router.rs index 730feeb197..898d8c29af 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/aws_json_11/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/aws_json_11/router.rs @@ -10,7 +10,7 @@ use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::AwsJson1_1; -pub use crate::proto::aws_json::router::*; +pub use crate::protocol::aws_json::router::*; impl IntoResponse for Error { fn into_response(self) -> http::Response { diff --git a/rust-runtime/aws-smithy-http-server/src/protocols.rs b/rust-runtime/aws-smithy-http-server/src/protocol/mod.rs similarity index 90% rename from rust-runtime/aws-smithy-http-server/src/protocols.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/mod.rs index 75d723f3fc..004d32a38b 100644 --- a/rust-runtime/aws-smithy-http-server/src/protocols.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/mod.rs @@ -3,10 +3,40 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Protocol helpers. +pub mod aws_json; +pub mod aws_json_10; +pub mod aws_json_11; +pub mod rest; +pub mod rest_json_1; +pub mod rest_xml; + use crate::rejection::MissingContentTypeReason; use http::HeaderMap; +#[cfg(test)] +pub mod test_helpers { + use http::{HeaderMap, Method, Request}; + + /// Helper function to build a `Request`. Used in other test modules. + pub fn req(method: &Method, uri: &str, headers: Option) -> Request<()> { + let mut r = Request::builder().method(method).uri(uri).body(()).unwrap(); + if let Some(headers) = headers { + *r.headers_mut() = headers + } + r + } + + // Returns a `Response`'s body as a `String`, without consuming the response. + pub async fn get_body_as_string(body: B) -> String + where + B: http_body::Body + std::marker::Unpin, + B::Error: std::fmt::Debug, + { + let body_bytes = hyper::body::to_bytes(body).await.unwrap(); + String::from(std::str::from_utf8(&body_bytes).unwrap()) + } +} + /// When there are no modeled inputs, /// a request body is empty and the content-type request header must not be set #[allow(clippy::result_large_err)] diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/rest/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest/router.rs similarity index 99% rename from rust-runtime/aws-smithy-http-server/src/proto/rest/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest/router.rs index 1d55f676d6..fbbb98ee81 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest/router.rs @@ -112,7 +112,7 @@ impl FromIterator<(RequestSpec, S)> for RestRouter { #[cfg(test)] mod tests { use super::*; - use crate::{proto::test_helpers::req, routing::request_spec::*}; + use crate::{protocol::test_helpers::req, routing::request_spec::*}; use http::Method; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/rejection.rs similarity index 97% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/rejection.rs index 5ccf0225f7..e931f31052 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/rejection.rs @@ -28,7 +28,7 @@ //! into responses. They serve as a mechanism to keep track of all the possible errors that can //! occur when processing a request or a response, in far more detail than what AWS protocols need //! to. This is why they are so granular: other (possibly protocol-specific) error types (like -//! [`crate::proto::rest_json_1::runtime_error::RuntimeError`]) can "group" them when exposing +//! [`crate::protocol::rest_json_1::runtime_error::RuntimeError`]) can "group" them when exposing //! errors to clients while the framework does not need to sacrifice fidelity in private error //! handling routines, and future-proofing itself at the same time (for example, we might want to //! record metrics about rejection types). @@ -36,16 +36,16 @@ //! Rejection types implement [`std::error::Error`], and some take in type-erased boxed errors //! (`crate::Error`) to represent their underlying causes, so they can be composed with other types //! that take in (possibly type-erased) [`std::error::Error`]s, like -//! [`crate::proto::rest_json_1::runtime_error::RuntimeError`], thus allowing us to represent the +//! [`crate::protocol::rest_json_1::runtime_error::RuntimeError`], thus allowing us to represent the //! full error chain. //! -//! This module hosts rejection types _specific_ to the [`crate::proto::rest_json_1`] protocol, but +//! This module hosts rejection types _specific_ to the [`crate::protocol::rest_json_1`] protocol, but //! the paragraphs above apply to _all_ protocol-specific rejection types. //! //! Similarly, rejection type variants are exhaustively documented solely in this module if they have //! direct counterparts in other protocols. This is to avoid documentation getting out of date. //! -//! Consult `crate::proto::$protocolName::rejection` for rejection types for other protocols. +//! Consult `crate::protocol::$protocolName::rejection` for rejection types for other protocols. use crate::rejection::MissingContentTypeReason; use std::num::TryFromIntError; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/router.rs similarity index 96% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/router.rs index 508812e554..76d3d69277 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/router.rs @@ -10,7 +10,7 @@ use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::RestJson1; -pub use crate::proto::rest::router::*; +pub use crate::protocol::rest::router::*; impl IntoResponse for Error { fn into_response(self) -> http::Response { diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/runtime_error.rs similarity index 92% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/runtime_error.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/runtime_error.rs index 0525d9b576..5faf3ea8cf 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_json_1/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_json_1/runtime_error.rs @@ -7,27 +7,27 @@ //! //! This module contains the [`RuntimeError`] type. //! -//! As opposed to rejection types (see [`crate::proto::rest_json_1::rejection`]), which are an internal detail about +//! As opposed to rejection types (see [`crate::protocol::rest_json_1::rejection`]), which are an internal detail about //! the framework, `RuntimeError` is surfaced to clients in HTTP responses: indeed, it implements //! [`RuntimeError::into_response`]. Rejections can be "grouped" and converted into a //! specific `RuntimeError` kind: for example, all request rejections due to serialization issues //! can be conflated under the [`RuntimeError::Serialization`] enum variant. //! //! The HTTP response representation of the specific `RuntimeError` is protocol-specific: for -//! example, the runtime error in the [`crate::proto::rest_json_1`] protocol sets the `X-Amzn-Errortype` header. +//! example, the runtime error in the [`crate::protocol::rest_json_1`] protocol sets the `X-Amzn-Errortype` header. //! //! Generated code works always works with [`crate::rejection`] types when deserializing requests //! and serializing response. Just before a response needs to be sent, the generated code looks up //! and converts into the corresponding `RuntimeError`, and then it uses the its //! [`RuntimeError::into_response`] method to render and send a response. //! -//! This module hosts the `RuntimeError` type _specific_ to the [`crate::proto::rest_json_1`] protocol, but +//! This module hosts the `RuntimeError` type _specific_ to the [`crate::protocol::rest_json_1`] protocol, but //! the paragraphs above apply to _all_ protocol-specific rejection types. //! //! Similarly, `RuntimeError` variants are exhaustively documented solely in this module if they have //! direct counterparts in other protocols. This is to avoid documentation getting out of date. //! -//! Consult `crate::proto::$protocolName::runtime_error` for the `RuntimeError` type for other protocols. +//! Consult `crate::protocol::$protocolName::runtime_error` for the `RuntimeError` type for other protocols. use super::rejection::RequestRejection; use super::rejection::ResponseRejection; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/mod.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/mod.rs similarity index 100% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_xml/mod.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/mod.rs diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/rejection.rs similarity index 93% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/rejection.rs index 51b0ac0ad6..3e1bed00ca 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/rejection.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/rejection.rs @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! This module hosts _exactly_ the same as [`crate::proto::rest_json_1::rejection`], except that -//! [`crate::proto::rest_json_1::rejection::RequestRejection::JsonDeserialize`] is swapped for +//! This module hosts _exactly_ the same as [`crate::protocol::rest_json_1::rejection`], except that +//! [`crate::protocol::rest_json_1::rejection::RequestRejection::JsonDeserialize`] is swapped for //! [`RequestRejection::XmlDeserialize`]. use crate::rejection::MissingContentTypeReason; diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/router.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/router.rs similarity index 96% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_xml/router.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/router.rs index 9dbdf1e43b..d8a0905a7f 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/router.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/router.rs @@ -11,7 +11,7 @@ use crate::routing::{method_disallowed, UNKNOWN_OPERATION_EXCEPTION}; use super::RestXml; -pub use crate::proto::rest::router::*; +pub use crate::protocol::rest::router::*; /// An AWS REST routing error. impl IntoResponse for Error { diff --git a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/runtime_error.rs similarity index 98% rename from rust-runtime/aws-smithy-http-server/src/proto/rest_xml/runtime_error.rs rename to rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/runtime_error.rs index 9a7a89e823..d4ecb5c218 100644 --- a/rust-runtime/aws-smithy-http-server/src/proto/rest_xml/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/protocol/rest_xml/runtime_error.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::proto::rest_xml::RestXml; +use crate::protocol::rest_xml::RestXml; use crate::response::IntoResponse; use crate::runtime_error::InternalFailureException; use crate::{extension::RuntimeErrorExtension, runtime_error::INVALID_HTTP_RESPONSE_FOR_RUNTIME_ERROR_PANIC_MESSAGE}; diff --git a/rust-runtime/aws-smithy-http-server/src/routing/request_spec.rs b/rust-runtime/aws-smithy-http-server/src/routing/request_spec.rs index 5ed493360b..3ae722f5fd 100644 --- a/rust-runtime/aws-smithy-http-server/src/routing/request_spec.rs +++ b/rust-runtime/aws-smithy-http-server/src/routing/request_spec.rs @@ -250,7 +250,7 @@ impl RequestSpec { #[cfg(test)] mod tests { use super::*; - use crate::proto::test_helpers::req; + use crate::protocol::test_helpers::req; use http::Method; diff --git a/rust-runtime/aws-smithy-http-server/src/runtime_error.rs b/rust-runtime/aws-smithy-http-server/src/runtime_error.rs index 97dca6ad19..7348f5d628 100644 --- a/rust-runtime/aws-smithy-http-server/src/runtime_error.rs +++ b/rust-runtime/aws-smithy-http-server/src/runtime_error.rs @@ -6,8 +6,8 @@ /// A _protocol-agnostic_ type representing an internal framework error. As of writing, this can only /// occur upon failure to extract an [`crate::extension::Extension`] from the request. /// This type is converted into protocol-specific error variants. For example, in the -/// [`crate::proto::rest_json_1`] protocol, it is converted to the -/// [`crate::proto::rest_json_1::runtime_error::RuntimeError::InternalFailure`] variant. +/// [`crate::protocol::rest_json_1`] protocol, it is converted to the +/// [`crate::protocol::rest_json_1::runtime_error::RuntimeError::InternalFailure`] variant. pub struct InternalFailureException; pub const INVALID_HTTP_RESPONSE_FOR_RUNTIME_ERROR_PANIC_MESSAGE: &str = "invalid HTTP response for `RuntimeError`; please file a bug report under https://github.com/awslabs/smithy-rs/issues"; diff --git a/rust-runtime/aws-smithy-http-server/src/service.rs b/rust-runtime/aws-smithy-http-server/src/service.rs index 8ad7a026fd..9b0537490f 100644 --- a/rust-runtime/aws-smithy-http-server/src/service.rs +++ b/rust-runtime/aws-smithy-http-server/src/service.rs @@ -6,7 +6,7 @@ //! The shape of a [Smithy service] is modelled by the [`ServiceShape`] trait. Its associated types //! [`ServiceShape::ID`], [`ServiceShape::VERSION`], [`ServiceShape::Protocol`], and [`ServiceShape::Operations`] map //! to the services [Shape ID](https://smithy.io/2.0/spec/model.html#shape-id), the version field, the applied -//! [protocol trait](https://smithy.io/2.0/aws/protocols/index.html) (see [`proto`](crate::proto) module), and the +//! [protocol trait](https://smithy.io/2.0/aws/protocols/index.html) (see [`protocol`](crate::protocol) module), and the //! operations field. //! //! We generate an implementation on this for every service struct (exported from the root of the generated crate). @@ -32,7 +32,7 @@ //! ```rust,no_run //! # use aws_smithy_http_server::shape_id::ShapeId; //! # use aws_smithy_http_server::service::{ServiceShape, ContainsOperation}; -//! # use aws_smithy_http_server::proto::rest_json_1::RestJson1; +//! # use aws_smithy_http_server::protocol::rest_json_1::RestJson1; //! # pub struct Shopping; //! // For more information on these marker structs see `OperationShape` //! struct GetShopping; From d5a315fa74df7aa68f0b9593289b6b92cad263fa Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Tue, 20 Jun 2023 13:05:57 -0500 Subject: [PATCH 168/253] Adaptive Retries 1/2 (rate limiter) (#2773) ## Motivation and Context #2190 ## Description this PR adds the rate limiter necessary for orchestrator support of adaptive retries. I'll integrate the rate limiter with the retry policy as part of a separate PR following this one. I wanted to keep these small and easy to review. This implementation is based on the Go v2 SDK's implementation. ## Testing tests are included ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-smithy-async/src/test_util.rs | 48 +- .../aws-smithy-runtime-api/src/lib.rs | 2 + .../aws-smithy-runtime-api/src/macros.rs | 165 +++++ rust-runtime/aws-smithy-runtime/Cargo.toml | 1 + .../aws-smithy-runtime/src/client/retries.rs | 6 + .../src/client/retries/client_rate_limiter.rs | 598 ++++++++++++++++++ .../src/client/retries/strategy/standard.rs | 42 +- .../token_bucket.rs} | 21 +- .../src/client/runtime_plugin.rs | 2 - rust-runtime/inlineable/Cargo.toml | 2 +- 10 files changed, 849 insertions(+), 38 deletions(-) create mode 100644 rust-runtime/aws-smithy-runtime-api/src/macros.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs rename rust-runtime/aws-smithy-runtime/src/client/{runtime_plugin/standard_token_bucket.rs => retries/token_bucket.rs} (80%) diff --git a/rust-runtime/aws-smithy-async/src/test_util.rs b/rust-runtime/aws-smithy-async/src/test_util.rs index 0aec83070d..8c867869e8 100644 --- a/rust-runtime/aws-smithy-async/src/test_util.rs +++ b/rust-runtime/aws-smithy-async/src/test_util.rs @@ -69,6 +69,40 @@ impl ControlledSleep { } } +/// A sleep implementation where calls to [`AsyncSleep::sleep`] will complete instantly. +/// +/// Create a [`InstantSleep`] with [`instant_time_and_sleep`] +#[derive(Debug, Clone)] +pub struct InstantSleep { + log: Arc>>, +} + +impl AsyncSleep for InstantSleep { + fn sleep(&self, duration: Duration) -> Sleep { + let log = self.log.clone(); + Sleep::new(async move { + log.lock().unwrap().push(duration); + }) + } +} + +impl InstantSleep { + /// Given a shared log for sleep durations, create a new `InstantSleep`. + pub fn new(log: Arc>>) -> Self { + Self { log } + } + + /// Return the sleep durations that were logged by this `InstantSleep`. + pub fn logs(&self) -> Vec { + self.log.lock().unwrap().iter().cloned().collect() + } + + /// Return the total sleep duration that was logged by this `InstantSleep`. + pub fn total_duration(&self) -> Duration { + self.log.lock().unwrap().iter().sum() + } +} + /// Guard returned from [`SleepGate::expect_sleep`] /// /// # Examples @@ -112,9 +146,9 @@ impl CapturedSleep<'_> { /// ```rust /// use std::time::Duration; /// use aws_smithy_async::rt::sleep::AsyncSleep; - /// fn do_something(sleep: &dyn AsyncSleep) { + /// async fn do_something(sleep: &dyn AsyncSleep) { /// println!("before sleep"); - /// sleep.sleep(Duration::from_secs(1)); + /// sleep.sleep(Duration::from_secs(1)).await; /// println!("after sleep"); /// } /// ``` @@ -194,6 +228,14 @@ pub fn controlled_time_and_sleep( (ManualTimeSource { start_time, log }, sleep, gate) } +/// Returns a duo of tools to test interactions with time. Sleeps will end instantly, but the +/// desired length of the sleeps will be recorded for later verification. +pub fn instant_time_and_sleep(start_time: SystemTime) -> (ManualTimeSource, InstantSleep) { + let log = Arc::new(Mutex::new(vec![])); + let sleep = InstantSleep::new(log.clone()); + (ManualTimeSource { start_time, log }, sleep) +} + impl TimeSource for SystemTime { fn now(&self) -> SystemTime { *self @@ -249,7 +291,7 @@ mod test { let guard = gate.expect_sleep().await; assert_eq!(progress.load(Ordering::Acquire), 2); - assert_eq!(task.is_finished(), false); + assert!(!task.is_finished(), "task should not be finished"); guard.allow_progress(); timeout(Duration::from_secs(1), task) .await diff --git a/rust-runtime/aws-smithy-runtime-api/src/lib.rs b/rust-runtime/aws-smithy-runtime-api/src/lib.rs index 1668685235..99a0494dbd 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/lib.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/lib.rs @@ -15,3 +15,5 @@ /// Smithy runtime for client orchestration. pub mod client; + +pub mod macros; diff --git a/rust-runtime/aws-smithy-runtime-api/src/macros.rs b/rust-runtime/aws-smithy-runtime-api/src/macros.rs new file mode 100644 index 0000000000..09efd0a74c --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/macros.rs @@ -0,0 +1,165 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Various utility macros to aid runtime crate writers. + +/// Define a new builder struct, along with a method to create it, and setters. +/// +/// ## Examples +/// +/// The builder macro takes a list of field definitions, each with four elements: +/// ```txt +/// set_optional_field, optional_field, String, "An optional field which may or may not be set when `.build()` is called.", +/// ^ The setter name, ^ The field name, ^ The type, ^ The documentation for the field and setter methods. +/// ``` +/// +/// The following example creates a new builder struct, along with a method to create it, and setters +/// for a struct `MyConfig` with three fields: +/// +/// ``` +/// use std::collections::HashMap; +/// use std::sync::{Arc, Mutex}; +/// use aws_smithy_runtime_api::{builder, builder_methods, builder_struct}; +/// +/// struct MyConfig { +/// optional_field: Option, +/// optional_field_with_a_default: f64, +/// required_field_with_no_default: Arc>>, +/// } +/// +/// impl MyConfig { +/// pub fn builder() -> Builder { +/// Builder::new() +/// } +/// } +/// +/// builder!( +/// set_optional_field, optional_field, String, "An optional field which may or may not be set when `.build()` is called.", +/// set_optional_field_with_a_default, optional_field_with_a_default, f64, "An optional field that will default to `f64::MAX` if it's unset when `.build()` is called.", +/// set_required_field_with_no_default, required_field_with_no_default, HashMap, "A required field that will cause the builder to panic if it's unset when `.build()` is called." +/// ); +/// +/// impl Builder { +/// fn build(self) -> MyConfig { +/// MyConfig { +/// optional_field: self.optional_field, +/// optional_field_with_a_default: self.optional_field_with_a_default.unwrap_or(f64::MAX), +/// required_field_with_no_default: Arc::new(Mutex::new( +/// self.required_field_with_no_default.expect("'required_field_with_no_default' is required") +/// )), +/// } +/// } +/// } +/// ``` +/// +/// In this example, the result of macro expansion would look like this: +/// +/// ``` +/// # use std::collections::HashMap; +/// # use std::sync::{Arc, Mutex}; +/// #[derive(Clone, Debug, Default)] +/// pub struct Builder { +/// #[doc = "An optional field which may or may not be set when `.build()` is called."] +/// optional_field: Option, +/// #[doc = "An optional field that will default to `f64::MAX` if it's unset when `.build()` is called."] +/// optional_field_with_a_default: Option, +/// #[doc = "A required field that will cause the builder to panic if it's unset when `.build()` is called."] +/// required_field_with_no_default: Option>, +/// } +/// +/// impl Builder { +/// pub fn new() -> Self { +/// Builder::default() +/// } +/// +/// #[doc = "An optional field which may or may not be set when `.build()` is called."] +/// pub fn set_optional_field(&mut self, optional_field: Option) -> &mut Self { +/// self.optional_field = optional_field; +/// self +/// } +/// +/// #[doc = "An optional field which may or may not be set when `.build()` is called."] +/// pub fn optional_field(mut self, optional_field: String) -> Self { +/// self.optional_field = Some(optional_field); +/// self +/// } +/// +/// #[doc = "An optional field that will default to `f64::MAX` if it's unset when `.build()` is called."] +/// pub fn set_optional_field_with_a_default(&mut self, optional_field_with_a_default: Option) -> &mut Self { +/// self.optional_field_with_a_default = optional_field_with_a_default; +/// self +/// } +/// +/// #[doc = "An optional field that will default to `f64::MAX` if it's unset when `.build()` is called."] +/// pub fn optional_field_with_a_default(mut self, optional_field_with_a_default: f64) -> Self { +/// self.optional_field_with_a_default = Some(optional_field_with_a_default); +/// self +/// } +/// +/// #[doc = "A required field that will cause the builder to panic if it's unset when `.build()` is called."] +/// pub fn set_required_field_with_no_default(&mut self, required_field_with_no_default: Option>) -> &mut Self { +/// self.required_field_with_no_default = required_field_with_no_default; +/// self +/// } +/// +/// #[doc = "A required field that will cause the builder to panic if it's unset when `.build()` is called."] +/// pub fn required_field_with_no_default(mut self, required_field_with_no_default: HashMap) -> Self { +/// self.required_field_with_no_default = Some(required_field_with_no_default); +/// self +/// } +/// } +/// ``` +#[macro_export] +macro_rules! builder { + ($($tt:tt)+) => { + builder_struct!($($tt)+); + + impl Builder { + pub fn new() -> Self { + Builder::default() + } + + builder_methods!($($tt)+); + } + } +} + +/// Define a new builder struct, its fields, and their docs. This macro is intended to be called +/// by the `builder!` macro and should not be called directly. +#[macro_export] +macro_rules! builder_struct { + ($($_setter_name:ident, $field_name:ident, $ty:ty, $doc:literal $(,)?)+) => { + #[derive(Clone, Debug, Default)] + pub struct Builder { + $( + #[doc = $doc] + $field_name: Option<$ty>, + )+ + } + } +} + +/// Define setter methods for a builder struct. Must be called from within an `impl` block. This +/// macro is intended to be called by the `builder!` macro and should not be called directly. +#[macro_export] +macro_rules! builder_methods { + ($fn_name:ident, $arg_name:ident, $ty:ty, $doc:literal, $($tail:tt)+) => { + builder_methods!($fn_name, $arg_name, $ty, $doc); + builder_methods!($($tail)+); + }; + ($fn_name:ident, $arg_name:ident, $ty:ty, $doc:literal) => { + #[doc = $doc] + pub fn $fn_name(&mut self, $arg_name: Option<$ty>) -> &mut Self { + self.$arg_name = $arg_name; + self + } + + #[doc = $doc] + pub fn $arg_name(mut self, $arg_name: $ty) -> Self { + self.$arg_name = Some($arg_name); + self + } + }; +} diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 3510ba2807..8c0847ccb1 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -31,6 +31,7 @@ tracing = "0.1.37" fastrand = "1.4" [dev-dependencies] +approx = "0.5.1" aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio", "test-util"] } aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api", features = ["test-util"] } aws-smithy-types = { path = "../aws-smithy-types", features = ["test-util"] } diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries.rs b/rust-runtime/aws-smithy-runtime/src/client/retries.rs index f8bbe70060..9c41e5366a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries.rs @@ -5,3 +5,9 @@ pub mod classifier; pub mod strategy; + +mod client_rate_limiter; +mod token_bucket; + +pub use client_rate_limiter::ClientRateLimiterRuntimePlugin; +pub use token_bucket::TokenBucketRuntimePlugin; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs new file mode 100644 index 0000000000..6113990b5a --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs @@ -0,0 +1,598 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! A rate limiter for controlling the rate at which AWS requests are made. The rate changes based +//! on the number of throttling errors encountered. + +// TODO(enableNewSmithyRuntimeLaunch): Zelda will integrate this rate limiter into the retry policy in a separate PR. +#![allow(dead_code)] + +use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::{builder, builder_methods, builder_struct}; +use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer, Storable, StoreReplace}; +use std::sync::{Arc, Mutex}; +use std::time::SystemTime; + +/// A [RuntimePlugin] to provide a client rate limiter, usable by a retry strategy. +#[non_exhaustive] +#[derive(Debug)] +pub struct ClientRateLimiterRuntimePlugin { + _rate_limiter: Arc>, +} + +impl ClientRateLimiterRuntimePlugin { + pub fn new(cfg: &ConfigBag) -> Self { + Self { + _rate_limiter: Arc::new(Mutex::new(ClientRateLimiter::new(cfg))), + } + } +} + +impl RuntimePlugin for ClientRateLimiterRuntimePlugin { + fn config(&self) -> Option { + let cfg = Layer::new("client rate limiter"); + // TODO(enableNewSmithyRuntimeLaunch) Move the Arc/Mutex inside the rate limiter so that it + // be both storable and cloneable. + // cfg.store_put(self.rate_limiter.clone()); + + Some(cfg.freeze()) + } +} + +const MIN_FILL_RATE: f64 = 0.5; +const MIN_CAPACITY: f64 = 1.0; +const SMOOTH: f64 = 0.8; +/// How much to scale back after receiving a throttling response +const BETA: f64 = 0.7; +/// Controls how aggressively we scale up after being throttled +const SCALE_CONSTANT: f64 = 0.4; + +#[derive(Clone, Debug)] +pub(crate) struct ClientRateLimiter { + /// The rate at which token are replenished. + token_refill_rate: f64, + /// The maximum capacity allowed in the token bucket. + maximum_bucket_capacity: f64, + /// The current capacity of the token bucket. + /// The minimum this can be is 1.0 + current_bucket_capacity: f64, + /// The last time the token bucket was refilled. + time_of_last_refill: Option, + /// The smoothed rate which tokens are being retrieved. + tokens_retrieved_per_second: f64, + /// The last half second time bucket used. + previous_time_bucket: f64, + /// The number of requests seen within the current time bucket. + request_count: u64, + /// Boolean indicating if the token bucket is enabled. + /// The token bucket is initially disabled. + /// When a throttling error is encountered it is enabled. + enable_throttling: bool, + /// The maximum rate when the client was last throttled. + tokens_retrieved_per_second_at_time_of_last_throttle: f64, + /// The last time when the client was throttled. + time_of_last_throttle: f64, + time_window: f64, + calculated_rate: f64, +} + +impl Storable for ClientRateLimiter { + type Storer = StoreReplace; +} + +impl ClientRateLimiter { + pub(crate) fn new(cfg: &ConfigBag) -> Self { + Self::builder() + .time_of_last_throttle(get_unix_timestamp(cfg)) + .previous_time_bucket(get_unix_timestamp(cfg).floor()) + .build() + } + + fn builder() -> Builder { + Builder::new() + } + + /// If this function returns `Ok(())`, you're OK to send a request. If it returns an error, + /// then you should not send a request; You've sent quite enough already. + pub(crate) fn acquire_permission_to_send_a_request( + &mut self, + seconds_since_unix_epoch: f64, + amount: f64, + ) -> Result<(), BoxError> { + if !self.enable_throttling { + // return early if we haven't encountered a throttling error yet + return Ok(()); + } + + self.refill(seconds_since_unix_epoch); + + if self.current_bucket_capacity < amount { + Err(BoxError::from("the client rate limiter is out of tokens")) + } else { + self.current_bucket_capacity -= amount; + Ok(()) + } + } + + pub(crate) fn update_rate_limiter( + &mut self, + seconds_since_unix_epoch: f64, + is_throttling_error: bool, + ) { + self.update_tokens_retrieved_per_second(seconds_since_unix_epoch); + + if is_throttling_error { + let rate_to_use = if self.enable_throttling { + f64::min(self.tokens_retrieved_per_second, self.token_refill_rate) + } else { + self.tokens_retrieved_per_second + }; + + // The fill_rate is from the token bucket + self.tokens_retrieved_per_second_at_time_of_last_throttle = rate_to_use; + self.calculate_time_window(); + self.time_of_last_throttle = seconds_since_unix_epoch; + self.calculated_rate = cubic_throttle(rate_to_use); + self.enable_token_bucket(); + } else { + self.calculate_time_window(); + self.calculated_rate = self.cubic_success(seconds_since_unix_epoch); + } + + let new_rate = f64::min(self.calculated_rate, 2.0 * self.tokens_retrieved_per_second); + self.update_bucket_refill_rate(seconds_since_unix_epoch, new_rate); + } + + fn refill(&mut self, seconds_since_unix_epoch: f64) { + if let Some(last_timestamp) = self.time_of_last_refill { + let fill_amount = (seconds_since_unix_epoch - last_timestamp) * self.token_refill_rate; + self.current_bucket_capacity = f64::min( + self.maximum_bucket_capacity, + self.current_bucket_capacity + fill_amount, + ); + } + self.time_of_last_refill = Some(seconds_since_unix_epoch); + } + + fn update_bucket_refill_rate(&mut self, seconds_since_unix_epoch: f64, new_fill_rate: f64) { + // Refill based on our current rate before we update to the new fill rate. + self.refill(seconds_since_unix_epoch); + + self.token_refill_rate = f64::max(new_fill_rate, MIN_FILL_RATE); + self.maximum_bucket_capacity = f64::max(new_fill_rate, MIN_CAPACITY); + // When we scale down we can't have a current capacity that exceeds our max_capacity. + self.current_bucket_capacity = + f64::min(self.current_bucket_capacity, self.maximum_bucket_capacity); + } + + fn enable_token_bucket(&mut self) { + self.enable_throttling = true; + } + + fn update_tokens_retrieved_per_second(&mut self, seconds_since_unix_epoch: f64) { + let next_time_bucket = (seconds_since_unix_epoch * 2.0).floor() / 2.0; + self.request_count += 1; + + if next_time_bucket > self.previous_time_bucket { + let current_rate = + self.request_count as f64 / (next_time_bucket - self.previous_time_bucket); + self.tokens_retrieved_per_second = + current_rate * SMOOTH + self.tokens_retrieved_per_second * (1.0 - SMOOTH); + self.request_count = 0; + self.previous_time_bucket = next_time_bucket; + } + } + + fn calculate_time_window(&mut self) { + // This is broken out into a separate calculation because it only + // gets updated when @tokens_retrieved_per_second_at_time_of_last_throttle() changes so it can be cached. + let base = (self.tokens_retrieved_per_second_at_time_of_last_throttle * (1.0 - BETA)) + / SCALE_CONSTANT; + self.time_window = base.powf(1.0 / 3.0); + } + + fn cubic_success(&self, seconds_since_unix_epoch: f64) -> f64 { + let dt = seconds_since_unix_epoch - self.time_of_last_throttle - self.time_window; + (SCALE_CONSTANT * dt.powi(3)) + self.tokens_retrieved_per_second_at_time_of_last_throttle + } +} + +fn cubic_throttle(rate_to_use: f64) -> f64 { + rate_to_use * BETA +} + +fn get_unix_timestamp(cfg: &ConfigBag) -> f64 { + let request_time = cfg.request_time().unwrap(); + request_time + .now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs_f64() +} + +builder!( + set_token_refill_rate, token_refill_rate, f64, "The rate at which token are replenished.", + set_maximum_bucket_capacity, maximum_bucket_capacity, f64, "The maximum capacity allowed in the token bucket.", + set_current_bucket_capacity, current_bucket_capacity, f64, "The current capacity of the token bucket. The minimum this can be is 1.0", + set_time_of_last_refill, time_of_last_refill, f64, "The last time the token bucket was refilled.", + set_tokens_retrieved_per_second, tokens_retrieved_per_second, f64, "The smoothed rate which tokens are being retrieved.", + set_previous_time_bucket, previous_time_bucket, f64, "The last half second time bucket used.", + set_request_count, request_count, u64, "The number of requests seen within the current time bucket.", + set_enable_throttling, enable_throttling, bool, "Boolean indicating if the token bucket is enabled. The token bucket is initially disabled. When a throttling error is encountered it is enabled.", + set_tokens_retrieved_per_second_at_time_of_last_throttle, tokens_retrieved_per_second_at_time_of_last_throttle, f64, "The maximum rate when the client was last throttled.", + set_time_of_last_throttle, time_of_last_throttle, f64, "The last time when the client was throttled.", + set_time_window, time_window, f64, "The time window used to calculate the cubic success rate.", + set_calculated_rate, calculated_rate, f64, "The calculated rate used to update the sending rate." +); + +impl Builder { + fn build(self) -> ClientRateLimiter { + ClientRateLimiter { + token_refill_rate: self.token_refill_rate.unwrap_or_default(), + maximum_bucket_capacity: self.maximum_bucket_capacity.unwrap_or(f64::MAX), + current_bucket_capacity: self.current_bucket_capacity.unwrap_or_default(), + time_of_last_refill: self.time_of_last_refill, + enable_throttling: self.enable_throttling.unwrap_or_default(), + tokens_retrieved_per_second: self.tokens_retrieved_per_second.unwrap_or_default(), + previous_time_bucket: self.previous_time_bucket.unwrap_or_default(), + request_count: self.request_count.unwrap_or_default(), + tokens_retrieved_per_second_at_time_of_last_throttle: self + .tokens_retrieved_per_second_at_time_of_last_throttle + .unwrap_or_default(), + time_of_last_throttle: self.time_of_last_throttle.unwrap_or_default(), + time_window: self.time_window.unwrap_or_default(), + calculated_rate: self.calculated_rate.unwrap_or_default(), + } + } +} + +#[cfg(test)] +mod tests { + use super::{cubic_throttle, get_unix_timestamp, ClientRateLimiter}; + use approx::assert_relative_eq; + use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; + use aws_smithy_async::test_util::instant_time_and_sleep; + use aws_smithy_async::time::SharedTimeSource; + use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; + use aws_smithy_types::config_bag::ConfigBag; + use std::time::{Duration, SystemTime}; + + #[test] + fn it_sets_the_time_window_correctly() { + let mut rate_limiter = ClientRateLimiter::builder() + .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) + .build(); + + rate_limiter.calculate_time_window(); + assert_relative_eq!(rate_limiter.time_window, 1.9574338205844317); + } + + #[test] + fn should_match_beta_decrease() { + let new_rate = cubic_throttle(10.0); + assert_relative_eq!(new_rate, 7.0); + + let mut rate_limiter = ClientRateLimiter::builder() + .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) + .time_of_last_throttle(1.0) + .build(); + + rate_limiter.calculate_time_window(); + let new_rate = rate_limiter.cubic_success(1.0); + assert_relative_eq!(new_rate, 7.0); + } + + #[tokio::test] + async fn throttling_is_enabled_once_throttling_error_is_received() { + let mut cfg = ConfigBag::base(); + let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); + cfg.interceptor_state() + .set_request_time(SharedTimeSource::new(time_source)); + cfg.interceptor_state() + .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl))); + let now = get_unix_timestamp(&cfg); + let mut rate_limiter = ClientRateLimiter::builder() + .previous_time_bucket((now).floor()) + .time_of_last_throttle(now) + .build(); + + assert!( + !rate_limiter.enable_throttling, + "rate_limiter should be disabled by default" + ); + rate_limiter.update_rate_limiter(now, true); + assert!( + rate_limiter.enable_throttling, + "rate_limiter should be enabled after throttling error" + ); + } + + #[tokio::test] + async fn test_calculated_rate_with_successes() { + let mut cfg = ConfigBag::base(); + let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); + sleep_impl.sleep(Duration::from_secs(5)).await; + cfg.interceptor_state() + .set_request_time(SharedTimeSource::new(time_source)); + cfg.interceptor_state() + .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl.clone()))); + let now = get_unix_timestamp(&cfg); + let mut rate_limiter = ClientRateLimiter::builder() + .time_of_last_throttle(now) + .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) + .build(); + + struct Attempt { + seconds_since_unix_epoch: f64, + expected_calculated_rate: f64, + } + + let attempts = [ + Attempt { + seconds_since_unix_epoch: 5.0, + expected_calculated_rate: 7.0, + }, + Attempt { + seconds_since_unix_epoch: 6.0, + expected_calculated_rate: 9.64893600966, + }, + Attempt { + seconds_since_unix_epoch: 7.0, + expected_calculated_rate: 10.000030849917364, + }, + Attempt { + seconds_since_unix_epoch: 8.0, + expected_calculated_rate: 10.453284520772092, + }, + Attempt { + seconds_since_unix_epoch: 9.0, + expected_calculated_rate: 13.408697022224185, + }, + Attempt { + seconds_since_unix_epoch: 10.0, + expected_calculated_rate: 21.26626835427364, + }, + Attempt { + seconds_since_unix_epoch: 11.0, + expected_calculated_rate: 36.425998516920465, + }, + ]; + + // Think this test is a little strange? I ported the test from Go v2, and this is how it + // was implemented. See for yourself: + // https://github.com/aws/aws-sdk-go-v2/blob/844ff45cdc76182229ad098c95bf3f5ab8c20e9f/aws/retry/adaptive_ratelimit_test.go#L97 + for attempt in attempts { + rate_limiter.calculate_time_window(); + let calculated_rate = rate_limiter.cubic_success(attempt.seconds_since_unix_epoch); + + assert_relative_eq!(attempt.expected_calculated_rate, calculated_rate); + } + } + + #[tokio::test] + async fn test_calculated_rate_with_throttles() { + let mut cfg = ConfigBag::base(); + let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); + sleep_impl.sleep(Duration::from_secs(5)).await; + cfg.interceptor_state() + .set_request_time(SharedTimeSource::new(time_source)); + cfg.interceptor_state() + .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl.clone()))); + let now = get_unix_timestamp(&cfg); + let mut rate_limiter = ClientRateLimiter::builder() + .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) + .time_of_last_throttle(now) + .build(); + + struct Attempt { + throttled: bool, + seconds_since_unix_epoch: f64, + expected_calculated_rate: f64, + } + + let attempts = [ + Attempt { + throttled: false, + seconds_since_unix_epoch: 5.0, + expected_calculated_rate: 7.0, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 6.0, + expected_calculated_rate: 9.64893600966, + }, + Attempt { + throttled: true, + seconds_since_unix_epoch: 7.0, + expected_calculated_rate: 6.754255206761999, + }, + Attempt { + throttled: true, + seconds_since_unix_epoch: 8.0, + expected_calculated_rate: 4.727978644733399, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 9.0, + expected_calculated_rate: 4.670125557970046, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 10.0, + expected_calculated_rate: 4.770870456867401, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 11.0, + expected_calculated_rate: 6.011819748005445, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 12.0, + expected_calculated_rate: 10.792973431384178, + }, + ]; + + // Think this test is a little strange? I ported the test from Go v2, and this is how it + // was implemented. See for yourself: + // https://github.com/aws/aws-sdk-go-v2/blob/844ff45cdc76182229ad098c95bf3f5ab8c20e9f/aws/retry/adaptive_ratelimit_test.go#L97 + let mut calculated_rate = 0.0; + for attempt in attempts { + rate_limiter.calculate_time_window(); + if attempt.throttled { + calculated_rate = cubic_throttle(calculated_rate); + rate_limiter.time_of_last_throttle = attempt.seconds_since_unix_epoch; + rate_limiter.tokens_retrieved_per_second_at_time_of_last_throttle = calculated_rate; + } else { + calculated_rate = rate_limiter.cubic_success(attempt.seconds_since_unix_epoch); + }; + + assert_relative_eq!(attempt.expected_calculated_rate, calculated_rate); + } + } + + #[tokio::test] + async fn test_client_sending_rates() { + let mut cfg = ConfigBag::base(); + let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); + cfg.interceptor_state() + .set_request_time(SharedTimeSource::new(time_source)); + cfg.interceptor_state() + .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl.clone()))); + let mut rate_limiter = ClientRateLimiter::builder().build(); + + struct Attempt { + throttled: bool, + seconds_since_unix_epoch: f64, + expected_tokens_retrieved_per_second: f64, + expected_token_refill_rate: f64, + } + + let attempts = [ + Attempt { + throttled: false, + seconds_since_unix_epoch: 0.2, + expected_tokens_retrieved_per_second: 0.000000, + expected_token_refill_rate: 0.500000, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 0.4, + expected_tokens_retrieved_per_second: 0.000000, + expected_token_refill_rate: 0.500000, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 0.6, + expected_tokens_retrieved_per_second: 4.800000000000001, + expected_token_refill_rate: 0.500000, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 0.8, + expected_tokens_retrieved_per_second: 4.800000000000001, + expected_token_refill_rate: 0.500000, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 1.0, + expected_tokens_retrieved_per_second: 4.160000, + expected_token_refill_rate: 0.500000, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 1.2, + expected_tokens_retrieved_per_second: 4.160000, + expected_token_refill_rate: 0.691200, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 1.4, + expected_tokens_retrieved_per_second: 4.160000, + expected_token_refill_rate: 1.0975999999999997, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 1.6, + expected_tokens_retrieved_per_second: 5.632000000000001, + expected_token_refill_rate: 1.6384000000000005, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 1.8, + expected_tokens_retrieved_per_second: 5.632000000000001, + expected_token_refill_rate: 2.332800, + }, + Attempt { + throttled: true, + seconds_since_unix_epoch: 2.0, + expected_tokens_retrieved_per_second: 4.326400, + expected_token_refill_rate: 3.0284799999999996, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 2.2, + expected_tokens_retrieved_per_second: 4.326400, + expected_token_refill_rate: 3.48663917347026, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 2.4, + expected_tokens_retrieved_per_second: 4.326400, + expected_token_refill_rate: 3.821874416040255, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 2.6, + expected_tokens_retrieved_per_second: 5.665280, + expected_token_refill_rate: 4.053385727709987, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 2.8, + expected_tokens_retrieved_per_second: 5.665280, + expected_token_refill_rate: 4.200373108479454, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 3.0, + expected_tokens_retrieved_per_second: 4.333056, + expected_token_refill_rate: 4.282036558348658, + }, + Attempt { + throttled: true, + seconds_since_unix_epoch: 3.2, + expected_tokens_retrieved_per_second: 4.333056, + expected_token_refill_rate: 2.99742559084406, + }, + Attempt { + throttled: false, + seconds_since_unix_epoch: 3.4, + expected_tokens_retrieved_per_second: 4.333056, + expected_token_refill_rate: 3.4522263943863463, + }, + ]; + + let two_hundred_milliseconds = Duration::from_millis(200); + for attempt in attempts { + sleep_impl.sleep(two_hundred_milliseconds).await; + assert_eq!( + attempt.seconds_since_unix_epoch, + sleep_impl.total_duration().as_secs_f64() + ); + + rate_limiter.update_rate_limiter(attempt.seconds_since_unix_epoch, attempt.throttled); + assert_relative_eq!( + attempt.expected_tokens_retrieved_per_second, + rate_limiter.tokens_retrieved_per_second + ); + assert_relative_eq!( + attempt.expected_token_refill_rate, + rate_limiter.token_refill_rate + ); + } + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index bf85fc5855..bde9be5170 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -6,7 +6,7 @@ use crate::client::retries::strategy::standard::ReleaseResult::{ APermitWasReleased, NoPermitWasReleased, }; -use crate::client::runtime_plugin::standard_token_bucket::StandardTokenBucket; +use crate::client::retries::token_bucket::TokenBucket; use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors}; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; @@ -114,7 +114,7 @@ impl RetryStrategy for StandardRetryStrategy { let output_or_error = ctx.output_or_error().expect( "This must never be called without reaching the point where the result exists.", ); - let token_bucket = cfg.get::(); + let token_bucket = cfg.get::(); if output_or_error.is_ok() { tracing::debug!("request succeeded, no retry necessary"); if let Some(tb) = token_bucket { @@ -159,8 +159,9 @@ impl RetryStrategy for StandardRetryStrategy { Some(permit) => self.set_retry_permit(permit), None => { tracing::debug!( - "attempt #{request_attempts} failed with {kind:?}; However, no retry permits are available, so no retry will be attempted.", - ); + "attempt #{request_attempts} failed with {kind:?}; \ + However, no retry permits are available, so no retry will be attempted.", + ); return Ok(ShouldAttempt::No); } } @@ -179,7 +180,7 @@ impl RetryStrategy for StandardRetryStrategy { } Some(_) => unreachable!("RetryReason is non-exhaustive"), None => { - tracing::trace!( + tracing::debug!( attempts = request_attempts, max_attempts = self.max_attempts, "encountered unretryable error" @@ -219,7 +220,7 @@ mod tests { use std::time::Duration; #[cfg(feature = "test-util")] - use crate::client::runtime_plugin::standard_token_bucket::StandardTokenBucket; + use crate::client::retries::token_bucket::TokenBucket; #[test] fn no_retry_necessary_for_ok_result() { @@ -370,8 +371,8 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5); - cfg.interceptor_state().put(StandardTokenBucket::default()); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().put(TokenBucket::default()); + let token_bucket = cfg.get::().unwrap().clone(); cfg.interceptor_state().put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); @@ -400,8 +401,8 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(3); - cfg.interceptor_state().put(StandardTokenBucket::default()); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().put(TokenBucket::default()); + let token_bucket = cfg.get::().unwrap().clone(); cfg.interceptor_state().put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); @@ -428,8 +429,8 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5); - cfg.interceptor_state().put(StandardTokenBucket::new(5)); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().put(TokenBucket::new(5)); + let token_bucket = cfg.get::().unwrap().clone(); cfg.interceptor_state().put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); @@ -453,8 +454,8 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5); - cfg.interceptor_state().put(StandardTokenBucket::new(100)); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().put(TokenBucket::new(100)); + let token_bucket = cfg.get::().unwrap().clone(); cfg.interceptor_state().put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); @@ -485,9 +486,8 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(usize::MAX); - cfg.interceptor_state() - .put(StandardTokenBucket::new(PERMIT_COUNT)); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().put(TokenBucket::new(PERMIT_COUNT)); + let token_bucket = cfg.get::().unwrap().clone(); let mut attempt = 1; @@ -533,8 +533,8 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5); - cfg.interceptor_state().put(StandardTokenBucket::default()); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().put(TokenBucket::default()); + let token_bucket = cfg.get::().unwrap().clone(); cfg.interceptor_state().put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); @@ -575,8 +575,8 @@ mod tests { .with_max_attempts(5) .with_initial_backoff(Duration::from_secs(1)) .with_max_backoff(Duration::from_secs(3)); - cfg.interceptor_state().put(StandardTokenBucket::default()); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().put(TokenBucket::default()); + let token_bucket = cfg.get::().unwrap().clone(); cfg.interceptor_state().put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/standard_token_bucket.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs similarity index 80% rename from rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/standard_token_bucket.rs rename to rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs index 548d63307f..df1510459a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/standard_token_bucket.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs @@ -10,23 +10,22 @@ use std::sync::Arc; use tokio::sync::{OwnedSemaphorePermit, Semaphore}; use tracing::trace; -/// A [RuntimePlugin] to provide a standard token bucket, usable by the -/// [`StandardRetryStrategy`](crate::client::retries::strategy::standard::StandardRetryStrategy). +/// A [RuntimePlugin] to provide a token bucket, usable by a retry strategy. #[non_exhaustive] #[derive(Debug, Default)] -pub struct StandardTokenBucketRuntimePlugin { - token_bucket: StandardTokenBucket, +pub struct TokenBucketRuntimePlugin { + token_bucket: TokenBucket, } -impl StandardTokenBucketRuntimePlugin { +impl TokenBucketRuntimePlugin { pub fn new(initial_tokens: usize) -> Self { Self { - token_bucket: StandardTokenBucket::new(initial_tokens), + token_bucket: TokenBucket::new(initial_tokens), } } } -impl RuntimePlugin for StandardTokenBucketRuntimePlugin { +impl RuntimePlugin for TokenBucketRuntimePlugin { fn config(&self) -> Option { let mut cfg = Layer::new("standard token bucket"); cfg.store_put(self.token_bucket.clone()); @@ -41,18 +40,18 @@ const RETRY_TIMEOUT_COST: u32 = RETRY_COST * 2; const PERMIT_REGENERATION_AMOUNT: usize = 1; #[derive(Clone, Debug)] -pub(crate) struct StandardTokenBucket { +pub(crate) struct TokenBucket { semaphore: Arc, max_permits: usize, timeout_retry_cost: u32, retry_cost: u32, } -impl Storable for StandardTokenBucket { +impl Storable for TokenBucket { type Storer = StoreReplace; } -impl Default for StandardTokenBucket { +impl Default for TokenBucket { fn default() -> Self { Self { semaphore: Arc::new(Semaphore::new(DEFAULT_CAPACITY)), @@ -63,7 +62,7 @@ impl Default for StandardTokenBucket { } } -impl StandardTokenBucket { +impl TokenBucket { pub(crate) fn new(initial_quota: usize) -> Self { Self { semaphore: Arc::new(Semaphore::new(initial_quota)), diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs index a127873bdb..784de5cd71 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs @@ -5,5 +5,3 @@ #[cfg(feature = "anonymous-auth")] pub mod anonymous_auth; - -pub mod standard_token_bucket; diff --git a/rust-runtime/inlineable/Cargo.toml b/rust-runtime/inlineable/Cargo.toml index b76a17e5e7..5c89e925bf 100644 --- a/rust-runtime/inlineable/Cargo.toml +++ b/rust-runtime/inlineable/Cargo.toml @@ -27,7 +27,7 @@ default = ["gated-tests"] "fastrand" = "1" "futures-util" = "0.3" "pin-project-lite" = "0.2" -"tower" = { version = "0.4.11", default_features = false } +"tower" = { version = "0.4.11", default-features = false } "async-trait" = "0.1" percent-encoding = "2.2.0" once_cell = "1.16.0" From 7ed51b21290aba818fcfd7a5501fe7035cde5c24 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Tue, 20 Jun 2023 15:41:19 -0500 Subject: [PATCH 169/253] Add `CloneableLayer` that can be `Clone`d (#2784) ## Motivation and Context Alternative to #2776, making `Layer` cloneable so it can be stored in a type that implements `Clone`. ## Description In an attempt to reduce struct fields of a service config builder so it only contains `Layer` (plus some exceptions such as `interceptors`), we need to make `Layer` clone since the service config builder `#[derive(Clone)]`. There are two options. One, adopted by this PR, is to have a separate `CloneableLayer` that primarily caters for the need for service config builders. The other option is #2776, where we require that items in `Layer` be `Clone`. Since the latter is rather a sledge hammer, changing the requirement for all of the items to be stored, we will go for the former, which is a more targeted approach and a two-way door if we want to switch to the latter in the future. ## Testing Added unit tests in `type_erase.rs` and `config_bag.rs`. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito Co-authored-by: Zelda Hessler --- .../aws-smithy-types/src/config_bag.rs | 173 +++++++++++++++++- .../aws-smithy-types/src/type_erasure.rs | 57 +++++- 2 files changed, 220 insertions(+), 10 deletions(-) diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index 81f4e5ca64..e2eea64252 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -19,7 +19,7 @@ use std::borrow::Cow; use std::fmt::{Debug, Formatter}; use std::iter::Rev; use std::marker::PhantomData; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use std::slice::Iter; use std::sync::Arc; @@ -65,7 +65,7 @@ impl Deref for FrozenLayer { /// Private module to keep Value type while avoiding "private type in public latest" pub(crate) mod value { - #[derive(Debug)] + #[derive(Clone, Debug)] pub enum Value { Set(T), ExplicitlyUnset(&'static str), @@ -79,6 +79,123 @@ impl Default for Value { } } +/// [`CloneableLayer`] allows itself to be cloned. This is useful when a type that implements +/// `Clone` wishes to store a config layer. +/// +/// It ensures that all the items in `CloneableLayer` are `Clone` upon entry, e.g. when they are +/// first stored, the mutable methods require that they have a `Clone` bound on them. +/// +/// While [`FrozenLayer`] is also cloneable, which is a shallow clone via `Arc`, `CloneableLayer` +/// performs a deep clone that newly allocates all the items stored in it. +#[derive(Debug)] +pub struct CloneableLayer(Layer); + +impl Deref for CloneableLayer { + type Target = Layer; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for CloneableLayer { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Clone for CloneableLayer { + fn clone(&self) -> Self { + Self( + self.try_clone() + .expect("only cloneable types can be inserted"), + ) + } +} + +// We need to "override" the mutable methods to encode the information that an item being stored +// implements `Clone`. For the immutable methods, they can just be delegated via the `Deref` trait. +impl CloneableLayer { + /// Creates a new `CloneableLayer` with a given name + pub fn new(name: impl Into>) -> Self { + Self(Layer::new(name)) + } + + pub fn freeze(self) -> FrozenLayer { + self.0.into() + } + + /// Removes `T` from this bag + pub fn unset(&mut self) -> &mut Self { + self.put_directly::>(Value::ExplicitlyUnset(type_name::())); + self + } + + fn put_directly(&mut self, value: T::StoredType) -> &mut Self + where + T::StoredType: Clone, + { + self.props + .insert(TypeId::of::(), TypeErasedBox::new_with_clone(value)); + self + } + + /// Stores `item` of type `T` into the config bag, overriding a previous value of the same type + pub fn store_put(&mut self, item: T) -> &mut Self + where + T: Storable> + Clone, + { + self.put_directly::>(Value::Set(item)); + self + } + + /// Stores `item` of type `T` into the config bag, overriding a previous value of the same type, + /// or unsets it by passing a `None` + pub fn store_or_unset(&mut self, item: Option) -> &mut Self + where + T: Storable> + Clone, + { + let item = match item { + Some(item) => Value::Set(item), + None => Value::ExplicitlyUnset(type_name::()), + }; + self.put_directly::>(item); + self + } + + /// Stores `item` of type `T` into the config bag, appending it to the existing list of the same + /// type + pub fn store_append(&mut self, item: T) -> &mut Self + where + T: Storable> + Clone, + { + match self.get_mut_or_default::>() { + Value::Set(list) => list.push(item), + v @ Value::ExplicitlyUnset(_) => *v = Value::Set(vec![item]), + } + self + } + + /// Clears the value of type `T` from the config bag + pub fn clear(&mut self) + where + T: Storable> + Clone, + { + self.put_directly::>(Value::ExplicitlyUnset(type_name::())); + } + + fn get_mut_or_default(&mut self) -> &mut T::StoredType + where + T::StoredType: Default + Clone, + { + self.props + .entry(TypeId::of::()) + .or_insert_with(|| TypeErasedBox::new_with_clone(T::StoredType::default())) + .downcast_mut() + .expect("typechecked") + } +} + /// A named layer comprising a config bag pub struct Layer { name: Cow<'static, str>, @@ -101,6 +218,22 @@ impl Debug for Layer { } impl Layer { + fn try_clone(&self) -> Option { + let new_props = self + .props + .iter() + .flat_map(|(tyid, erased)| erased.try_clone().map(|e| (*tyid, e))) + .collect::>(); + if new_props.len() == self.props.len() { + Some(Layer { + name: self.name.clone(), + props: new_props, + }) + } else { + None + } + } + /// Inserts `value` into the layer directly fn put_directly(&mut self, value: T::StoredType) -> &mut Self { self.props @@ -485,7 +618,7 @@ impl From for FrozenLayer { #[cfg(test)] mod test { use super::ConfigBag; - use crate::config_bag::{Layer, Storable, StoreAppend, StoreReplace}; + use crate::config_bag::{CloneableLayer, Layer, Storable, StoreAppend, StoreReplace}; #[test] fn layered_property_bag() { @@ -672,4 +805,38 @@ mod test { assert_eq!(bag.get_mut::(), None); assert_eq!(bag.get_mut_or_default::(), &Foo(0)); } + + #[test] + fn cloning_layers() { + #[derive(Clone, Debug)] + struct TestStr(String); + impl Storable for TestStr { + type Storer = StoreReplace; + } + let mut layer_1 = CloneableLayer::new("layer_1"); + let expected_str = "I can be cloned"; + layer_1.store_put(TestStr(expected_str.to_owned())); + let layer_1_cloned = layer_1.clone(); + assert_eq!(expected_str, &layer_1_cloned.load::().unwrap().0); + + #[derive(Clone, Debug)] + struct Rope(String); + impl Storable for Rope { + type Storer = StoreAppend; + } + let mut layer_2 = CloneableLayer::new("layer_2"); + layer_2.store_append(Rope("A".to_owned())); + layer_2.store_append(Rope("big".to_owned())); + layer_2.store_append(Rope("rope".to_owned())); + let layer_2_cloned = layer_2.clone(); + let rope = layer_2_cloned.load::().cloned().collect::>(); + assert_eq!( + "A big rope", + rope.iter() + .rev() + .map(|r| r.0.clone()) + .collect::>() + .join(" ") + ); + } } diff --git a/rust-runtime/aws-smithy-types/src/type_erasure.rs b/rust-runtime/aws-smithy-types/src/type_erasure.rs index 99025d4617..2665585640 100644 --- a/rust-runtime/aws-smithy-types/src/type_erasure.rs +++ b/rust-runtime/aws-smithy-types/src/type_erasure.rs @@ -8,6 +8,7 @@ use std::error::Error as StdError; use std::fmt; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; +use std::sync::Arc; /// A [`TypeErasedBox`] with type information tracked via generics at compile-time /// @@ -101,9 +102,11 @@ impl DerefMut for TypedBox { pub struct TypeErasedBox { field: Box, #[allow(clippy::type_complexity)] - debug: Box< + debug: Arc< dyn Fn(&Box, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, >, + #[allow(clippy::type_complexity)] + clone: Option) -> TypeErasedBox + Send + Sync>>, } #[cfg(feature = "test-util")] @@ -132,14 +135,41 @@ impl TypeErasedBox { }; Self { field: Box::new(value), - debug: Box::new(debug), + debug: Arc::new(debug), + clone: None, } } + pub fn new_with_clone(value: T) -> Self { + let debug = |value: &Box, f: &mut fmt::Formatter<'_>| { + fmt::Debug::fmt(value.downcast_ref::().expect("type-checked"), f) + }; + let clone = |value: &Box| { + TypeErasedBox::new(value.downcast_ref::().expect("typechecked").clone()) + }; + Self { + field: Box::new(value), + debug: Arc::new(debug), + clone: Some(Arc::new(clone)), + } + } + + pub fn try_clone(&self) -> Option { + Some((self.clone.as_ref()?)(&self.field)) + } + /// Downcast into a `Box`, or return `Self` if it is not a `T`. pub fn downcast(self) -> Result, Self> { - let TypeErasedBox { field, debug } = self; - field.downcast().map_err(|field| Self { field, debug }) + let TypeErasedBox { + field, + debug, + clone, + } = self; + field.downcast().map_err(|field| Self { + field, + debug, + clone, + }) } /// Downcast as a `&T`, or return `None` if it is not a `T`. @@ -158,6 +188,7 @@ impl From for TypeErasedBox { TypeErasedBox { field: value.field, debug: value.debug, + clone: None, } } } @@ -166,7 +197,7 @@ impl From for TypeErasedBox { pub struct TypeErasedError { field: Box, #[allow(clippy::type_complexity)] - debug: Box< + debug: Arc< dyn Fn(&Box, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, >, #[allow(clippy::type_complexity)] @@ -200,7 +231,7 @@ impl TypeErasedError { }; Self { field: Box::new(value), - debug: Box::new(debug), + debug: Arc::new(debug), as_error: Box::new(|value: &TypeErasedError| { value.downcast_ref::().expect("typechecked") as _ }), @@ -238,7 +269,7 @@ impl TypeErasedError { #[cfg(test)] mod tests { - use super::{TypeErasedError, TypedBox}; + use super::{TypeErasedBox, TypeErasedError, TypedBox}; use std::fmt; #[derive(Debug)] @@ -320,4 +351,16 @@ mod tests { .unwrap(); assert_eq!(test_err, actual); } + + #[test] + fn test_typed_cloneable_boxes() { + let expected_str = "I can be cloned"; + let cloneable = TypeErasedBox::new_with_clone(expected_str.to_owned()); + // ensure it can be cloned + let cloned = cloneable.try_clone().unwrap(); + let actual_str = cloned.downcast_ref::().unwrap(); + assert_eq!(expected_str, actual_str); + // they should point to different addresses + assert_ne!(format!("{expected_str:p}"), format! {"{actual_str:p}"}); + } } From e03c75c5b6067c9e2324c825660791b4df461ec5 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 20 Jun 2023 14:49:29 -0700 Subject: [PATCH 170/253] Write an S3 throughput benchmark (#2747) ## Motivation and Context This PR adds a S3 throughput benchmark for upload, download, multipart upload across multiple threads, and multipart download across multiple threads. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/sdk/s3-benchmark/README.md | 32 + aws/sdk/s3-benchmark/benchmark/Cargo.lock | 1803 +++ aws/sdk/s3-benchmark/benchmark/Cargo.toml | 17 + .../s3-benchmark/benchmark/src/latencies.rs | 90 + aws/sdk/s3-benchmark/benchmark/src/main.rs | 220 + .../benchmark/src/multipart_get.rs | 89 + .../benchmark/src/multipart_put.rs | 111 + .../infrastructure/.eslintrc.json | 16 + .../s3-benchmark/infrastructure/.gitignore | 9 + .../s3-benchmark/infrastructure/.npmignore | 6 + .../s3-benchmark/infrastructure/.prettierrc | 5 + .../infrastructure/assets/init_instance.sh | 37 + .../infrastructure/assets/run_benchmark.sh | 42 + .../infrastructure/bin/infrastructure.ts | 14 + .../infrastructure/cdk.context.json | 10 + aws/sdk/s3-benchmark/infrastructure/cdk.json | 53 + .../lib/infrastructure-stack.ts | 89 + .../infrastructure/package-lock.json | 9757 +++++++++++++++++ .../s3-benchmark/infrastructure/package.json | 39 + .../s3-benchmark/infrastructure/tsconfig.json | 33 + 20 files changed, 12472 insertions(+) create mode 100644 aws/sdk/s3-benchmark/README.md create mode 100644 aws/sdk/s3-benchmark/benchmark/Cargo.lock create mode 100644 aws/sdk/s3-benchmark/benchmark/Cargo.toml create mode 100644 aws/sdk/s3-benchmark/benchmark/src/latencies.rs create mode 100644 aws/sdk/s3-benchmark/benchmark/src/main.rs create mode 100644 aws/sdk/s3-benchmark/benchmark/src/multipart_get.rs create mode 100644 aws/sdk/s3-benchmark/benchmark/src/multipart_put.rs create mode 100644 aws/sdk/s3-benchmark/infrastructure/.eslintrc.json create mode 100644 aws/sdk/s3-benchmark/infrastructure/.gitignore create mode 100644 aws/sdk/s3-benchmark/infrastructure/.npmignore create mode 100644 aws/sdk/s3-benchmark/infrastructure/.prettierrc create mode 100755 aws/sdk/s3-benchmark/infrastructure/assets/init_instance.sh create mode 100755 aws/sdk/s3-benchmark/infrastructure/assets/run_benchmark.sh create mode 100644 aws/sdk/s3-benchmark/infrastructure/bin/infrastructure.ts create mode 100644 aws/sdk/s3-benchmark/infrastructure/cdk.context.json create mode 100644 aws/sdk/s3-benchmark/infrastructure/cdk.json create mode 100644 aws/sdk/s3-benchmark/infrastructure/lib/infrastructure-stack.ts create mode 100644 aws/sdk/s3-benchmark/infrastructure/package-lock.json create mode 100644 aws/sdk/s3-benchmark/infrastructure/package.json create mode 100644 aws/sdk/s3-benchmark/infrastructure/tsconfig.json diff --git a/aws/sdk/s3-benchmark/README.md b/aws/sdk/s3-benchmark/README.md new file mode 100644 index 0000000000..6bbceaebe4 --- /dev/null +++ b/aws/sdk/s3-benchmark/README.md @@ -0,0 +1,32 @@ +S3 Benchmark +============ + +This directory contains a S3 benchmark that measures throughput when using the AWS Rust SDK to put and get objects to/from S3. +The `benchmark/` directory has the Rust benchmark code, and `infrastructure/` contains the CDK infrastructure to stand up +a `c5n.18xlarge` EC2 instance, compile/run the benchmark, and upload the results to S3. + +Example of running the `get-object-multipart` benchmark in local dev: + +```bash +cargo run -- --bench get-object-multipart --fs disk --part-size-bytes 5242880 --size-bytes 6000000 --bucket my-test-bucket --region us-west-2 --profile my-aws-credentials-profile +``` + +On Linux, the `--fs` option can be given either `disk` or `tmpfs` (for an in-memory filesystem), while on other OSes, only `disk` is available. + +In addition to `get-object-multipart`, there are `put-object-multipart`, `put-object`, and `get-object`. All of these take the +same CLI arguments, although `--part-size-bytes` is unused by `put-object` and `get-object`. + +To run the actual benchmark, it must be deployed via CDK from the `infrastructure/` directory: + +```bash +npm install +npm run build +npx cdk bootstrap --profile my-aws-credentials-profile +npx cdk synthesize --profile my-aws-credentials-profile +npx cdk deploy --profile my-aws-credentials-profile +``` + +The `lib/instrastructure-stack.ts` defines the actual CloudFormation stack that creates the EC2 Instance. +This instance is configured to run the `assets/init_instance.sh` and `assets/run_benchmark.sh` scripts on start-up. +It's also configured for SSH access via a key pair named "S3BenchmarkKeyPair". This key pair has to be created manually +before deploying the CDK stack. diff --git a/aws/sdk/s3-benchmark/benchmark/Cargo.lock b/aws/sdk/s3-benchmark/benchmark/Cargo.lock new file mode 100644 index 0000000000..d53f4b830a --- /dev/null +++ b/aws/sdk/s3-benchmark/benchmark/Cargo.lock @@ -0,0 +1,1803 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "aws-config" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcdcf0d683fe9c23d32cf5b53c9918ea0a500375a9fb20109802552658e576c9" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-sdk-sso", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http", + "hyper", + "ring", + "time", + "tokio", + "tower", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcdb2f7acbc076ff5ad05e7864bdb191ca70a6fd07668dc3a1a8bcd051de5ae" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "fastrand", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-endpoint" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cce1c41a6cfaa726adee9ebb9a56fcd2bbfd8be49fd8a04c5e20fd968330b04" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "aws-types", + "http", + "regex", + "tracing", +] + +[[package]] +name = "aws-http" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aadbc44e7a8f3e71c8b374e03ecd972869eb91dd2bc89ed018954a52ba84bc44" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-types", + "aws-types", + "bytes", + "http", + "http-body", + "lazy_static", + "percent-encoding", + "pin-project-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-s3" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba197193cbb4bcb6aad8d99796b2291f36fa89562ded5d4501363055b0de89f" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-client", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "http", + "http-body", + "once_cell", + "percent-encoding", + "regex", + "tokio-stream", + "tower", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sso" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8b812340d86d4a766b2ca73f740dfd47a97c2dff0c06c8517a16d88241957e4" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "http", + "regex", + "tokio-stream", + "tower", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265fac131fbfc188e5c3d96652ea90ecc676a934e3174eaaee523c6cec040b3b" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "http", + "regex", + "tower", + "tracing", +] + +[[package]] +name = "aws-sig-auth" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b94acb10af0c879ecd5c7bdf51cda6679a0a4f4643ce630905a77673bfa3c61" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-types", + "http", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2ce6f507be68e968a33485ced670111d1cbad161ddbbab1e313c03d37d8f4c" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-http", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http", + "once_cell", + "percent-encoding", + "regex", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bda3996044c202d75b91afeb11a9afae9db9a721c6a7a427410018e286b880" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ed8b96d95402f3f6b8b57eb4e0e45ee365f78b1a924faf20ff6e97abf1eae6" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http", + "http-body", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-client" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a86aa6e21e86c4252ad6a0e3e74da9617295d8d6e374d552be7d3059c41cedd" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-types", + "bytes", + "fastrand", + "http", + "http-body", + "hyper", + "hyper-rustls", + "lazy_static", + "pin-project-lite", + "rustls", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460c8da5110835e3d9a717c61f5556b20d03c32a1dec57f8fc559b360f733bb8" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b3b693869133551f135e1f2c77cb0b8277d9e3e17feaf2213f735857c4f0d28" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http", + "http-body", + "hyper", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "aws-smithy-http-tower" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae4f6c5798a247fac98a867698197d9ac22643596dc3777f0c76b91917616b9" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "http", + "http-body", + "pin-project-lite", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f9f42fbfa96d095194a632fbac19f60077748eba536eb0b9fecc28659807f8" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98819eb0b04020a1c791903533b638534ae6c12e2aceda3e6e6fba015608d51d" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a3d0bf4f324f4ef9793b86a1701d9700fbcdbd12a846da45eed104c634c6e8" +dependencies = [ + "base64-simd", + "itoa", + "num-integer", + "ryu", + "time", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1b9d12875731bd07e767be7baad95700c3137b56730ec9ddeedb52a5e5ca63b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd209616cc8d7bfb82f87811a5c655dc97537f592689b18743bddf5dc5c4829" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-types", + "http", + "rustc_version", + "tracing", +] + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "benchmark" +version = "0.1.0" +dependencies = [ + "aws-config", + "aws-sdk-s3", + "aws-smithy-http", + "clap", + "tokio", + "tracing-subscriber", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "bytes-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47d3a8076e283f3acd27400535992edb3ba4b5bb72f8891ad8fbe7932a7d4b9" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "401a4694d2bf92537b6867d94de48c4842089645fdcdf6c71865b175d836e9c2" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980" +dependencies = [ + "anstyle", + "bitflags", + "clap_lex", +] + +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32c" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfea2db42e9927a3845fb268a10a72faed6d416065f77873f05e411457c363e" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "h2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +dependencies = [ + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + +[[package]] +name = "web-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "xmlparser" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/aws/sdk/s3-benchmark/benchmark/Cargo.toml b/aws/sdk/s3-benchmark/benchmark/Cargo.toml new file mode 100644 index 0000000000..2fe087c0c2 --- /dev/null +++ b/aws/sdk/s3-benchmark/benchmark/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "benchmark" +version = "0.1.0" +authors = ["AWS Rust SDK Team ", "John DiSanti "] +description = "S3 benchmark" +edition = "2021" +license = "Apache-2.0" +repository = "https://github.com/awslabs/smithy-rs" +publish = false + +[dependencies] +aws-config = "0.55.3" +aws-sdk-s3 = "0.28.0" +aws-smithy-http = "0.55.3" +clap = { version = "4.3.2", default-features = false, features = ["derive", "std", "help"] } +tokio = { version = "1.28.2", features = ["full"] } +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } diff --git a/aws/sdk/s3-benchmark/benchmark/src/latencies.rs b/aws/sdk/s3-benchmark/benchmark/src/latencies.rs new file mode 100644 index 0000000000..8ff3879b41 --- /dev/null +++ b/aws/sdk/s3-benchmark/benchmark/src/latencies.rs @@ -0,0 +1,90 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::fmt; +use std::time; + +const ONE_GIGABYTE: u64 = 1024 * 1024 * 1024; + +#[derive(Debug)] +pub struct Latencies { + object_size_bytes: u64, + raw_values: Vec, +} + +impl Latencies { + pub fn new(object_size_bytes: u64) -> Self { + Self { + object_size_bytes, + raw_values: Vec::new(), + } + } + + pub fn push(&mut self, value: time::Duration) { + self.raw_values.push(value.as_secs_f64()); + } + + /// Calculates the standard deviation squared of the given values. + fn variance(values: &[f64], average: f64) -> f64 { + values + .iter() + .map(|value| (value - average).powi(2)) + .sum::() + / values.len() as f64 + } +} + +impl fmt::Display for Latencies { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let object_size_gigabits = self.object_size_bytes as f64 / ONE_GIGABYTE as f64 * 8f64; + + let average_latency = self.raw_values.iter().sum::() / self.raw_values.len() as f64; + let lowest_latency = self + .raw_values + .iter() + .fold(std::f64::INFINITY, |acc, &x| acc.min(x)); + let variance = Self::variance(&self.raw_values, average_latency); + writeln!(f, "Latency values (s): {:?}", self.raw_values)?; + writeln!(f, "Average latency (s): {average_latency}")?; + writeln!(f, "Latency variance (s): {variance}")?; + writeln!(f, "Object size (Gigabits): {object_size_gigabits}")?; + writeln!( + f, + "Average throughput (Gbps): {}", + object_size_gigabits / average_latency + )?; + writeln!( + f, + "Highest average throughput (Gbps): {}", + object_size_gigabits / lowest_latency + )?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::latencies::{Latencies, ONE_GIGABYTE}; + + #[test] + fn test_display() { + let latencies = Latencies { + object_size_bytes: 30 * ONE_GIGABYTE, + raw_values: vec![ + 33.261f64, 41.114, 33.014, 32.97, 34.138, 33.972, 33.001, 34.12, + ], + }; + + let expected = "\ + Latency values (s): [33.261, 41.114, 33.014, 32.97, 34.138, 33.972, 33.001, 34.12]\n\ + Average latency (s): 34.448750000000004\n\ + Latency variance (s): 6.576178687499994\n\ + Object size (Gigabits): 240\n\ + Average throughput (Gbps): 6.966871076599295\n\ + Highest average throughput (Gbps): 7.279344858962694\n"; + let actual = latencies.to_string(); + assert_eq!(expected, actual); + } +} diff --git a/aws/sdk/s3-benchmark/benchmark/src/main.rs b/aws/sdk/s3-benchmark/benchmark/src/main.rs new file mode 100644 index 0000000000..91a6214a7b --- /dev/null +++ b/aws/sdk/s3-benchmark/benchmark/src/main.rs @@ -0,0 +1,220 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::latencies::Latencies; +use crate::multipart_get::get_object_multipart; +use crate::multipart_put::put_object_multipart; +use aws_sdk_s3 as s3; +use clap::Parser as _; +use s3::error::DisplayErrorContext; +use s3::primitives::ByteStream; +use std::error::Error as StdError; +use std::path::Path; +use std::path::PathBuf; +use std::process; +use std::time; + +mod latencies; +mod multipart_get; +mod multipart_put; + +pub type BoxError = Box; + +pub const BENCH_KEY: &str = "s3_bench_file"; + +#[derive(Copy, Clone, Debug, clap::ValueEnum)] +pub enum Fs { + #[cfg(target_os = "linux")] + // Use tmpfs + Tmpfs, + // Use the disk + Disk, +} + +#[derive(Copy, Clone, Debug, clap::ValueEnum)] +pub enum Bench { + PutObject, + GetObject, + PutObjectMultipart, + GetObjectMultipart, +} + +#[derive(Debug, clap::Parser)] +#[command()] +pub struct Args { + /// Which benchmark to run. + #[arg(long)] + bench: Bench, + + /// Local FS type to use. + #[arg(long)] + fs: Fs, + + /// Size of the object to benchmark with. + #[arg(long)] + size_bytes: u64, + + /// S3 bucket to test against. + #[arg(long)] + bucket: String, + + /// AWS region to use. Defaults to us-east-1. + #[arg(long, default_value = "us-east-1")] + region: String, + + /// AWS credentials profile to use. + #[arg(long)] + profile: Option, + + /// Part size for multipart benchmarks. Defaults to 8 MiB. + #[arg(long, default_value_t = 8_388_608)] + part_size_bytes: u64, + + /// Number of benchmark iterations to perform. + #[arg(long, default_value_t = 8)] + iterations: usize, + + /// Number of concurrent uploads/downloads to perform. + #[arg(long, default_value_t = 4)] + concurrency: usize, +} + +#[tokio::main] +async fn main() { + tracing_subscriber::fmt::init(); + + let args = Args::parse(); + let config = { + let mut loader = + aws_config::from_env().region(s3::config::Region::new(args.region.clone())); + if let Some(profile) = args.profile.as_ref() { + loader = loader.profile_name(profile); + } + loader.load().await + }; + let client = s3::Client::new(&config); + + let result = match args.bench { + Bench::PutObject => benchmark_put_object(&client, &args).await, + Bench::GetObject => benchmark_get_object(&client, &args).await, + Bench::PutObjectMultipart => benchmark_put_object_multipart(&client, &args).await, + Bench::GetObjectMultipart => benchmark_get_object_multipart(&client, &args).await, + }; + match result { + Ok(latencies) => { + println!("benchmark succeeded"); + println!("=============== {:?} Result ================", args.bench); + println!("{latencies}"); + println!("=========================================================="); + } + Err(err) => { + println!("benchmark failed: {}", DisplayErrorContext(err.as_ref())); + process::exit(1); + } + } +} + +macro_rules! benchmark { + ($client:ident, $args:ident, setup => $setup:expr, operation => $operation:expr) => {{ + let test_file_path = generate_test_file($args)?; + $setup($client, $args, &test_file_path).await?; + + let mut latencies = Latencies::new($args.size_bytes); + for i in 0..$args.iterations { + let start = time::Instant::now(); + $operation($client, $args, &test_file_path).await?; + let latency = start.elapsed(); + latencies.push(latency); + println!( + "finished iteration {i} in {} seconds", + latency.as_secs_f64() + ); + } + + Ok(latencies) + }}; +} + +async fn benchmark_put_object(client: &s3::Client, args: &Args) -> Result { + benchmark!(client, args, setup => no_setup, operation => put_object) +} + +async fn benchmark_get_object(client: &s3::Client, args: &Args) -> Result { + async fn operation(client: &s3::Client, args: &Args, path: &Path) -> Result<(), BoxError> { + let output = client + .get_object() + .bucket(&args.bucket) + .key(BENCH_KEY) + .send() + .await?; + let mut body = output.body.into_async_read(); + let mut file = tokio::fs::File::create(path).await?; + tokio::io::copy(&mut body, &mut file).await?; + Ok(()) + } + benchmark!(client, args, setup => put_object_intelligent, operation => operation) +} + +async fn benchmark_put_object_multipart( + client: &s3::Client, + args: &Args, +) -> Result { + benchmark!(client, args, setup => no_setup, operation => put_object_multipart) +} + +async fn benchmark_get_object_multipart( + client: &s3::Client, + args: &Args, +) -> Result { + benchmark!(client, args, setup => put_object_intelligent, operation => get_object_multipart) +} + +fn generate_test_file(args: &Args) -> Result { + let path = match args.fs { + Fs::Disk => format!("/tmp/{BENCH_KEY}").into(), + #[cfg(target_os = "linux")] + Fs::Tmpfs => { + if !PathBuf::from("/dev/shm").exists() { + return Err("tmpfs not available on this machine".into()); + } + format!("/dev/shm/{BENCH_KEY}").into() + } + }; + + process::Command::new("truncate") + .arg("-s") + .arg(format!("{}", args.size_bytes)) + .arg(&path) + .output()?; + + Ok(path) +} + +async fn no_setup(_client: &s3::Client, _args: &Args, _path: &Path) -> Result<(), BoxError> { + Ok(()) +} + +async fn put_object_intelligent( + client: &s3::Client, + args: &Args, + path: &Path, +) -> Result<(), BoxError> { + if args.size_bytes > args.part_size_bytes { + put_object_multipart(client, args, path).await + } else { + put_object(client, args, path).await + } +} + +async fn put_object(client: &s3::Client, args: &Args, path: &Path) -> Result<(), BoxError> { + client + .put_object() + .bucket(&args.bucket) + .key(BENCH_KEY) + .body(ByteStream::from_path(&path).await?) + .send() + .await?; + Ok(()) +} diff --git a/aws/sdk/s3-benchmark/benchmark/src/multipart_get.rs b/aws/sdk/s3-benchmark/benchmark/src/multipart_get.rs new file mode 100644 index 0000000000..08e0f3a9c2 --- /dev/null +++ b/aws/sdk/s3-benchmark/benchmark/src/multipart_get.rs @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::{Args, BoxError, BENCH_KEY}; +use aws_sdk_s3 as s3; +use std::fmt; +use std::path::Path; +use std::sync::Arc; +use tokio::sync::watch::channel; +use tokio::sync::Semaphore; + +pub async fn get_object_multipart( + client: &s3::Client, + args: &Args, + path: &Path, +) -> Result<(), BoxError> { + let mut part_count = (args.size_bytes / args.part_size_bytes + 1) as i64; + let mut size_of_last_part = (args.size_bytes % args.part_size_bytes) as i64; + if size_of_last_part == 0 { + size_of_last_part = args.part_size_bytes as i64; + part_count -= 1; + } + + let mut ranges = (0..part_count).map(|i| { + if i == part_count - 1 { + let start = i * args.part_size_bytes as i64; + ContentRange::new(start, start + size_of_last_part - 1) + } else { + ContentRange::new( + i * args.part_size_bytes as i64, + (i + 1) * args.part_size_bytes as i64 - 1, + ) + } + }); + let (tx, rx) = channel(ranges.next().unwrap()); + for range in ranges { + tx.send(range)?; + } + + let semaphore = Arc::new(Semaphore::new(args.concurrency)); + let mut tasks = Vec::new(); + for _ in 0..part_count { + let semaphore = semaphore.clone(); + let client = client.clone(); + let bucket = args.bucket.clone(); + let mut rx = rx.clone(); + tasks.push(tokio::spawn(async move { + let _permit = semaphore.acquire().await?; + let range = rx.borrow_and_update().to_string(); + + let part = client + .get_object() + .bucket(bucket) + .key(BENCH_KEY) + .range(range) + .send() + .await?; + + Result::<_, BoxError>::Ok(part.body) + })); + } + for task in tasks { + let mut body = task.await??.into_async_read(); + let mut file = tokio::fs::File::create(path).await?; + tokio::io::copy(&mut body, &mut file).await?; + } + + Ok(()) +} + +#[derive(Debug)] +struct ContentRange { + start: i64, + end: i64, +} + +impl ContentRange { + fn new(start: i64, end: i64) -> Self { + Self { start, end } + } +} + +impl fmt::Display for ContentRange { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "bytes={}-{}", self.start, self.end) + } +} diff --git a/aws/sdk/s3-benchmark/benchmark/src/multipart_put.rs b/aws/sdk/s3-benchmark/benchmark/src/multipart_put.rs new file mode 100644 index 0000000000..4c8213b0c6 --- /dev/null +++ b/aws/sdk/s3-benchmark/benchmark/src/multipart_put.rs @@ -0,0 +1,111 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::{Args, BoxError, BENCH_KEY}; +use aws_sdk_s3 as s3; +use aws_smithy_http::byte_stream::ByteStream; +use aws_smithy_http::byte_stream::Length; +use s3::types::CompletedMultipartUpload; +use s3::types::CompletedPart; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; +use tokio::sync::Semaphore; + +pub async fn put_object_multipart( + client: &s3::Client, + args: &Args, + path: &Path, +) -> Result<(), BoxError> { + let upload_id = client + .create_multipart_upload() + .bucket(&args.bucket) + .key(BENCH_KEY) + .send() + .await? + .upload_id + .expect("missing upload id"); + + let mut part_count = args.size_bytes / args.part_size_bytes + 1; + let mut size_of_last_part = args.size_bytes % args.part_size_bytes; + if size_of_last_part == 0 { + size_of_last_part = args.part_size_bytes; + part_count -= 1; + } + + let semaphore = Arc::new(Semaphore::new(args.concurrency)); + let mut tasks = Vec::new(); + for part in 0..part_count { + let offset = args.part_size_bytes * part; + let length = if part == part_count - 1 { + size_of_last_part + } else { + args.part_size_bytes + }; + tasks.push(tokio::spawn(upload_part( + semaphore.clone(), + client.clone(), + args.bucket.clone(), + upload_id.clone(), + path.to_path_buf(), + offset, + length, + part, + ))); + } + let mut parts = Vec::new(); + for task in tasks { + parts.push(task.await??); + } + + client + .complete_multipart_upload() + .bucket(&args.bucket) + .key(BENCH_KEY) + .upload_id(&upload_id) + .multipart_upload( + CompletedMultipartUpload::builder() + .set_parts(Some(parts)) + .build(), + ) + .send() + .await?; + + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +async fn upload_part( + semaphore: Arc, + client: s3::Client, + bucket: String, + upload_id: String, + path: PathBuf, + offset: u64, + length: u64, + part: u64, +) -> Result { + let _permit = semaphore.acquire().await?; + + let stream = ByteStream::read_from() + .path(path) + .offset(offset) + .length(Length::Exact(length)) + .build() + .await?; + let part_output = client + .upload_part() + .key(BENCH_KEY) + .bucket(bucket) + .upload_id(upload_id) + .body(stream) + .part_number(part as i32 + 1) // S3 takes a 1-based index + .send() + .await?; + Ok(CompletedPart::builder() + .part_number(part as i32 + 1) + .e_tag(part_output.e_tag.expect("must have an e-tag")) + .build()) +} diff --git a/aws/sdk/s3-benchmark/infrastructure/.eslintrc.json b/aws/sdk/s3-benchmark/infrastructure/.eslintrc.json new file mode 100644 index 0000000000..2612e1f904 --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/.eslintrc.json @@ -0,0 +1,16 @@ +{ + "env": { + "browser": false, + "es2021": true + }, + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 13, + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "rules": { + "@typescript-eslint/no-empty-function": "off" + } +} diff --git a/aws/sdk/s3-benchmark/infrastructure/.gitignore b/aws/sdk/s3-benchmark/infrastructure/.gitignore new file mode 100644 index 0000000000..3fae389cc5 --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/.gitignore @@ -0,0 +1,9 @@ +*.js +!jest.config.js +*.d.ts +/node_modules +/build + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/aws/sdk/s3-benchmark/infrastructure/.npmignore b/aws/sdk/s3-benchmark/infrastructure/.npmignore new file mode 100644 index 0000000000..c1d6d45dcf --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/aws/sdk/s3-benchmark/infrastructure/.prettierrc b/aws/sdk/s3-benchmark/infrastructure/.prettierrc new file mode 100644 index 0000000000..6505068233 --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/.prettierrc @@ -0,0 +1,5 @@ +tabWidth: 4 +singleQuote: false +bracketSpacing: true +trailingComma: all +printWidth: 100 diff --git a/aws/sdk/s3-benchmark/infrastructure/assets/init_instance.sh b/aws/sdk/s3-benchmark/infrastructure/assets/init_instance.sh new file mode 100755 index 0000000000..ba2c9ff446 --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/assets/init_instance.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +echo "init_instance.sh starting" +set -eux + +BENCHMARK_ZIP_PATH="${1}" +echo "benchmark zip path: ${BENCHMARK_ZIP_PATH}" + +sudo yum -y install \ + autoconf \ + automake \ + gcc \ + gcc-c++ \ + git \ + make \ + openssl-devel + +# Install Rustup and Rust +curl https://static.rust-lang.org/rustup/archive/1.24.3/x86_64-unknown-linux-gnu/rustup-init --output rustup-init +echo "3dc5ef50861ee18657f9db2eeb7392f9c2a6c95c90ab41e45ab4ca71476b4338 rustup-init" | sha256sum --check +chmod +x rustup-init +./rustup-init -y --no-modify-path --profile minimal --default-toolchain 1.67.1 +rm rustup-init + +# Verify install +source "${HOME}/.cargo/env" +rustc --version +cargo --version + +# Compile the benchmark +unzip -d benchmark "${BENCHMARK_ZIP_PATH}" +cd benchmark +cargo build --release diff --git a/aws/sdk/s3-benchmark/infrastructure/assets/run_benchmark.sh b/aws/sdk/s3-benchmark/infrastructure/assets/run_benchmark.sh new file mode 100755 index 0000000000..56c617d3e4 --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/assets/run_benchmark.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +echo "run_benchmark.sh starting" +set -eux + +BENCHMARK_BUCKET="${1}" +echo "benchmark bucket: ${BENCHMARK_BUCKET}" + +COMMON_ARGS="--concurrency 30 --bucket ${BENCHMARK_BUCKET} --region us-east-1" + +source "${HOME}/.cargo/env" +cd benchmark + +# 1B +for fs in "tmpfs" "disk"; do + BENCH_RESULT_FILE="bench_results_put_object_${fs}_1B.txt" + cargo run --release -- --bench put-object --fs "${fs}" --size-bytes 1 ${COMMON_ARGS} &> "${BENCH_RESULT_FILE}" + aws s3 cp "${BENCH_RESULT_FILE}" "s3://${BENCHMARK_BUCKET}/" + + BENCH_RESULT_FILE="bench_results_get_object_${fs}_1B.txt" + cargo run --release -- --bench get-object --fs "${fs}" --size-bytes 1 ${COMMON_ARGS} &> "${BENCH_RESULT_FILE}" + aws s3 cp "${BENCH_RESULT_FILE}" "s3://${BENCHMARK_BUCKET}/" +done + +# multipart +for fs in "tmpfs" "disk"; do + for size_bytes in "8388607" "8388609" "134217728" "4294967296" "32212254720"; do + BENCH_RESULT_FILE="bench_results_put_object_multipart_${fs}_${size_bytes}B.txt" + cargo run --release -- --bench put-object-multipart --fs "${fs}" --size-bytes "${size_bytes}" --part-size-bytes "8388608" ${COMMON_ARGS} &> "${BENCH_RESULT_FILE}" + aws s3 cp "${BENCH_RESULT_FILE}" "s3://${BENCHMARK_BUCKET}/" + + BENCH_RESULT_FILE="bench_results_get_object_multipart_${fs}_${size_bytes}B.txt" + cargo run --release -- --bench get-object-multipart --fs "${fs}" --size-bytes "${size_bytes}" --part-size-bytes "8388608" ${COMMON_ARGS} &> "${BENCH_RESULT_FILE}" + aws s3 cp "${BENCH_RESULT_FILE}" "s3://${BENCHMARK_BUCKET}/" + done +done + +echo "Benchmark finished. Results have been uploaded to ${BENCHMARK_BUCKET}" diff --git a/aws/sdk/s3-benchmark/infrastructure/bin/infrastructure.ts b/aws/sdk/s3-benchmark/infrastructure/bin/infrastructure.ts new file mode 100644 index 0000000000..6370d93bb6 --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/bin/infrastructure.ts @@ -0,0 +1,14 @@ +#!/usr/bin/env node +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import "source-map-support/register"; +import * as cdk from "aws-cdk-lib"; +import { InfrastructureStack } from "../lib/infrastructure-stack"; + +const app = new cdk.App(); +new InfrastructureStack(app, "InfrastructureStack", { + env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: "us-east-1" }, +}); diff --git a/aws/sdk/s3-benchmark/infrastructure/cdk.context.json b/aws/sdk/s3-benchmark/infrastructure/cdk.context.json new file mode 100644 index 0000000000..f64f1bac1d --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/cdk.context.json @@ -0,0 +1,10 @@ +{ + "availability-zones:account=422563069514:region=us-east-1": [ + "us-east-1a", + "us-east-1b", + "us-east-1c", + "us-east-1d", + "us-east-1e", + "us-east-1f" + ] +} diff --git a/aws/sdk/s3-benchmark/infrastructure/cdk.json b/aws/sdk/s3-benchmark/infrastructure/cdk.json new file mode 100644 index 0000000000..9f5670963c --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/cdk.json @@ -0,0 +1,53 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/infrastructure.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-iam:standardizedServicePrincipals": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true + } +} diff --git a/aws/sdk/s3-benchmark/infrastructure/lib/infrastructure-stack.ts b/aws/sdk/s3-benchmark/infrastructure/lib/infrastructure-stack.ts new file mode 100644 index 0000000000..416111dbbd --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/lib/infrastructure-stack.ts @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import * as cdk from "aws-cdk-lib"; +import { Construct } from "constructs"; +import * as ec2 from "aws-cdk-lib/aws-ec2"; +import * as s3 from "aws-cdk-lib/aws-s3"; +import * as iam from "aws-cdk-lib/aws-iam"; +import * as assets from "aws-cdk-lib/aws-s3-assets"; +import * as path from "path"; + +export class InfrastructureStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const assetInitInstance = new assets.Asset(this, "assetInitInstance", { + path: path.join(__dirname, "../assets/init_instance.sh"), + }); + const assetRunBenchmark = new assets.Asset(this, "assetRunBenchmark", { + path: path.join(__dirname, "../assets/run_benchmark.sh"), + }); + const assetBenchmark = new assets.Asset(this, "assetBenchmark", { + path: path.join(__dirname, "../../benchmark"), + }); + const assetBucket = s3.Bucket.fromBucketName( + this, + "assetBucket", + assetInitInstance.s3BucketName, + ); + + const benchmarkBucket = new s3.Bucket(this, "benchmarkBucket", { + blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, + encryption: s3.BucketEncryption.S3_MANAGED, + enforceSSL: true, + versioned: false, + removalPolicy: cdk.RemovalPolicy.DESTROY, + }); + + const instanceUserData = ec2.UserData.forLinux(); + const initInstancePath = instanceUserData.addS3DownloadCommand({ + bucket: assetBucket, + bucketKey: assetInitInstance.s3ObjectKey, + }); + const runBenchmarkPath = instanceUserData.addS3DownloadCommand({ + bucket: assetBucket, + bucketKey: assetRunBenchmark.s3ObjectKey, + }); + const benchmarkPath = instanceUserData.addS3DownloadCommand({ + bucket: assetBucket, + bucketKey: assetBenchmark.s3ObjectKey, + }); + instanceUserData.addExecuteFileCommand({ + filePath: initInstancePath, + arguments: `${benchmarkPath}`, + }); + instanceUserData.addExecuteFileCommand({ + filePath: runBenchmarkPath, + arguments: `${benchmarkBucket.bucketName}`, + }); + + const vpc = new ec2.Vpc(this, "VPC", {}); + const securityGroup = new ec2.SecurityGroup(this, "securityGroup", { + vpc, + description: "Allow outbound and SSH inbound", + allowAllOutbound: true, + }); + securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(22), "SSH"); + + const executionRole = new iam.Role(this, "executionRole", { + assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"), + }); + assetBucket.grantRead(executionRole); + benchmarkBucket.grantReadWrite(executionRole); + + new ec2.Instance(this, `S3Benchmark_${this.stackName}`, { + instanceType: ec2.InstanceType.of(ec2.InstanceClass.C5N, ec2.InstanceSize.XLARGE18), + vpc, + machineImage: ec2.MachineImage.latestAmazonLinux2023(), + userData: instanceUserData, + role: executionRole, + keyName: "S3BenchmarkKeyPair", + securityGroup, + vpcSubnets: { subnets: vpc.publicSubnets }, + requireImdsv2: true, + }); + } +} diff --git a/aws/sdk/s3-benchmark/infrastructure/package-lock.json b/aws/sdk/s3-benchmark/infrastructure/package-lock.json new file mode 100644 index 0000000000..4e749c56b5 --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/package-lock.json @@ -0,0 +1,9757 @@ +{ + "name": "infrastructure", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "infrastructure", + "version": "1.0.0", + "license": "Apache-2.0", + "devDependencies": { + "@types/node": "20.1.7", + "@typescript-eslint/eslint-plugin": "^5.59.9", + "@typescript-eslint/parser": "^5.59.9", + "aws-cdk": "^2.82.0", + "aws-cdk-lib": "^2.82.0", + "constructs": "^10.0.0", + "eslint": "^8.42.0", + "eslint-config-prettier": "^8.8.0", + "prettier": "^2.8.8", + "source-map-support": "^0.5.21", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "^5.1.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@aws-cdk/asset-awscli-v1": { + "version": "2.2.186", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.186.tgz", + "integrity": "sha512-2wSuOWQlrWc0AFuPCzXYn2Y8oK2vTfpNrVa8dxBxfswbwUrXMAirhpsP1f1J/4KEhA/4Hs4l27dKiC/IcDrvIQ==", + "dev": true + }, + "node_modules/@aws-cdk/asset-kubectl-v20": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz", + "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==", + "dev": true + }, + "node_modules/@aws-cdk/asset-node-proxy-agent-v5": { + "version": "2.0.155", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.155.tgz", + "integrity": "sha512-Q+Ny25hUPINlBbS6lmbUr4m6Tr6ToEJBla7sXA3FO3JUD0Z69ddcgbhuEBF8Rh1a2xmPONm89eX77kwK2fb4vQ==", + "dev": true + }, + "node_modules/@babel/code-frame": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.3.tgz", + "integrity": "sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.1.tgz", + "integrity": "sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA==", + "dev": true, + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.22.0", + "@babel/helper-compilation-targets": "^7.22.1", + "@babel/helper-module-transforms": "^7.22.1", + "@babel/helpers": "^7.22.0", + "@babel/parser": "^7.22.0", + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "peer": true + }, + "node_modules/@babel/generator": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz", + "integrity": "sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.22.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz", + "integrity": "sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/compat-data": "^7.22.0", + "@babel/helper-validator-option": "^7.21.0", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz", + "integrity": "sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.21.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz", + "integrity": "sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-module-imports": "^7.21.4", + "@babel/helper-simple-access": "^7.21.5", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", + "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", + "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", + "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.3.tgz", + "integrity": "sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz", + "integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==", + "dev": true, + "peer": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.21.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz", + "integrity": "sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.21.4", + "@babel/parser": "^7.21.9", + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.4.tgz", + "integrity": "sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.22.3", + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.22.4", + "@babel/types": "^7.22.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz", + "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-string-parser": "^7.21.5", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "peer": true + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "peer": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", + "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", + "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/reporters": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.5.0", + "jest-config": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-resolve-dependencies": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "jest-watcher": "^29.5.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", + "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", + "dev": true, + "peer": true, + "dependencies": { + "expect": "^29.5.0", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, + "peer": true, + "dependencies": { + "jest-get-type": "^29.4.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", + "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", + "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/types": "^29.5.0", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", + "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", + "dev": true, + "peer": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", + "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", + "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", + "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", + "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.5.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true, + "peer": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "peer": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz", + "integrity": "sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg==", + "dev": true, + "peer": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.1.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.7.tgz", + "integrity": "sha512-WCuw/o4GSwDGMoonES8rcvwsig77dGCMbZDrZr2x4ZZiNW4P/gcoZXe/0twgtobcTkmg9TuKflxYL/DuwDyJzg==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "peer": true + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true, + "peer": true + }, + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz", + "integrity": "sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/type-utils": "5.59.9", + "@typescript-eslint/utils": "5.59.9", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz", + "integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/typescript-estree": "5.59.9", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz", + "integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/visitor-keys": "5.59.9" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz", + "integrity": "sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.59.9", + "@typescript-eslint/utils": "5.59.9", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz", + "integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz", + "integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/visitor-keys": "5.59.9", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.9.tgz", + "integrity": "sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/typescript-estree": "5.59.9", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz", + "integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.9", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "peer": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "peer": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk": { + "version": "2.82.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.82.0.tgz", + "integrity": "sha512-4uAhKN8HMdxxM10Th8aMQJLSINO6evYV9UKTPL0hbVQ6dh6+i5LbSejcvDRw0HfBoP6qV1LNV8P8XGLYIC3tyQ==", + "dev": true, + "bin": { + "cdk": "bin/cdk" + }, + "engines": { + "node": ">= 14.15.0" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/aws-cdk-lib": { + "version": "2.82.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.82.0.tgz", + "integrity": "sha512-icLhHvoxxo5mu9z8oplSHF+A7scbRiXYoRp2hyFkYSCoY9H+eBeIVXKA2S5YPpJfJO4SeORbCQnsyXBbz31XXw==", + "bundleDependencies": [ + "@balena/dockerignore", + "case", + "fs-extra", + "ignore", + "jsonschema", + "minimatch", + "punycode", + "semver", + "table", + "yaml" + ], + "dev": true, + "dependencies": { + "@aws-cdk/asset-awscli-v1": "^2.2.177", + "@aws-cdk/asset-kubectl-v20": "^2.1.1", + "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.1.1", + "ignore": "^5.2.4", + "jsonschema": "^1.4.1", + "minimatch": "^3.1.2", + "punycode": "^2.3.0", + "semver": "^7.5.1", + "table": "^6.8.1", + "yaml": "1.10.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "constructs": "^10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/astral-regex": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/aws-cdk-lib/node_modules/case": { + "version": "1.6.3", + "dev": true, + "inBundle": true, + "license": "(MIT OR GPL-3.0-or-later)", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fs-extra": { + "version": "11.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/aws-cdk-lib/node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/ignore": { + "version": "5.2.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/aws-cdk-lib/node_modules/jsonschema": { + "version": "1.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/aws-cdk-lib/node_modules/punycode": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/semver": { + "version": "7.5.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/table": { + "version": "6.8.1", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/aws-cdk-lib/node_modules/yaml": { + "version": "1.10.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", + "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/transform": "^29.5.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.5.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", + "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", + "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "dev": true, + "peer": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.5.0", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz", + "integrity": "sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001489", + "electron-to-chromium": "^1.4.411", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "peer": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001495", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz", + "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true, + "peer": true + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "peer": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true, + "peer": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/constructs": { + "version": "10.2.44", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.2.44.tgz", + "integrity": "sha512-7v4KnradGM1JeOuuhey4m9t6pLkwc0XOlgO1TrjB8HLRbfxDHeJ10Tt6IfxihgkrDjYN1SOMmpeVFzLLNjGRQw==", + "dev": true, + "engines": { + "node": ">= 16.14.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "peer": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true, + "peer": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.422", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.422.tgz", + "integrity": "sha512-OQMid0IRbJv27BhlPiBK8CfGzjeq4ZCBSmpwNi1abyS8w17/BajOUu7hBI49ptDTBCz9NRFbORhWvt41dF7dwg==", + "dev": true, + "peer": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "peer": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "peer": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/expect-utils": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "peer": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "peer": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true, + "peer": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "peer": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "peer": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "peer": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "peer": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "peer": true + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "peer": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "peer": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "peer": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "peer": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/types": "^29.5.0", + "import-local": "^3.0.2", + "jest-cli": "^29.5.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", + "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "dev": true, + "peer": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", + "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.5.0", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.5.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", + "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", + "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.5.0", + "@jest/types": "^29.5.0", + "babel-jest": "^29.5.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.5.0", + "jest-environment-node": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", + "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "dev": true, + "peer": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", + "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "jest-util": "^29.5.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", + "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", + "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", + "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", + "dev": true, + "peer": true, + "dependencies": { + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.5.0", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", + "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", + "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "dev": true, + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", + "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", + "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", + "dev": true, + "peer": true, + "dependencies": { + "jest-regex-util": "^29.4.3", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", + "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/environment": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.4.3", + "jest-environment-node": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-leak-detector": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-resolve": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-util": "^29.5.0", + "jest-watcher": "^29.5.0", + "jest-worker": "^29.5.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", + "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/globals": "^29.5.0", + "@jest/source-map": "^29.4.3", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", + "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.5.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "peer": true + }, + "node_modules/jest-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", + "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.5.0", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "leven": "^3.1.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", + "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.5.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.5.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "peer": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "peer": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "peer": true + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "peer": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "peer": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "peer": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "peer": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "peer": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "peer": true + }, + "node_modules/node-releases": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", + "dev": true, + "peer": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "peer": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "peer": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "peer": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "peer": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "peer": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true, + "peer": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "peer": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "peer": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", + "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "peer": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true, + "peer": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "peer": true, + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "peer": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "peer": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "peer": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "peer": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "peer": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "peer": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "peer": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "peer": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-jest": { + "version": "29.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz", + "integrity": "sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "peer": true + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "peer": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "peer": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "peer": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@aws-cdk/asset-awscli-v1": { + "version": "2.2.186", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.186.tgz", + "integrity": "sha512-2wSuOWQlrWc0AFuPCzXYn2Y8oK2vTfpNrVa8dxBxfswbwUrXMAirhpsP1f1J/4KEhA/4Hs4l27dKiC/IcDrvIQ==", + "dev": true + }, + "@aws-cdk/asset-kubectl-v20": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz", + "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==", + "dev": true + }, + "@aws-cdk/asset-node-proxy-agent-v5": { + "version": "2.0.155", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.155.tgz", + "integrity": "sha512-Q+Ny25hUPINlBbS6lmbUr4m6Tr6ToEJBla7sXA3FO3JUD0Z69ddcgbhuEBF8Rh1a2xmPONm89eX77kwK2fb4vQ==", + "dev": true + }, + "@babel/code-frame": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "dev": true, + "peer": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.3.tgz", + "integrity": "sha512-aNtko9OPOwVESUFp3MZfD8Uzxl7JzSeJpd7npIoxCasU37PFbAQRpKglkaKwlHOyeJdrREpo8TW8ldrkYWwvIQ==", + "dev": true, + "peer": true + }, + "@babel/core": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.1.tgz", + "integrity": "sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA==", + "dev": true, + "peer": true, + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.22.0", + "@babel/helper-compilation-targets": "^7.22.1", + "@babel/helper-module-transforms": "^7.22.1", + "@babel/helpers": "^7.22.0", + "@babel/parser": "^7.22.0", + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "peer": true + } + } + }, + "@babel/generator": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.3.tgz", + "integrity": "sha512-C17MW4wlk//ES/CJDL51kPNwl+qiBQyN7b9SKyVp11BLGFeSPoVaHrv+MNt8jwQFhQWowW88z1eeBx3pFz9v8A==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.22.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.1.tgz", + "integrity": "sha512-Rqx13UM3yVB5q0D/KwQ8+SPfX/+Rnsy1Lw1k/UwOC4KC6qrzIQoY3lYnBu5EHKBlEHHcj0M0W8ltPSkD8rqfsQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/compat-data": "^7.22.0", + "@babel/helper-validator-option": "^7.21.0", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.1.tgz", + "integrity": "sha512-Z2tgopurB/kTbidvzeBrc2To3PUP/9i5MUe+fU6QJCQDyPwSH2oRapkLw3KGECDYSjhQZCNxEvNvZlLw8JjGwA==", + "dev": true, + "peer": true + }, + "@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.21.4" + } + }, + "@babel/helper-module-transforms": { + "version": "7.22.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.1.tgz", + "integrity": "sha512-dxAe9E7ySDGbQdCVOY/4+UcD8M9ZFqZcZhSPsPacvCG4M+9lwtDDQfI2EoaSvmf7W/8yCBkGU0m7Pvt1ru3UZw==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-module-imports": "^7.21.4", + "@babel/helper-simple-access": "^7.21.5", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", + "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", + "dev": true, + "peer": true + }, + "@babel/helper-simple-access": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", + "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.21.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", + "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==", + "dev": true, + "peer": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "peer": true + }, + "@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "dev": true, + "peer": true + }, + "@babel/helpers": { + "version": "7.22.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.3.tgz", + "integrity": "sha512-jBJ7jWblbgr7r6wYZHMdIqKc73ycaTcCaWRq4/2LpuPHcx7xMlZvpGQkOYc9HeSjn6rcx15CPlgVcBtZ4WZJ2w==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.21.9", + "@babel/traverse": "^7.22.1", + "@babel/types": "^7.22.3" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "peer": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "peer": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "peer": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "peer": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "peer": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "peer": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz", + "integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==", + "dev": true, + "peer": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + } + }, + "@babel/template": { + "version": "7.21.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz", + "integrity": "sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.21.4", + "@babel/parser": "^7.21.9", + "@babel/types": "^7.21.5" + } + }, + "@babel/traverse": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.4.tgz", + "integrity": "sha512-Tn1pDsjIcI+JcLKq1AVlZEr4226gpuAQTsLMorsYg9tuS/kG7nuwwJ4AB8jfQuEgb/COBwR/DqJxmoiYFu5/rQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.22.3", + "@babel/helper-environment-visitor": "^7.22.1", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.22.4", + "@babel/types": "^7.22.4", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.22.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz", + "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-string-parser": "^7.21.5", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "peer": true + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@eslint/js": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "peer": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "peer": true + }, + "@jest/console": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", + "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", + "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/console": "^29.5.0", + "@jest/reporters": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.5.0", + "jest-config": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-resolve-dependencies": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "jest-watcher": "^29.5.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", + "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0" + } + }, + "@jest/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", + "dev": true, + "peer": true, + "requires": { + "expect": "^29.5.0", + "jest-snapshot": "^29.5.0" + } + }, + "@jest/expect-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, + "peer": true, + "requires": { + "jest-get-type": "^29.4.3" + } + }, + "@jest/fake-timers": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", + "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + } + }, + "@jest/globals": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", + "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/types": "^29.5.0", + "jest-mock": "^29.5.0" + } + }, + "@jest/reporters": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", + "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", + "dev": true, + "peer": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + } + }, + "@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.25.16" + } + }, + "@jest/source-map": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", + "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", + "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/console": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", + "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/test-result": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", + "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.5.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + } + }, + "@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "requires": { + "@jest/schemas": "^29.4.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "peer": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + }, + "dependencies": { + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true, + "peer": true + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "peer": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz", + "integrity": "sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg==", + "dev": true, + "peer": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dev": true, + "peer": true, + "requires": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "peer": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.20.7" + } + }, + "@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "peer": true, + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "@types/node": { + "version": "20.1.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.7.tgz", + "integrity": "sha512-WCuw/o4GSwDGMoonES8rcvwsig77dGCMbZDrZr2x4ZZiNW4P/gcoZXe/0twgtobcTkmg9TuKflxYL/DuwDyJzg==", + "dev": true + }, + "@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "peer": true + }, + "@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true, + "peer": true + }, + "@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz", + "integrity": "sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/type-utils": "5.59.9", + "@typescript-eslint/utils": "5.59.9", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/parser": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz", + "integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/typescript-estree": "5.59.9", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz", + "integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/visitor-keys": "5.59.9" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz", + "integrity": "sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.59.9", + "@typescript-eslint/utils": "5.59.9", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz", + "integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz", + "integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/visitor-keys": "5.59.9", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/utils": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.9.tgz", + "integrity": "sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.9", + "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/typescript-estree": "5.59.9", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.59.9", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz", + "integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.9", + "eslint-visitor-keys": "^3.3.0" + } + }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "peer": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "peer": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "peer": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "aws-cdk": { + "version": "2.82.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.82.0.tgz", + "integrity": "sha512-4uAhKN8HMdxxM10Th8aMQJLSINO6evYV9UKTPL0hbVQ6dh6+i5LbSejcvDRw0HfBoP6qV1LNV8P8XGLYIC3tyQ==", + "dev": true, + "requires": { + "fsevents": "2.3.2" + } + }, + "aws-cdk-lib": { + "version": "2.82.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.82.0.tgz", + "integrity": "sha512-icLhHvoxxo5mu9z8oplSHF+A7scbRiXYoRp2hyFkYSCoY9H+eBeIVXKA2S5YPpJfJO4SeORbCQnsyXBbz31XXw==", + "dev": true, + "requires": { + "@aws-cdk/asset-awscli-v1": "^2.2.177", + "@aws-cdk/asset-kubectl-v20": "^2.1.1", + "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", + "@balena/dockerignore": "^1.0.2", + "case": "1.6.3", + "fs-extra": "^11.1.1", + "ignore": "^5.2.4", + "jsonschema": "^1.4.1", + "minimatch": "^3.1.2", + "punycode": "^2.3.0", + "semver": "^7.5.1", + "table": "^6.8.1", + "yaml": "1.10.2" + }, + "dependencies": { + "@balena/dockerignore": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "ajv": { + "version": "8.12.0", + "bundled": true, + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "case": { + "version": "1.6.3", + "bundled": true, + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "bundled": true, + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true, + "dev": true + }, + "fs-extra": { + "version": "11.1.1", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "bundled": true, + "dev": true + }, + "ignore": { + "version": "5.2.4", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "bundled": true, + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonschema": { + "version": "1.4.1", + "bundled": true, + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "bundled": true, + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "punycode": { + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "bundled": true, + "dev": true + }, + "semver": { + "version": "7.5.1", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "slice-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "table": { + "version": "6.8.1", + "bundled": true, + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + } + }, + "universalify": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "yallist": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "yaml": { + "version": "1.10.2", + "bundled": true, + "dev": true + } + } + }, + "babel-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", + "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", + "dev": true, + "peer": true, + "requires": { + "@jest/transform": "^29.5.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.5.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", + "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", + "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "dev": true, + "peer": true, + "requires": { + "babel-plugin-jest-hoist": "^29.5.0", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.7.tgz", + "integrity": "sha512-BauCXrQ7I2ftSqd2mvKHGo85XR0u7Ru3C/Hxsy/0TkfCtjrmAbPdzLGasmoiBxplpDXlPvdjX9u7srIMfgasNA==", + "dev": true, + "peer": true, + "requires": { + "caniuse-lite": "^1.0.30001489", + "electron-to-chromium": "^1.4.411", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "peer": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "peer": true + }, + "caniuse-lite": { + "version": "1.0.30001495", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001495.tgz", + "integrity": "sha512-F6x5IEuigtUfU5ZMQK2jsy5JqUUlEFRVZq8bO2a+ysq5K7jD6PPc9YXZj78xDNS3uNchesp1Jw47YXEqr+Viyg==", + "dev": true, + "peer": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "peer": true + }, + "ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true, + "peer": true + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "peer": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "peer": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true, + "peer": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "constructs": { + "version": "10.2.44", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.2.44.tgz", + "integrity": "sha512-7v4KnradGM1JeOuuhey4m9t6pLkwc0XOlgO1TrjB8HLRbfxDHeJ10Tt6IfxihgkrDjYN1SOMmpeVFzLLNjGRQw==", + "dev": true + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "peer": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true, + "peer": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "peer": true + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "peer": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "peer": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "electron-to-chromium": { + "version": "1.4.422", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.422.tgz", + "integrity": "sha512-OQMid0IRbJv27BhlPiBK8CfGzjeq4ZCBSmpwNi1abyS8w17/BajOUu7hBI49ptDTBCz9NRFbORhWvt41dF7dwg==", + "dev": true, + "peer": true + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "peer": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "peer": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "peer": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "peer": true + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "peer": true + }, + "eslint": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "dev": true, + "requires": {} + }, + "eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true + }, + "espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "peer": true + }, + "esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "peer": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "peer": true + }, + "expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, + "peer": true, + "requires": { + "@jest/expect-utils": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "peer": true, + "requires": { + "bser": "2.1.1" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "peer": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true, + "peer": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "peer": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "peer": true + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "peer": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "peer": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "peer": true + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "peer": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "peer": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "peer": true + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "peer": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "peer": true + }, + "is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "peer": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "peer": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "peer": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "peer": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "peer": true + }, + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "peer": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "peer": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "peer": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/core": "^29.5.0", + "@jest/types": "^29.5.0", + "import-local": "^3.0.2", + "jest-cli": "^29.5.0" + } + }, + "jest-changed-files": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", + "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "dev": true, + "peer": true, + "requires": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + } + }, + "jest-circus": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", + "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.5.0", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.5.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-cli": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", + "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", + "dev": true, + "peer": true, + "requires": { + "@jest/core": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + } + }, + "jest-config": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", + "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.5.0", + "@jest/types": "^29.5.0", + "babel-jest": "^29.5.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.5.0", + "jest-environment-node": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + } + }, + "jest-docblock": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", + "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "dev": true, + "peer": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", + "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "jest-util": "^29.5.0", + "pretty-format": "^29.5.0" + } + }, + "jest-environment-node": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", + "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + } + }, + "jest-get-type": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, + "peer": true + }, + "jest-haste-map": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", + "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.4.3", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", + "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", + "dev": true, + "peer": true, + "requires": { + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + } + }, + "jest-matcher-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + } + }, + "jest-message-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.5.0", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", + "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-util": "^29.5.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "peer": true, + "requires": {} + }, + "jest-regex-util": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", + "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "dev": true, + "peer": true + }, + "jest-resolve": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", + "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", + "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", + "dev": true, + "peer": true, + "requires": { + "jest-regex-util": "^29.4.3", + "jest-snapshot": "^29.5.0" + } + }, + "jest-runner": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", + "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/console": "^29.5.0", + "@jest/environment": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.4.3", + "jest-environment-node": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-leak-detector": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-resolve": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-util": "^29.5.0", + "jest-watcher": "^29.5.0", + "jest-worker": "^29.5.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "dependencies": { + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "peer": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, + "jest-runtime": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", + "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/globals": "^29.5.0", + "@jest/source-map": "^29.4.3", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", + "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.5.0", + "semver": "^7.3.5" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "peer": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "peer": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "peer": true + } + } + }, + "jest-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "requires": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", + "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.5.0", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "leven": "^3.1.0", + "pretty-format": "^29.5.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "peer": true + } + } + }, + "jest-watcher": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", + "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "dev": true, + "peer": true, + "requires": { + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.5.0", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "dev": true, + "peer": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.5.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "peer": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "peer": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "peer": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "peer": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "peer": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "peer": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "peer": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "peer": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "peer": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "peer": true, + "requires": { + "semver": "^6.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "peer": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "peer": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "peer": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "peer": true + }, + "node-releases": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", + "dev": true, + "peer": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "peer": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "peer": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "peer": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "peer": true, + "requires": { + "p-limit": "^2.2.0" + }, + "dependencies": { + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "peer": true, + "requires": { + "p-try": "^2.0.0" + } + } + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "peer": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "peer": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true, + "peer": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "peer": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "peer": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true + }, + "pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "peer": true, + "requires": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "peer": true + } + } + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "peer": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true + }, + "pure-rand": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", + "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "dev": true, + "peer": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true, + "peer": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "peer": true + }, + "resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "peer": true, + "requires": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "peer": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "peer": true + }, + "resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "peer": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "peer": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "peer": true + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "peer": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "peer": true + }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "peer": true, + "requires": { + "escape-string-regexp": "^2.0.0" + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "peer": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "peer": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "peer": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "peer": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "peer": true + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "peer": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "peer": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "peer": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-jest": { + "version": "29.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz", + "integrity": "sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "peer": true + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "peer": true + }, + "typescript": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", + "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "peer": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "dependencies": { + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "peer": true + } + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "peer": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "peer": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "peer": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "peer": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "peer": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "peer": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/aws/sdk/s3-benchmark/infrastructure/package.json b/aws/sdk/s3-benchmark/infrastructure/package.json new file mode 100644 index 0000000000..46e0b34ee2 --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/package.json @@ -0,0 +1,39 @@ +{ + "name": "infrastructure", + "version": "1.0.0", + "description": "CDK infrastructure for the S3 benchmark", + "main": "index.js", + "scripts": { + "format": "prettier --write '**/*.ts'", + "lint": "eslint --ext .ts lib", + "build": "tsc", + "watch": "tsc -w", + "cdk": "cdk" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/awslabs/smithy-rs.git" + }, + "author": "AWS Rust SDK Team", + "license": "Apache-2.0", + "bugs": { + "url": "https://github.com/awslabs/smithy-rs/issues" + }, + "homepage": "https://github.com/awslabs/smithy-rs#readme", + "publish": false, + "devDependencies": { + "@types/node": "20.1.7", + "@typescript-eslint/eslint-plugin": "^5.59.9", + "@typescript-eslint/parser": "^5.59.9", + "aws-cdk": "^2.82.0", + "aws-cdk-lib": "^2.82.0", + "constructs": "^10.0.0", + "eslint": "^8.42.0", + "eslint-config-prettier": "^8.8.0", + "prettier": "^2.8.8", + "source-map-support": "^0.5.21", + "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", + "typescript": "^5.1.3" + } +} diff --git a/aws/sdk/s3-benchmark/infrastructure/tsconfig.json b/aws/sdk/s3-benchmark/infrastructure/tsconfig.json new file mode 100644 index 0000000000..7ec537b68e --- /dev/null +++ b/aws/sdk/s3-benchmark/infrastructure/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020", + "dom" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ], + "outDir": "./build" + }, + "exclude": [ + "node_modules", + "cdk.out", + "build" + ] +} From f97aa12d8e12e372901994d716d6642abed1a713 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 21 Jun 2023 06:16:05 -0700 Subject: [PATCH 171/253] Fix clippy lints on the orchestrator generated code (#2798) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../smithy/rustsdk/EndpointBuiltInsDecorator.kt | 2 +- .../customizations/ResiliencyConfigCustomization.kt | 13 ++++++++++--- .../smithy/customize/RequiredCustomizations.kt | 2 +- .../EndpointParamsInterceptorGenerator.kt | 7 +------ .../client/smithy/generators/OperationGenerator.kt | 2 +- .../generators/ServiceRuntimePluginGenerator.kt | 4 ++-- tools/ci-scripts/check-aws-sdk-orchestrator-impl | 12 +++++++++++- 7 files changed, 27 insertions(+), 15 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt index 9eec481e0c..02922234e3 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt @@ -143,7 +143,7 @@ fun decoratorForBuiltIn( } else { rust("$configRef.$name") } - if (parameter.type == ParameterType.STRING) { + if (codegenContext.smithyRuntimeMode.defaultToMiddleware && parameter.type == ParameterType.STRING) { rust(".clone()") } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 44c91aa1de..dbd696248e 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -302,10 +302,12 @@ class ResiliencyReExportCustomization(private val runtimeConfig: RuntimeConfig) } } -class ResiliencyServiceRuntimePluginCustomization : ServiceRuntimePluginCustomization() { +class ResiliencyServiceRuntimePluginCustomization( + private val codegenContext: ClientCodegenContext, +) : ServiceRuntimePluginCustomization() { override fun section(section: ServiceRuntimePluginSection): Writable = writable { if (section is ServiceRuntimePluginSection.AdditionalConfig) { - rust( + rustTemplate( """ if let Some(sleep_impl) = self.handle.conf.sleep_impl() { ${section.newLayerName}.put(sleep_impl); @@ -313,8 +315,13 @@ class ResiliencyServiceRuntimePluginCustomization : ServiceRuntimePluginCustomiz if let Some(timeout_config) = self.handle.conf.timeout_config() { ${section.newLayerName}.put(timeout_config.clone()); } - ${section.newLayerName}.put(self.handle.conf.time_source().clone()); + ${section.newLayerName}.put(self.handle.conf.time_source()#{maybe_clone}); """, + "maybe_clone" to writable { + if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + rust(".clone()") + } + }, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index ea24e78b3d..6c7882cba6 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -97,5 +97,5 @@ class RequiredCustomizations : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations + listOf(ResiliencyServiceRuntimePluginCustomization()) + baseCustomizations + listOf(ResiliencyServiceRuntimePluginCustomization(codegenContext)) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index ff15b8f659..1371203522 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -9,7 +9,6 @@ import software.amazon.smithy.model.node.BooleanNode import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.node.StringNode import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.ShapeType import software.amazon.smithy.model.traits.EndpointTrait import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters import software.amazon.smithy.rulesengine.traits.ContextIndex @@ -118,11 +117,7 @@ class EndpointParamsInterceptorGenerator( idx.getClientContextParams(codegenContext.serviceShape).orNull()?.parameters?.forEach { (name, param) -> val paramName = EndpointParamsGenerator.memberName(name) val setterName = EndpointParamsGenerator.setterName(name) - if (param.type == ShapeType.BOOLEAN) { - rust(".$setterName(_config.$paramName())") - } else { - rust(".$setterName(_config.$paramName().clone())") - } + rust(".$setterName(_config.$paramName())") } idx.getStaticContextParams(operationShape).orNull()?.parameters?.forEach { (name, param) -> diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 442d95ff0f..6f9eecc412 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -157,7 +157,7 @@ open class OperationGenerator( .unwrap() }) }; - let context = Self::orchestrate_with_stop_point(&runtime_plugins, input, #{StopPoint}::None) + let context = Self::orchestrate_with_stop_point(runtime_plugins, input, #{StopPoint}::None) .await .map_err(map_err)?; let output = context.finalize().map_err(map_err)?; diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index aeac4f1c58..d5eafa4f1f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -149,8 +149,8 @@ class ServiceRuntimePluginGenerator( // list of classifiers defined here, rather than replacing them. let sleep_impl = self.handle.conf.sleep_impl(); - let timeout_config = self.handle.conf.timeout_config().cloned().unwrap_or_else(|| #{TimeoutConfig}::disabled()); - let retry_config = self.handle.conf.retry_config().cloned().unwrap_or_else(|| #{RetryConfig}::disabled()); + let timeout_config = self.handle.conf.timeout_config().cloned().unwrap_or_else(#{TimeoutConfig}::disabled); + let retry_config = self.handle.conf.retry_config().cloned().unwrap_or_else(#{RetryConfig}::disabled); cfg.set_retry_strategy(#{StandardRetryStrategy}::new(&retry_config)); diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index 226cc8a386..45c0630a44 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -12,6 +12,12 @@ C_RESET='\033[0m' set -eu cd smithy-rs +# TODO(enableNewSmithyRuntimeLaunch): Move these into `services_that_compile` as more progress is made +services_that_dont_compile=(\ + "timestreamquery", + "timestreamwrite", +) + # TODO(enableNewSmithyRuntimeLaunch): Move these into `services_that_pass_tests` as more progress is made services_that_compile=(\ "aws-config"\ @@ -41,8 +47,10 @@ services_that_pass_tests=(\ cd aws/sdk/build/aws-sdk/sdk for service in "${services_that_compile[@]}"; do pushd "${service}" - echo -e "${C_YELLOW}# Running 'cargo check --all-features' on '${service}'${C_RESET}" + echo -e "${C_YELLOW}# Running 'cargo check --all-features --all-targets' on '${service}'${C_RESET}" RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo check --all-features --all-targets + echo -e "${C_YELLOW}# Running 'cargo clippy --all-features' on '${service}'${C_RESET}" + RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo clippy --all-features popd done @@ -50,6 +58,8 @@ for service in "${services_that_pass_tests[@]}"; do pushd "${service}" echo -e "${C_YELLOW}# Running 'cargo test --all-features' on '${service}'${C_RESET}" RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo test --all-features --all-targets --no-fail-fast + echo -e "${C_YELLOW}# Running 'cargo clippy --all-features' on '${service}'${C_RESET}" + RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo clippy --all-features popd done From 22018ce107aeca55903d6da2878695344c74d2fb Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 21 Jun 2023 07:48:00 -0700 Subject: [PATCH 172/253] Fix the Route53 tests in the orchestrator impl (#2796) This PR fixes the Route53 tests for the client orchestrator implementation. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-inlineable/src/lib.rs | 3 + .../src/route53_resource_id_preprocessor.rs | 59 ++++++++++++- ...e53_resource_id_preprocessor_middleware.rs | 84 +++++++++++++++++++ .../customize/route53/Route53Decorator.kt | 58 +++++++++---- .../check-aws-sdk-orchestrator-impl | 4 +- 5 files changed, 190 insertions(+), 18 deletions(-) create mode 100644 aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor_middleware.rs diff --git a/aws/rust-runtime/aws-inlineable/src/lib.rs b/aws/rust-runtime/aws-inlineable/src/lib.rs index 8f2476ace8..8cc771b9f6 100644 --- a/aws/rust-runtime/aws-inlineable/src/lib.rs +++ b/aws/rust-runtime/aws-inlineable/src/lib.rs @@ -46,6 +46,9 @@ pub mod glacier_interceptors; /// Default middleware stack for AWS services pub mod middleware; +/// Strip prefixes from IDs returned by Route53 operations when those IDs are used to construct requests +pub mod route53_resource_id_preprocessor_middleware; + /// Strip prefixes from IDs returned by Route53 operations when those IDs are used to construct requests pub mod route53_resource_id_preprocessor; diff --git a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs index b1827f065a..132b84613b 100644 --- a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs +++ b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs @@ -3,10 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ +#![allow(dead_code)] + +use aws_smithy_runtime_api::client::interceptors::{ + BeforeSerializationInterceptorContextMut, BoxError, Interceptor, +}; +use aws_smithy_types::config_bag::ConfigBag; +use std::fmt; +use std::marker::PhantomData; + // This function is only used to strip prefixes from resource IDs at the time they're passed as // input to a request. Resource IDs returned in responses may or may not include a prefix. /// Strip the resource type prefix from resource ID return -pub fn trim_resource_id(resource_id: &mut Option) { +fn trim_resource_id(resource_id: &mut Option) { const PREFIXES: &[&str] = &[ "/hostedzone/", "hostedzone/", @@ -28,9 +37,55 @@ pub fn trim_resource_id(resource_id: &mut Option) { } } +pub(crate) struct Route53ResourceIdInterceptor +where + G: for<'a> Fn(&'a mut T) -> &'a mut Option, +{ + get_mut_resource_id: G, + _phantom: PhantomData, +} + +impl fmt::Debug for Route53ResourceIdInterceptor +where + G: for<'a> Fn(&'a mut T) -> &'a mut Option, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Route53ResourceIdInterceptor").finish() + } +} + +impl Route53ResourceIdInterceptor +where + G: for<'a> Fn(&'a mut T) -> &'a mut Option, +{ + pub(crate) fn new(get_mut_resource_id: G) -> Self { + Self { + get_mut_resource_id, + _phantom: Default::default(), + } + } +} + +impl Interceptor for Route53ResourceIdInterceptor +where + G: for<'a> Fn(&'a mut T) -> &'a mut Option, + T: fmt::Debug + Send + Sync + 'static, +{ + fn modify_before_serialization( + &self, + context: &mut BeforeSerializationInterceptorContextMut<'_>, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let input: &mut T = context.input_mut().downcast_mut().expect("correct type"); + let field = (self.get_mut_resource_id)(input); + trim_resource_id(field); + Ok(()) + } +} + #[cfg(test)] mod test { - use crate::route53_resource_id_preprocessor::trim_resource_id; + use super::trim_resource_id; #[test] fn does_not_change_regular_zones() { diff --git a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor_middleware.rs b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor_middleware.rs new file mode 100644 index 0000000000..545be270de --- /dev/null +++ b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor_middleware.rs @@ -0,0 +1,84 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// TODO(enableNewSmithyRuntimeCleanup): Delete this module + +// This function is only used to strip prefixes from resource IDs at the time they're passed as +// input to a request. Resource IDs returned in responses may or may not include a prefix. +/// Strip the resource type prefix from resource ID return +pub fn trim_resource_id(resource_id: &mut Option) { + const PREFIXES: &[&str] = &[ + "/hostedzone/", + "hostedzone/", + "/change/", + "change/", + "/delegationset/", + "delegationset/", + ]; + + for prefix in PREFIXES { + if let Some(id) = resource_id + .as_deref() + .unwrap_or_default() + .strip_prefix(prefix) + { + *resource_id = Some(id.to_string()); + return; + } + } +} + +#[cfg(test)] +mod test { + use crate::route53_resource_id_preprocessor_middleware::trim_resource_id; + + #[test] + fn does_not_change_regular_zones() { + struct OperationInput { + resource: Option, + } + + let mut operation = OperationInput { + resource: Some("Z0441723226OZ66S5ZCNZ".to_string()), + }; + trim_resource_id(&mut operation.resource); + assert_eq!( + &operation.resource.unwrap_or_default(), + "Z0441723226OZ66S5ZCNZ" + ); + } + + #[test] + fn sanitizes_prefixed_zone() { + struct OperationInput { + change_id: Option, + } + + let mut operation = OperationInput { + change_id: Some("/change/Z0441723226OZ66S5ZCNZ".to_string()), + }; + trim_resource_id(&mut operation.change_id); + assert_eq!( + &operation.change_id.unwrap_or_default(), + "Z0441723226OZ66S5ZCNZ" + ); + } + + #[test] + fn allow_no_leading_slash() { + struct OperationInput { + hosted_zone: Option, + } + + let mut operation = OperationInput { + hosted_zone: Some("hostedzone/Z0441723226OZ66S5ZCNZ".to_string()), + }; + trim_resource_id(&mut operation.hosted_zone); + assert_eq!( + &operation.hosted_zone.unwrap_or_default(), + "Z0441723226OZ66S5ZCNZ" + ); + } +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt index 4f9d6fbf2d..befc4021b0 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt @@ -10,6 +10,7 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.HttpLabelTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext @@ -45,10 +46,14 @@ class Route53Decorator : ClientCodegenDecorator { operation: OperationShape, baseCustomizations: List, ): List { - val hostedZoneMember = - operation.inputShape(codegenContext.model).members().find { it.hasTrait() } + val inputShape = operation.inputShape(codegenContext.model) + val hostedZoneMember = inputShape.members().find { it.hasTrait() } return if (hostedZoneMember != null) { - baseCustomizations + TrimResourceIdCustomization(codegenContext.symbolProvider.toMemberName(hostedZoneMember)) + baseCustomizations + TrimResourceIdCustomization( + codegenContext, + inputShape, + codegenContext.symbolProvider.toMemberName(hostedZoneMember), + ) } else { baseCustomizations } @@ -59,25 +64,50 @@ class Route53Decorator : ClientCodegenDecorator { } } -class TrimResourceIdCustomization(private val fieldName: String) : OperationCustomization() { +class TrimResourceIdCustomization( + private val codegenContext: ClientCodegenContext, + private val inputShape: StructureShape, + private val fieldName: String, +) : + OperationCustomization() { override fun mutSelf(): Boolean = true override fun consumesSelf(): Boolean = true - private val trimResourceId = - RuntimeType.forInlineDependency( - InlineAwsDependency.forRustFile("route53_resource_id_preprocessor"), - ) - .resolve("trim_resource_id") - - override fun section(section: OperationSection): Writable { - return when (section) { - is OperationSection.MutateInput -> writable { + override fun section(section: OperationSection): Writable = writable { + when (section) { + // TODO(enableNewSmithyRuntimeCleanup): Delete this `MutateInput` section + is OperationSection.MutateInput -> { + val trimResourceId = + RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile("route53_resource_id_preprocessor_middleware"), + ) + .resolve("trim_resource_id") rustTemplate( "#{trim_resource_id}(&mut ${section.input}.$fieldName);", "trim_resource_id" to trimResourceId, ) } - else -> emptySection + + is OperationSection.AdditionalInterceptors -> { + section.registerInterceptor(codegenContext.runtimeConfig, this) { + val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) + val interceptor = + RuntimeType.forInlineDependency( + InlineAwsDependency.forRustFile("route53_resource_id_preprocessor"), + ).resolve("Route53ResourceIdInterceptor") + rustTemplate( + """ + #{Route53ResourceIdInterceptor}::new(|input: &mut #{Input}| { + &mut input.$fieldName + }) + """, + "Input" to codegenContext.symbolProvider.toSymbol(inputShape), + "Route53ResourceIdInterceptor" to interceptor, + "SharedInterceptor" to smithyRuntimeApi.resolve("client::interceptors::SharedInterceptor"), + ) + } + } + else -> {} } } } diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index 45c0630a44..809c069909 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -21,14 +21,13 @@ services_that_dont_compile=(\ # TODO(enableNewSmithyRuntimeLaunch): Move these into `services_that_pass_tests` as more progress is made services_that_compile=(\ "aws-config"\ - "dynamodb"\ - "route53"\ "s3"\ "sts"\ ) services_that_pass_tests=(\ "config"\ + "dynamodb"\ "ec2"\ "ecs"\ "glacier"\ @@ -37,6 +36,7 @@ services_that_pass_tests=(\ "lambda"\ "polly"\ "qldbsession"\ + "route53"\ "s3control"\ "sso"\ "transcribestreaming"\ From 47718f9da37ac6e5c1569d159f716cb96564d4f5 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 21 Jun 2023 07:48:31 -0700 Subject: [PATCH 173/253] Upgrade `aws-actions/configure-aws-credentials` (#2797) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .github/workflows/ci-main.yml | 2 +- .github/workflows/ci-merge-queue.yml | 4 ++-- .github/workflows/ci-pr.yml | 4 ++-- .github/workflows/pull-request-bot.yml | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-main.yml b/.github/workflows/ci-main.yml index 2229148de3..6f361c926d 100644 --- a/.github/workflows/ci-main.yml +++ b/.github/workflows/ci-main.yml @@ -32,7 +32,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Acquire credentials - uses: aws-actions/configure-aws-credentials@v1-node16 + uses: aws-actions/configure-aws-credentials@v2.2.0 with: role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} role-session-name: GitHubActions diff --git a/.github/workflows/ci-merge-queue.yml b/.github/workflows/ci-merge-queue.yml index bedab9beb5..cfad5aab2f 100644 --- a/.github/workflows/ci-merge-queue.yml +++ b/.github/workflows/ci-merge-queue.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Attempt to load a docker login password - uses: aws-actions/configure-aws-credentials@v1-node16 + uses: aws-actions/configure-aws-credentials@v2.2.0 with: role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} role-session-name: GitHubActions @@ -67,7 +67,7 @@ jobs: DOCKER_BUILDKIT: 1 run: ./smithy-rs/.github/scripts/acquire-build-image - name: Acquire credentials - uses: aws-actions/configure-aws-credentials@v1-node16 + uses: aws-actions/configure-aws-credentials@v2.2.0 with: role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} role-session-name: GitHubActions diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index 22f830e8bc..89d8099519 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Attempt to load a docker login password - uses: aws-actions/configure-aws-credentials@v1-node16 + uses: aws-actions/configure-aws-credentials@v2.2.0 with: role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} role-session-name: GitHubActions @@ -69,7 +69,7 @@ jobs: DOCKER_BUILDKIT: 1 run: ./smithy-rs/.github/scripts/acquire-build-image - name: Acquire credentials - uses: aws-actions/configure-aws-credentials@v1-node16 + uses: aws-actions/configure-aws-credentials@v2.2.0 with: role-to-assume: ${{ secrets.SMITHY_RS_PUBLIC_ECR_PUSH_ROLE_ARN }} role-session-name: GitHubActions diff --git a/.github/workflows/pull-request-bot.yml b/.github/workflows/pull-request-bot.yml index c24ac70aae..28a5c8aea6 100644 --- a/.github/workflows/pull-request-bot.yml +++ b/.github/workflows/pull-request-bot.yml @@ -55,7 +55,7 @@ jobs: with: action: generate-codegen-diff action-arguments: ${{ inputs.base_revision }} - - uses: aws-actions/configure-aws-credentials@v1-node16 + - uses: aws-actions/configure-aws-credentials@v2.2.0 name: Acquire credentials for uploading to S3 with: role-to-assume: ${{ secrets.SMITHY_RS_PULL_REQUEST_CDN_ROLE_ARN }} @@ -125,7 +125,7 @@ jobs: ./tools/ci-scripts/generate-doc-preview-index.sh ${{ inputs.base_revision }} echo 'bot-message=A [new doc preview](https://d2luzm2xt3nokh.cloudfront.net/docs/'${{ inputs.head_revision }}'/index.html) is ready to view.' >> "${GITHUB_OUTPUT}" - - uses: aws-actions/configure-aws-credentials@v1-node16 + - uses: aws-actions/configure-aws-credentials@v2.2.0 name: Acquire credentials for uploading to S3 with: role-to-assume: ${{ secrets.SMITHY_RS_PULL_REQUEST_CDN_ROLE_ARN }} From df62f5c6961e100bee555a1e11914b2ad6586df7 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Wed, 21 Jun 2023 15:55:48 -0500 Subject: [PATCH 174/253] Make service config builder contain `CloneableLayer` (#2795) ## Motivation and Context This PR will reduce fields in service config builders to contain only `CloneableLayer` (except for `interceptors` and `identity_resolvers`). ## Description This is the third PR in a series of config refactoring in the orchestrator mode. Just like #2762, this PR reduces fields in service config builders to contain `CloneableLayer`. The code changes follow a straightforward pattern where builders' setters are implemented via a config layer. ## Testing Relies on the existing tests in CI ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito Co-authored-by: Zelda Hessler --- .../rustsdk/AwsFluentClientDecorator.kt | 22 ++- .../amazon/smithy/rustsdk/CredentialCaches.kt | 50 +++++-- .../smithy/rustsdk/CredentialProviders.kt | 55 ++++++-- .../HttpConnectorConfigCustomization.kt | 37 +++-- .../amazon/smithy/rustsdk/RegionDecorator.kt | 58 +++++--- .../smithy/rustsdk/UserAgentDecorator.kt | 47 +++++-- .../rustsdk/CredentialProviderConfigTest.kt | 2 +- .../HttpConnectorConfigDecorator.kt | 38 +++-- .../ResiliencyConfigCustomization.kt | 130 +++++++++++++----- .../customizations/TimeSourceCustomization.kt | 60 +++++--- .../endpoint/EndpointConfigCustomization.kt | 45 ++++-- .../IdempotencyTokenProviderCustomization.kt | 36 +++-- .../config/ServiceConfigGenerator.kt | 70 ++++++---- .../testutil/TestConfigCustomization.kt | 34 +++-- .../config/ServiceConfigGeneratorTest.kt | 25 +++- .../aws-smithy-types/src/config_bag.rs | 3 +- 16 files changed, 510 insertions(+), 202 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index bd619ea746..2a558daf03 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -103,13 +103,25 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { baseGenerator.protocolSupport, baseGenerator.operationShape, renderClientCreation = { params -> + rust("let mut ${params.configBuilderName} = ${params.configBuilderName};") + if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + // TODO(enableNewSmithyRuntimeLaunch): A builder field could not be accessed directly in the orchestrator + // mode when this code change was made. smithy-rs#2792 will enable us to use getters in builders. + // Make this `set_region` conditional by checking if `config_builder.region().is_none()` once the PR + // has been merged to main. + rust("""${params.configBuilderName}.set_region(Some(crate::config::Region::new("us-east-1")));""") + } else { + rust( + """ + // If the test case was missing endpoint parameters, default a region so it doesn't fail + if ${params.configBuilderName}.region.is_none() { + ${params.configBuilderName}.set_region(Some(crate::config::Region::new("us-east-1"))); + } + """, + ) + } rustTemplate( """ - // If the test case was missing endpoint parameters, default a region so it doesn't fail - let mut ${params.configBuilderName} = ${params.configBuilderName}; - if ${params.configBuilderName}.region.is_none() { - ${params.configBuilderName}.set_region(Some(crate::config::Region::new("us-east-1"))); - } let config = ${params.configBuilderName}.http_connector(${params.connectorName}).build(); let ${params.clientName} = #{Client}::from_conf(config); """, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt index 8bf443d8f9..8a5822127a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt @@ -17,6 +17,8 @@ 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.RuntimeConfig +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.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization @@ -53,8 +55,10 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom private val runtimeConfig = codegenContext.runtimeConfig private val runtimeMode = codegenContext.smithyRuntimeMode private val codegenScope = arrayOf( + *preludeScope, "CredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("cache::CredentialsCache"), "DefaultProvider" to defaultProvider(), + "SharedAsyncSleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep::SharedAsyncSleep"), "SharedCredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("cache::SharedCredentialsCache"), "SharedCredentialsProvider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider::SharedCredentialsProvider"), ) @@ -95,35 +99,57 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom } ServiceConfig.BuilderStruct -> - rustTemplate("credentials_cache: Option<#{CredentialsCache}>,", *codegenScope) + if (runtimeMode.defaultToMiddleware) { + rustTemplate("credentials_cache: #{Option}<#{CredentialsCache}>,", *codegenScope) + } ServiceConfig.BuilderImpl -> { rustTemplate( """ /// Sets the credentials cache for this service pub fn credentials_cache(mut self, credentials_cache: #{CredentialsCache}) -> Self { - self.set_credentials_cache(Some(credentials_cache)); + self.set_credentials_cache(#{Some}(credentials_cache)); self } - /// Sets the credentials cache for this service - pub fn set_credentials_cache(&mut self, credentials_cache: Option<#{CredentialsCache}>) -> &mut Self { - self.credentials_cache = credentials_cache; - self - } """, *codegenScope, ) + + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Sets the credentials cache for this service + pub fn set_credentials_cache(&mut self, credentials_cache: #{Option}<#{CredentialsCache}>) -> &mut Self { + self.inner.store_or_unset(credentials_cache); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Sets the credentials cache for this service + pub fn set_credentials_cache(&mut self, credentials_cache: Option<#{CredentialsCache}>) -> &mut Self { + self.credentials_cache = credentials_cache; + self + } + """, + *codegenScope, + ) + } } ServiceConfig.BuilderBuild -> { if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - layer.store_put( - self.credentials_cache + self.inner.store_put( + self.inner.load::<#{CredentialsCache}>() + .cloned() .unwrap_or_else({ - let sleep = self.sleep_impl.clone(); + let sleep = self.inner.load::<#{SharedAsyncSleep}>().cloned(); || match sleep { Some(sleep) => { #{CredentialsCache}::lazy_builder() @@ -133,9 +159,9 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom None => #{CredentialsCache}::lazy(), } }) - .create_cache(self.credentials_provider.unwrap_or_else(|| { + .create_cache(self.inner.load::<#{SharedCredentialsProvider}>().cloned().unwrap_or_else(|| { #{SharedCredentialsProvider}::new(#{DefaultProvider}) - })), + })) ); """, *codegenScope, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index 6bc42cdbc2..58688d3a7b 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -17,8 +17,8 @@ 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.RuntimeConfig 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.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization @@ -40,7 +40,7 @@ class CredentialsProviderDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List { - return baseCustomizations + CredentialProviderConfig(codegenContext.runtimeConfig) + return baseCustomizations + CredentialProviderConfig(codegenContext) } override fun extraSections(codegenContext: ClientCodegenContext): List = @@ -65,38 +65,63 @@ class CredentialsProviderDecorator : ClientCodegenDecorator { /** * Add a `.credentials_provider` field and builder to the `Config` for a given service */ -class CredentialProviderConfig(runtimeConfig: RuntimeConfig) : ConfigCustomization() { +class CredentialProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomization() { + private val smithyRuntimeMode = codegenContext.smithyRuntimeMode + private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope = arrayOf( - "provider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider"), + *preludeScope, "Credentials" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("Credentials"), + "ProvideCredentials" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider::ProvideCredentials"), + "SharedCredentialsProvider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig).resolve("provider::SharedCredentialsProvider"), "TestCredentials" to AwsRuntimeType.awsCredentialTypesTestUtil(runtimeConfig).resolve("Credentials"), ) override fun section(section: ServiceConfig) = writable { when (section) { - ServiceConfig.BuilderStruct -> - rustTemplate("credentials_provider: Option<#{provider}::SharedCredentialsProvider>,", *codegenScope) + ServiceConfig.BuilderStruct -> { + if (smithyRuntimeMode.defaultToMiddleware) { + rustTemplate("credentials_provider: #{Option}<#{SharedCredentialsProvider}>,", *codegenScope) + } + } ServiceConfig.BuilderImpl -> { rustTemplate( """ /// Sets the credentials provider for this service - pub fn credentials_provider(mut self, credentials_provider: impl #{provider}::ProvideCredentials + 'static) -> Self { - self.set_credentials_provider(Some(#{provider}::SharedCredentialsProvider::new(credentials_provider))); - self - } - - /// Sets the credentials provider for this service - pub fn set_credentials_provider(&mut self, credentials_provider: Option<#{provider}::SharedCredentialsProvider>) -> &mut Self { - self.credentials_provider = credentials_provider; + pub fn credentials_provider(mut self, credentials_provider: impl #{ProvideCredentials} + 'static) -> Self { + self.set_credentials_provider(#{Some}(#{SharedCredentialsProvider}::new(credentials_provider))); self } """, *codegenScope, ) + + if (smithyRuntimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Sets the credentials provider for this service + pub fn set_credentials_provider(&mut self, credentials_provider: #{Option}<#{SharedCredentialsProvider}>) -> &mut Self { + self.inner.store_or_unset(credentials_provider); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Sets the credentials provider for this service + pub fn set_credentials_provider(&mut self, credentials_provider: #{Option}<#{SharedCredentialsProvider}>) -> &mut Self { + self.credentials_provider = credentials_provider; + self + } + """, + *codegenScope, + ) + } } is ServiceConfig.DefaultForTests -> rustTemplate( - "${section.configBuilderRef}.set_credentials_provider(Some(#{provider}::SharedCredentialsProvider::new(#{TestCredentials}::for_tests())));", + "${section.configBuilderRef}.set_credentials_provider(Some(#{SharedCredentialsProvider}::new(#{TestCredentials}::for_tests())));", *codegenScope, ) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt index e33e7ec8d1..5be6feefbd 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt @@ -14,6 +14,7 @@ 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.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.util.letIf // TODO(enableNewSmithyRuntimeCleanup): Delete this decorator since it's now in `codegen-client` @@ -37,6 +38,7 @@ class HttpConnectorConfigCustomization( private val runtimeMode = codegenContext.smithyRuntimeMode private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( + *preludeScope, "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), ) @@ -71,7 +73,9 @@ class HttpConnectorConfigCustomization( } } is ServiceConfig.BuilderStruct -> writable { - rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + if (runtimeMode.defaultToMiddleware) { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } } ServiceConfig.BuilderImpl -> writable { rustTemplate( @@ -108,7 +112,7 @@ class HttpConnectorConfigCustomization( /// ## } /// ``` pub fn http_connector(mut self, http_connector: impl Into<#{HttpConnector}>) -> Self { - self.http_connector = Some(http_connector.into()); + self.set_http_connector(#{Some}(http_connector)); self } @@ -150,18 +154,33 @@ class HttpConnectorConfigCustomization( /// ## } /// ## } /// ``` - pub fn set_http_connector(&mut self, http_connector: Option>) -> &mut Self { - self.http_connector = http_connector.map(|inner| inner.into()); - self - } """, *codegenScope, ) - } - is ServiceConfig.BuilderBuild -> writable { if (runtimeMode.defaultToOrchestrator) { - rust("layer.store_or_unset(self.http_connector);") + rustTemplate( + """ + pub fn set_http_connector(&mut self, http_connector: #{Option}>) -> &mut Self { + http_connector.map(|c| self.inner.store_put(c.into())); + self + } + """, + *codegenScope, + ) } else { + rustTemplate( + """ + pub fn set_http_connector(&mut self, http_connector: #{Option}>) -> &mut Self { + self.http_connector = http_connector.map(|inner| inner.into()); + self + } + """, + *codegenScope, + ) + } + } + is ServiceConfig.BuilderBuild -> writable { + if (runtimeMode.defaultToMiddleware) { rust("http_connector: self.http_connector,") } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt index ae3f8b248e..172ba2c1ae 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt @@ -22,6 +22,7 @@ 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.RuntimeConfig +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization @@ -160,12 +161,15 @@ class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomi private val region = region(codegenContext.runtimeConfig) private val moduleUseName = codegenContext.moduleUseName() private val runtimeMode = codegenContext.smithyRuntimeMode - private val codegenScope = arrayOf("Region" to region.resolve("Region")) + private val codegenScope = arrayOf( + *preludeScope, + "Region" to region.resolve("Region"), + ) override fun section(section: ServiceConfig) = writable { when (section) { ServiceConfig.ConfigStruct -> { if (runtimeMode.defaultToMiddleware) { - rustTemplate("pub(crate) region: Option<#{Region}>,", *codegenScope) + rustTemplate("pub(crate) region: #{Option}<#{Region}>,", *codegenScope) } } ServiceConfig.ConfigImpl -> { @@ -173,7 +177,7 @@ class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomi rustTemplate( """ /// Returns the AWS region, if it was provided. - pub fn region(&self) -> Option<&#{Region}> { + pub fn region(&self) -> #{Option}<&#{Region}> { self.inner.load::<#{Region}>() } """, @@ -183,7 +187,7 @@ class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomi rustTemplate( """ /// Returns the AWS region, if it was provided. - pub fn region(&self) -> Option<&#{Region}> { + pub fn region(&self) -> #{Option}<&#{Region}> { self.region.as_ref() } """, @@ -192,10 +196,13 @@ class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomi } } - ServiceConfig.BuilderStruct -> - rustTemplate("pub(crate) region: Option<#{Region}>,", *codegenScope) + ServiceConfig.BuilderStruct -> { + if (runtimeMode.defaultToMiddleware) { + rustTemplate("pub(crate) region: #{Option}<#{Region}>,", *codegenScope) + } + } - ServiceConfig.BuilderImpl -> + ServiceConfig.BuilderImpl -> { rustTemplate( """ /// Sets the AWS region to use when making requests. @@ -209,24 +216,41 @@ class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomi /// .region(Region::new("us-east-1")) /// .build(); /// ``` - pub fn region(mut self, region: impl Into>) -> Self { - self.region = region.into(); - self - } - - /// Sets the AWS region to use when making requests. - pub fn set_region(&mut self, region: Option<#{Region}>) -> &mut Self { - self.region = region; + pub fn region(mut self, region: impl #{Into}<#{Option}<#{Region}>>) -> Self { + self.set_region(region.into()); self } """, *codegenScope, ) - ServiceConfig.BuilderBuild -> { if (runtimeMode.defaultToOrchestrator) { - rust("layer.store_or_unset(self.region);") + rustTemplate( + """ + /// Sets the AWS region to use when making requests. + pub fn set_region(&mut self, region: #{Option}<#{Region}>) -> &mut Self { + self.inner.store_or_unset(region); + self + } + """, + *codegenScope, + ) } else { + rustTemplate( + """ + /// Sets the AWS region to use when making requests. + pub fn set_region(&mut self, region: #{Option}<#{Region}>) -> &mut Self { + self.region = region; + self + } + """, + *codegenScope, + ) + } + } + + ServiceConfig.BuilderBuild -> { + if (runtimeMode.defaultToMiddleware) { rust("region: self.region,") } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index 2e9a751686..cd8af6822f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -160,7 +160,9 @@ class UserAgentDecorator : ClientCodegenDecorator { override fun section(section: ServiceConfig): Writable = when (section) { is ServiceConfig.BuilderStruct -> writable { - rustTemplate("app_name: #{Option}<#{AppName}>,", *codegenScope) + if (runtimeMode.defaultToMiddleware) { + rustTemplate("app_name: #{Option}<#{AppName}>,", *codegenScope) + } } is ServiceConfig.BuilderImpl -> writable { @@ -174,24 +176,43 @@ class UserAgentDecorator : ClientCodegenDecorator { self.set_app_name(Some(app_name)); self } - - /// Sets the name of the app that is using the client. - /// - /// This _optional_ name is used to identify the application in the user agent that - /// gets sent along with requests. - pub fn set_app_name(&mut self, app_name: #{Option}<#{AppName}>) -> &mut Self { - self.app_name = app_name; - self - } """, *codegenScope, ) - } - is ServiceConfig.BuilderBuild -> writable { if (runtimeMode.defaultToOrchestrator) { - rust("layer.store_or_unset(self.app_name);") + rustTemplate( + """ + /// Sets the name of the app that is using the client. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn set_app_name(&mut self, app_name: #{Option}<#{AppName}>) -> &mut Self { + self.inner.store_or_unset(app_name); + self + } + """, + *codegenScope, + ) } else { + rustTemplate( + """ + /// Sets the name of the app that is using the client. + /// + /// This _optional_ name is used to identify the application in the user agent that + /// gets sent along with requests. + pub fn set_app_name(&mut self, app_name: #{Option}<#{AppName}>) -> &mut Self { + self.app_name = app_name; + self + } + """, + *codegenScope, + ) + } + } + + is ServiceConfig.BuilderBuild -> writable { + if (runtimeMode.defaultToMiddleware) { rust("app_name: self.app_name,") } } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt index df19f6f16b..b405678853 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialProviderConfigTest.kt @@ -17,6 +17,6 @@ internal class CredentialProviderConfigTest { fun `generates a valid config`(smithyRuntimeModeStr: String) { val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) val codegenContext = awsTestCodegenContext().withSmithyRuntimeMode(smithyRuntimeMode) - validateConfigCustomizations(codegenContext, CredentialProviderConfig(codegenContext.runtimeConfig)) + validateConfigCustomizations(codegenContext, CredentialProviderConfig(codegenContext)) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index 3fe3c56737..4003b0cacb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -14,6 +14,7 @@ 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.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.util.letIf class HttpConnectorConfigDecorator : ClientCodegenDecorator { @@ -36,6 +37,7 @@ private class HttpConnectorConfigCustomization( private val runtimeMode = codegenContext.smithyRuntimeMode private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( + *preludeScope, "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), ) @@ -72,7 +74,9 @@ private class HttpConnectorConfigCustomization( } is ServiceConfig.BuilderStruct -> writable { - rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + if (runtimeMode.defaultToMiddleware) { + rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) + } } ServiceConfig.BuilderImpl -> writable { @@ -110,7 +114,7 @@ private class HttpConnectorConfigCustomization( /// ## } /// ``` pub fn http_connector(mut self, http_connector: impl Into<#{HttpConnector}>) -> Self { - self.http_connector = Some(http_connector.into()); + self.set_http_connector(#{Some}(http_connector)); self } @@ -151,19 +155,35 @@ private class HttpConnectorConfigCustomization( /// ## } /// ## } /// ``` - pub fn set_http_connector(&mut self, http_connector: Option>) -> &mut Self { - self.http_connector = http_connector.map(|inner| inner.into()); - self - } """, *codegenScope, ) - } - is ServiceConfig.BuilderBuild -> writable { if (runtimeMode.defaultToOrchestrator) { - rust("self.http_connector.map(|c| layer.store_put(c));") + rustTemplate( + """ + pub fn set_http_connector(&mut self, http_connector: Option>) -> &mut Self { + http_connector.map(|c| self.inner.store_put(c.into())); + self + } + """, + *codegenScope, + ) } else { + rustTemplate( + """ + pub fn set_http_connector(&mut self, http_connector: Option>) -> &mut Self { + self.http_connector = http_connector.map(|inner| inner.into()); + self + } + """, + *codegenScope, + ) + } + } + + is ServiceConfig.BuilderBuild -> writable { + if (runtimeMode.defaultToMiddleware) { rust("http_connector: self.http_connector,") } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index dbd696248e..5b973feed5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -17,6 +17,7 @@ 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.RuntimeConfig 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.smithy.RustCrate class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { @@ -27,6 +28,7 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf private val timeoutModule = RuntimeType.smithyTypes(runtimeConfig).resolve("timeout") private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( + *preludeScope, "RetryConfig" to retryConfig.resolve("RetryConfig"), "SharedAsyncSleep" to sleepModule.resolve("SharedAsyncSleep"), "Sleep" to sleepModule.resolve("Sleep"), @@ -40,9 +42,9 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf if (runtimeMode.defaultToMiddleware) { rustTemplate( """ - retry_config: Option<#{RetryConfig}>, - sleep_impl: Option<#{SharedAsyncSleep}>, - timeout_config: Option<#{TimeoutConfig}>, + retry_config: #{Option}<#{RetryConfig}>, + sleep_impl: #{Option}<#{SharedAsyncSleep}>, + timeout_config: #{Option}<#{TimeoutConfig}>, """, *codegenScope, ) @@ -54,17 +56,17 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf rustTemplate( """ /// Return a reference to the retry configuration contained in this config, if any. - pub fn retry_config(&self) -> Option<&#{RetryConfig}> { + pub fn retry_config(&self) -> #{Option}<&#{RetryConfig}> { self.inner.load::<#{RetryConfig}>() } /// Return a cloned shared async sleep implementation from this config, if any. - pub fn sleep_impl(&self) -> Option<#{SharedAsyncSleep}> { + pub fn sleep_impl(&self) -> #{Option}<#{SharedAsyncSleep}> { self.inner.load::<#{SharedAsyncSleep}>().cloned() } /// Return a reference to the timeout configuration contained in this config, if any. - pub fn timeout_config(&self) -> Option<&#{TimeoutConfig}> { + pub fn timeout_config(&self) -> #{Option}<&#{TimeoutConfig}> { self.inner.load::<#{TimeoutConfig}>() } """, @@ -74,17 +76,17 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf rustTemplate( """ /// Return a reference to the retry configuration contained in this config, if any. - pub fn retry_config(&self) -> Option<&#{RetryConfig}> { + pub fn retry_config(&self) -> #{Option}<&#{RetryConfig}> { self.retry_config.as_ref() } /// Return a cloned shared async sleep implementation from this config, if any. - pub fn sleep_impl(&self) -> Option<#{SharedAsyncSleep}> { + pub fn sleep_impl(&self) -> #{Option}<#{SharedAsyncSleep}> { self.sleep_impl.clone() } /// Return a reference to the timeout configuration contained in this config, if any. - pub fn timeout_config(&self) -> Option<&#{TimeoutConfig}> { + pub fn timeout_config(&self) -> #{Option}<&#{TimeoutConfig}> { self.timeout_config.as_ref() } """, @@ -94,17 +96,19 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf } is ServiceConfig.BuilderStruct -> { - rustTemplate( - """ - retry_config: Option<#{RetryConfig}>, - sleep_impl: Option<#{SharedAsyncSleep}>, - timeout_config: Option<#{TimeoutConfig}>, - """, - *codegenScope, - ) + if (runtimeMode.defaultToMiddleware) { + rustTemplate( + """ + retry_config: #{Option}<#{RetryConfig}>, + sleep_impl: #{Option}<#{SharedAsyncSleep}>, + timeout_config: #{Option}<#{TimeoutConfig}>, + """, + *codegenScope, + ) + } } - ServiceConfig.BuilderImpl -> + is ServiceConfig.BuilderImpl -> { rustTemplate( """ /// Set the retry_config for the builder @@ -138,10 +142,34 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf /// disable_retries(&mut builder); /// let config = builder.build(); /// ``` - pub fn set_retry_config(&mut self, retry_config: Option<#{RetryConfig}>) -> &mut Self { - self.retry_config = retry_config; - self - } + """, + *codegenScope, + ) + + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + pub fn set_retry_config(&mut self, retry_config: #{Option}<#{RetryConfig}>) -> &mut Self { + retry_config.map(|r| self.inner.store_put(r)); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + pub fn set_retry_config(&mut self, retry_config: #{Option}<#{RetryConfig}>) -> &mut Self { + self.retry_config = retry_config; + self + } + """, + *codegenScope, + ) + } + + rustTemplate( + """ /// Set the sleep_impl for the builder /// @@ -192,10 +220,34 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf /// set_never_ending_sleep_impl(&mut builder); /// let config = builder.build(); /// ``` - pub fn set_sleep_impl(&mut self, sleep_impl: Option<#{SharedAsyncSleep}>) -> &mut Self { - self.sleep_impl = sleep_impl; - self - } + """, + *codegenScope, + ) + + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + pub fn set_sleep_impl(&mut self, sleep_impl: #{Option}<#{SharedAsyncSleep}>) -> &mut Self { + sleep_impl.map(|s| self.inner.store_put(s)); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + pub fn set_sleep_impl(&mut self, sleep_impl: #{Option}<#{SharedAsyncSleep}>) -> &mut Self { + self.sleep_impl = sleep_impl; + self + } + """, + *codegenScope, + ) + } + + rustTemplate( + """ /// Set the timeout_config for the builder /// @@ -236,25 +288,35 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf /// set_request_timeout(&mut builder); /// let config = builder.build(); /// ``` - pub fn set_timeout_config(&mut self, timeout_config: Option<#{TimeoutConfig}>) -> &mut Self { - self.timeout_config = timeout_config; - self - } """, *codegenScope, ) - ServiceConfig.BuilderBuild -> { if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - self.retry_config.map(|r| layer.store_put(r)); - self.sleep_impl.clone().map(|s| layer.store_put(s)); - self.timeout_config.map(|t| layer.store_put(t)); + pub fn set_timeout_config(&mut self, timeout_config: #{Option}<#{TimeoutConfig}>) -> &mut Self { + timeout_config.map(|t| self.inner.store_put(t)); + self + } """, *codegenScope, ) } else { + rustTemplate( + """ + pub fn set_timeout_config(&mut self, timeout_config: #{Option}<#{TimeoutConfig}>) -> &mut Self { + self.timeout_config = timeout_config; + self + } + """, + *codegenScope, + ) + } + } + + ServiceConfig.BuilderBuild -> { + if (runtimeMode.defaultToMiddleware) { rustTemplate( // We call clone on sleep_impl because the field is used by // initializing the credentials_cache field later in the build diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt index e27531814e..c26b620b03 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt @@ -56,15 +56,16 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust } } - is ServiceConfig.BuilderStruct -> - rustTemplate( - """ - time_source: #{Option}<#{SharedTimeSource}>, - """, - *codegenScope, - ) + is ServiceConfig.BuilderStruct -> { + if (runtimeMode.defaultToMiddleware) { + rustTemplate( + "time_source: #{Option}<#{SharedTimeSource}>,", + *codegenScope, + ) + } + } - ServiceConfig.BuilderImpl -> + ServiceConfig.BuilderImpl -> { rustTemplate( """ /// Sets the time source used for this service @@ -72,39 +73,58 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust mut self, time_source: impl #{Into}<#{SharedTimeSource}>, ) -> Self { - self.time_source = Some(time_source.into()); - self - } - /// Sets the time source used for this service - pub fn set_time_source( - &mut self, - time_source: #{Option}<#{SharedTimeSource}>, - ) -> &mut Self { - self.time_source = time_source; + self.set_time_source(#{Some}(time_source.into())); self } """, *codegenScope, ) - ServiceConfig.BuilderBuild -> { if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - layer.store_put(self.time_source.unwrap_or_default()); + /// Sets the time source used for this service + pub fn set_time_source( + &mut self, + time_source: #{Option}<#{SharedTimeSource}>, + ) -> &mut Self { + self.inner.store_or_unset(time_source); + self + } """, *codegenScope, ) } else { rustTemplate( """ - time_source: self.time_source.unwrap_or_default(), + /// Sets the time source used for this service + pub fn set_time_source( + &mut self, + time_source: #{Option}<#{SharedTimeSource}>, + ) -> &mut Self { + self.time_source = time_source; + self + } """, *codegenScope, ) } } + ServiceConfig.BuilderBuild -> { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + "self.inner.store_put(self.inner.load::<#{SharedTimeSource}>().cloned().unwrap_or_default());", + *codegenScope, + ) + } else { + rustTemplate( + "time_source: self.time_source.unwrap_or_default(),", + *codegenScope, + ) + } + } + else -> emptySection } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index a3308ce4da..7f97053e01 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -72,11 +72,14 @@ internal class EndpointConfigCustomization( } } - is ServiceConfig.BuilderStruct -> - rustTemplate( - "endpoint_resolver: #{Option}<$sharedEndpointResolver>,", - *codegenScope, - ) + is ServiceConfig.BuilderStruct -> { + if (runtimeMode.defaultToMiddleware) { + rustTemplate( + "endpoint_resolver: #{Option}<$sharedEndpointResolver>,", + *codegenScope, + ) + } + } ServiceConfig.BuilderImpl -> { // if there are no rules, we don't generate a default resolver—we need to also suppress those docs. @@ -121,7 +124,7 @@ internal class EndpointConfigCustomization( /// Sets the endpoint resolver to use when making requests. $defaultResolverDocs pub fn endpoint_resolver(mut self, endpoint_resolver: impl $resolverTrait + 'static) -> Self { - self.endpoint_resolver = #{Some}(#{SharedEndpointResolver}::new(endpoint_resolver)); + self.set_endpoint_resolver(#{Some}(#{SharedEndpointResolver}::new(endpoint_resolver))); self } @@ -129,13 +132,31 @@ internal class EndpointConfigCustomization( /// /// When unset, the client will used a generated endpoint resolver based on the endpoint resolution /// rules for `$moduleUseName`. - pub fn set_endpoint_resolver(&mut self, endpoint_resolver: #{Option}<$sharedEndpointResolver>) -> &mut Self { - self.endpoint_resolver = endpoint_resolver; - self - } """, *codegenScope, ) + + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + pub fn set_endpoint_resolver(&mut self, endpoint_resolver: #{Option}<$sharedEndpointResolver>) -> &mut Self { + self.inner.store_or_unset(endpoint_resolver); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + pub fn set_endpoint_resolver(&mut self, endpoint_resolver: #{Option}<$sharedEndpointResolver>) -> &mut Self { + self.endpoint_resolver = endpoint_resolver; + self + } + """, + *codegenScope, + ) + } } ServiceConfig.BuilderBuild -> { @@ -144,7 +165,7 @@ internal class EndpointConfigCustomization( if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - layer.store_put(self.endpoint_resolver.unwrap_or_else(|| + self.inner.store_put(self.inner.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(|| #{SharedEndpointResolver}::new(#{DefaultResolver}::new()) )); """, @@ -185,7 +206,7 @@ internal class EndpointConfigCustomization( if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - layer.store_put(self.endpoint_resolver.unwrap_or_else(||#{SharedEndpointResolver}::new(#{FailingResolver}))); + self.inner.store_put(self.inner.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(||#{SharedEndpointResolver}::new(#{FailingResolver}))); """, *codegenScope, "FailingResolver" to alwaysFailsResolver, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt index d2e779f1ff..8a78ee4863 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt @@ -63,7 +63,9 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext } ServiceConfig.BuilderStruct -> writable { - rustTemplate("idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>,", *codegenScope) + if (runtimeMode.defaultToMiddleware) { + rustTemplate("idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>,", *codegenScope) + } } ServiceConfig.BuilderImpl -> writable { @@ -74,21 +76,39 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext self.set_idempotency_token_provider(#{Some}(idempotency_token_provider.into())); self } - - /// Sets the idempotency token provider to use for service calls that require tokens. - pub fn set_idempotency_token_provider(&mut self, idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>) -> &mut Self { - self.idempotency_token_provider = idempotency_token_provider; - self - } """, *codegenScope, ) + + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Sets the idempotency token provider to use for service calls that require tokens. + pub fn set_idempotency_token_provider(&mut self, idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>) -> &mut Self { + self.inner.store_or_unset(idempotency_token_provider); + self + } + """, + *codegenScope, + ) + } else { + rustTemplate( + """ + /// Sets the idempotency token provider to use for service calls that require tokens. + pub fn set_idempotency_token_provider(&mut self, idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>) -> &mut Self { + self.idempotency_token_provider = idempotency_token_provider; + self + } + """, + *codegenScope, + ) + } } ServiceConfig.BuilderBuild -> writable { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - "layer.store_put(self.idempotency_token_provider.unwrap_or_else(#{default_provider}));", + "self.inner.store_put(self.inner.load::<#{IdempotencyTokenProvider}>().cloned().unwrap_or_else(#{default_provider}));", *codegenScope, ) } else { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index f533740ce5..7096a93168 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -13,7 +13,6 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.traits.IdempotencyTokenTrait 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.customizations.codegenScope import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -92,7 +91,7 @@ sealed class ServiceConfig(name: String) : Section(name) { */ object BuilderBuild : ServiceConfig("BuilderBuild") - // TODO(enableNewSmithyRuntime): This is temporary until config builder is backed by a CloneableLayer. + // TODO(enableNewSmithyRuntimeLaunch): This is temporary until config builder is backed by a CloneableLayer. // It is needed because certain config fields appear explicitly regardless of the smithy runtime mode, e.g. // interceptors. The [BuilderBuild] section is bifurcated depending on the runtime mode (in the orchestrator mode, // storing a field into a frozen layer and in the middleware moving it into a corresponding service config field) @@ -217,7 +216,9 @@ fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext } ServiceConfig.BuilderStruct -> writable { - rust("${param.name}: #T,", param.type.makeOptional()) + if (runtimeMode.defaultToMiddleware) { + rust("${param.name}: #T,", param.type.makeOptional()) + } } ServiceConfig.BuilderImpl -> writable { @@ -225,31 +226,39 @@ fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext rust( """ pub fn ${param.name}(mut self, ${param.name}: impl Into<#T>) -> Self { - self.${param.name} = Some(${param.name}.into()); + self.set_${param.name}(Some(${param.name}.into())); self - }""", + }""", param.type, ) docsOrFallback(param.setterDocs) - rust( - """ - pub fn set_${param.name}(&mut self, ${param.name}: Option<#T>) -> &mut Self { - self.${param.name} = ${param.name}; - self - } - """, - param.type, - ) - } - - ServiceConfig.BuilderBuild -> writable { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - "layer.store_or_unset(self.${param.name}.map(#{newtype}));", + """ + pub fn set_${param.name}(&mut self, ${param.name}: Option<#{T}>) -> &mut Self { + self.inner.store_or_unset(${param.name}.map(#{newtype})); + self + } + """, + "T" to param.type, "newtype" to param.newtype!!, ) } else { + rust( + """ + pub fn set_${param.name}(&mut self, ${param.name}: Option<#T>) -> &mut Self { + self.${param.name} = ${param.name}; + self + } + """, + param.type, + ) + } + } + + ServiceConfig.BuilderBuild -> writable { + if (runtimeMode.defaultToMiddleware) { val default = "".letIf(!param.optional) { ".unwrap_or_default() " } rust("${param.name}: self.${param.name}$default,") } @@ -310,7 +319,7 @@ class ServiceConfigGenerator( private val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig) val codegenScope = arrayOf( "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), - "Layer" to smithyTypes.resolve("config_bag::Layer"), + "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), @@ -365,6 +374,12 @@ class ServiceConfigGenerator( writer.docs("Builder for creating a `Config`.") writer.raw("#[derive(Clone, Default)]") writer.rustBlock("pub struct Builder") { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + "inner: #{CloneableLayer},", + *codegenScope, + ) + } customizations.forEach { it.section(ServiceConfig.BuilderStruct)(this) } @@ -408,12 +423,9 @@ class ServiceConfigGenerator( } docs("Builds a [`Config`].") - rustBlock("pub fn build(self) -> Config") { - if (runtimeMode.defaultToOrchestrator) { - rustTemplate( - """let mut layer = #{Layer}::new("$moduleUseName::Config");""", - *codegenScope, - ) + if (runtimeMode.defaultToOrchestrator) { + rust("##[allow(unused_mut)]") + rustBlock("pub fn build(mut self) -> Config") { customizations.forEach { it.section(ServiceConfig.BuilderBuild)(this) } @@ -421,9 +433,11 @@ class ServiceConfigGenerator( customizations.forEach { it.section(ServiceConfig.BuilderBuildExtras)(this) } - rust("inner: layer.freeze(),") + rust("inner: self.inner.freeze(),") } - } else { + } + } else { + rustBlock("pub fn build(self) -> Config") { rustBlock("Config") { customizations.forEach { it.section(ServiceConfig.BuilderBuild)(this) @@ -444,7 +458,7 @@ class ServiceConfigGenerator( fn config(&self) -> #{Option}<#{FrozenLayer}> { // TODO(enableNewSmithyRuntimeLaunch): Put into `cfg` the fields in `self.config_override` that are not `None` ##[allow(unused_mut)] - let mut cfg = #{Layer}::new("service config"); + let mut cfg = #{CloneableLayer}::new("service config"); #{config} Some(cfg.freeze()) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt index d45a2300ee..f3d12e9b00 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt @@ -60,21 +60,20 @@ fun stubConfigCustomization(name: String, codegenContext: ClientCodegenContext): ) } } - ServiceConfig.BuilderStruct -> rust("_$name: Option,") - ServiceConfig.BuilderImpl -> rust( - """ - /// docs! - pub fn $name(mut self, $name: u64) -> Self { - self._$name = Some($name); - self + ServiceConfig.BuilderStruct -> { + if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + rust("_$name: Option,") } - """, - ) - ServiceConfig.BuilderBuild -> { + } + ServiceConfig.BuilderImpl -> { if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { rustTemplate( """ - layer.store_or_unset(self._$name.map(#{T})); + /// docs! + pub fn $name(mut self, $name: u64) -> Self { + self.inner.store_put(#{T}($name)); + self + } """, "T" to configParamNewtype( "_$name".toPascalCase(), RuntimeType.U64.toSymbol(), @@ -82,6 +81,19 @@ fun stubConfigCustomization(name: String, codegenContext: ClientCodegenContext): ), ) } else { + rust( + """ + /// docs! + pub fn $name(mut self, $name: u64) -> Self { + self._$name = Some($name); + self + } + """, + ) + } + } + ServiceConfig.BuilderBuild -> { + if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { rust( """ _$name: self._$name.unwrap_or(123), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt index f5b3a39c27..be848af92f 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt @@ -126,18 +126,30 @@ internal class ServiceConfigGeneratorTest { } } - ServiceConfig.BuilderStruct -> writable { rust("config_field: Option") } - ServiceConfig.BuilderImpl -> emptySection - ServiceConfig.BuilderBuild -> writable { + ServiceConfig.BuilderStruct -> writable { + if (runtimeMode.defaultToMiddleware) { + rust("config_field: Option") + } + } + ServiceConfig.BuilderImpl -> writable { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - "layer.store_or_unset(self.config_field.map(#{T}));", + """ + ##[allow(missing_docs)] + pub fn config_field(mut self, config_field: u64) -> Self { + self.inner.store_put(#{T}(config_field)); + self + } + """, "T" to configParamNewtype( "config_field".toPascalCase(), RuntimeType.U64.toSymbol(), codegenContext.runtimeConfig, ), ) - } else { + } + } + ServiceConfig.BuilderBuild -> writable { + if (runtimeMode.defaultToMiddleware) { rust("config_field: self.config_field.unwrap_or_default(),") } } @@ -159,8 +171,7 @@ internal class ServiceConfigGeneratorTest { unitTest( "set_config_fields", """ - let mut builder = Config::builder(); - builder.config_field = Some(99); + let builder = Config::builder().config_field(99); let config = builder.build(); assert_eq!(config.config_field(), 99); """, diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index e2eea64252..994b0ea849 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -87,7 +87,7 @@ impl Default for Value { /// /// While [`FrozenLayer`] is also cloneable, which is a shallow clone via `Arc`, `CloneableLayer` /// performs a deep clone that newly allocates all the items stored in it. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct CloneableLayer(Layer); impl Deref for CloneableLayer { @@ -197,6 +197,7 @@ impl CloneableLayer { } /// A named layer comprising a config bag +#[derive(Default)] pub struct Layer { name: Cow<'static, str>, props: TypeIdMap, From e6293b228294899225847ccbc565fee550a341a5 Mon Sep 17 00:00:00 2001 From: David Souther Date: Wed, 21 Jun 2023 16:28:03 -0700 Subject: [PATCH 175/253] Add get_foo() -> &Option accessors for builders and fluent builders. (#2792) This allows testing, as well as making decisions while creating builders. The references are not mutable, so these props remain read only keeping the Builder invariants. ## Motivation and Context Some operations are built up using various logic (for instance, choosing an S3 bucket based on a logged in user). It is a lot of overhead to test at the mock level, and the Debug formatting is typically not stable. Closes #2791 ## Description 1. Add `get_foo(&self) -> &Option` to Builders 2. Add `as_input(&self) -> &OperationInputBuilder` to Fluent Builders 3. Add `get_foo(&self) -> &Option` to FluentBuilder which delegates to Builders ## Testing Included unit tests, as well as [proof of concept tests](https://github.com/DavidSouther/aws-doc-sdk-examples/commit/017e8a2e407eba6a4c259bca0334f1356ab822d9) ## Checklist - [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._ --------- Co-authored-by: John DiSanti --- CHANGELOG.next.toml | 12 +++++ .../generators/client/FluentClientCore.kt | 12 +++++ .../client/FluentClientGenerator.kt | 12 +++++ .../client/FluentClientGeneratorTest.kt | 51 ++++++++++++++++++- .../smithy/generators/BuilderGenerator.kt | 26 ++++++++++ .../smithy/generators/BuilderGeneratorTest.kt | 5 +- .../UnconstrainedCollectionGeneratorTest.kt | 10 ++-- .../UnconstrainedMapGeneratorTest.kt | 8 +-- 8 files changed, 125 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index aec1605b37..c1389a7463 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -11,6 +11,18 @@ # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} # author = "rcoh" + [[aws-sdk-rust]] + message = "Add accessors to Builders" + references = ["smithy-rs#2791"] + meta = { "breaking" = false, "tada" = false, "bug" = false } + author = "davidsouther" + + [[smithy-rs]] + message = "Add accessors to Builders" + references = ["smithy-rs#2791"] + meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client"} + author = "davidsouther" + [[smithy-rs]] message = "Avoid intermediate vec allocations in AggregatedBytes::to_vec." author = "yotamofek" diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientCore.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientCore.kt index fb9750119b..3073413efe 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientCore.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientCore.kt @@ -15,6 +15,7 @@ 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.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName class FluentClientCore(private val model: Model) { @@ -67,4 +68,15 @@ class FluentClientCore(private val model: Model) { write("self") } } + + /** + * Generate and write Rust code for a getter method that returns a reference to the inner data. + */ + fun RustWriter.renderGetterHelper(member: MemberShape, memberName: String, coreType: RustType) { + documentShape(member, model) + deprecatedShape(member) + withBlockTemplate("pub fn $memberName(&self) -> &#{CoreType} {", "}", "CoreType" to coreType) { + write("self.inner.$memberName()") + } + } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 7a4dc7b707..3f2f451f44 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -47,6 +47,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.expectRustMetadata +import software.amazon.smithy.rust.codegen.core.smithy.generators.getterName import software.amazon.smithy.rust.codegen.core.smithy.generators.setterName import software.amazon.smithy.rust.codegen.core.smithy.rustType import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -409,6 +410,14 @@ class FluentClientGenerator( } } + rust("/// Access the ${operationSymbol.name} as a reference.\n") + withBlockTemplate( + "pub fn as_input(&self) -> &#{Inner} {", "}", + "Inner" to symbolProvider.symbolForBuilder(input), + ) { + write("&self.inner") + } + if (smithyRuntimeMode.generateMiddleware) { val middlewareScope = arrayOf( *preludeScope, @@ -630,6 +639,9 @@ class FluentClientGenerator( val setterName = member.setterName() val optionalInputType = outerType.asOptional() with(core) { renderInputHelper(member, setterName, optionalInputType) } + + val getterName = member.getterName() + with(core) { renderGetterHelper(member, getterName, optionalInputType) } } } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt index 6caee6aa0c..b1a48abc63 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt @@ -30,7 +30,10 @@ class FluentClientGeneratorTest { } operation SayHello { input: TestInput } - structure TestInput {} + structure TestInput { + foo: String, + byteValue: Byte, + } """.asSmithyModel() @Test @@ -76,4 +79,50 @@ class FluentClientGeneratorTest { test = test, ) } + + @Test + fun `generate inner builders`() { + val test: (ClientCodegenContext, RustCrate) -> Unit = { codegenContext, rustCrate -> + rustCrate.integrationTest("inner_builder") { + val moduleName = codegenContext.moduleUseName() + rustTemplate( + """ + ##[test] + fn test() { + let connector = #{TestConnection}::<#{SdkBody}>::new(Vec::new()); + let config = $moduleName::Config::builder() + .endpoint_resolver("http://localhost:1234") + #{set_http_connector} + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + + let say_hello_fluent_builder = client.say_hello().byte_value(4).foo("hello!"); + assert_eq!(*say_hello_fluent_builder.get_foo(), Some("hello!".to_string())); + let input = say_hello_fluent_builder.as_input(); + assert_eq!(*input.get_byte_value(), Some(4)); + } + """, + "TestConnection" to CargoDependency.smithyClient(codegenContext.runtimeConfig) + .withFeature("test-util").toType() + .resolve("test_connection::TestConnection"), + "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), + "set_http_connector" to writable { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + rust(".http_connector(connector.clone())") + } + }, + ) + } + } + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams, test = test) + clientIntegrationTest( + model, + TestCodegenSettings.orchestratorModeTestParams, + test = test, + ) + } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt index da008a6bee..e86061c4c3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGenerator.kt @@ -93,6 +93,9 @@ class OperationBuildError(private val runtimeConfig: RuntimeConfig) { // Setter names will never hit a reserved word and therefore never need escaping. fun MemberShape.setterName() = "set_${this.memberName.toSnakeCase()}" +// Getter names will never hit a reserved word and therefore never need escaping. +fun MemberShape.getterName() = "get_${this.memberName.toSnakeCase()}" + class BuilderGenerator( private val model: Model, private val symbolProvider: RustSymbolProvider, @@ -210,6 +213,28 @@ class BuilderGenerator( } } + /** + * Render a `get_foo` method. This is useful as a target for code generation, because the argument type + * is the same as the resulting member type, and is always optional. + */ + private fun renderBuilderMemberGetterFn( + writer: RustWriter, + outerType: RustType, + member: MemberShape, + memberName: String, + ) { + // TODO(https://github.com/awslabs/smithy-rs/issues/1302): This `asOptional()` call is superfluous except in + // the case where the shape is a `@streaming` blob, because [StreamingTraitSymbolProvider] always generates + // a non `Option`al target type: in all other cases the client generates `Option`al types. + val inputType = outerType.asOptional() + + writer.documentShape(member, model) + writer.deprecatedShape(member) + writer.rustBlock("pub fn ${member.getterName()}(&self) -> &${inputType.render(true)}") { + rust("&self.$memberName") + } + } + private fun renderBuilder(writer: RustWriter) { writer.docs("A builder for #D.", structureSymbol) metadata.additionalAttributes.render(writer) @@ -240,6 +265,7 @@ class BuilderGenerator( } renderBuilderMemberSetterFn(this, outerType, member, memberName) + renderBuilderMemberGetterFn(this, outerType, member, memberName) } writeCustomizations(customizations, BuilderSection.AdditionalMethods(shape)) renderBuildFn(this) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt index ec107f3fcb..fcec11601e 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/BuilderGeneratorTest.kt @@ -40,7 +40,10 @@ internal class BuilderGeneratorTest { unitTest("generate_builders") { rust( """ - let my_struct = MyStruct::builder().byte_value(4).foo("hello!").build(); + let my_struct_builder = MyStruct::builder().byte_value(4).foo("hello!"); + assert_eq!(*my_struct_builder.get_byte_value(), Some(4)); + + let my_struct = my_struct_builder.build(); assert_eq!(my_struct.foo.unwrap(), "hello!"); assert_eq!(my_struct.bar, 0); """, diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt index 885b54f61f..feceb42209 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedCollectionGeneratorTest.kt @@ -18,22 +18,22 @@ class UnconstrainedCollectionGeneratorTest { val model = """ namespace test - + use aws.protocols#restJson1 use smithy.framework#ValidationException - + @restJson1 service TestService { operations: ["Operation"] } - + @http(uri: "/operation", method: "POST") operation Operation { input: OperationInputOutput output: OperationInputOutput errors: [ValidationException] } - + structure OperationInputOutput { list: ListA } @@ -103,7 +103,7 @@ class UnconstrainedCollectionGeneratorTest { let c_builder = crate::model::StructureC::builder(); let list_b_unconstrained = crate::unconstrained::list_b_unconstrained::ListBUnconstrained(vec![c_builder]); let list_a_unconstrained = crate::unconstrained::list_a_unconstrained::ListAUnconstrained(vec![list_b_unconstrained]); - + let _list_a: crate::constrained::MaybeConstrained = list_a_unconstrained.into(); """, ) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt index 8b2cbd3907..45f8b33fbc 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/UnconstrainedMapGeneratorTest.kt @@ -23,22 +23,22 @@ class UnconstrainedMapGeneratorTest { val model = """ namespace test - + use aws.protocols#restJson1 use smithy.framework#ValidationException - + @restJson1 service TestService { operations: ["Operation"] } - + @http(uri: "/operation", method: "POST") operation Operation { input: OperationInputOutput output: OperationInputOutput errors: [ValidationException] } - + structure OperationInputOutput { map: MapA } From 8dc896314697b20b8f5e3f13c453ff8cf98166ff Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Wed, 21 Jun 2023 22:33:17 -0500 Subject: [PATCH 176/253] Configure orchestrator's key components in builder's build method (#2802) ## Motivation and Context Moves setting orchestrator components out of `ServiceRuntimePlugin` and puts it in service config builders' build method. ## Description This PR is the forth in a series of config refactoring. Here, we move pieces of code out of `ServiceRuntimePlugin::config` method so that key orchestrator components meant for the service-level config should only be constructed once when a service config is created, e.g. during builder's `build` method. Previously, those components were newly created every time an operation is invoked. Wherever `self.handle.conf...` is used, the PR has moved it from `ServiceRuntimePlugin::config` to the builders' `build` method. Note that there will be a separate PR to better handle auth resolver & identity resolver in the context of the ongoing config refactoring. ## Testing - [x] Passed tests in CI ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- .../amazon/smithy/rustsdk/CredentialCaches.kt | 8 +-- .../smithy/rustsdk/SigV4AuthDecorator.kt | 16 ------ .../smithy/rustsdk/SigV4SigningDecorator.kt | 52 ++++++++++++++----- .../smithy/rustsdk/UserAgentDecorator.kt | 10 ++-- .../rustsdk/SigV4SigningDecoratorTest.kt | 1 + .../HttpConnectorConfigDecorator.kt | 29 ++++++++++- .../ResiliencyConfigCustomization.kt | 39 ++++---------- .../customizations/TimeSourceCustomization.kt | 2 +- .../customize/RequiredCustomizations.kt | 8 --- .../endpoint/EndpointConfigCustomization.kt | 11 +++- .../smithy/generators/OperationGenerator.kt | 1 + .../smithy/generators/ServiceGenerator.kt | 1 + .../ServiceRuntimePluginGenerator.kt | 30 +---------- .../IdempotencyTokenProviderCustomization.kt | 2 +- .../config/ServiceConfigGenerator.kt | 36 ++++++++++++- .../src/client/orchestrator/endpoints.rs | 9 +++- .../src/client/retries/strategy/standard.rs | 6 ++- .../aws-smithy-types/src/config_bag.rs | 6 +++ 18 files changed, 152 insertions(+), 115 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt index 8a5822127a..fcf2cd82cb 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt @@ -145,11 +145,11 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - self.inner.store_put( - self.inner.load::<#{CredentialsCache}>() + layer.store_put( + layer.load::<#{CredentialsCache}>() .cloned() .unwrap_or_else({ - let sleep = self.inner.load::<#{SharedAsyncSleep}>().cloned(); + let sleep = layer.load::<#{SharedAsyncSleep}>().cloned(); || match sleep { Some(sleep) => { #{CredentialsCache}::lazy_builder() @@ -159,7 +159,7 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom None => #{CredentialsCache}::lazy(), } }) - .create_cache(self.inner.load::<#{SharedCredentialsProvider}>().cloned().unwrap_or_else(|| { + .create_cache(layer.load::<#{SharedCredentialsProvider}>().cloned().unwrap_or_else(|| { #{SharedCredentialsProvider}::new(#{DefaultProvider}) })) ); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 5e2c7d9cbe..42084d0600 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -78,22 +78,6 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext: // enable the aws-runtime `sign-eventstream` feature addDependency(AwsCargoDependency.awsRuntime(runtimeConfig).withFeature("event-stream").toType().toSymbol()) } - section.putConfigValue(this) { - rustTemplate("#{SigningService}::from_static(self.handle.conf.signing_service())", *codegenScope) - } - rustTemplate( - """ - if let Some(region) = self.handle.conf.region() { - #{put_signing_region} - } - """, - *codegenScope, - "put_signing_region" to writable { - section.putConfigValue(this) { - rustTemplate("#{SigningRegion}::from(region.clone())", *codegenScope) - } - }, - ) } else -> {} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt index 5f5877396b..dd1584ffc5 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection @@ -54,6 +55,7 @@ class SigV4SigningDecorator : ClientCodegenDecorator { return baseCustomizations.extendIf(applies(codegenContext)) { SigV4SigningConfig( codegenContext.runtimeConfig, + codegenContext.smithyRuntimeMode, codegenContext.serviceShape.hasEventStreamOperations(codegenContext.model), codegenContext.serviceShape.expectTrait(), ) @@ -78,26 +80,48 @@ class SigV4SigningDecorator : ClientCodegenDecorator { class SigV4SigningConfig( private val runtimeConfig: RuntimeConfig, + private val runtimeMode: SmithyRuntimeMode, private val serviceHasEventStream: Boolean, private val sigV4Trait: SigV4Trait, ) : ConfigCustomization() { + private val codegenScope = arrayOf( + "Region" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::Region"), + "SigningService" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("SigningService"), + "SigningRegion" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::SigningRegion"), + ) + override fun section(section: ServiceConfig): Writable = writable { - if (section is ServiceConfig.ConfigImpl) { - if (serviceHasEventStream) { - // enable the aws-sig-auth `sign-eventstream` feature - addDependency(AwsRuntimeType.awsSigAuthEventStream(runtimeConfig).toSymbol()) + when (section) { + ServiceConfig.ConfigImpl -> { + if (serviceHasEventStream) { + // enable the aws-sig-auth `sign-eventstream` feature + addDependency(AwsRuntimeType.awsSigAuthEventStream(runtimeConfig).toSymbol()) + } + rust( + """ + /// The signature version 4 service signing name to use in the credential scope when signing requests. + /// + /// The signing service may be overridden by the `Endpoint`, or by specifying a custom + /// [`SigningService`](aws_types::SigningService) during operation construction + pub fn signing_service(&self) -> &'static str { + ${sigV4Trait.name.dq()} + } + """, + ) } - rust( - """ - /// The signature version 4 service signing name to use in the credential scope when signing requests. - /// - /// The signing service may be overridden by the `Endpoint`, or by specifying a custom - /// [`SigningService`](aws_types::SigningService) during operation construction - pub fn signing_service(&self) -> &'static str { - ${sigV4Trait.name.dq()} + ServiceConfig.BuilderBuild -> { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + layer.put(#{SigningService}::from_static(${sigV4Trait.name.dq()})); + layer.load::<#{Region}>().cloned().map(|r| layer.put(#{SigningRegion}::from(r))); + """, + *codegenScope, + ) } - """, - ) + } + + else -> emptySection } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index cd8af6822f..64c62b400a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -104,12 +104,6 @@ class UserAgentDecorator : ClientCodegenDecorator { override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { - is ServiceRuntimePluginSection.AdditionalConfig -> { - section.putConfigValue(this) { - rust("#T.clone()", ClientRustModule.Meta.toType().resolve("API_METADATA")) - } - } - is ServiceRuntimePluginSection.RegisterInterceptor -> { section.registerInterceptor(runtimeConfig, this) { rust("#T::new()", awsRuntime.resolve("user_agent::UserAgentInterceptor")) @@ -212,7 +206,9 @@ class UserAgentDecorator : ClientCodegenDecorator { } is ServiceConfig.BuilderBuild -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.defaultToOrchestrator) { + rust("layer.put(#T.clone());", ClientRustModule.Meta.toType().resolve("API_METADATA")) + } else { rust("app_name: self.app_name,") } } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt index 68a6610180..fae4da0386 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecoratorTest.kt @@ -30,6 +30,7 @@ internal class SigV4SigningDecoratorTest { codegenContext, SigV4SigningConfig( codegenContext.runtimeConfig, + codegenContext.smithyRuntimeMode, true, SigV4Trait.builder().name("test-service").build(), ), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index 4003b0cacb..c6ba33baa1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -38,7 +38,12 @@ private class HttpConnectorConfigCustomization( private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( *preludeScope, + "Connection" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::Connection"), + "ConnectorSettings" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::ConnectorSettings"), + "DynConnectorAdapter" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::connections::adapter::DynConnectorAdapter"), "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), + "SharedAsyncSleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep::SharedAsyncSleep"), + "TimeoutConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout::TimeoutConfig"), ) override fun section(section: ServiceConfig): Writable { @@ -183,7 +188,29 @@ private class HttpConnectorConfigCustomization( } is ServiceConfig.BuilderBuild -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + let sleep_impl = layer.load::<#{SharedAsyncSleep}>().cloned(); + let timeout_config = layer.load::<#{TimeoutConfig}>().cloned().unwrap_or_else(#{TimeoutConfig}::disabled); + + let connector_settings = #{ConnectorSettings}::from_timeout_config(&timeout_config); + + if let Some(connection) = layer.load::<#{HttpConnector}>() + .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) + .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) { + let connection: #{Box} = #{Box}::new(#{DynConnectorAdapter}::new( + // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation + connection + )) as _; + layer.set_connection(connection); + } + + """, + *codegenScope, + "default_connector" to RuntimeType.smithyClient(runtimeConfig).resolve("conns::default_connector"), + ) + } else { rust("http_connector: self.http_connector,") } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 5b973feed5..1804635d34 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -7,11 +7,8 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations 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.ServiceRuntimePluginCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection 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.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 @@ -32,6 +29,7 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf "RetryConfig" to retryConfig.resolve("RetryConfig"), "SharedAsyncSleep" to sleepModule.resolve("SharedAsyncSleep"), "Sleep" to sleepModule.resolve("Sleep"), + "StandardRetryStrategy" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::retries::strategy::StandardRetryStrategy"), "TimeoutConfig" to timeoutModule.resolve("TimeoutConfig"), ) @@ -316,7 +314,15 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf } ServiceConfig.BuilderBuild -> { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + let retry_config = layer.load::<#{RetryConfig}>().cloned().unwrap_or_else(#{RetryConfig}::disabled); + layer.set_retry_strategy(#{StandardRetryStrategy}::new(&retry_config)); + """, + *codegenScope, + ) + } else { rustTemplate( // We call clone on sleep_impl because the field is used by // initializing the credentials_cache field later in the build @@ -363,28 +369,3 @@ class ResiliencyReExportCustomization(private val runtimeConfig: RuntimeConfig) } } } - -class ResiliencyServiceRuntimePluginCustomization( - private val codegenContext: ClientCodegenContext, -) : ServiceRuntimePluginCustomization() { - override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.AdditionalConfig) { - rustTemplate( - """ - if let Some(sleep_impl) = self.handle.conf.sleep_impl() { - ${section.newLayerName}.put(sleep_impl); - } - if let Some(timeout_config) = self.handle.conf.timeout_config() { - ${section.newLayerName}.put(timeout_config.clone()); - } - ${section.newLayerName}.put(self.handle.conf.time_source()#{maybe_clone}); - """, - "maybe_clone" to writable { - if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { - rust(".clone()") - } - }, - ) - } - } -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt index c26b620b03..0dacbd7290 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt @@ -114,7 +114,7 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust ServiceConfig.BuilderBuild -> { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - "self.inner.store_put(self.inner.load::<#{SharedTimeSource}>().cloned().unwrap_or_default());", + "layer.store_put(layer.load::<#{SharedTimeSource}>().cloned().unwrap_or_default());", *codegenScope, ) } else { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index 6c7882cba6..fcd61da066 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -15,11 +15,9 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.Idempote import software.amazon.smithy.rust.codegen.client.smithy.customizations.InterceptorConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization -import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.TimeSourceCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.TimeSourceOperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.smithy.RustCrate @@ -92,10 +90,4 @@ class RequiredCustomizations : ClientCodegenDecorator { } } } - - override fun serviceRuntimePluginCustomizations( - codegenContext: ClientCodegenContext, - baseCustomizations: List, - ): List = - baseCustomizations + listOf(ResiliencyServiceRuntimePluginCustomization(codegenContext)) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index 7f97053e01..9df495f67b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -34,6 +34,7 @@ internal class EndpointConfigCustomization( val resolverTrait = "#{SmithyResolver}<#{Params}>" val codegenScope = arrayOf( *preludeScope, + "DefaultEndpointResolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::endpoints::DefaultEndpointResolver"), "SharedEndpointResolver" to types.sharedEndpointResolver, "SmithyResolver" to types.resolveEndpoint, "Params" to typesGenerator.paramsStruct(), @@ -165,9 +166,11 @@ internal class EndpointConfigCustomization( if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - self.inner.store_put(self.inner.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(|| + let endpoint_resolver = #{DefaultEndpointResolver}::<#{Params}>::new( + layer.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(|| #{SharedEndpointResolver}::new(#{DefaultResolver}::new()) )); + layer.set_endpoint_resolver(endpoint_resolver); """, *codegenScope, "DefaultResolver" to defaultResolver, @@ -206,7 +209,11 @@ internal class EndpointConfigCustomization( if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - self.inner.store_put(self.inner.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(||#{SharedEndpointResolver}::new(#{FailingResolver}))); + let endpoint_resolver = #{DefaultEndpointResolver}::<#{Params}>::new( + layer.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(|| + #{SharedEndpointResolver}::new(#{FailingResolver}) + ).clone()); + layer.set_endpoint_resolver(endpoint_resolver); """, *codegenScope, "FailingResolver" to alwaysFailsResolver, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 6f9eecc412..aba765ff04 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -56,6 +56,7 @@ open class OperationGenerator( config_override: #{Option}, ) -> #{RuntimePlugins} { let mut runtime_plugins = runtime_plugins + .with_client_plugin(handle.conf.clone()) .with_client_plugin(crate::config::ServiceRuntimePlugin::new(handle)) .with_operation_plugin(operation); if let Some(config_override) = config_override { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index 40fa4859a0..f9c037494a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -54,6 +54,7 @@ class ServiceGenerator( ServiceRuntimePluginGenerator(codegenContext) .render(this, decorator.serviceRuntimePluginCustomizations(codegenContext, emptyList())) + serviceConfigGenerator.renderRuntimePluginImplForSelf(this) serviceConfigGenerator.renderRuntimePluginImplForBuilder(this) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index d5eafa4f1f..572e026f0a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -90,7 +90,6 @@ class ServiceRuntimePluginGenerator( "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), "Connection" to runtimeApi.resolve("client::orchestrator::Connection"), "ConnectorSettings" to RuntimeType.smithyClient(rc).resolve("http_connector::ConnectorSettings"), - "DefaultEndpointResolver" to runtime.resolve("client::orchestrator::endpoints::DefaultEndpointResolver"), "DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"), "HttpAuthSchemes" to runtimeApi.resolve("client::auth::HttpAuthSchemes"), "HttpConnector" to client.resolve("http_connector::HttpConnector"), @@ -103,7 +102,6 @@ class ServiceRuntimePluginGenerator( "ResolveEndpoint" to http.resolve("endpoint::ResolveEndpoint"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), - "default_connector" to client.resolve("conns::default_connector"), "require_connector" to client.resolve("conns::require_connector"), "TimeoutConfig" to smithyTypes.resolve("timeout::TimeoutConfig"), "RetryConfig" to smithyTypes.resolve("retry::RetryConfig"), @@ -140,37 +138,13 @@ class ServiceRuntimePluginGenerator( // Set an empty auth option resolver to be overridden by operations that need auth. cfg.set_auth_option_resolver(#{StaticAuthOptionResolver}::new(#{Vec}::new())); - let endpoint_resolver = #{DefaultEndpointResolver}::<#{Params}>::new( - self.handle.conf.endpoint_resolver()); - cfg.set_endpoint_resolver(endpoint_resolver); - - // TODO(enableNewSmithyRuntimeLaunch): Make it possible to set retry classifiers at the service level. - // Retry classifiers can also be set at the operation level and those should be added to the - // list of classifiers defined here, rather than replacing them. - - let sleep_impl = self.handle.conf.sleep_impl(); - let timeout_config = self.handle.conf.timeout_config().cloned().unwrap_or_else(#{TimeoutConfig}::disabled); - let retry_config = self.handle.conf.retry_config().cloned().unwrap_or_else(#{RetryConfig}::disabled); - - cfg.set_retry_strategy(#{StandardRetryStrategy}::new(&retry_config)); - - let connector_settings = #{ConnectorSettings}::from_timeout_config(&timeout_config); - if let Some(connection) = self.handle.conf.http_connector() - .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) - .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) { - let connection: #{Box} = #{Box}::new(#{DynConnectorAdapter}::new( - // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation - connection - )) as _; - cfg.set_connection(connection); - } #{additional_config} Some(cfg.freeze()) } fn interceptors(&self, interceptors: &mut #{InterceptorRegistrar}) { - interceptors.extend(self.handle.conf.interceptors().cloned()); + let _interceptors = interceptors; #{additional_interceptors} } } @@ -183,7 +157,7 @@ class ServiceRuntimePluginGenerator( writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg")) }, "additional_interceptors" to writable { - writeCustomizations(customizations, ServiceRuntimePluginSection.RegisterInterceptor("interceptors")) + writeCustomizations(customizations, ServiceRuntimePluginSection.RegisterInterceptor("_interceptors")) }, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt index 8a78ee4863..7e03e1ece5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt @@ -108,7 +108,7 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext ServiceConfig.BuilderBuild -> writable { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - "self.inner.store_put(self.inner.load::<#{IdempotencyTokenProvider}>().cloned().unwrap_or_else(#{default_provider}));", + "layer.store_put(layer.load::<#{IdempotencyTokenProvider}>().cloned().unwrap_or_else(#{default_provider}));", *codegenScope, ) } else { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 7096a93168..af7617766b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -321,8 +321,10 @@ class ServiceConfigGenerator( "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), + "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), + "Layer" to smithyTypes.resolve("config_bag::Layer"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), *preludeScope, ) @@ -426,6 +428,17 @@ class ServiceConfigGenerator( if (runtimeMode.defaultToOrchestrator) { rust("##[allow(unused_mut)]") rustBlock("pub fn build(mut self) -> Config") { + rustTemplate( + """ + use #{ConfigBagAccessors}; + // The builder is being turned into a service config. While doing so, we'd like to avoid + // requiring that items created and stored _during_ the build method be `Clone`, since they + // will soon be part of a `FrozenLayer` owned by the service config. So we will convert the + // current `CloneableLayer` into a `Layer` that does not impose the `Clone` requirement. + let mut layer: #{Layer} = self.inner.into(); + """, + *codegenScope, + ) customizations.forEach { it.section(ServiceConfig.BuilderBuild)(this) } @@ -433,7 +446,7 @@ class ServiceConfigGenerator( customizations.forEach { it.section(ServiceConfig.BuilderBuildExtras)(this) } - rust("inner: self.inner.freeze(),") + rust("inner: layer.freeze(),") } } } else { @@ -451,6 +464,25 @@ class ServiceConfigGenerator( } } + fun renderRuntimePluginImplForSelf(writer: RustWriter) { + writer.rustTemplate( + """ + impl #{RuntimePlugin} for Config { + fn config(&self) -> #{Option}<#{FrozenLayer}> { + #{Some}(self.inner.clone()) + } + + fn interceptors(&self, interceptors: &mut #{InterceptorRegistrar}) { + interceptors.extend(self.interceptors.iter().cloned()); + } + } + + """, + *codegenScope, + "config" to writable { writeCustomizations(customizations, ServiceConfig.RuntimePluginConfig("cfg")) }, + ) + } + fun renderRuntimePluginImplForBuilder(writer: RustWriter) { writer.rustTemplate( """ @@ -460,7 +492,7 @@ class ServiceConfigGenerator( ##[allow(unused_mut)] let mut cfg = #{CloneableLayer}::new("service config"); #{config} - Some(cfg.freeze()) + #{Some}(cfg.freeze()) } fn interceptors(&self, _interceptors: &mut #{InterceptorRegistrar}) { diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index aa7fefa9cb..0a68454097 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -12,7 +12,7 @@ use aws_smithy_runtime_api::client::interceptors::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ BoxError, ConfigBagAccessors, EndpointResolver, EndpointResolverParams, HttpRequest, }; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::endpoint::Endpoint; use http::header::HeaderName; use http::{HeaderValue, Uri}; @@ -65,6 +65,13 @@ pub struct DefaultEndpointResolver { inner: SharedEndpointResolver, } +impl Storable for DefaultEndpointResolver +where + Params: Debug + Send + Sync + 'static, +{ + type Storer = StoreReplace; +} + impl DefaultEndpointResolver { pub fn new(resolve_endpoint: SharedEndpointResolver) -> Self { Self { diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index bde9be5170..ac5b6b8cc3 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -13,7 +13,7 @@ use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt, }; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::retry::RetryConfig; use std::sync::Mutex; use std::time::Duration; @@ -32,6 +32,10 @@ pub struct StandardRetryStrategy { retry_permit: Mutex>, } +impl Storable for StandardRetryStrategy { + type Storer = StoreReplace; +} + impl StandardRetryStrategy { pub fn new(retry_config: &RetryConfig) -> Self { // TODO(enableNewSmithyRuntimeLaunch) add support for `retry_config.reconnect_mode()` here or in the orchestrator flow. diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index 994b0ea849..29400c2faa 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -113,6 +113,12 @@ impl Clone for CloneableLayer { } } +impl From for Layer { + fn from(cloneable_layer: CloneableLayer) -> Layer { + cloneable_layer.0 + } +} + // We need to "override" the mutable methods to encode the information that an item being stored // implements `Clone`. For the immutable methods, they can just be delegated via the `Deref` trait. impl CloneableLayer { From 6ad3195731df21ca68061fbc605587bce3d650b0 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 22 Jun 2023 11:05:10 -0700 Subject: [PATCH 177/253] Fixes for orchestrator doc hiddens and re-exports (#2801) ## Motivation and Context This PR: - Makes the `#[doc(hidden)]` attributes on orchestrator-only config functions be conditional based on whether the orchestrator is default or not. - Adds re-exports specific to the orchestrator. - Centralizes `BoxError`. - Changes organization of interceptor context types a bit. - Reduces visibility of `Phase` to `pub(crate)`. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../src/apigateway_interceptors.rs | 6 +- .../src/glacier_interceptors.rs | 11 +-- .../src/http_request_checksum.rs | 8 +- .../src/http_response_checksum.rs | 8 +- .../src/presigning_interceptors.rs | 8 +- .../src/route53_resource_id_preprocessor.rs | 6 +- .../aws-runtime/src/auth/sigv4.rs | 3 +- aws/rust-runtime/aws-runtime/src/identity.rs | 3 +- .../aws-runtime/src/invocation_id.rs | 12 +-- .../aws-runtime/src/recursion_detection.rs | 8 +- .../aws-runtime/src/request_info.rs | 9 ++- .../aws-runtime/src/retries/classifier.rs | 8 +- .../aws-runtime/src/user_agent.rs | 10 +-- .../AwsCustomizableOperationDecorator.kt | 8 +- .../smithy/rustsdk/CredentialProviders.kt | 2 +- .../rustsdk/HttpRequestChecksumDecorator.kt | 2 +- .../amazon/smithy/rustsdk/RegionDecorator.kt | 2 +- .../rustsdk/RetryClassifierDecorator.kt | 2 +- .../smithy/rustsdk/SdkConfigDecorator.kt | 2 +- .../smithy/rustsdk/UserAgentDecorator.kt | 2 +- .../customize/glacier/GlacierDecorator.kt | 3 - .../codegen/client/smithy/ClientRustModule.kt | 22 ++++- .../customizations/ApiKeyAuthDecorator.kt | 2 +- .../customizations/HttpAuthDecorator.kt | 2 +- .../InterceptorConfigCustomization.kt | 29 ++++--- .../ResiliencyConfigCustomization.kt | 31 +++---- .../EndpointParamsInterceptorGenerator.kt | 10 +-- .../ClientRuntimeTypesReExportGenerator.kt | 81 +++++++++++++++++++ .../smithy/generators/OperationGenerator.kt | 2 +- .../OperationRuntimePluginGenerator.kt | 4 +- .../smithy/generators/ServiceGenerator.kt | 4 +- .../ServiceRuntimePluginGenerator.kt | 4 +- .../client/CustomizableOperationGenerator.kt | 30 ++++--- .../config/ServiceConfigGenerator.kt | 8 +- .../protocol/MakeOperationGenerator.kt | 2 +- .../protocol/ProtocolTestGenerator.kt | 2 +- .../protocol/RequestSerializerGenerator.kt | 11 ++- .../testutil/TestConfigCustomization.kt | 2 +- .../config/ServiceConfigGeneratorTest.kt | 2 +- .../rust/codegen/core/smithy/RuntimeType.kt | 30 +++++++ .../aws-smithy-runtime-api/src/box_error.rs | 7 ++ .../aws-smithy-runtime-api/src/client/auth.rs | 3 +- .../src/client/auth/option_resolver.rs | 2 +- .../src/client/interceptors.rs | 27 +++---- .../src/client/interceptors/context.rs | 39 +++++---- .../src/client/interceptors/context/phase.rs | 16 ++-- .../client/interceptors/context/wrappers.rs | 11 ++- .../src/client/interceptors/error.rs | 4 +- .../src/client/orchestrator.rs | 2 +- .../src/client/orchestrator/error.rs | 2 +- .../src/client/retries.rs | 6 +- .../src/client/runtime_plugin.rs | 3 +- .../aws-smithy-runtime-api/src/lib.rs | 3 + .../src/client/auth/http.rs | 3 +- .../src/client/interceptors.rs | 6 +- .../src/client/orchestrator.rs | 31 ++++--- .../src/client/orchestrator/auth.rs | 7 +- .../src/client/orchestrator/endpoints.rs | 5 +- .../interceptors/service_clock_skew.rs | 6 +- .../src/client/retries/classifier.rs | 4 +- .../src/client/retries/client_rate_limiter.rs | 3 +- .../client/retries/strategy/fixed_delay.rs | 4 +- .../src/client/retries/strategy/never.rs | 4 +- .../src/client/retries/strategy/standard.rs | 11 ++- .../client/runtime_plugin/anonymous_auth.rs | 3 +- .../src/client/test_util/interceptors.rs | 8 +- .../src/client/test_util/serializer.rs | 3 +- 67 files changed, 383 insertions(+), 241 deletions(-) create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt create mode 100644 rust-runtime/aws-smithy-runtime-api/src/box_error.rs diff --git a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs index 76953ac510..06398a325b 100644 --- a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs @@ -5,9 +5,9 @@ #![allow(dead_code)] -use aws_smithy_runtime_api::client::interceptors::{ - BeforeTransmitInterceptorContextMut, BoxError, Interceptor, -}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::ConfigBag; use http::header::ACCEPT; use http::HeaderValue; diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs index b581f4009e..55aa9fd822 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -10,10 +10,11 @@ use aws_runtime::auth::sigv4::SigV4OperationSigningConfig; use aws_sigv4::http_request::SignableBody; use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream; -use aws_smithy_runtime_api::client::interceptors::{ - BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, BoxError, - Interceptor, +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, }; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, LoadedRequestBody}; use aws_smithy_types::config_bag::ConfigBag; use bytes::Bytes; @@ -234,7 +235,7 @@ fn compute_hash_tree(mut hashes: Vec) -> Digest { #[cfg(test)] mod account_id_autofill_tests { use super::*; - use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_types::type_erasure::TypedBox; #[test] @@ -273,7 +274,7 @@ mod account_id_autofill_tests { #[cfg(test)] mod api_version_tests { use super::*; - use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_types::type_erasure::TypedBox; #[test] diff --git a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs index f64bde8173..2cb87a04af 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs @@ -14,11 +14,11 @@ use aws_smithy_checksums::ChecksumAlgorithm; use aws_smithy_checksums::{body::calculate, http::HttpChecksum}; use aws_smithy_http::body::{BoxBody, SdkBody}; use aws_smithy_http::operation::error::BuildError; -use aws_smithy_runtime_api::client::interceptors::context::Input; -use aws_smithy_runtime_api::client::interceptors::{ - BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, BoxError, - Interceptor, +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, Input, }; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; use http::HeaderValue; use http_body::Body; diff --git a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs index c6d504edd8..4f3f9dfea5 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs @@ -9,11 +9,11 @@ use aws_smithy_checksums::ChecksumAlgorithm; use aws_smithy_http::body::{BoxBody, SdkBody}; -use aws_smithy_runtime_api::client::interceptors::context::Input; -use aws_smithy_runtime_api::client::interceptors::{ - BeforeDeserializationInterceptorContextMut, BeforeSerializationInterceptorContextRef, BoxError, - Interceptor, +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeDeserializationInterceptorContextMut, BeforeSerializationInterceptorContextRef, Input, }; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; use http::HeaderValue; use std::{fmt, mem}; diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs index 8d67e3ae6a..c3151503b6 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs @@ -14,10 +14,12 @@ use aws_runtime::user_agent::UserAgentInterceptor; use aws_sigv4::http_request::SignableBody; use aws_smithy_async::time::{SharedTimeSource, StaticTimeSource}; use aws_smithy_runtime::client::retries::strategy::NeverRetryStrategy; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, +}; use aws_smithy_runtime_api::client::interceptors::{ - disable_interceptor, BeforeSerializationInterceptorContextMut, - BeforeTransmitInterceptorContextMut, BoxError, Interceptor, InterceptorRegistrar, - SharedInterceptor, + disable_interceptor, Interceptor, InterceptorRegistrar, SharedInterceptor, }; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; diff --git a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs index 132b84613b..0ed2a354d2 100644 --- a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs +++ b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs @@ -5,9 +5,9 @@ #![allow(dead_code)] -use aws_smithy_runtime_api::client::interceptors::{ - BeforeSerializationInterceptorContextMut, BoxError, Interceptor, -}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeSerializationInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::ConfigBag; use std::fmt; use std::marker::PhantomData; diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs index 1ac3f800de..0e64408830 100644 --- a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -8,11 +8,12 @@ use aws_sigv4::http_request::{ sign, PayloadChecksumKind, PercentEncodingMode, SessionTokenMode, SignableBody, SignableRequest, SignatureLocation, SigningParams, SigningSettings, UriPathNormalizationMode, }; +use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::{ AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpRequest}; +use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpRequest}; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::Document; use aws_types::region::{Region, SigningRegion}; diff --git a/aws/rust-runtime/aws-runtime/src/identity.rs b/aws/rust-runtime/aws-runtime/src/identity.rs index ba3d5f9fb4..84e20d2afe 100644 --- a/aws/rust-runtime/aws-runtime/src/identity.rs +++ b/aws/rust-runtime/aws-runtime/src/identity.rs @@ -6,8 +6,9 @@ /// Credentials-based identity support. pub mod credentials { use aws_credential_types::cache::SharedCredentialsCache; + use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver}; - use aws_smithy_runtime_api::client::orchestrator::{BoxError, Future}; + use aws_smithy_runtime_api::client::orchestrator::Future; use aws_smithy_types::config_bag::ConfigBag; /// Smithy identity resolver for AWS credentials. diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index cd4bd4a438..49d9b56c7f 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -3,10 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::interceptors::error::BoxError; -use aws_smithy_runtime_api::client::interceptors::{ - BeforeTransmitInterceptorContextMut, Interceptor, -}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::ConfigBag; use http::{HeaderName, HeaderValue}; use std::fmt::Debug; @@ -156,9 +155,10 @@ mod test_util { mod tests { use crate::invocation_id::{InvocationId, InvocationIdInterceptor}; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::{ - BeforeTransmitInterceptorContextMut, Interceptor, InterceptorContext, + use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeTransmitInterceptorContextMut, InterceptorContext, }; + use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::type_erasure::TypeErasedBox; use http::HeaderValue; diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs index d7101e686a..c8b28b07b4 100644 --- a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::interceptors::{ - BeforeTransmitInterceptorContextMut, BoxError, Interceptor, -}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::ConfigBag; use aws_types::os_shim_internal::Env; use http::HeaderValue; @@ -74,7 +74,7 @@ mod tests { use super::*; use aws_smithy_http::body::SdkBody; use aws_smithy_protocol_test::{assert_ok, validate_headers}; - use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_types::type_erasure::TypeErasedBox; use aws_types::os_shim_internal::Env; use http::HeaderValue; diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index d5776a70d3..974ca051aa 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -4,9 +4,9 @@ */ use aws_smithy_runtime::client::orchestrator::interceptors::ServiceClockSkew; -use aws_smithy_runtime_api::client::interceptors::{ - BeforeTransmitInterceptorContextMut, BoxError, Interceptor, -}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::date_time::Format; @@ -164,7 +164,8 @@ mod tests { use super::RequestInfoInterceptor; use crate::request_info::RequestPairs; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; diff --git a/aws/rust-runtime/aws-runtime/src/retries/classifier.rs b/aws/rust-runtime/aws-runtime/src/retries/classifier.rs index 0cbeb902a0..5a7675dba2 100644 --- a/aws/rust-runtime/aws-runtime/src/retries/classifier.rs +++ b/aws/rust-runtime/aws-runtime/src/retries/classifier.rs @@ -4,7 +4,7 @@ */ use aws_smithy_http::http::HttpHeaders; -use aws_smithy_runtime_api::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::error::metadata::ProvideErrorMetadata; @@ -105,12 +105,8 @@ impl ClassifyRetry for AmzRetryAfterHeaderClassifier { #[cfg(test)] mod test { - use super::{AmzRetryAfterHeaderClassifier, AwsErrorCodeClassifier}; + use super::*; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::InterceptorContext; - use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; - use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; - use aws_smithy_types::error::metadata::ProvideErrorMetadata; use aws_smithy_types::error::ErrorMetadata; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 7275490432..566dd37a07 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -4,10 +4,9 @@ */ use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; -use aws_smithy_runtime_api::client::interceptors::error::BoxError; -use aws_smithy_runtime_api::client::interceptors::{ - BeforeTransmitInterceptorContextMut, Interceptor, -}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::ConfigBag; use aws_types::app_name::AppName; use aws_types::os_shim_internal::Env; @@ -109,7 +108,8 @@ impl Interceptor for UserAgentInterceptor { mod tests { use super::*; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::{Interceptor, InterceptorContext}; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::type_erasure::TypeErasedBox; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index baeb8c4866..fa98df834a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -20,14 +20,12 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : *RuntimeType.preludeScope, "AwsUserAgent" to AwsRuntimeType.awsHttp(runtimeConfig) .resolve("user_agent::AwsUserAgent"), - "BeforeTransmitInterceptorContextMut" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::interceptors::BeforeTransmitInterceptorContextMut"), - "ConfigBag" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag"), + "BeforeTransmitInterceptorContextMut" to RuntimeType.beforeTransmitInterceptorContextMut(runtimeConfig), + "ConfigBag" to RuntimeType.configBag(runtimeConfig), "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::orchestrator::ConfigBagAccessors"), "http" to CargoDependency.Http.toType(), - "InterceptorContext" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::interceptors::InterceptorContext"), + "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), "SharedTimeSource" to CargoDependency.smithyAsync(runtimeConfig).withFeature("test-util").toType() .resolve("time::SharedTimeSource"), "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index 58688d3a7b..fd0595d74d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -53,7 +53,7 @@ class CredentialsProviderDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { rustCrate.mergeFeature(TestUtilFeature.copy(deps = listOf("aws-credential-types/test-util"))) - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { rust( "pub use #T::Credentials;", AwsRuntimeType.awsCredentialTypes(codegenContext.runtimeConfig), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt index b6bca07f39..610ad1c579 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt @@ -154,7 +154,7 @@ class HttpRequestChecksumCustomization( }) """, *preludeScope, - "BoxError" to runtimeApi.resolve("client::interceptors::error::BoxError"), + "BoxError" to RuntimeType.boxError(runtimeConfig), "Input" to runtimeApi.resolve("client::interceptors::context::Input"), "OperationInput" to codegenContext.symbolProvider.toSymbol(inputShape), "RequestChecksumInterceptor" to runtimeConfig.awsInlineableHttpRequestChecksum() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt index 172ba2c1ae..7e38722e3f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt @@ -116,7 +116,7 @@ class RegionDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { if (usesRegion(codegenContext)) { - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { rust("pub use #T::Region;", region(codegenContext.runtimeConfig)) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt index 8eb68da500..b9f901131d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt @@ -64,7 +64,7 @@ class OperationRetryClassifiersFeature( "HttpStatusCodeClassifier" to smithyRuntime.resolve("client::retries::classifier::HttpStatusCodeClassifier"), // Other Types "ClassifyRetry" to smithyRuntimeApi.resolve("client::retries::ClassifyRetry"), - "InterceptorContext" to smithyRuntimeApi.resolve("client::interceptor::InterceptorContext"), + "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), "OperationError" to codegenContext.symbolProvider.symbolForOperationError(operation), "OrchestratorError" to smithyRuntimeApi.resolve("client::orchestrator::OrchestratorError"), "RetryReason" to smithyRuntimeApi.resolve("client::retries::RetryReason"), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt index 20457237c9..f3277d06d3 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SdkConfigDecorator.kt @@ -105,7 +105,7 @@ class SdkConfigDecorator : ClientCodegenDecorator { val codegenScope = arrayOf( "SdkConfig" to AwsRuntimeType.awsTypes(codegenContext.runtimeConfig).resolve("sdk_config::SdkConfig"), ) - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { rustTemplate( """ impl From<&#{SdkConfig}> for Builder { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index 64c62b400a..ebeee1c85a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -88,7 +88,7 @@ class UserAgentDecorator : ClientCodegenDecorator { ) } - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { // Re-export the app name so that it can be specified in config programmatically without an explicit dependency rustTemplate( "pub use #{AppName};", diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt index 5262471c09..525acffd0a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt @@ -97,9 +97,6 @@ private class GlacierAccountIdCustomization(private val codegenContext: ClientCo } } -// TODO(enableNewSmithyRuntimeLaunch): Install the glacier customizations as a single additional runtime plugin instead -// of wiring up the interceptors individually - /** Adds the `x-amz-glacier-version` header to all requests */ private class GlacierApiVersionCustomization(private val codegenContext: ClientCodegenContext) : ServiceRuntimePluginCustomization() { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt index 988cac62f4..5f46cdafb1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt @@ -54,7 +54,22 @@ object ClientRustModule { val customize = RustModule.public("customize", parent = self) } - val Config = RustModule.public("config") + /** crate::config */ + val config = Config.self + object Config { + /** crate::client */ + val self = RustModule.public("config") + + /** crate::config::retry */ + val retry = RustModule.public("retry", parent = self) + + /** crate::config::timeout */ + val timeout = RustModule.public("timeout", parent = self) + + /** crate::config::interceptors */ + val interceptors = RustModule.public("interceptors", parent = self) + } + val Error = RustModule.public("error") val Endpoint = RustModule.public("endpoint") val Operation = RustModule.public("operation") @@ -83,7 +98,10 @@ class ClientModuleDocProvider( return when (module) { ClientRustModule.client -> clientModuleDoc() ClientRustModule.Client.customize -> customizeModuleDoc() - ClientRustModule.Config -> strDoc("Configuration for $serviceName.") + ClientRustModule.config -> strDoc("Configuration for $serviceName.") + ClientRustModule.Config.retry -> strDoc("Retry configuration.") + ClientRustModule.Config.timeout -> strDoc("Timeout configuration.") + ClientRustModule.Config.interceptors -> strDoc("Types needed to implement [`Interceptor`](crate::config::Interceptor).") ClientRustModule.Error -> strDoc("Common errors and error handling utilities.") ClientRustModule.Endpoint -> strDoc("Endpoint resolution functionality.") ClientRustModule.Operation -> strDoc("All operations that this crate can perform.") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt index a0b6a4fabf..4cf45a49ef 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt @@ -66,7 +66,7 @@ class ApiKeyAuthDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { if (applies(codegenContext)) { - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { rust("pub use #T;", apiKey(codegenContext.runtimeConfig)) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index c985d7fd47..bc3d2de4f1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -121,7 +121,7 @@ class HttpAuthDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { val authSchemes = HttpAuthSchemes.from(codegenContext) if (authSchemes.anyEnabled()) { - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { val codegenScope = codegenScope(codegenContext.runtimeConfig) if (authSchemes.isTokenBased()) { rustTemplate("pub use #{Token};", *codegenScope) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt index 7bca1950d3..b193172b55 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt @@ -5,21 +5,28 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext 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.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.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCustomization() { +class InterceptorConfigCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { private val moduleUseName = codegenContext.moduleUseName() private val runtimeConfig = codegenContext.runtimeConfig - private val interceptors = RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors") + + // TODO(enableNewSmithyRuntimeCleanup): Remove the writable below + private val maybeHideOrchestratorCode = writable { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rust("##[doc(hidden)]") + } + } private val codegenScope = arrayOf( - "Interceptor" to interceptors.resolve("Interceptor"), - "SharedInterceptor" to interceptors.resolve("SharedInterceptor"), + "Interceptor" to RuntimeType.interceptor(runtimeConfig), + "SharedInterceptor" to RuntimeType.sharedInterceptor(runtimeConfig), + "maybe_hide_orchestrator_code" to maybeHideOrchestratorCode, ) override fun section(section: ServiceConfig) = @@ -38,8 +45,7 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus ServiceConfig.ConfigImpl -> rustTemplate( """ - // TODO(enableNewSmithyRuntimeLaunch): Remove this doc hidden upon launch - ##[doc(hidden)] + #{maybe_hide_orchestrator_code} /// Returns interceptors currently registered by the user. pub fn interceptors(&self) -> impl Iterator + '_ { self.interceptors.iter() @@ -51,8 +57,7 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus ServiceConfig.BuilderImpl -> rustTemplate( """ - // TODO(enableNewSmithyRuntimeLaunch): Remove this doc hidden upon launch - ##[doc(hidden)] + #{maybe_hide_orchestrator_code} /// Add an [`Interceptor`](#{Interceptor}) that runs at specific stages of the request execution pipeline. /// /// Interceptors targeted at a certain stage are executed according to the pre-defined priority. @@ -102,8 +107,7 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus self } - // TODO(enableNewSmithyRuntimeLaunch): Remove this doc hidden upon launch - ##[doc(hidden)] + #{maybe_hide_orchestrator_code} /// Add a [`SharedInterceptor`](#{SharedInterceptor}) that runs at specific stages of the request execution pipeline. /// /// Interceptors targeted at a certain stage are executed according to the pre-defined priority. @@ -156,8 +160,7 @@ class InterceptorConfigCustomization(codegenContext: CodegenContext) : ConfigCus self } - // TODO(enableNewSmithyRuntimeLaunch): Remove this doc hidden upon launch - ##[doc(hidden)] + #{maybe_hide_orchestrator_code} /// Set [`SharedInterceptor`](#{SharedInterceptor})s for the builder. pub fn set_interceptors(&mut self, interceptors: impl IntoIterator) -> &mut Self { self.interceptors = interceptors.into_iter().collect(); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 1804635d34..314dd8d668 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -344,26 +344,21 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf class ResiliencyReExportCustomization(private val runtimeConfig: RuntimeConfig) { fun extras(rustCrate: RustCrate) { - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { rustTemplate( - """ - pub use #{sleep}::{AsyncSleep, SharedAsyncSleep, Sleep}; - - /// Retry configuration - /// - /// These are re-exported from `aws-smithy-types` for convenience. - pub mod retry { - pub use #{types_retry}::{RetryConfig, RetryConfigBuilder, RetryMode}; - } - /// Timeout configuration - /// - /// These are re-exported from `aws-smithy-types` for convenience. - pub mod timeout { - pub use #{timeout}::{TimeoutConfig, TimeoutConfigBuilder}; - } - """, - "types_retry" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry"), + "pub use #{sleep}::{AsyncSleep, SharedAsyncSleep, Sleep};", "sleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep"), + ) + } + rustCrate.withModule(ClientRustModule.Config.retry) { + rustTemplate( + "pub use #{types_retry}::{RetryConfig, RetryConfigBuilder, RetryMode};", + "types_retry" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry"), + ) + } + rustCrate.withModule(ClientRustModule.Config.timeout) { + rustTemplate( + "pub use #{timeout}::{TimeoutConfig, TimeoutConfigBuilder};", "timeout" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout"), ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 1371203522..44245bdabe 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -42,17 +42,17 @@ class EndpointParamsInterceptorGenerator( val orchestrator = runtimeApi.resolve("client::orchestrator") val smithyTypes = CargoDependency.smithyTypes(rc).toType() arrayOf( - "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), - "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), + "BoxError" to RuntimeType.boxError(rc), + "ConfigBag" to RuntimeType.configBag(rc), "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(rc) .resolve("client::orchestrator::ConfigBagAccessors"), "ContextAttachedError" to interceptors.resolve("error::ContextAttachedError"), "EndpointResolverParams" to orchestrator.resolve("EndpointResolverParams"), "HttpRequest" to orchestrator.resolve("HttpRequest"), "HttpResponse" to orchestrator.resolve("HttpResponse"), - "Interceptor" to interceptors.resolve("Interceptor"), - "InterceptorContext" to interceptors.resolve("InterceptorContext"), - "BeforeSerializationInterceptorContextRef" to interceptors.resolve("context::wrappers::BeforeSerializationInterceptorContextRef"), + "Interceptor" to RuntimeType.interceptor(rc), + "InterceptorContext" to RuntimeType.interceptorContext(rc), + "BeforeSerializationInterceptorContextRef" to RuntimeType.beforeSerializationInterceptorContextRef(rc), "Input" to interceptors.resolve("context::Input"), "Output" to interceptors.resolve("context::Output"), "Error" to interceptors.resolve("context::Error"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt new file mode 100644 index 0000000000..71d8ab9e4c --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt @@ -0,0 +1,81 @@ +/* + * 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 + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule +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.RustCrate + +class ClientRuntimeTypesReExportGenerator( + private val codegenContext: ClientCodegenContext, + private val rustCrate: RustCrate, +) { + fun render() { + if (!codegenContext.smithyRuntimeMode.generateOrchestrator) { + return + } + + val rc = codegenContext.runtimeConfig + val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(rc) + + rustCrate.withModule(ClientRustModule.config) { + rustTemplate( + """ + pub use #{ConfigBag}; + pub use #{Interceptor}; + """, + "ConfigBag" to RuntimeType.configBag(rc), + "Interceptor" to RuntimeType.interceptor(rc), + ) + } + rustCrate.withModule(ClientRustModule.Config.retry) { + rustTemplate( + """ + pub use #{ClassifyRetry}; + pub use #{RetryReason}; + pub use #{ShouldAttempt}; + """, + "ClassifyRetry" to smithyRuntimeApi.resolve("client::retries::ClassifyRetry"), + "RetryReason" to smithyRuntimeApi.resolve("client::retries::RetryReason"), + "ShouldAttempt" to smithyRuntimeApi.resolve("client::retries::ShouldAttempt"), + ) + } + rustCrate.withModule(ClientRustModule.Config.interceptors) { + rustTemplate( + """ + pub use #{AfterDeserializationInterceptorContextRef}; + pub use #{BeforeDeserializationInterceptorContextMut}; + pub use #{BeforeDeserializationInterceptorContextRef}; + pub use #{BeforeSerializationInterceptorContextMut}; + pub use #{BeforeSerializationInterceptorContextRef}; + pub use #{BeforeTransmitInterceptorContextMut}; + pub use #{BeforeTransmitInterceptorContextRef}; + pub use #{FinalizerInterceptorContextMut}; + pub use #{FinalizerInterceptorContextRef}; + pub use #{InterceptorContext}; + """, + "AfterDeserializationInterceptorContextRef" to RuntimeType.afterDeserializationInterceptorContextRef(rc), + "BeforeDeserializationInterceptorContextMut" to RuntimeType.beforeDeserializationInterceptorContextMut(rc), + "BeforeDeserializationInterceptorContextRef" to RuntimeType.beforeDeserializationInterceptorContextRef(rc), + "BeforeSerializationInterceptorContextMut" to RuntimeType.beforeSerializationInterceptorContextMut(rc), + "BeforeSerializationInterceptorContextRef" to RuntimeType.beforeSerializationInterceptorContextRef(rc), + "BeforeTransmitInterceptorContextMut" to RuntimeType.beforeTransmitInterceptorContextMut(rc), + "BeforeTransmitInterceptorContextRef" to RuntimeType.beforeTransmitInterceptorContextRef(rc), + "FinalizerInterceptorContextMut" to RuntimeType.finalizerInterceptorContextMut(rc), + "FinalizerInterceptorContextRef" to RuntimeType.finalizerInterceptorContextRef(rc), + "InterceptorContext" to RuntimeType.interceptorContext(rc), + ) + } + rustCrate.withModule(ClientRustModule.Error) { + rustTemplate( + "pub use #{BoxError};", + "BoxError" to RuntimeType.boxError(rc), + ) + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index aba765ff04..163d8e8bcd 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -191,7 +191,7 @@ open class OperationGenerator( *codegenScope, "Error" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::Error"), "TypedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypedBox"), - "InterceptorContext" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::InterceptorContext"), + "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), "OrchestratorError" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::error::OrchestratorError"), "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugins"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index 4a3d6d5530..e47e279421 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -26,10 +26,10 @@ class OperationRuntimePluginGenerator( val smithyTypes = RuntimeType.smithyTypes(rc) arrayOf( "AuthOptionResolverParams" to runtimeApi.resolve("client::auth::AuthOptionResolverParams"), - "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), + "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "Layer" to smithyTypes.resolve("config_bag::Layer"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), - "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), + "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index f9c037494a..571abbd508 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -40,7 +40,7 @@ class ServiceGenerator( decorator.errorCustomizations(codegenContext, emptyList()), ).render(rustCrate) - rustCrate.withModule(ClientRustModule.Config) { + rustCrate.withModule(ClientRustModule.config) { val serviceConfigGenerator = ServiceConfigGenerator.withBaseBehavior( codegenContext, extraCustomizations = decorator.configCustomizations(codegenContext, listOf()), @@ -63,5 +63,7 @@ class ServiceGenerator( Attribute.DocInline.render(this) write("pub use config::Config;") } + + ClientRuntimeTypesReExportGenerator(codegenContext, rustCrate).render() } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 572e026f0a..0a73a283b0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -83,8 +83,8 @@ class ServiceRuntimePluginGenerator( *preludeScope, "Arc" to RuntimeType.Arc, "AnonymousIdentityResolver" to runtimeApi.resolve("client::identity::AnonymousIdentityResolver"), - "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), - "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), + "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), + "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), "Layer" to smithyTypes.resolve("config_bag::Layer"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index 37fac72b63..c8afcb7a96 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -35,23 +35,21 @@ class CustomizableOperationGenerator( fun render(crate: RustCrate) { crate.withModule(ClientRustModule.Client.customize) { - rustTemplate( - // TODO(enableNewSmithyRuntimeCleanup): Stop exporting `Operation` when removing middleware - // TODO(enableNewSmithyRuntimeLaunch): Re-export orchestrator equivalents for retry types when defaulting to orchestrator - """ - pub use #{Operation}; - pub use #{Request}; - pub use #{Response}; - pub use #{ClassifyRetry}; - pub use #{RetryKind}; - """, - "Operation" to smithyHttp.resolve("operation::Operation"), - "Request" to smithyHttp.resolve("operation::Request"), - "Response" to smithyHttp.resolve("operation::Response"), - "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), - "RetryKind" to smithyTypes.resolve("retry::RetryKind"), - ) if (codegenContext.smithyRuntimeMode.generateMiddleware) { + rustTemplate( + """ + pub use #{Operation}; + pub use #{Request}; + pub use #{Response}; + pub use #{ClassifyRetry}; + pub use #{RetryKind}; + """, + "Operation" to smithyHttp.resolve("operation::Operation"), + "Request" to smithyHttp.resolve("operation::Request"), + "Response" to smithyHttp.resolve("operation::Response"), + "ClassifyRetry" to RuntimeType.classifyRetry(runtimeConfig), + "RetryKind" to smithyTypes.resolve("retry::RetryKind"), + ) renderCustomizableOperationModule(this) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index af7617766b..6e899a5e99 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -151,7 +151,7 @@ data class ConfigParam( * Therefore, primitive types, such as bool and String, need to be wrapped in newtypes to make them distinct. */ fun configParamNewtype(newtypeName: String, inner: Symbol, runtimeConfig: RuntimeConfig) = - RuntimeType.forInlineFun(newtypeName, ClientRustModule.Config) { + RuntimeType.forInlineFun(newtypeName, ClientRustModule.config) { val codegenScope = arrayOf( "Storable" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::Storable"), "StoreReplace" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::StoreReplace"), @@ -299,7 +299,7 @@ typealias ConfigCustomization = NamedCustomization */ class ServiceConfigGenerator( - private val codegenContext: ClientCodegenContext, + codegenContext: ClientCodegenContext, private val customizations: List = listOf(), ) { companion object { @@ -318,9 +318,9 @@ class ServiceConfigGenerator( private val runtimeApi = RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) private val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig) val codegenScope = arrayOf( - "BoxError" to runtimeApi.resolve("client::runtime_plugin::BoxError"), + "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), - "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), + "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt index 54c2e3a7eb..c4af5f97fb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt @@ -59,7 +59,7 @@ open class MakeOperationGenerator( private val codegenScope = arrayOf( *preludeScope, - "config" to ClientRustModule.Config, + "config" to ClientRustModule.config, "header_util" to RuntimeType.smithyHttp(runtimeConfig).resolve("header"), "http" to RuntimeType.Http, "operation" to RuntimeType.operationModule(runtimeConfig), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index 4ed9e678a5..d9d978301c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -219,7 +219,7 @@ class DefaultProtocolTestGenerator( .withFeature("test-util") .toType() .resolve("test_connection::capture_request"), - "config" to ClientRustModule.Config, + "config" to ClientRustModule.config, "customParams" to customParams, ) renderClientCreation(this, ClientCreationParams(codegenContext, "conn", "config_builder", "client")) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt index 99adee16b5..d89536960b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt @@ -11,7 +11,6 @@ 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.http.RequestBindingGenerator import software.amazon.smithy.rust.codegen.client.smithy.protocols.ClientAdditionalPayloadContext -import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -35,14 +34,14 @@ class RequestSerializerGenerator( private val httpBindingResolver = protocol.httpBindingResolver private val symbolProvider = codegenContext.symbolProvider private val codegenScope by lazy { - val runtimeApi = CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig).toType() + val runtimeApi = RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) val interceptorContext = runtimeApi.resolve("client::interceptors::context") val orchestrator = runtimeApi.resolve("client::orchestrator") - val smithyTypes = CargoDependency.smithyTypes(codegenContext.runtimeConfig).toType() + val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig) arrayOf( - "BoxError" to orchestrator.resolve("BoxError"), - "config" to ClientRustModule.Config, - "ConfigBag" to smithyTypes.resolve("config_bag::ConfigBag"), + "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), + "config" to ClientRustModule.config, + "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), "header_util" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("header"), "http" to RuntimeType.Http, "HttpRequest" to orchestrator.resolve("HttpRequest"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt index f3d12e9b00..f37a925ae1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt @@ -127,7 +127,7 @@ fun validateConfigCustomizations( 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) { + project.withModule(ClientRustModule.config) { generator.render(this) unitTest( "config_send_sync", diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt index be848af92f..646d6863e9 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt @@ -165,7 +165,7 @@ internal class ServiceConfigGeneratorTest { val sut = ServiceConfigGenerator(codegenContext, listOf(ServiceCustomizer(codegenContext))) val symbolProvider = codegenContext.symbolProvider val project = TestWorkspace.testProject(symbolProvider) - project.withModule(ClientRustModule.Config) { + project.withModule(ClientRustModule.config) { sut.render(this) if (smithyRuntimeMode.defaultToOrchestrator) { unitTest( diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 5a8f8a0020..4b77c0f589 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -334,6 +334,36 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun base64Encode(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("base64::encode") + fun configBag(runtimeConfig: RuntimeConfig): RuntimeType = + smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag") + fun boxError(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("box_error::BoxError") + fun interceptor(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::Interceptor") + fun interceptorContext(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::InterceptorContext") + fun sharedInterceptor(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::SharedInterceptor") + + fun afterDeserializationInterceptorContextRef(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::AfterDeserializationInterceptorContextRef") + fun beforeSerializationInterceptorContextRef(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeSerializationInterceptorContextRef") + fun beforeSerializationInterceptorContextMut(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeSerializationInterceptorContextMut") + fun beforeDeserializationInterceptorContextRef(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeDeserializationInterceptorContextRef") + fun beforeDeserializationInterceptorContextMut(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeDeserializationInterceptorContextMut") + fun beforeTransmitInterceptorContextRef(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeTransmitInterceptorContextRef") + fun beforeTransmitInterceptorContextMut(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::BeforeTransmitInterceptorContextMut") + fun finalizerInterceptorContextRef(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::FinalizerInterceptorContextRef") + fun finalizerInterceptorContextMut(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::FinalizerInterceptorContextMut") + fun blob(runtimeConfig: RuntimeConfig) = smithyTypes(runtimeConfig).resolve("Blob") fun byteStream(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("byte_stream::ByteStream") fun classifyRetry(runtimeConfig: RuntimeConfig) = smithyHttp(runtimeConfig).resolve("retry::ClassifyRetry") diff --git a/rust-runtime/aws-smithy-runtime-api/src/box_error.rs b/rust-runtime/aws-smithy-runtime-api/src/box_error.rs new file mode 100644 index 0000000000..1ed436d404 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/box_error.rs @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// A boxed error that is `Send` and `Sync`. +pub type BoxError = Box; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 08d2586171..16470a9490 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::box_error::BoxError; use crate::client::identity::{Identity, IdentityResolver, IdentityResolvers}; -use crate::client::orchestrator::{BoxError, HttpRequest}; +use crate::client::orchestrator::HttpRequest; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_types::Document; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs index 8f61bc8e0c..bf973bf6a4 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::box_error::BoxError; use crate::client::auth::{AuthOptionResolver, AuthOptionResolverParams, AuthSchemeId}; -use crate::client::orchestrator::BoxError; use std::borrow::Cow; /// New-type around a `Vec` that implements `AuthOptionResolver`. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index 0f13473ce0..d0e678563b 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -3,30 +3,27 @@ * SPDX-License-Identifier: Apache-2.0 */ -pub mod context; -pub mod error; - -use crate::client::interceptors::context::wrappers::{ - FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, +use crate::box_error::BoxError; +use crate::client::interceptors::context::{ + AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, + BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, + BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, + BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut, + FinalizerInterceptorContextRef, InterceptorContext, }; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend}; use aws_smithy_types::error::display::DisplayErrorContext; -pub use context::{ - wrappers::{ - AfterDeserializationInterceptorContextMut, AfterDeserializationInterceptorContextRef, - BeforeDeserializationInterceptorContextMut, BeforeDeserializationInterceptorContextRef, - BeforeSerializationInterceptorContextMut, BeforeSerializationInterceptorContextRef, - BeforeTransmitInterceptorContextMut, BeforeTransmitInterceptorContextRef, - }, - InterceptorContext, -}; use context::{Error, Input, Output}; -pub use error::{BoxError, InterceptorError}; use std::fmt::{Debug, Formatter}; use std::marker::PhantomData; use std::ops::Deref; use std::sync::Arc; +pub mod context; +pub mod error; + +pub use error::InterceptorError; + macro_rules! interceptor_trait_fn { ($name:ident, $phase:ident, $docs:tt) => { #[doc = $docs] diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index f3b50e281e..a33b62db63 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -7,19 +7,19 @@ //! //! Interceptors have access to varying pieces of context during the course of an operation. //! -//! An operation is composed of multiple phases. The initial phase is [`Phase::BeforeSerialization`], which -//! has the original input as context. The next phase is [`Phase::BeforeTransmit`], which has the serialized +//! An operation is composed of multiple phases. The initial phase is "before serialization", which +//! has the original input as context. The next phase is "before transmit", which has the serialized //! request as context. Depending on which hook is being called with the dispatch context, //! the serialized request may or may not be signed (which should be apparent from the hook name). -//! Following the [`Phase::BeforeTransmit`] phase is the [`Phase::BeforeDeserialization`] phase, which has -//! the raw response available as context. Finally, the [`Phase::AfterDeserialization`] phase +//! Following the "before transmit" phase is the "before deserialization" phase, which has +//! the raw response available as context. Finally, the "after deserialization" phase //! has both the raw and parsed response available. //! //! To summarize: -//! 1. [`Phase::BeforeSerialization`]: Only has the operation input. -//! 2. [`Phase::BeforeTransmit`]: Only has the serialized request. -//! 3. [`Phase::BeforeDeserialization`]: Has the raw response. -//! 3. [`Phase::AfterDeserialization`]: Has the raw response and the parsed response. +//! 1. Before serialization: Only has the operation input. +//! 2. Before transmit: Only has the serialized request. +//! 3. Before deserialization: Has the raw response. +//! 3. After deserialization: Has the raw response and the parsed response. //! //! When implementing hooks, if information from a previous phase is required, then implement //! an earlier hook to examine that context, and save off any necessary information into the @@ -27,10 +27,6 @@ //! recommended for storing request-specific information in your interceptor implementation. //! Use the [`ConfigBag`] instead. -/// Operation phases. -pub mod phase; -pub mod wrappers; - use crate::client::orchestrator::{HttpRequest, HttpResponse, OrchestratorError}; use aws_smithy_http::result::SdkError; use aws_smithy_types::config_bag::ConfigBag; @@ -48,11 +44,24 @@ pub type OutputOrError = Result>; type Request = HttpRequest; type Response = HttpResponse; +pub use wrappers::{ + AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, + BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, + BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, + BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut, + FinalizerInterceptorContextRef, +}; + +mod wrappers; + +/// Operation phases. +pub(crate) mod phase; + /// A container for the data currently available to an interceptor. /// /// Different context is available based on which phase the operation is currently in. For example, -/// context in the [`Phase::BeforeSerialization`] phase won't have a `request` yet since the input hasn't been -/// serialized at that point. But once it gets into the [`Phase::BeforeTransmit`] phase, the `request` will be set. +/// context in the "before serialization" phase won't have a `request` yet since the input hasn't been +/// serialized at that point. But once it gets into the "before transmit" phase, the `request` will be set. #[derive(Debug)] pub struct InterceptorContext { pub(crate) input: Option, @@ -65,7 +74,7 @@ pub struct InterceptorContext { } impl InterceptorContext { - /// Creates a new interceptor context in the [`Phase::BeforeSerialization`] phase. + /// Creates a new interceptor context in the "before serialization" phase. pub fn new(input: Input) -> InterceptorContext { InterceptorContext { input: Some(input), diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/phase.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/phase.rs index 01c7f2f12d..050aa1e7eb 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/phase.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/phase.rs @@ -5,7 +5,7 @@ #[derive(Debug)] #[non_exhaustive] -pub enum Phase { +pub(crate) enum Phase { /// Represents the phase of an operation prior to serialization. BeforeSerialization, /// Represents the phase of an operation where the request is serialized. @@ -23,31 +23,31 @@ pub enum Phase { } impl Phase { - pub fn is_before_serialization(&self) -> bool { + pub(crate) fn is_before_serialization(&self) -> bool { matches!(self, Self::BeforeSerialization) } - pub fn is_serialization(&self) -> bool { + pub(crate) fn is_serialization(&self) -> bool { matches!(self, Self::Serialization) } - pub fn is_before_transmit(&self) -> bool { + pub(crate) fn is_before_transmit(&self) -> bool { matches!(self, Self::BeforeTransmit) } - pub fn is_transmit(&self) -> bool { + pub(crate) fn is_transmit(&self) -> bool { matches!(self, Self::Transmit) } - pub fn is_before_deserialization(&self) -> bool { + pub(crate) fn is_before_deserialization(&self) -> bool { matches!(self, Self::BeforeDeserialization) } - pub fn is_deserialization(&self) -> bool { + pub(crate) fn is_deserialization(&self) -> bool { matches!(self, Self::Deserialization) } - pub fn is_after_deserialization(&self) -> bool { + pub(crate) fn is_after_deserialization(&self) -> bool { matches!(self, Self::AfterDeserialization) } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs index 810598f9a0..1094ce4ab2 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs @@ -84,7 +84,7 @@ macro_rules! declare_known_method { } macro_rules! declare_wrapper { - (($ref_struct_name:ident $mut_struct_name:ident)$($tt:tt)+) => { + (($ref_struct_name:ident readonly)$($tt:tt)+) => { pub struct $ref_struct_name<'a, I = Input, O = Output, E = Error> { inner: &'a InterceptorContext, } @@ -99,6 +99,9 @@ macro_rules! declare_wrapper { impl<'a, I, O, E: Debug> $ref_struct_name<'a, I, O, E> { declare_ref_wrapper_methods!($($tt)+); } + }; + (($ref_struct_name:ident $mut_struct_name:ident)$($tt:tt)+) => { + declare_wrapper!(($ref_struct_name readonly) $($tt)+); pub struct $mut_struct_name<'a, I = Input, O = Output, E = Error> { inner: &'a mut InterceptorContext, @@ -156,12 +159,12 @@ declare_wrapper!( ); declare_wrapper!( - (AfterDeserializationInterceptorContextRef AfterDeserializationInterceptorContextMut) + (AfterDeserializationInterceptorContextRef readonly) (input: I) (request: Request) (response: Response) - (output_or_error: Result>) -); + (output_or_error: Result> +)); // Why are all the rest of these defined with a macro but these last two aren't? I simply ran out of // time. Consider updating the macros to support these last two if you're looking for a challenge. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs index 4026269779..f01dace42d 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs @@ -5,6 +5,7 @@ //! Errors related to Smithy interceptors +use crate::box_error::BoxError; use std::fmt; macro_rules! interceptor_error_fn { @@ -30,9 +31,6 @@ macro_rules! interceptor_error_fn { } } -/// A generic error that behaves itself in async contexts -pub type BoxError = Box; - /// An error related to Smithy interceptors. #[derive(Debug)] pub struct InterceptorError { diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 54e37f559f..0567eca853 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::box_error::BoxError; use crate::client::auth::{AuthOptionResolver, AuthOptionResolverParams, HttpAuthSchemes}; use crate::client::identity::IdentityResolvers; use crate::client::interceptors::context::{Error, Input, Output}; @@ -29,7 +30,6 @@ pub use error::OrchestratorError; pub type HttpRequest = http::Request; pub type HttpResponse = http::Response; -pub type BoxError = Box; pub type BoxFuture = Pin> + Send>>; pub type Future = NowOrLater, BoxFuture>; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs index 06d240f7ef..fa2aa7361a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs @@ -70,7 +70,7 @@ impl OrchestratorError { } /// Convert the `OrchestratorError` into an [`SdkError`]. - pub fn into_sdk_error( + pub(crate) fn into_sdk_error( self, phase: &Phase, response: Option, diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index 1a8f0f8cf2..93f075a6fc 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -3,8 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::client::interceptors::InterceptorContext; -use crate::client::orchestrator::BoxError; +use crate::client::interceptors::context::InterceptorContext; use aws_smithy_types::config_bag::ConfigBag; use std::fmt::Debug; use std::time::Duration; @@ -108,7 +107,7 @@ impl ClassifyRetry for RetryClassifiers { #[cfg(feature = "test-util")] mod test_util { use super::{ClassifyRetry, ErrorKind, RetryReason}; - use crate::client::interceptors::InterceptorContext; + use crate::client::interceptors::context::InterceptorContext; use tracing::trace; /// A retry classifier for testing purposes. This classifier always returns @@ -129,5 +128,6 @@ mod test_util { } } +use crate::box_error::BoxError; #[cfg(feature = "test-util")] pub use test_util::AlwaysRetry; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index cd5845c43a..15a29c3ba5 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -3,13 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::box_error::BoxError; use crate::client::interceptors::InterceptorRegistrar; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer}; use std::fmt::Debug; use std::sync::Arc; -pub type BoxError = Box; - /// RuntimePlugin Trait /// /// A RuntimePlugin is the unit of configuration for augmenting the SDK with new behavior diff --git a/rust-runtime/aws-smithy-runtime-api/src/lib.rs b/rust-runtime/aws-smithy-runtime-api/src/lib.rs index 99a0494dbd..8cc3db6309 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/lib.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/lib.rs @@ -13,6 +13,9 @@ //! Basic types for the new smithy client orchestrator. +/// A boxed error that is `Send` and `Sync`. +pub mod box_error; + /// Smithy runtime for client orchestration. pub mod client; diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs index 18f713ae13..efd397576d 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs @@ -4,6 +4,7 @@ */ use aws_smithy_http::query_writer::QueryWriter; +use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::http::{ HTTP_API_KEY_AUTH_SCHEME_ID, HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID, HTTP_DIGEST_AUTH_SCHEME_ID, @@ -13,7 +14,7 @@ use aws_smithy_runtime_api::client::auth::{ }; use aws_smithy_runtime_api::client::identity::http::{Login, Token}; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, HttpRequest}; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_types::base64::encode; use aws_smithy_types::config_bag::ConfigBag; use http::header::HeaderName; diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs index 2eb0278840..c289821e9b 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs @@ -4,9 +4,9 @@ */ use aws_smithy_http::body::SdkBody; -use aws_smithy_runtime_api::client::interceptors::{ - BeforeTransmitInterceptorContextMut, BoxError, Interceptor, -}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_types::config_bag::ConfigBag; use std::error::Error as StdError; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index f86679e093..8f2dffbf80 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -14,10 +14,13 @@ use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream::ByteStream; use aws_smithy_http::result::SdkError; -use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, Output, RewindResult}; -use aws_smithy_runtime_api::client::interceptors::{InterceptorContext, Interceptors}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + Error, Input, InterceptorContext, Output, RewindResult, +}; +use aws_smithy_runtime_api::client::interceptors::Interceptors; use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, ConfigBagAccessors, HttpResponse, LoadedRequestBody, OrchestratorError, + ConfigBagAccessors, HttpResponse, LoadedRequestBody, OrchestratorError, }; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::ShouldAttempt; @@ -322,43 +325,37 @@ async fn finally_op( #[cfg(all(test, feature = "test-util", feature = "anonymous-auth"))] mod tests { - use super::invoke; + use super::*; use crate::client::orchestrator::endpoints::{ StaticUriEndpointResolver, StaticUriEndpointResolverParams, }; - use crate::client::orchestrator::{invoke_with_stop_point, StopPoint}; use crate::client::retries::strategy::NeverRetryStrategy; use crate::client::runtime_plugin::anonymous_auth::AnonymousAuthRuntimePlugin; use crate::client::test_util::{ connector::OkConnector, deserializer::CannedResponseDeserializer, serializer::CannedRequestSerializer, }; - use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::context::wrappers::{ - FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, - }; - use aws_smithy_runtime_api::client::interceptors::context::Output; - use aws_smithy_runtime_api::client::interceptors::{ + use ::http::{Request, Response, StatusCode}; + use aws_smithy_runtime_api::client::interceptors::context::{ AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, - BeforeTransmitInterceptorContextRef, + BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut, + FinalizerInterceptorContextRef, }; use aws_smithy_runtime_api::client::interceptors::{ Interceptor, InterceptorRegistrar, SharedInterceptor, }; - use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, OrchestratorError}; - use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin, RuntimePlugins}; + use aws_smithy_runtime_api::client::runtime_plugin::{RuntimePlugin, RuntimePlugins}; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; - use http::StatusCode; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use tracing_test::traced_test; fn new_request_serializer() -> CannedRequestSerializer { CannedRequestSerializer::success( - http::Request::builder() + Request::builder() .body(SdkBody::empty()) .expect("request is valid"), ) @@ -366,7 +363,7 @@ mod tests { fn new_response_deserializer() -> CannedResponseDeserializer { CannedResponseDeserializer::new( - http::Response::builder() + Response::builder() .status(StatusCode::OK) .body(SdkBody::empty()) .map_err(|err| OrchestratorError::other(Box::new(err))) diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 3ab18caf35..c6bacc1c42 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -3,9 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::{AuthSchemeEndpointConfig, AuthSchemeId}; -use aws_smithy_runtime_api::client::interceptors::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors}; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::endpoint::Endpoint; use aws_smithy_types::Document; @@ -138,7 +139,7 @@ mod tests { AuthOptionResolverParams, AuthSchemeId, HttpAuthScheme, HttpAuthSchemes, HttpRequestSigner, }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; - use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; use aws_smithy_types::config_bag::Layer; use aws_smithy_types::type_erasure::TypedBox; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 0a68454097..7c3720d399 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -8,9 +8,10 @@ use aws_smithy_http::endpoint::{ apply_endpoint as apply_endpoint_to_request_uri, EndpointPrefix, ResolveEndpoint, SharedEndpointResolver, }; -use aws_smithy_runtime_api::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ - BoxError, ConfigBagAccessors, EndpointResolver, EndpointResolverParams, HttpRequest, + ConfigBagAccessors, EndpointResolver, EndpointResolverParams, HttpRequest, }; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::endpoint::Endpoint; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs index 01f0ebfdb1..0b928e7348 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::interceptors::{ - BeforeDeserializationInterceptorContextMut, BoxError, Interceptor, -}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeDeserializationInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::date_time::Format; use aws_smithy_types::DateTime; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs index 0139e9b31f..86bbdde0f6 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::interceptors::InterceptorContext; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; @@ -152,7 +152,7 @@ mod test { HttpStatusCodeClassifier, ModeledAsRetryableClassifier, }; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs index 6113990b5a..42a50017b1 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs @@ -9,7 +9,8 @@ // TODO(enableNewSmithyRuntimeLaunch): Zelda will integrate this rate limiter into the retry policy in a separate PR. #![allow(dead_code)] -use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::{builder, builder_methods, builder_struct}; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer, Storable, StoreReplace}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs index 66db997eae..0976f14512 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::interceptors::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::BoxError; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, ShouldAttempt, diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs index 3f0a2325ec..c06a745a27 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs @@ -3,8 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::interceptors::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::BoxError; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; use aws_smithy_types::config_bag::ConfigBag; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index ac5b6b8cc3..7dcaf7d79c 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -7,8 +7,9 @@ use crate::client::retries::strategy::standard::ReleaseResult::{ APermitWasReleased, NoPermitWasReleased, }; use crate::client::retries::token_bucket::TokenBucket; -use aws_smithy_runtime_api::client::interceptors::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; +use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt, @@ -209,14 +210,12 @@ fn calculate_exponential_backoff(base: f64, initial_backoff: f64, retry_attempts #[cfg(test)] mod tests { - use super::{calculate_exponential_backoff, ShouldAttempt, StandardRetryStrategy}; - use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use super::*; use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, OrchestratorError}; - use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ AlwaysRetry, ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, }; - use aws_smithy_types::config_bag::{ConfigBag, Layer}; + use aws_smithy_types::config_bag::Layer; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; use aws_smithy_types::type_erasure::TypeErasedBox; use std::fmt; diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs index 37ddc9c3d9..7e34d0e7f7 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs @@ -6,6 +6,7 @@ //! The [AnonymousAuthRuntimePlugin] and supporting code. use crate::client::identity::anonymous::AnonymousIdentityResolver; +use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::option_resolver::{ StaticAuthOptionResolver, StaticAuthOptionResolverParams, }; @@ -13,7 +14,7 @@ use aws_smithy_runtime_api::client::auth::{ AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpAuthSchemes, HttpRequestSigner, }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; -use aws_smithy_runtime_api::client::orchestrator::{BoxError, ConfigBagAccessors, HttpRequest}; +use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpRequest}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs index 357894f82f..8135151f1a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs @@ -5,9 +5,9 @@ // TODO(enableNewSmithyRuntimeCleanup): Delete this file once test helpers on `CustomizableOperation` have been removed -use aws_smithy_runtime_api::client::interceptors::{ - BeforeTransmitInterceptorContextMut, BoxError, Interceptor, -}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::ConfigBag; use std::fmt; @@ -46,7 +46,7 @@ where mod tests { use super::*; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_types::type_erasure::TypedBox; use std::time::{Duration, UNIX_EPOCH}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs index faa2bbac3c..87bb2011e4 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -3,11 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::Input; use aws_smithy_runtime_api::client::orchestrator::{ ConfigBagAccessors, HttpRequest, RequestSerializer, }; -use aws_smithy_runtime_api::client::runtime_plugin::{BoxError, RuntimePlugin}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; use std::sync::Mutex; From 177965cd2dcb28efd7845abbcf52b268baecc792 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Thu, 22 Jun 2023 21:35:47 -0500 Subject: [PATCH 178/253] Update SDK models for release (#2790) ## Motivation and Context This PR updates AWS models for the upcoming model-only release. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ Co-authored-by: Yuki Saito --- aws/sdk/aws-models/config.json | 121 +- aws/sdk/aws-models/dynamodb.json | 8 +- aws/sdk/aws-models/ec2.json | 2111 +++++++++++++- aws/sdk/aws-models/iam.json | 42 +- aws/sdk/aws-models/kms.json | 50 +- aws/sdk/aws-models/lambda.json | 24 +- aws/sdk/aws-models/polly.json | 18 + aws/sdk/aws-models/s3.json | 3175 +++++++++++++++++++--- aws/sdk/aws-models/sdk-endpoints.json | 228 +- aws/sdk/aws-models/timestream-query.json | 122 +- aws/sdk/aws-models/timestream-write.json | 244 +- 11 files changed, 5434 insertions(+), 709 deletions(-) diff --git a/aws/sdk/aws-models/config.json b/aws/sdk/aws-models/config.json index 6b4b82802d..f96f7b6f75 100644 --- a/aws/sdk/aws-models/config.json +++ b/aws/sdk/aws-models/config.json @@ -1776,24 +1776,24 @@ "name": { "target": "com.amazonaws.configservice#RecorderName", "traits": { - "smithy.api#documentation": "

The name of the recorder. By default, Config automatically\n\t\t\tassigns the name \"default\" when creating the configuration recorder.\n\t\t\tYou cannot change the assigned name.

" + "smithy.api#documentation": "

The name of the configuration recorder. Config automatically assigns the name of \"default\" when creating the configuration recorder.

\n

You cannot change the name of the configuration recorder after it has been created. To change the configuration recorder name, you must delete it and create a new configuration recorder with a new name.

" } }, "roleARN": { "target": "com.amazonaws.configservice#String", "traits": { - "smithy.api#documentation": "

Amazon Resource Name (ARN) of the IAM role used to describe the\n\t\t\tAmazon Web Services resources associated with the account.

\n \n

While the API model does not require this field, the server will reject a request without a defined roleARN for the configuration recorder.

\n
" + "smithy.api#documentation": "

Amazon Resource Name (ARN) of the IAM role assumed by Config and used by the configuration recorder.

\n \n

While the API model does not require this field, the server will reject a request without a defined roleARN for the configuration recorder.

\n
\n \n

\n Pre-existing Config role\n

\n

If you have used an Amazon Web Services service that uses Config, such as Security Hub or\n\t\t\t\tControl Tower, and an Config role has already been created, make sure that the\n\t\t\t\tIAM role that you use when setting up Config keeps the same minimum\n\t\t\t\tpermissions as the already created Config role. You must do this so that the\n\t\t\t\tother Amazon Web Services service continues to run as expected.

\n

For example, if Control Tower has an IAM role that allows Config to read\n\t\t\t\tAmazon Simple Storage Service (Amazon S3) objects, make sure that the same permissions are granted\n\t\t\t\twithin the IAM role you use when setting up Config. Otherwise, it may\n\t\t\t\tinterfere with how Control Tower operates. For more information about IAM\n\t\t\t\troles for Config,\n\t\t\t\tsee \n Identity and Access Management for Config\n in the Config Developer Guide.\n\t\t\t

\n " } }, "recordingGroup": { "target": "com.amazonaws.configservice#RecordingGroup", "traits": { - "smithy.api#documentation": "

Specifies the types of Amazon Web Services resources for which Config\n\t\t\trecords configuration changes.

" + "smithy.api#documentation": "

Specifies which resource types Config\n\t\t\trecords for configuration changes.

\n \n

\n High Number of Config Evaluations\n

\n

You may notice increased activity in your account during your initial month recording with Config when compared to subsequent months. During the\n\t\t\t\tinitial bootstrapping process, Config runs evaluations on all the resources in your account that you have selected\n\t\t\t\tfor Config to record.

\n

If you are running ephemeral workloads, you may see increased activity from Config as it records configuration changes associated with creating and deleting these\n\t\t\t\ttemporary resources. An ephemeral workload is a temporary use of computing resources that are loaded\n\t\t\t\tand run when needed. Examples include Amazon Elastic Compute Cloud (Amazon EC2)\n\t\t\t\tSpot Instances, Amazon EMR jobs, and Auto Scaling. If you want\n\t\t\t\tto avoid the increased activity from running ephemeral workloads, you can run these\n\t\t\t\ttypes of workloads in a separate account with Config turned off to avoid\n\t\t\t\tincreased configuration recording and rule evaluations.

\n
" } } }, "traits": { - "smithy.api#documentation": "

An object that represents the recording of configuration\n\t\t\tchanges of an Amazon Web Services resource.

" + "smithy.api#documentation": "

Records configuration changes to specified resource types.\n\t\t\tFor more information about the configuration recorder,\n\t\t\tsee \n Managing the Configuration Recorder\n in the Config Developer Guide.

" } }, "com.amazonaws.configservice#ConfigurationRecorderList": { @@ -3825,7 +3825,7 @@ } }, "traits": { - "smithy.api#documentation": "

Returns a filtered list of Detective or Proactive Config rules. By default, if the filter is not defined, this API returns an unfiltered list. For more information on Detective or Proactive Config rules,\n\t\t\tsee \n Evaluation Mode\n in the Config Developer Guide.

" + "smithy.api#documentation": "

Returns a filtered list of Detective or Proactive Config rules. By default, if the filter is not defined, this API returns an unfiltered list. For more information on Detective or Proactive Config rules,\n\t\t\tsee \n Evaluation Mode\n in the Config Developer Guide.

" } }, "com.amazonaws.configservice#DescribeConfigRulesRequest": { @@ -3846,7 +3846,7 @@ "Filters": { "target": "com.amazonaws.configservice#DescribeConfigRulesFilters", "traits": { - "smithy.api#documentation": "

Returns a list of Detective or Proactive Config rules. By default, this API returns an unfiltered list. For more information on Detective or Proactive Config rules,\n\t\t\tsee \n Evaluation Mode\n in the Config Developer Guide.

" + "smithy.api#documentation": "

Returns a list of Detective or Proactive Config rules. By default, this API returns an unfiltered list. For more information on Detective or Proactive Config rules,\n\t\t\tsee \n Evaluation Mode\n in the Config Developer Guide.

" } } }, @@ -4055,7 +4055,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the current status of the specified configuration\n\t\t\trecorder as well as the status of the last recording event for the recorder. If a configuration recorder is not specified, this action\n\t\t\treturns the status of all configuration recorders associated with\n\t\t\tthe account.

\n \n

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account. For a detailed status of recording events over time, add your Config events to Amazon CloudWatch metrics and use CloudWatch metrics.

\n
" + "smithy.api#documentation": "

Returns the current status of the specified configuration\n\t\t\trecorder as well as the status of the last recording event for the recorder. If a configuration recorder is not specified, this action\n\t\t\treturns the status of all configuration recorders associated with\n\t\t\tthe account.

\n \n

>You can specify only one configuration recorder for each Amazon Web Services Region for each account.\n\t\t\t\tFor a detailed status of recording events over time, add your Config events to Amazon CloudWatch metrics and use CloudWatch metrics.

\n
" } }, "com.amazonaws.configservice#DescribeConfigurationRecorderStatusRequest": { @@ -4102,7 +4102,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the details for the specified configuration recorders.\n\t\t\tIf the configuration recorder is not specified, this action returns\n\t\t\tthe details for all configuration recorders associated with the\n\t\t\taccount.

\n \n

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account.

\n
" + "smithy.api#documentation": "

Returns the details for the specified configuration recorders.\n\t\t\tIf the configuration recorder is not specified, this action returns\n\t\t\tthe details for all configuration recorders associated with the\n\t\t\taccount.

\n \n

You can specify only one configuration recorder for each Amazon Web Services Region for each account.

\n
" } }, "com.amazonaws.configservice#DescribeConfigurationRecordersRequest": { @@ -5454,6 +5454,20 @@ } } }, + "com.amazonaws.configservice#ExclusionByResourceTypes": { + "type": "structure", + "members": { + "resourceTypes": { + "target": "com.amazonaws.configservice#ResourceTypeList", + "traits": { + "smithy.api#documentation": "

A comma-separated list of resource types to exclude from recording by the configuration\n\t\t\trecorder.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies whether the configuration recorder excludes resource types from being recorded.\n\t\t\tUse the resourceTypes field to enter a comma-separated list of resource types to exclude as exemptions.

" + } + }, "com.amazonaws.configservice#ExecutionControls": { "type": "structure", "members": { @@ -7208,7 +7222,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have provided a configuration recorder name that is not\n\t\t\tvalid.

", + "smithy.api#documentation": "

You have provided a name for the configuration recorder that is not\n\t\t\tvalid.

", "smithy.api#error": "client" } }, @@ -7298,7 +7312,7 @@ } }, "traits": { - "smithy.api#documentation": "

Config throws an exception if the recording group does not contain a valid list of resource types. Values that are not valid might also be incorrectly formatted.

", + "smithy.api#documentation": "

Indicates one of the following errors:

\n
    \n
  • \n

    You have provided a combination of parameter values that is not valid. For example:

    \n
      \n
    • \n

      Setting the allSupported field of RecordingGroup to true,\n\t\t\t\t\t\tbut providing a non-empty list for the resourceTypesfield of RecordingGroup.

      \n
    • \n
    • \n

      Setting the allSupported field of RecordingGroup to true, but also setting the useOnly field of RecordingStrategy to EXCLUSION_BY_RESOURCE_TYPES.

      \n
    • \n
    \n
  • \n
  • \n

    Every parameter is either null, false, or empty.

    \n
  • \n
  • \n

    You have reached the limit of the number of resource types you can provide for the recording group.

    \n
  • \n
  • \n

    You have provided resource types or a recording strategy that are not valid.

    \n
  • \n
", "smithy.api#error": "client" } }, @@ -7328,7 +7342,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have provided a null or empty role ARN.

", + "smithy.api#documentation": "

You have provided a null or empty Amazon Resource Name (ARN) for the IAM role assumed by Config and used by the configuration recorder.

", "smithy.api#error": "client" } }, @@ -7999,7 +8013,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have reached the limit of the number of recorders you can\n\t\t\tcreate.

", + "smithy.api#documentation": "

You have reached the limit of the number of configuration recorders you can\n\t\t\tcreate.

", "smithy.api#error": "client" } }, @@ -8014,7 +8028,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have reached the limit of the number of conformance packs you can create in an account. For more information, see \n Service Limits\n in the Config Developer Guide.

", + "smithy.api#documentation": "

You have reached the limit of the number of conformance packs you can create in an account. For more information, see \n Service Limits\n in the Config Developer Guide.

", "smithy.api#error": "client" } }, @@ -8044,7 +8058,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have reached the limit of the number of organization Config rules you can create. For more information, see see \n Service Limits\n in the Config Developer Guide.

", + "smithy.api#documentation": "

You have reached the limit of the number of organization Config rules you can create. For more information, see see \n Service Limits\n in the Config Developer Guide.

", "smithy.api#error": "client" } }, @@ -8059,7 +8073,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have reached the limit of the number of organization conformance packs you can create in an account. For more information, see \n Service Limits\n in the Config Developer Guide.

", + "smithy.api#documentation": "

You have reached the limit of the number of organization conformance packs you can create in an account. For more information, see \n Service Limits\n in the Config Developer Guide.

", "smithy.api#error": "client" } }, @@ -9075,7 +9089,7 @@ } }, "traits": { - "smithy.api#documentation": "

An object that specifies metadata for your organization Config Custom Policy rule including the runtime system in use, which accounts have debug logging enabled, and\n\t\t\tother custom rule metadata such as resource type, resource ID of Amazon Web Services\n\t\t\tresource, and organization trigger types that trigger Config to evaluate\n\t\t\t\tAmazon Web Services resources against a rule.

" + "smithy.api#documentation": "

metadata for your organization Config Custom Policy rule including the runtime system in use, which accounts have debug logging enabled, and\n\t\t\tother custom rule metadata such as resource type, resource ID of Amazon Web Services\n\t\t\tresource, and organization trigger types that trigger Config to evaluate\n\t\t\t\tAmazon Web Services resources against a rule.

" } }, "com.amazonaws.configservice#OrganizationCustomRuleMetadata": { @@ -9139,7 +9153,7 @@ } }, "traits": { - "smithy.api#documentation": "

An object that specifies organization custom rule metadata such as resource type, resource ID of Amazon Web Services resource, Lambda function ARN, \n\t\t\tand organization trigger types that trigger Config to evaluate your Amazon Web Services resources against a rule. \n\t\t\tIt also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

" + "smithy.api#documentation": "

organization custom rule metadata such as resource type, resource ID of Amazon Web Services resource, Lambda function ARN, \n\t\t\tand organization trigger types that trigger Config to evaluate your Amazon Web Services resources against a rule. \n\t\t\tIt also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

" } }, "com.amazonaws.configservice#OrganizationManagedRuleMetadata": { @@ -9196,7 +9210,7 @@ } }, "traits": { - "smithy.api#documentation": "

An object that specifies organization managed rule metadata such as resource type and ID of Amazon Web Services resource along with the rule identifier. \n\t\t\tIt also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

" + "smithy.api#documentation": "

organization managed rule metadata such as resource type and ID of Amazon Web Services resource along with the rule identifier. \n\t\t\tIt also provides the frequency with which you want Config to run evaluations for the rule if the trigger type is periodic.

" } }, "com.amazonaws.configservice#OrganizationResourceDetailedStatus": { @@ -9727,7 +9741,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new configuration recorder to record the selected\n\t\t\tresource configurations.

\n

You can use this action to change the role roleARN\n\t\t\tor the recordingGroup of an existing recorder. To\n\t\t\tchange the role, call the action on the existing configuration\n\t\t\trecorder and specify a role.

\n \n

Currently, you can specify only one configuration recorder\n\t\t\t\tper region in your account.

\n

If ConfigurationRecorder does not have the\n\t\t\t\t\trecordingGroup parameter\n\t\t\t\tspecified, the default is to record all supported resource\n\t\t\t\ttypes.

\n
" + "smithy.api#documentation": "

Creates a new configuration recorder to record configuration changes for specified resource types.

\n

You can also use this action to change the roleARN\n\t\t\tor the recordingGroup of an existing recorder.\n\t\t\tFor more information, see \n Managing the Configuration Recorder\n in the Config Developer Guide.

\n \n

You can specify only one configuration recorder for each Amazon Web Services Region for each account.

\n

If the configuration recorder does not have the\n\t\t\t\t\trecordingGroup field\n\t\t\t\tspecified, the default is to record all supported resource\n\t\t\t\ttypes.

\n
" } }, "com.amazonaws.configservice#PutConfigurationRecorderRequest": { @@ -9736,7 +9750,7 @@ "ConfigurationRecorder": { "target": "com.amazonaws.configservice#ConfigurationRecorder", "traits": { - "smithy.api#documentation": "

The configuration recorder object that records each\n\t\t\tconfiguration change made to the resources.

", + "smithy.api#documentation": "

An object for the configuration recorder to record configuration changes for specified resource types.

", "smithy.api#required": {} } } @@ -9772,7 +9786,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates or updates a conformance pack. A conformance pack is a collection of Config rules that can be easily deployed in an account and a region and across an organization.\n\t\t\tFor information on how many conformance packs you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

This API creates a service-linked role AWSServiceRoleForConfigConforms in your account. \n\t\tThe service-linked role is created only when the role does not exist in your account.

\n \n

You must specify only one of the follow parameters: TemplateS3Uri, TemplateBody or TemplateSSMDocumentDetails.

\n
" + "smithy.api#documentation": "

Creates or updates a conformance pack. A conformance pack is a collection of Config rules that can be easily deployed in an account and a region and across an organization.\n\t\t\tFor information on how many conformance packs you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

This API creates a service-linked role AWSServiceRoleForConfigConforms in your account. \n\t\tThe service-linked role is created only when the role does not exist in your account.

\n \n

You must specify only one of the follow parameters: TemplateS3Uri, TemplateBody or TemplateSSMDocumentDetails.

\n
" } }, "com.amazonaws.configservice#PutConformancePackRequest": { @@ -10136,7 +10150,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deploys conformance packs across member accounts in an Amazon Web Services Organization. For information on how many organization conformance packs and how many Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

Only a management account and a delegated administrator can call this API. \n\t\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\t\tListDelegatedAdministrator permissions are added. An organization can have up to 3 delegated administrators.

\n

This API enables organization service access for config-multiaccountsetup.amazonaws.com\n\t\t\tthrough the EnableAWSServiceAccess action and creates a \n\t\t\tservice-linked role AWSServiceRoleForConfigMultiAccountSetup in the management or delegated administrator account of your organization. \n\t\t\tThe service-linked role is created only when the role does not exist in the caller account. \n\t\t\tTo use this API with delegated administrator, register a delegated administrator by calling Amazon Web Services Organization \n\t\t\tregister-delegate-admin for config-multiaccountsetup.amazonaws.com.

\n \n

Prerequisite: Ensure you call EnableAllFeatures API to enable all features in an organization.

\n

You must specify either the TemplateS3Uri or the TemplateBody parameter, but not both. \n\t\t\tIf you provide both Config uses the TemplateS3Uri parameter and ignores the TemplateBody parameter.

\n

Config sets the state of a conformance pack to CREATE_IN_PROGRESS and UPDATE_IN_PROGRESS until the conformance pack is created or updated. \n\t\t\t\tYou cannot update a conformance pack while it is in this state.

\n
" + "smithy.api#documentation": "

Deploys conformance packs across member accounts in an Amazon Web Services Organization. For information on how many organization conformance packs and how many Config rules you can have per account, \n\t\t\tsee \n Service Limits\n in the Config Developer Guide.

\n

Only a management account and a delegated administrator can call this API. \n\t\t\tWhen calling this API with a delegated administrator, you must ensure Organizations \n\t\t\tListDelegatedAdministrator permissions are added. An organization can have up to 3 delegated administrators.

\n

This API enables organization service access for config-multiaccountsetup.amazonaws.com\n\t\t\tthrough the EnableAWSServiceAccess action and creates a \n\t\t\tservice-linked role AWSServiceRoleForConfigMultiAccountSetup in the management or delegated administrator account of your organization. \n\t\t\tThe service-linked role is created only when the role does not exist in the caller account. \n\t\t\tTo use this API with delegated administrator, register a delegated administrator by calling Amazon Web Services Organization \n\t\t\tregister-delegate-admin for config-multiaccountsetup.amazonaws.com.

\n \n

Prerequisite: Ensure you call EnableAllFeatures API to enable all features in an organization.

\n

You must specify either the TemplateS3Uri or the TemplateBody parameter, but not both. \n\t\t\tIf you provide both Config uses the TemplateS3Uri parameter and ignores the TemplateBody parameter.

\n

Config sets the state of a conformance pack to CREATE_IN_PROGRESS and UPDATE_IN_PROGRESS until the conformance pack is created or updated. \n\t\t\t\tYou cannot update a conformance pack while it is in this state.

\n
" } }, "com.amazonaws.configservice#PutOrganizationConformancePackRequest": { @@ -10270,7 +10284,7 @@ } ], "traits": { - "smithy.api#documentation": "

A remediation exception is when a specified resource is no longer considered for auto-remediation. \n\t\t\tThis API adds a new exception or updates an existing exception for a specified resource with a specified Config rule.

\n \n

Config generates a remediation exception when a problem occurs running a remediation action for a specified resource. \n\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n
\n \n

When placing an exception on an Amazon Web Services resource, it is recommended that remediation is set as manual remediation until\n\t\t\tthe given Config rule for the specified resource evaluates the resource as NON_COMPLIANT.\n\t\t\tOnce the resource has been evaluated as NON_COMPLIANT, you can add remediation exceptions and change the remediation type back from Manual to Auto if you want to use auto-remediation.\n\t\t\tOtherwise, using auto-remediation before a NON_COMPLIANT evaluation result can delete resources before the exception is applied.

\n
\n \n

Placing an exception can only be performed on resources that are NON_COMPLIANT.\n\t\t\tIf you use this API for COMPLIANT resources or resources that are NOT_APPLICABLE, a remediation exception will not be generated.\n\t\t\tFor more information on the conditions that initiate the possible Config evaluation results,\n\t\t\tsee Concepts | Config Rules in the Config Developer Guide.

\n
" + "smithy.api#documentation": "

A remediation exception is when a specified resource is no longer considered for auto-remediation. \n\t\t\tThis API adds a new exception or updates an existing exception for a specified resource with a specified Config rule.

\n \n

Config generates a remediation exception when a problem occurs running a remediation action for a specified resource. \n\t\t\tRemediation exceptions blocks auto-remediation until the exception is cleared.

\n
\n \n

When placing an exception on an Amazon Web Services resource, it is recommended that remediation is set as manual remediation until\n\t\t\tthe given Config rule for the specified resource evaluates the resource as NON_COMPLIANT.\n\t\t\tOnce the resource has been evaluated as NON_COMPLIANT, you can add remediation exceptions and change the remediation type back from Manual to Auto if you want to use auto-remediation.\n\t\t\tOtherwise, using auto-remediation before a NON_COMPLIANT evaluation result can delete resources before the exception is applied.

\n
\n \n

Placing an exception can only be performed on resources that are NON_COMPLIANT.\n\t\t\tIf you use this API for COMPLIANT resources or resources that are NOT_APPLICABLE, a remediation exception will not be generated.\n\t\t\tFor more information on the conditions that initiate the possible Config evaluation results,\n\t\t\tsee Concepts | Config Rules in the Config Developer Guide.

\n
" } }, "com.amazonaws.configservice#PutRemediationExceptionsRequest": { @@ -10606,25 +10620,74 @@ "target": "com.amazonaws.configservice#AllSupported", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Config records configuration changes for\n\t\t\tevery supported type of regional resource.

\n

If you set this option to true, when Config\n\t\t\tadds support for a new type of regional resource, it starts\n\t\t\trecording resources of that type automatically.

\n

If you set this option to true, you cannot\n\t\t\tenumerate a list of resourceTypes.

" + "smithy.api#documentation": "

Specifies whether Config records configuration changes for all supported regional resource types.

\n

If you set this field to true, when Config\n\t\t\tadds support for a new type of regional resource, Config starts recording resources of that type automatically.

\n

If you set this field to true,\n\t\t\tyou cannot enumerate specific resource types to record in the resourceTypes field of RecordingGroup, or to exclude in the resourceTypes field of ExclusionByResourceTypes.

" } }, "includeGlobalResourceTypes": { "target": "com.amazonaws.configservice#IncludeGlobalResourceTypes", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Config includes all supported types of\n\t\t\tglobal resources (for example, IAM resources) with the resources\n\t\t\tthat it records.

\n

Before you can set this option to true, you must\n\t\t\tset the allSupported option to\n\t\t\ttrue.

\n

If you set this option to true, when Config\n\t\t\tadds support for a new type of global resource, it starts recording\n\t\t\tresources of that type automatically.

\n

The configuration details for any global resource are the same\n\t\t\tin all regions. To prevent duplicate configuration items, you should\n\t\t\tconsider customizing Config in only one region to record global\n\t\t\tresources.

" + "smithy.api#documentation": "

Specifies whether Config records configuration changes for all supported global resources.

\n

Before you set this field to true,\n\t\t\tset the allSupported field of RecordingGroup to\n\t\t\ttrue. Optionally, you can set the useOnly field of RecordingStrategy to ALL_SUPPORTED_RESOURCE_TYPES.

\n

If you set this field to true, when Config\n\t\t\tadds support for a new type of global resource in the Region where you set up the configuration recorder, Config starts recording\n\t\t\tresources of that type automatically.

\n \n

If you set this field to false but list global resource types in the resourceTypes field of RecordingGroup,\n\t\t\tConfig will still record configuration changes for those specified resource types regardless of if you set the includeGlobalResourceTypes field to false.

\n

If you do not want to record configuration changes to global resource types, make sure to not list them in the resourceTypes field\n\t\t\tin addition to setting the includeGlobalResourceTypes field to false.

\n
" } }, "resourceTypes": { "target": "com.amazonaws.configservice#ResourceTypeList", "traits": { - "smithy.api#documentation": "

A comma-separated list that specifies the types of Amazon Web Services\n\t\t\tresources for which Config records configuration changes (for\n\t\t\texample, AWS::EC2::Instance or\n\t\t\t\tAWS::CloudTrail::Trail).

\n

To record all configuration changes, you must\n\t\t\tset the allSupported option to\n\t\t\ttrue.

\n

If you set the AllSupported option to false and populate the ResourceTypes option with values,\n\t\t\twhen Config adds support for a new type of resource,\n\t\t\tit will not record resources of that type unless you manually add that type to your recording group.

\n

For a list of valid resourceTypes values, see the\n\t\t\t\tresourceType Value column in\n\t\t\t\tSupported Amazon Web Services resource Types.

" + "smithy.api#documentation": "

A comma-separated list that specifies which resource types Config\n\t\t\trecords.

\n

Optionally, you can set the useOnly field of RecordingStrategy to INCLUSION_BY_RESOURCE_TYPES.

\n

To record all configuration changes,\n\t\t\t\tset the allSupported field of RecordingGroup to\n\t\t\t\ttrue, and either omit this field or don't specify any resource types in this field. If you set the allSupported field to false and specify values for resourceTypes,\n\t\t\t\t\twhen Config adds support for a new type of resource,\n\t\t\t\t\tit will not record resources of that type unless you manually add that type to your recording group.

\n

For a list of valid resourceTypes values, see the\n\t\t\t\tResource Type Value column in\n\t\t\t\tSupported Amazon Web Services resource Types in the Config developer guide.

\n \n

\n Region Availability\n

\n

Before specifying a resource type for Config to track,\n\t\t\t\tcheck Resource Coverage by Region Availability\n\t\t\t\tto see if the resource type is supported in the Amazon Web Services Region where you set up Config.\n\t\t\t\tIf a resource type is supported by Config in at least one Region,\n\t\t\t\tyou can enable the recording of that resource type in all Regions supported by Config,\n\t\t\t\teven if the specified resource type is not supported in the Amazon Web Services Region where you set up Config.

\n
" + } + }, + "exclusionByResourceTypes": { + "target": "com.amazonaws.configservice#ExclusionByResourceTypes", + "traits": { + "smithy.api#documentation": "

An object that specifies how Config excludes resource types from being recorded by the configuration recorder.

\n

To use this option, you must set the useOnly field of RecordingStrategy to EXCLUSION_BY_RESOURCE_TYPES.

" + } + }, + "recordingStrategy": { + "target": "com.amazonaws.configservice#RecordingStrategy", + "traits": { + "smithy.api#documentation": "

An object that specifies the recording strategy for the configuration recorder.

\n
    \n
  • \n

    If you set the useOnly field of RecordingStrategy to ALL_SUPPORTED_RESOURCE_TYPES, Config records configuration changes for all supported regional resource types. You also must set the allSupported field of RecordingGroup to true. When Config adds support for a new type of regional resource, Config automatically starts recording resources of that type.

    \n
  • \n
  • \n

    If you set the useOnly field of RecordingStrategy to INCLUSION_BY_RESOURCE_TYPES, Config records configuration changes for only the resource types you specify in the resourceTypes field of RecordingGroup.

    \n
  • \n
  • \n

    If you set the useOnly field of RecordingStrategy to EXCLUSION_BY_RESOURCE_TYPES, Config records configuration changes for all supported resource types\n\t\t\t\texcept the resource types that you specify as exemptions to exclude from being recorded in the resourceTypes field of ExclusionByResourceTypes.

    \n
  • \n
\n \n

The recordingStrategy field is optional when you set the\n\t\t\tallSupported field of RecordingGroup to true.

\n

The recordingStrategy field is optional when you list resource types in the\n\t\t\t\tresourceTypes field of RecordingGroup.

\n

The recordingStrategy field is required if you list resource types to exclude from recording in the resourceTypes field of ExclusionByResourceTypes.

\n
\n \n

If you choose EXCLUSION_BY_RESOURCE_TYPES for the recording strategy, the exclusionByResourceTypes field will override other properties in the request.

\n

For example, even if you set includeGlobalResourceTypes to false, global resource types will still be automatically\n\t\t\trecorded in this option unless those resource types are specifically listed as exemptions in the resourceTypes field of exclusionByResourceTypes.

\n

By default, if you choose the EXCLUSION_BY_RESOURCE_TYPES recording strategy,\n\t\t\t\twhen Config adds support for a new resource type in the Region where you set up the configuration recorder, including global resource types,\n\t\t\t\tConfig starts recording resources of that type automatically.

\n
" + } + } + }, + "traits": { + "smithy.api#documentation": "

Specifies which resource types Config\n\t\t\trecords for configuration changes.\n\t\t\tIn the recording group, you specify whether you want to record all supported resource types or to include or exclude specific types of resources.

\n

By default, Config records configuration changes for all supported types of\n\t\t\t\tRegional resources that Config discovers in the\n\t\t\t\tAmazon Web Services Region in which it is running. Regional resources are tied to a\n\t\t\tRegion and can be used only in that Region. Examples of Regional resources are Amazon EC2 instances and Amazon EBS volumes.

\n

You can also have Config record supported types of global resources.\n\t\t\t\tGlobal resources are not tied to a specific Region and can be used in all Regions. The global\n\t\t\t\tresource types that Config supports include IAM users, groups, roles, and customer managed\n\t\t\t\tpolicies.

\n \n

Global resource types onboarded to Config recording after February 2022 will\n\t\t\t\tbe recorded only in the service's home Region for the commercial partition and\n\t\t\t\tAmazon Web Services GovCloud (US-West) for the Amazon Web Services GovCloud (US) partition. You can view the\n\t\t\t\tConfiguration Items for these new global resource types only in their home Region\n\t\t\t\tand Amazon Web Services GovCloud (US-West).

\n
\n

If you don't want Config to record all resources, you can specify which types of resources Config records with the resourceTypes parameter.

\n

For a list of supported resource types, see Supported Resource Types in the Config developer guide.

\n

For more information and a table of the Home Regions for Global Resource Types Onboarded after February 2022, see Selecting Which Resources Config Records in the Config developer guide.

" + } + }, + "com.amazonaws.configservice#RecordingStrategy": { + "type": "structure", + "members": { + "useOnly": { + "target": "com.amazonaws.configservice#RecordingStrategyType", + "traits": { + "smithy.api#documentation": "

The recording strategy for the configuration recorder.

\n
    \n
  • \n

    If you set this option to ALL_SUPPORTED_RESOURCE_TYPES, Config records configuration changes for all supported regional resource types. You also must set the allSupported field of RecordingGroup to true.

    \n

    When Config adds support for a new type of regional resource, Config automatically starts recording resources of that type. For a list of supported resource types,\n\t\t\t\tsee Supported Resource Types in the Config developer guide.

    \n
  • \n
  • \n

    If you set this option to INCLUSION_BY_RESOURCE_TYPES, Config records\n\t\t\t\t\tconfiguration changes for only the resource types that you specify in the\n\t\t\t\t\t\tresourceTypes field of RecordingGroup.

    \n
  • \n
  • \n

    If you set this option to EXCLUSION_BY_RESOURCE_TYPES, Config records\n\t\t\t\t\tconfiguration changes for all supported resource types, except the resource\n\t\t\t\t\ttypes that you specify as exemptions to exclude from being recorded in the\n\t\t\t\t\t\tresourceTypes field of ExclusionByResourceTypes.

    \n
  • \n
\n \n

The recordingStrategy field is optional when you set the\n\t\t\tallSupported field of RecordingGroup to true.

\n

The recordingStrategy field is optional when you list resource types in the\n\t\t\t\tresourceTypes field of RecordingGroup.

\n

The recordingStrategy field is required if you list resource types to exclude from recording in the resourceTypes field of ExclusionByResourceTypes.

\n
\n \n

If you choose EXCLUSION_BY_RESOURCE_TYPES for the recording strategy, the exclusionByResourceTypes field will override other properties in the request.

\n

For example, even if you set includeGlobalResourceTypes to false, global resource types will still be automatically\n\t\t\trecorded in this option unless those resource types are specifically listed as exemptions in the resourceTypes field of exclusionByResourceTypes.

\n

By default, if you choose the EXCLUSION_BY_RESOURCE_TYPES recording strategy,\n\t\t\t\twhen Config adds support for a new resource type in the Region where you set up the configuration recorder, including global resource types,\n\t\t\t\tConfig starts recording resources of that type automatically.

\n
" } } }, "traits": { - "smithy.api#documentation": "

Specifies which Amazon Web Services resource types Config\n\t\t\trecords for configuration changes. In the recording group, you specify whether you want to record all supported resource types\n\t\t\tor only specific types of resources.

\n

By default, Config records the configuration changes for all supported types of\n\t\t\t\tregional resources that Config discovers in the region in which it is\n\t\t\t\trunning. Regional resources are tied to a region and can be used only in that region. Examples\n\t\t\t\tof regional resources are EC2 instances and EBS volumes.

\n

You can also have Config record supported types of global resources.\n\t\t\t\tGlobal resources are not tied to a specific region and can be used in all regions. The global\n\t\t\t\tresource types that Config supports include IAM users, groups, roles, and customer managed\n\t\t\t\tpolicies.

\n \n

Global resource types onboarded to Config recording after February 2022 will only be\n\t\t\t\trecorded in the service's home region for the commercial partition and\n\t\t\t\tAmazon Web Services GovCloud (US) West for the GovCloud partition. You can view the Configuration Items for\n\t\t\t\tthese new global resource types only in their home region and Amazon Web Services GovCloud (US) West.

\n

Supported global resource types onboarded before February 2022 such as\n\t\t\t\tAWS::IAM::Group, AWS::IAM::Policy, AWS::IAM::Role,\n\t\t\t\tAWS::IAM::User remain unchanged, and they will continue to deliver\n\t\t\t\tConfiguration Items in all supported regions in Config. The change will only affect new global\n\t\t\t\tresource types onboarded after February 2022.

\n

To record global resource types onboarded after February 2022,\n\t\t\t\tenable All Supported Resource Types in the home region of the global resource type you want to record.

\n
\n

If you don't want Config to record all resources, you can\n\t\t\tspecify which types of resources it will record with the\n\t\t\t\tresourceTypes parameter.

\n

For a list of supported resource types, see Supported Resource Types.

\n

For more information and a table of the Home Regions for Global Resource Types Onboarded after February 2022, see Selecting Which Resources Config Records.

" + "smithy.api#documentation": "

Specifies the recording strategy of the configuration recorder.

" + } + }, + "com.amazonaws.configservice#RecordingStrategyType": { + "type": "enum", + "members": { + "ALL_SUPPORTED_RESOURCE_TYPES": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ALL_SUPPORTED_RESOURCE_TYPES" + } + }, + "INCLUSION_BY_RESOURCE_TYPES": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "INCLUSION_BY_RESOURCE_TYPES" + } + }, + "EXCLUSION_BY_RESOURCE_TYPES": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "EXCLUSION_BY_RESOURCE_TYPES" + } + } } }, "com.amazonaws.configservice#ReevaluateConfigRuleNames": { @@ -13492,7 +13555,7 @@ } ], "traits": { - "smithy.api#documentation": "

Accepts a structured query language (SQL) SELECT command and an aggregator to query configuration state of Amazon Web Services resources across multiple accounts and regions, \n\t\t\tperforms the corresponding search, and returns resource configurations matching the properties.

\n

For more information about query components, see the \n\t\t\t\n Query Components\n section in the Config Developer Guide.

\n \n

If you run an aggregation query (i.e., using GROUP BY or using aggregate functions such as COUNT; e.g., SELECT resourceId, COUNT(*) WHERE resourceType = 'AWS::IAM::Role' GROUP BY resourceId)\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 500.

\n

If you run a non-aggregation query (i.e., not using GROUP BY or aggregate function; e.g., SELECT * WHERE resourceType = 'AWS::IAM::Role')\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 25.

\n
", + "smithy.api#documentation": "

Accepts a structured query language (SQL) SELECT command and an aggregator to query configuration state of Amazon Web Services resources across multiple accounts and regions, \n\t\t\tperforms the corresponding search, and returns resource configurations matching the properties.

\n

For more information about query components, see the \n\t\t\t\n Query Components\n section in the Config Developer Guide.

\n \n

If you run an aggregation query (i.e., using GROUP BY or using aggregate functions such as COUNT; e.g., SELECT resourceId, COUNT(*) WHERE resourceType = 'AWS::IAM::Role' GROUP BY resourceId)\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 500.

\n

If you run a non-aggregation query (i.e., not using GROUP BY or aggregate function; e.g., SELECT * WHERE resourceType = 'AWS::IAM::Role')\n\t\t\t\tand do not specify the MaxResults or the Limit query parameters, the default page size is set to 25.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -15795,7 +15858,7 @@ } }, "traits": { - "smithy.api#documentation": "

You have reached the limit of the number of tags you can use. \n\t\t\tFor more information, see \n Service Limits\n in the Config Developer Guide.

", + "smithy.api#documentation": "

You have reached the limit of the number of tags you can use. \n\t\t\tFor more information, see \n Service Limits\n in the Config Developer Guide.

", "smithy.api#error": "client" } }, diff --git a/aws/sdk/aws-models/dynamodb.json b/aws/sdk/aws-models/dynamodb.json index adb4cf00eb..7c5a2d7678 100644 --- a/aws/sdk/aws-models/dynamodb.json +++ b/aws/sdk/aws-models/dynamodb.json @@ -833,7 +833,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": false }, - "smithy.api#documentation": "

The BatchGetItem operation returns the attributes of one or more items\n from one or more tables. You identify requested items by primary key.

\n

A single operation can retrieve up to 16 MB of data, which can contain as many as 100\n items. BatchGetItem returns a partial result if the response size limit is\n exceeded, the table's provisioned throughput is exceeded, or an internal processing\n failure occurs. If a partial result is returned, the operation returns a value for\n UnprocessedKeys. You can use this value to retry the operation starting\n with the next item to get.

\n \n

If you request more than 100 items, BatchGetItem returns a\n ValidationException with the message \"Too many items requested for\n the BatchGetItem call.\"

\n
\n

For example, if you ask to retrieve 100 items, but each individual item is 300 KB in\n size, the system returns 52 items (so as not to exceed the 16 MB limit). It also returns\n an appropriate UnprocessedKeys value so you can get the next page of\n results. If desired, your application can include its own logic to assemble the pages of\n results into one dataset.

\n

If none of the items can be processed due to insufficient\n provisioned throughput on all of the tables in the request, then\n BatchGetItem returns a\n ProvisionedThroughputExceededException. If at least\n one of the items is successfully processed, then\n BatchGetItem completes successfully, while returning the keys of the\n unread items in UnprocessedKeys.

\n \n

If DynamoDB returns any unprocessed items, you should retry the batch operation on\n those items. However, we strongly recommend that you use an exponential\n backoff algorithm. If you retry the batch operation immediately, the\n underlying read or write requests can still fail due to throttling on the individual\n tables. If you delay the batch operation using exponential backoff, the individual\n requests in the batch are much more likely to succeed.

\n

For more information, see Batch Operations and Error Handling in the Amazon DynamoDB\n Developer Guide.

\n
\n

By default, BatchGetItem performs eventually consistent reads on every\n table in the request. If you want strongly consistent reads instead, you can set\n ConsistentRead to true for any or all tables.

\n

In order to minimize response latency, BatchGetItem may retrieve items in\n parallel.

\n

When designing your application, keep in mind that DynamoDB does not return items in\n any particular order. To help parse the response by item, include the primary key values\n for the items in your request in the ProjectionExpression parameter.

\n

If a requested item does not exist, it is not returned in the result. Requests for\n nonexistent items consume the minimum read capacity units according to the type of read.\n For more information, see Working with Tables in the Amazon DynamoDB Developer\n Guide.

" + "smithy.api#documentation": "

The BatchGetItem operation returns the attributes of one or more items\n from one or more tables. You identify requested items by primary key.

\n

A single operation can retrieve up to 16 MB of data, which can contain as many as 100\n items. BatchGetItem returns a partial result if the response size limit is\n exceeded, the table's provisioned throughput is exceeded, more than 1MB per partition is requested,\n or an internal processing failure occurs. If a partial result is returned, the operation returns a value for\n UnprocessedKeys. You can use this value to retry the operation starting\n with the next item to get.

\n \n

If you request more than 100 items, BatchGetItem returns a\n ValidationException with the message \"Too many items requested for\n the BatchGetItem call.\"

\n
\n

For example, if you ask to retrieve 100 items, but each individual item is 300 KB in\n size, the system returns 52 items (so as not to exceed the 16 MB limit). It also returns\n an appropriate UnprocessedKeys value so you can get the next page of\n results. If desired, your application can include its own logic to assemble the pages of\n results into one dataset.

\n

If none of the items can be processed due to insufficient\n provisioned throughput on all of the tables in the request, then\n BatchGetItem returns a\n ProvisionedThroughputExceededException. If at least\n one of the items is successfully processed, then\n BatchGetItem completes successfully, while returning the keys of the\n unread items in UnprocessedKeys.

\n \n

If DynamoDB returns any unprocessed items, you should retry the batch operation on\n those items. However, we strongly recommend that you use an exponential\n backoff algorithm. If you retry the batch operation immediately, the\n underlying read or write requests can still fail due to throttling on the individual\n tables. If you delay the batch operation using exponential backoff, the individual\n requests in the batch are much more likely to succeed.

\n

For more information, see Batch Operations and Error Handling in the Amazon DynamoDB\n Developer Guide.

\n
\n

By default, BatchGetItem performs eventually consistent reads on every\n table in the request. If you want strongly consistent reads instead, you can set\n ConsistentRead to true for any or all tables.

\n

In order to minimize response latency, BatchGetItem may retrieve items in\n parallel.

\n

When designing your application, keep in mind that DynamoDB does not return items in\n any particular order. To help parse the response by item, include the primary key values\n for the items in your request in the ProjectionExpression parameter.

\n

If a requested item does not exist, it is not returned in the result. Requests for\n nonexistent items consume the minimum read capacity units according to the type of read.\n For more information, see Working with Tables in the Amazon DynamoDB Developer\n Guide.

" } }, "com.amazonaws.dynamodb#BatchGetItemInput": { @@ -6732,7 +6732,7 @@ } }, "traits": { - "smithy.api#documentation": "

There is no limit to the number of daily on-demand backups that can be taken.

\n

For most purposes, up to 500 simultaneous table operations are allowed per account. These operations\n include CreateTable, UpdateTable,\n DeleteTable,UpdateTimeToLive,\n RestoreTableFromBackup, and RestoreTableToPointInTime.

\n

When you are creating a table with one or more secondary\n indexes, you can have up to 250 such requests running at a time. However, if the table or\n index specifications are complex, then DynamoDB might temporarily reduce the number\n of concurrent operations.

\n

When importing into DynamoDB, up to 50 simultaneous import table operations are allowed per account.

\n

There is a soft account quota of 2,500 tables.

", + "smithy.api#documentation": "

There is no limit to the number of daily on-demand backups that can be taken.

\n

For most purposes, up to 500 simultaneous table operations are allowed per account. These operations\n include CreateTable, UpdateTable,\n DeleteTable,UpdateTimeToLive,\n RestoreTableFromBackup, and RestoreTableToPointInTime.

\n

When you are creating a table with one or more secondary\n indexes, you can have up to 250 such requests running at a time. However, if the table or\n index specifications are complex, then DynamoDB might temporarily reduce the number\n of concurrent operations.

\n

When importing into DynamoDB, up to 50 simultaneous import table operations are allowed per account.

\n

There is a soft account quota of 2,500 tables.

\n

GetRecords was called with a value of more than 1000 for the limit request parameter.

\n

More than 2 processes are reading from the same streams shard at the same time. Exceeding\n this limit may result in request throttling.

", "smithy.api#error": "client" } }, @@ -7689,14 +7689,14 @@ "ReadCapacityUnits": { "target": "com.amazonaws.dynamodb#PositiveLongObject", "traits": { - "smithy.api#documentation": "

The maximum number of strongly consistent reads consumed per second before DynamoDB\n returns a ThrottlingException. For more information, see Specifying Read and Write Requirements in the Amazon DynamoDB\n Developer Guide.

\n

If read/write capacity mode is PAY_PER_REQUEST the value is set to\n 0.

", + "smithy.api#documentation": "

The maximum number of strongly consistent reads consumed per second before DynamoDB\n returns a ThrottlingException. For more information, see Specifying Read and Write Requirements in the Amazon DynamoDB\n Developer Guide.

\n

If read/write capacity mode is PAY_PER_REQUEST the value is set to\n 0.

", "smithy.api#required": {} } }, "WriteCapacityUnits": { "target": "com.amazonaws.dynamodb#PositiveLongObject", "traits": { - "smithy.api#documentation": "

The maximum number of writes consumed per second before DynamoDB returns a\n ThrottlingException. For more information, see Specifying Read and Write Requirements in the Amazon DynamoDB\n Developer Guide.

\n

If read/write capacity mode is PAY_PER_REQUEST the value is set to\n 0.

", + "smithy.api#documentation": "

The maximum number of writes consumed per second before DynamoDB returns a\n ThrottlingException. For more information, see Specifying Read and Write Requirements in the Amazon DynamoDB\n Developer Guide.

\n

If read/write capacity mode is PAY_PER_REQUEST the value is set to\n 0.

", "smithy.api#required": {} } } diff --git a/aws/sdk/aws-models/ec2.json b/aws/sdk/aws-models/ec2.json index ca86cbdf2b..4d35d56d54 100644 --- a/aws/sdk/aws-models/ec2.json +++ b/aws/sdk/aws-models/ec2.json @@ -325,6 +325,9 @@ "smithy.api#xmlName": "addressTransfer" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptReservedInstancesExchangeQuote": { @@ -385,7 +388,8 @@ } }, "traits": { - "smithy.api#documentation": "

The result of the exchange and whether it was successful.

" + "smithy.api#documentation": "

The result of the exchange and whether it was successful.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptTransitGatewayMulticastDomainAssociations": { @@ -445,6 +449,9 @@ "smithy.api#xmlName": "associations" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptTransitGatewayPeeringAttachment": { @@ -494,6 +501,9 @@ "smithy.api#xmlName": "transitGatewayPeeringAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptTransitGatewayVpcAttachment": { @@ -543,6 +553,9 @@ "smithy.api#xmlName": "transitGatewayVpcAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptVpcEndpointConnections": { @@ -601,6 +614,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AcceptVpcPeeringConnection": { @@ -654,6 +670,9 @@ "smithy.api#xmlName": "vpcPeeringConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AccessScopeAnalysisFinding": { @@ -1157,7 +1176,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "AllocationId", - "smithy.api#documentation": "

The ID representing the allocation of the address for use with EC2-VPC.

", + "smithy.api#documentation": "

The ID representing the allocation of the address.

", "smithy.api#xmlName": "allocationId" } }, @@ -1165,7 +1184,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "AssociationId", - "smithy.api#documentation": "

The ID representing the association of the address with an instance in a VPC.

", + "smithy.api#documentation": "

The ID representing the association of the address with an instance.

", "smithy.api#xmlName": "associationId" } }, @@ -1173,7 +1192,7 @@ "target": "com.amazonaws.ec2#DomainType", "traits": { "aws.protocols#ec2QueryName": "Domain", - "smithy.api#documentation": "

Indicates whether this Elastic IP address is for use with instances\n\t\t\t\tin EC2-Classic (standard) or instances in a VPC (vpc).

", + "smithy.api#documentation": "

The network (vpc).

", "smithy.api#xmlName": "domain" } }, @@ -1485,6 +1504,9 @@ "smithy.api#xmlName": "byoipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#Affinity": { @@ -1513,7 +1535,7 @@ "target": "com.amazonaws.ec2#AllocateAddressResult" }, "traits": { - "smithy.api#documentation": "

Allocates an Elastic IP address to your Amazon Web Services account. After you allocate the Elastic IP address you can associate \n it with an instance or network interface. After you release an Elastic IP address, it is released to the IP address \n pool and can be allocated to a different Amazon Web Services account.

\n

You can allocate an Elastic IP address from an address pool owned by Amazon Web Services or from an address pool created \n from a public IPv4 address range that you have brought to Amazon Web Services for use with your Amazon Web Services resources using bring your own \n IP addresses (BYOIP). For more information, see Bring Your Own IP Addresses (BYOIP) in the Amazon Elastic Compute Cloud User Guide.

\n

[EC2-VPC] If you release an Elastic IP address, you might be able to recover it. You cannot recover an \n Elastic IP address that you released after it is allocated to another Amazon Web Services account. You cannot recover an Elastic IP\n address for EC2-Classic. To attempt to recover an Elastic IP address that you released, specify it in this operation.

\n

An Elastic IP address is for use either in the EC2-Classic platform or in a VPC. By default, you can allocate\n 5 Elastic IP addresses for EC2-Classic per Region and 5 Elastic IP addresses for EC2-VPC per Region.

\n

For more information, see Elastic IP Addresses in the Amazon Elastic Compute Cloud User Guide.

\n

You can allocate a carrier IP address which is a public IP address from a telecommunication carrier, to a network interface which resides in a subnet in a Wavelength Zone (for example an EC2 instance).

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "

Allocates an Elastic IP address to your Amazon Web Services account. After you allocate the Elastic IP address you can associate \n it with an instance or network interface. After you release an Elastic IP address, it is released to the IP address \n pool and can be allocated to a different Amazon Web Services account.

\n

You can allocate an Elastic IP address from an address pool owned by Amazon Web Services or from an address pool created \n from a public IPv4 address range that you have brought to Amazon Web Services for use with your Amazon Web Services resources using bring your own \n IP addresses (BYOIP). For more information, see Bring Your Own IP Addresses (BYOIP) in the Amazon Elastic Compute Cloud User Guide.

\n

If you release an Elastic IP address, you might be able to recover it. You cannot recover\n an Elastic IP address that you released after it is allocated to another Amazon Web Services account. To attempt to recover an Elastic IP address that you released, specify\n it in this operation.

\n

For more information, see Elastic IP Addresses in the Amazon Elastic Compute Cloud User Guide.

\n

You can allocate a carrier IP address which is a public IP address from a telecommunication carrier, \n to a network interface which resides in a subnet in a Wavelength Zone (for example an EC2 instance).

" } }, "com.amazonaws.ec2#AllocateAddressRequest": { @@ -1522,13 +1544,13 @@ "Domain": { "target": "com.amazonaws.ec2#DomainType", "traits": { - "smithy.api#documentation": "

Indicates whether the Elastic IP address is for use with instances in a VPC or instances in EC2-Classic.

\n

Default: If the Region supports EC2-Classic, the default is standard. Otherwise, the default\n is vpc.

" + "smithy.api#documentation": "

The network (vpc).

" } }, "Address": { "target": "com.amazonaws.ec2#PublicIpAddress", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The Elastic IP address to recover or an IPv4 address from an address pool.

" + "smithy.api#documentation": "

The Elastic IP address to recover or an IPv4 address from an address pool.

" } }, "PublicIpv4Pool": { @@ -1586,7 +1608,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "AllocationId", - "smithy.api#documentation": "

[EC2-VPC] The ID that Amazon Web Services assigns to represent the allocation of the Elastic IP address for use with instances in a VPC.

", + "smithy.api#documentation": "

The ID that represents the allocation of the Elastic IP address.

", "smithy.api#xmlName": "allocationId" } }, @@ -1610,7 +1632,7 @@ "target": "com.amazonaws.ec2#DomainType", "traits": { "aws.protocols#ec2QueryName": "Domain", - "smithy.api#documentation": "

Indicates whether the Elastic IP address is for use with instances in a VPC (vpc) or\n\t\t\t\tinstances in EC2-Classic (standard).

", + "smithy.api#documentation": "

The network (vpc).

", "smithy.api#xmlName": "domain" } }, @@ -1634,10 +1656,13 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "CarrierIp", - "smithy.api#documentation": "

The carrier IP address. This option is only available for network interfaces which reside\n in a subnet in a Wavelength Zone (for example an EC2 instance).

", + "smithy.api#documentation": "

The carrier IP address. This option is only available for network interfaces that reside\n in a subnet in a Wavelength Zone.

", "smithy.api#xmlName": "carrierIp" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AllocateHosts": { @@ -1749,7 +1774,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of AllocateHosts.

" + "smithy.api#documentation": "

Contains the output of AllocateHosts.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#AllocateIpamPoolCidr": { @@ -1761,7 +1787,7 @@ "target": "com.amazonaws.ec2#AllocateIpamPoolCidrResult" }, "traits": { - "smithy.api#documentation": "

Allocate a CIDR from an IPAM pool. In IPAM, an allocation is a CIDR assignment from an IPAM pool to another IPAM pool or to a resource. For more information, see Allocate CIDRs in the Amazon VPC IPAM User Guide.

\n \n

This action creates an allocation with strong consistency. The returned CIDR will not overlap with any other allocations from the same pool.

\n
" + "smithy.api#documentation": "

Allocate a CIDR from an IPAM pool. The Region you use should be the IPAM pool locale. The locale is the Amazon Web Services Region where this IPAM pool is available for allocations.

\n

In IPAM, an allocation is a CIDR assignment from an IPAM pool to another IPAM pool or to a resource. For more information, see Allocate CIDRs in the Amazon VPC IPAM User Guide.

\n \n

This action creates an allocation with strong consistency. The returned CIDR will not overlap with any other allocations from the same pool.

\n
" } }, "com.amazonaws.ec2#AllocateIpamPoolCidrRequest": { @@ -1841,6 +1867,9 @@ "smithy.api#xmlName": "ipamPoolAllocation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AllocationId": { @@ -2297,6 +2326,9 @@ { "target": "com.amazonaws.ec2#CreateImage" }, + { + "target": "com.amazonaws.ec2#CreateInstanceConnectEndpoint" + }, { "target": "com.amazonaws.ec2#CreateInstanceEventWindow" }, @@ -2525,6 +2557,9 @@ { "target": "com.amazonaws.ec2#DeleteFpgaImage" }, + { + "target": "com.amazonaws.ec2#DeleteInstanceConnectEndpoint" + }, { "target": "com.amazonaws.ec2#DeleteInstanceEventWindow" }, @@ -2867,6 +2902,9 @@ { "target": "com.amazonaws.ec2#DescribeInstanceAttribute" }, + { + "target": "com.amazonaws.ec2#DescribeInstanceConnectEndpoints" + }, { "target": "com.amazonaws.ec2#DescribeInstanceCreditSpecifications" }, @@ -5514,6 +5552,9 @@ "smithy.api#xmlName": "securityGroupIds" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ArchitectureType": { @@ -5709,6 +5750,9 @@ "smithy.api#xmlName": "networkInterfaceId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssignPrivateIpAddresses": { @@ -5812,6 +5856,9 @@ "smithy.api#xmlName": "assignedIpv4PrefixSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssignPrivateNatGatewayAddress": { @@ -5884,6 +5931,9 @@ "smithy.api#xmlName": "natGatewayAddressSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssignedPrivateIpAddress": { @@ -5920,7 +5970,7 @@ "target": "com.amazonaws.ec2#AssociateAddressResult" }, "traits": { - "smithy.api#documentation": "

Associates an Elastic IP address, or carrier IP address (for instances that are in\n subnets in Wavelength Zones) with an instance or a network interface. Before you can use an\n Elastic IP address, you must allocate it to your account.

\n

An Elastic IP address is for use in either the EC2-Classic platform or in a VPC.\n\t\t\tFor more information, see Elastic IP Addresses in the Amazon Elastic Compute Cloud User Guide.

\n

[EC2-Classic, VPC in an EC2-VPC-only account] If the Elastic IP address is already\n associated with a different instance, it is disassociated from that instance and associated\n with the specified instance. If you associate an Elastic IP address with an instance that has\n an existing Elastic IP address, the existing address is disassociated from the instance, but\n remains allocated to your account.

\n

[VPC in an EC2-Classic account] If you don't specify a private IP address, the Elastic\n IP address is associated with the primary IP address. If the Elastic IP address is already\n associated with a different instance or a network interface, you get an error unless you allow\n reassociation. You cannot associate an Elastic IP address with an instance or network\n interface that has an existing Elastic IP address.

\n

[Subnets in Wavelength Zones] You can associate an IP address from the telecommunication\n carrier to the instance or network interface.

\n

You cannot associate an Elastic IP address with an interface in a different network border group.

\n \n

This is an idempotent operation. If you perform the operation more than once, Amazon EC2\n doesn't return an error, and you may be charged for each time the Elastic IP address is\n remapped to the same instance. For more information, see the Elastic IP\n Addresses section of Amazon EC2\n Pricing.

\n
\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "

Associates an Elastic IP address, or carrier IP address (for instances that are in\n subnets in Wavelength Zones) with an instance or a network interface. Before you can use an\n Elastic IP address, you must allocate it to your account.

\n

If the Elastic IP address is already\n associated with a different instance, it is disassociated from that instance and associated\n with the specified instance. If you associate an Elastic IP address with an instance that has\n an existing Elastic IP address, the existing address is disassociated from the instance, but\n remains allocated to your account.

\n

[Subnets in Wavelength Zones] You can associate an IP address from the telecommunication\n carrier to the instance or network interface.

\n

You cannot associate an Elastic IP address with an interface in a different network border group.

\n \n

This is an idempotent operation. If you perform the operation more than once, Amazon EC2\n doesn't return an error, and you may be charged for each time the Elastic IP address is\n remapped to the same instance. For more information, see the Elastic IP\n Addresses section of Amazon EC2\n Pricing.

\n
" } }, "com.amazonaws.ec2#AssociateAddressRequest": { @@ -5929,19 +5979,19 @@ "AllocationId": { "target": "com.amazonaws.ec2#AllocationId", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The allocation ID. This is required for EC2-VPC.

" + "smithy.api#documentation": "

The allocation ID. This is required.

" } }, "InstanceId": { "target": "com.amazonaws.ec2#InstanceId", "traits": { - "smithy.api#documentation": "

The ID of the instance. The instance must have exactly one attached network interface.\n For EC2-VPC, you can specify either the instance ID or the network interface ID, but not both.\n For EC2-Classic, you must specify an instance ID and the instance must be in the running\n state.

" + "smithy.api#documentation": "

The ID of the instance. The instance must have exactly one attached network interface.\n You can specify either the instance ID or the network interface ID, but not both.

" } }, "PublicIp": { "target": "com.amazonaws.ec2#EipAllocationPublicIp", "traits": { - "smithy.api#documentation": "

[EC2-Classic] The Elastic IP address to associate with the instance. This is required for\n EC2-Classic.

" + "smithy.api#documentation": "

Deprecated.

" } }, "AllowReassociation": { @@ -5950,7 +6000,7 @@ "aws.protocols#ec2QueryName": "AllowReassociation", "smithy.api#clientOptional": {}, "smithy.api#default": false, - "smithy.api#documentation": "

[EC2-VPC] For a VPC in an EC2-Classic account, specify true to allow an Elastic IP address that is already associated with an instance or network interface to be reassociated with the specified instance or network interface. Otherwise, the operation fails. In a VPC in an EC2-VPC-only account, reassociation is automatic, therefore you can specify false to ensure the operation fails if the Elastic IP address is already associated with another resource.

", + "smithy.api#documentation": "

Reassociation is automatic, but you can specify false to ensure the operation fails if the Elastic IP address is already associated with another resource.

", "smithy.api#xmlName": "allowReassociation" } }, @@ -5968,7 +6018,7 @@ "target": "com.amazonaws.ec2#NetworkInterfaceId", "traits": { "aws.protocols#ec2QueryName": "NetworkInterfaceId", - "smithy.api#documentation": "

[EC2-VPC] The ID of the network interface. If the instance has more than one network interface, you must specify a network interface ID.

\n

For EC2-VPC, you can specify either the instance ID or the network interface ID, but not both.

", + "smithy.api#documentation": "

The ID of the network interface. If the instance has more than one network interface, you must specify a network interface ID.

\n

You can specify either the instance ID or the network interface ID, but not both.

", "smithy.api#xmlName": "networkInterfaceId" } }, @@ -5976,7 +6026,7 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "PrivateIpAddress", - "smithy.api#documentation": "

[EC2-VPC] The primary or secondary private IP address to associate with the Elastic IP address. If no private IP address is specified, the Elastic IP address is associated with the primary private IP address.

", + "smithy.api#documentation": "

The primary or secondary private IP address to associate with the Elastic IP address. If no private IP address is specified, the Elastic IP address is associated with the primary private IP address.

", "smithy.api#xmlName": "privateIpAddress" } } @@ -5992,10 +6042,13 @@ "target": "com.amazonaws.ec2#String", "traits": { "aws.protocols#ec2QueryName": "AssociationId", - "smithy.api#documentation": "

[EC2-VPC] The ID that represents the association of the Elastic IP address with an instance.

", + "smithy.api#documentation": "

The ID that represents the association of the Elastic IP address with an instance.

", "smithy.api#xmlName": "associationId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateClientVpnTargetNetwork": { @@ -6068,6 +6121,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateDhcpOptions": { @@ -6187,6 +6243,9 @@ "smithy.api#xmlName": "encryptionKmsKeyId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateIamInstanceProfile": { @@ -6236,6 +6295,9 @@ "smithy.api#xmlName": "iamInstanceProfileAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateInstanceEventWindow": { @@ -6293,6 +6355,9 @@ "smithy.api#xmlName": "instanceEventWindow" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateIpamResourceDiscovery": { @@ -6364,6 +6429,9 @@ "smithy.api#xmlName": "ipamResourceDiscoveryAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateNatGatewayAddress": { @@ -6437,6 +6505,9 @@ "smithy.api#xmlName": "natGatewayAddressSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateRouteTable": { @@ -6512,6 +6583,9 @@ "smithy.api#xmlName": "associationState" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateSubnetCidrBlock": { @@ -6573,6 +6647,9 @@ "smithy.api#xmlName": "subnetId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateTransitGatewayMulticastDomain": { @@ -6638,6 +6715,9 @@ "smithy.api#xmlName": "associations" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateTransitGatewayPolicyTable": { @@ -6695,6 +6775,9 @@ "smithy.api#xmlName": "association" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateTransitGatewayRouteTable": { @@ -6752,6 +6835,9 @@ "smithy.api#xmlName": "association" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateTrunkInterface": { @@ -6840,6 +6926,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociateVpcCidrBlock": { @@ -6957,6 +7046,9 @@ "smithy.api#xmlName": "vpcId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AssociatedNetworkType": { @@ -7243,6 +7335,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AttachInternetGateway": { @@ -7394,7 +7489,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of AttachNetworkInterface.

" + "smithy.api#documentation": "

Contains the output of AttachNetworkInterface.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#AttachVerifiedAccessTrustProvider": { @@ -7467,6 +7563,9 @@ "smithy.api#xmlName": "verifiedAccessInstance" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AttachVolume": { @@ -7583,7 +7682,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of AttachVpnGateway.

" + "smithy.api#documentation": "

Contains the output of AttachVpnGateway.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#AttachmentEnaSrdSpecification": { @@ -7842,6 +7942,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AuthorizeSecurityGroupEgress": { @@ -7972,6 +8075,9 @@ "smithy.api#xmlName": "securityGroupRuleSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AuthorizeSecurityGroupIngress": { @@ -8090,6 +8196,9 @@ "smithy.api#xmlName": "securityGroupRuleSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#AutoAcceptSharedAssociationsValue": { @@ -8738,7 +8847,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of BundleInstance.

" + "smithy.api#documentation": "

Contains the output of BundleInstance.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#BundleTask": { @@ -9102,7 +9212,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CancelBundleTask.

" + "smithy.api#documentation": "

Contains the output of CancelBundleTask.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelCapacityReservation": { @@ -9203,6 +9314,9 @@ "smithy.api#xmlName": "failedFleetCancellationSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelCapacityReservationRequest": { @@ -9242,6 +9356,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelConversionRequest": { @@ -9371,6 +9488,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelImportTask": { @@ -9440,6 +9560,9 @@ "smithy.api#xmlName": "state" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelReservedInstancesListing": { @@ -9486,7 +9609,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CancelReservedInstancesListing.

" + "smithy.api#documentation": "

Contains the output of CancelReservedInstancesListing.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelSpotFleetRequests": { @@ -9752,7 +9876,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CancelSpotInstanceRequests.

" + "smithy.api#documentation": "

Contains the output of CancelSpotInstanceRequests.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CancelledSpotInstanceRequest": { @@ -12200,6 +12325,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ConnectionLogOptions": { @@ -12577,6 +12705,9 @@ "smithy.api#xmlName": "fpgaImageId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CopyImage": { @@ -12691,7 +12822,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CopyImage.

" + "smithy.api#documentation": "

Contains the output of CopyImage.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CopySnapshot": { @@ -12818,6 +12950,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CopyTagsFromSource": { @@ -13135,6 +13270,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateCapacityReservationRequest": { @@ -13265,6 +13403,9 @@ "smithy.api#xmlName": "capacityReservation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateCarrierGateway": { @@ -13328,6 +13469,9 @@ "smithy.api#xmlName": "carrierGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateClientVpnEndpoint": { @@ -13505,6 +13649,9 @@ "smithy.api#xmlName": "dnsName" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateClientVpnRoute": { @@ -13583,6 +13730,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateCoipCidr": { @@ -13640,6 +13790,9 @@ "smithy.api#xmlName": "coipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateCoipPool": { @@ -13696,6 +13849,9 @@ "smithy.api#xmlName": "coipPool" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateCustomerGateway": { @@ -13789,7 +13945,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateCustomerGateway.

" + "smithy.api#documentation": "

Contains the output of CreateCustomerGateway.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateDefaultSubnet": { @@ -13847,6 +14004,9 @@ "smithy.api#xmlName": "subnet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateDefaultVpc": { @@ -13888,6 +14048,9 @@ "smithy.api#xmlName": "vpc" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateDhcpOptions": { @@ -13948,6 +14111,9 @@ "smithy.api#xmlName": "dhcpOptions" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateEgressOnlyInternetGateway": { @@ -14018,6 +14184,9 @@ "smithy.api#xmlName": "egressOnlyInternetGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateFleet": { @@ -14268,6 +14437,9 @@ "smithy.api#xmlName": "fleetInstanceSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateFlowLogs": { @@ -14411,6 +14583,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateFpgaImage": { @@ -14499,6 +14674,9 @@ "smithy.api#xmlName": "fpgaImageGlobalId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateImage": { @@ -14595,6 +14773,98 @@ "smithy.api#xmlName": "imageId" } } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.ec2#CreateInstanceConnectEndpoint": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#CreateInstanceConnectEndpointRequest" + }, + "output": { + "target": "com.amazonaws.ec2#CreateInstanceConnectEndpointResult" + }, + "traits": { + "smithy.api#documentation": "

Creates an EC2 Instance Connect Endpoint.

\n

An EC2 Instance Connect Endpoint allows you to connect to a resource, without\n requiring the resource to have a public IPv4 address. For more information, see Connect to your resources without requiring a public IPv4 address using EC2\n Instance Connect Endpoint in the Amazon EC2 User\n Guide.

" + } + }, + "com.amazonaws.ec2#CreateInstanceConnectEndpointRequest": { + "type": "structure", + "members": { + "DryRun": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually making the request, \n and provides an error response. If you have the required permissions, the error response is DryRunOperation. \n Otherwise, it is UnauthorizedOperation.

" + } + }, + "SubnetId": { + "target": "com.amazonaws.ec2#SubnetId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The ID of the subnet in which to create the EC2 Instance Connect Endpoint.

", + "smithy.api#required": {} + } + }, + "SecurityGroupIds": { + "target": "com.amazonaws.ec2#SecurityGroupIdStringListRequest", + "traits": { + "smithy.api#documentation": "

One or more security groups to associate with the endpoint. If you don't specify a security group, \n the default security group for your VPC will be associated with the endpoint.

", + "smithy.api#xmlName": "SecurityGroupId" + } + }, + "PreserveClientIp": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Indicates whether your client's IP address is preserved as the source. The value is true or false.

\n
    \n
  • \n

    If true, your client's IP address is used when you connect to a resource.

    \n
  • \n
  • \n

    If false, the elastic network interface IP address is used when you connect to a resource.

    \n
  • \n
\n

Default: true\n

" + } + }, + "ClientToken": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#documentation": "

Unique, case-sensitive identifier that you provide to ensure the idempotency of the request.

", + "smithy.api#idempotencyToken": {} + } + }, + "TagSpecifications": { + "target": "com.amazonaws.ec2#TagSpecificationList", + "traits": { + "smithy.api#documentation": "

The tags to apply to the EC2 Instance Connect Endpoint during creation.

", + "smithy.api#xmlName": "TagSpecification" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#CreateInstanceConnectEndpointResult": { + "type": "structure", + "members": { + "InstanceConnectEndpoint": { + "target": "com.amazonaws.ec2#Ec2InstanceConnectEndpoint", + "traits": { + "aws.protocols#ec2QueryName": "InstanceConnectEndpoint", + "smithy.api#documentation": "

Information about the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "instanceConnectEndpoint" + } + }, + "ClientToken": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "ClientToken", + "smithy.api#documentation": "

Unique, case-sensitive idempotency token provided by the client in the the request.

", + "smithy.api#xmlName": "clientToken" + } + } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateInstanceEventWindow": { @@ -14662,6 +14932,9 @@ "smithy.api#xmlName": "instanceEventWindow" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateInstanceExportTask": { @@ -14740,6 +15013,9 @@ "smithy.api#xmlName": "exportTask" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateInternetGateway": { @@ -14790,6 +15066,9 @@ "smithy.api#xmlName": "internetGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateIpam": { @@ -14950,6 +15229,9 @@ "smithy.api#xmlName": "ipamPool" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateIpamRequest": { @@ -15061,6 +15343,9 @@ "smithy.api#xmlName": "ipamResourceDiscovery" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateIpamResult": { @@ -15074,6 +15359,9 @@ "smithy.api#xmlName": "ipam" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateIpamScope": { @@ -15143,6 +15431,9 @@ "smithy.api#xmlName": "ipamScope" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateKeyPair": { @@ -15284,6 +15575,9 @@ "smithy.api#xmlName": "warning" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateLaunchTemplateVersion": { @@ -15379,6 +15673,9 @@ "smithy.api#xmlName": "warning" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateLocalGatewayRoute": { @@ -15452,6 +15749,9 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateLocalGatewayRouteTable": { @@ -15514,6 +15814,9 @@ "smithy.api#xmlName": "localGatewayRouteTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateLocalGatewayRouteTableVirtualInterfaceGroupAssociation": { @@ -15578,6 +15881,9 @@ "smithy.api#xmlName": "localGatewayRouteTableVirtualInterfaceGroupAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateLocalGatewayRouteTableVpcAssociation": { @@ -15642,6 +15948,9 @@ "smithy.api#xmlName": "localGatewayRouteTableVpcAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateManagedPrefixList": { @@ -15729,6 +16038,9 @@ "smithy.api#xmlName": "prefixList" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNatGateway": { @@ -15840,6 +16152,9 @@ "smithy.api#xmlName": "natGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNetworkAcl": { @@ -16013,6 +16328,9 @@ "smithy.api#xmlName": "networkAcl" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNetworkInsightsAccessScope": { @@ -16092,6 +16410,9 @@ "smithy.api#xmlName": "networkInsightsAccessScopeContent" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNetworkInsightsPath": { @@ -16203,6 +16524,9 @@ "smithy.api#xmlName": "networkInsightsPath" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNetworkInterface": { @@ -16287,7 +16611,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateNetworkInterfacePermission.

" + "smithy.api#documentation": "

Contains the output of CreateNetworkInterfacePermission.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateNetworkInterfaceRequest": { @@ -16395,7 +16720,7 @@ "InterfaceType": { "target": "com.amazonaws.ec2#NetworkInterfaceCreationType", "traits": { - "smithy.api#documentation": "

The type of network interface. The default is interface.

\n

The only supported values are efa and trunk.

" + "smithy.api#documentation": "

The type of network interface. The default is interface.

\n

The only supported values are interface, efa, and trunk.

" } }, "SubnetId": { @@ -16446,6 +16771,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreatePlacementGroup": { @@ -16526,6 +16854,9 @@ "smithy.api#xmlName": "placementGroup" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreatePublicIpv4Pool": { @@ -16574,6 +16905,9 @@ "smithy.api#xmlName": "poolId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateReplaceRootVolumeTask": { @@ -16657,6 +16991,9 @@ "smithy.api#xmlName": "replaceRootVolumeTask" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateReservedInstancesListing": { @@ -16734,7 +17071,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateReservedInstancesListing.

" + "smithy.api#documentation": "

Contains the output of CreateReservedInstancesListing.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateRestoreImageTask": { @@ -16805,6 +17143,9 @@ "smithy.api#xmlName": "imageId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateRoute": { @@ -16960,6 +17301,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateRouteTable": { @@ -17020,6 +17364,9 @@ "smithy.api#xmlName": "routeTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateSecurityGroup": { @@ -17101,6 +17448,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateSnapshot": { @@ -17232,6 +17582,9 @@ "smithy.api#xmlName": "snapshotSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateSpotDatafeedSubscription": { @@ -17296,7 +17649,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateSpotDatafeedSubscription.

" + "smithy.api#documentation": "

Contains the output of CreateSpotDatafeedSubscription.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateStoreImageTask": { @@ -17361,6 +17715,9 @@ "smithy.api#xmlName": "objectKey" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateSubnet": { @@ -17451,6 +17808,9 @@ "smithy.api#xmlName": "subnetCidrReservation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateSubnetRequest": { @@ -17535,6 +17895,9 @@ "smithy.api#xmlName": "subnet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTags": { @@ -17652,6 +18015,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTrafficMirrorFilterRule": { @@ -17783,6 +18149,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTrafficMirrorSession": { @@ -17901,6 +18270,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTrafficMirrorTarget": { @@ -17988,6 +18360,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGateway": { @@ -18096,6 +18471,9 @@ "smithy.api#xmlName": "transitGatewayConnectPeer" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayConnectRequest": { @@ -18164,6 +18542,9 @@ "smithy.api#xmlName": "transitGatewayConnect" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayMulticastDomain": { @@ -18252,6 +18633,9 @@ "smithy.api#xmlName": "transitGatewayMulticastDomain" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayPeeringAttachment": { @@ -18352,6 +18736,9 @@ "smithy.api#xmlName": "transitGatewayPeeringAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayPolicyTable": { @@ -18407,6 +18794,9 @@ "smithy.api#xmlName": "transitGatewayPolicyTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayPrefixListReference": { @@ -18478,6 +18868,9 @@ "smithy.api#xmlName": "transitGatewayPrefixListReference" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayRequest": { @@ -18526,6 +18919,9 @@ "smithy.api#xmlName": "transitGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayRoute": { @@ -18597,6 +18993,9 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayRouteTable": { @@ -18673,6 +19072,9 @@ "smithy.api#xmlName": "transitGatewayRouteTableAnnouncement" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayRouteTableRequest": { @@ -18716,6 +19118,9 @@ "smithy.api#xmlName": "transitGatewayRouteTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateTransitGatewayVpcAttachment": { @@ -18819,6 +19224,9 @@ "smithy.api#xmlName": "transitGatewayVpcAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVerifiedAccessEndpoint": { @@ -19016,6 +19424,9 @@ "smithy.api#xmlName": "verifiedAccessEndpoint" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVerifiedAccessEndpointSubnetIdList": { @@ -19100,6 +19511,9 @@ "smithy.api#xmlName": "verifiedAccessGroup" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVerifiedAccessInstance": { @@ -19161,6 +19575,9 @@ "smithy.api#xmlName": "verifiedAccessInstance" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVerifiedAccessTrustProvider": { @@ -19326,6 +19743,9 @@ "smithy.api#xmlName": "verifiedAccessTrustProvider" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVolume": { @@ -19604,6 +20024,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpcEndpointRequest": { @@ -19723,6 +20146,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpcEndpointServiceConfiguration": { @@ -19820,6 +20246,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpcPeeringConnection": { @@ -19902,6 +20331,9 @@ "smithy.api#xmlName": "vpcPeeringConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpcRequest": { @@ -20006,6 +20438,9 @@ "smithy.api#xmlName": "vpc" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpnConnection": { @@ -20095,7 +20530,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateVpnConnection.

" + "smithy.api#documentation": "

Contains the output of CreateVpnConnection.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreateVpnConnectionRoute": { @@ -20208,7 +20644,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of CreateVpnGateway.

" + "smithy.api#documentation": "

Contains the output of CreateVpnGateway.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#CreditSpecification": { @@ -20630,6 +21067,9 @@ "smithy.api#xmlName": "carrierGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteClientVpnEndpoint": { @@ -20679,6 +21119,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteClientVpnRoute": { @@ -20742,6 +21185,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteCoipCidr": { @@ -20799,6 +21245,9 @@ "smithy.api#xmlName": "coipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteCoipPool": { @@ -20848,6 +21297,9 @@ "smithy.api#xmlName": "coipPool" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteCustomerGateway": { @@ -20976,6 +21428,9 @@ "smithy.api#xmlName": "returnCode" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteFleetError": { @@ -21170,6 +21625,9 @@ "smithy.api#xmlName": "unsuccessfulFleetDeletionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteFlowLogs": { @@ -21220,6 +21678,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteFpgaImage": { @@ -21271,6 +21732,61 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} + } + }, + "com.amazonaws.ec2#DeleteInstanceConnectEndpoint": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#DeleteInstanceConnectEndpointRequest" + }, + "output": { + "target": "com.amazonaws.ec2#DeleteInstanceConnectEndpointResult" + }, + "traits": { + "smithy.api#documentation": "

Deletes the specified EC2 Instance Connect Endpoint.

" + } + }, + "com.amazonaws.ec2#DeleteInstanceConnectEndpointRequest": { + "type": "structure", + "members": { + "DryRun": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually making the request, \n and provides an error response. If you have the required permissions, the error response is DryRunOperation. \n Otherwise, it is UnauthorizedOperation.

" + } + }, + "InstanceConnectEndpointId": { + "target": "com.amazonaws.ec2#InstanceConnectEndpointId", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

The ID of the EC2 Instance Connect Endpoint to delete.

", + "smithy.api#required": {} + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#DeleteInstanceConnectEndpointResult": { + "type": "structure", + "members": { + "InstanceConnectEndpoint": { + "target": "com.amazonaws.ec2#Ec2InstanceConnectEndpoint", + "traits": { + "aws.protocols#ec2QueryName": "InstanceConnectEndpoint", + "smithy.api#documentation": "

Information about the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "instanceConnectEndpoint" + } + } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteInstanceEventWindow": { @@ -21329,6 +21845,9 @@ "smithy.api#xmlName": "instanceEventWindowState" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteInternetGateway": { @@ -21430,6 +21949,9 @@ "smithy.api#xmlName": "ipamPool" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteIpamRequest": { @@ -21511,6 +22033,9 @@ "smithy.api#xmlName": "ipamResourceDiscovery" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteIpamResult": { @@ -21524,6 +22049,9 @@ "smithy.api#xmlName": "ipam" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteIpamScope": { @@ -21573,6 +22101,9 @@ "smithy.api#xmlName": "ipamScope" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteKeyPair": { @@ -21668,6 +22199,9 @@ "smithy.api#xmlName": "launchTemplate" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteLaunchTemplateVersions": { @@ -21832,6 +22366,9 @@ "smithy.api#xmlName": "unsuccessfullyDeletedLaunchTemplateVersionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteLocalGatewayRoute": { @@ -21893,6 +22430,9 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteLocalGatewayRouteTable": { @@ -21942,6 +22482,9 @@ "smithy.api#xmlName": "localGatewayRouteTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteLocalGatewayRouteTableVirtualInterfaceGroupAssociation": { @@ -21991,6 +22534,9 @@ "smithy.api#xmlName": "localGatewayRouteTableVirtualInterfaceGroupAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteLocalGatewayRouteTableVpcAssociation": { @@ -22040,6 +22586,9 @@ "smithy.api#xmlName": "localGatewayRouteTableVpcAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteManagedPrefixList": { @@ -22089,6 +22638,9 @@ "smithy.api#xmlName": "prefixList" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNatGateway": { @@ -22139,6 +22691,9 @@ "smithy.api#xmlName": "natGatewayId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkAcl": { @@ -22302,6 +22857,9 @@ "smithy.api#xmlName": "networkInsightsAccessScopeAnalysisId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkInsightsAccessScopeRequest": { @@ -22339,6 +22897,9 @@ "smithy.api#xmlName": "networkInsightsAccessScopeId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkInsightsAnalysis": { @@ -22388,6 +22949,9 @@ "smithy.api#xmlName": "networkInsightsAnalysisId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkInsightsPath": { @@ -22437,6 +23001,9 @@ "smithy.api#xmlName": "networkInsightsPathId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkInterface": { @@ -22511,7 +23078,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output for DeleteNetworkInterfacePermission.

" + "smithy.api#documentation": "

Contains the output for DeleteNetworkInterfacePermission.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteNetworkInterfaceRequest": { @@ -22632,6 +23200,9 @@ "smithy.api#xmlName": "returnValue" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteQueuedReservedInstances": { @@ -22752,6 +23323,9 @@ "smithy.api#xmlName": "failedQueuedPurchaseDeletionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteRoute": { @@ -23026,6 +23600,9 @@ "smithy.api#xmlName": "deletedSubnetCidrReservation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteSubnetRequest": { @@ -23149,6 +23726,9 @@ "smithy.api#xmlName": "trafficMirrorFilterId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTrafficMirrorFilterRule": { @@ -23198,6 +23778,9 @@ "smithy.api#xmlName": "trafficMirrorFilterRuleId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTrafficMirrorSession": { @@ -23247,6 +23830,9 @@ "smithy.api#xmlName": "trafficMirrorSessionId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTrafficMirrorTarget": { @@ -23296,6 +23882,9 @@ "smithy.api#xmlName": "trafficMirrorTargetId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGateway": { @@ -23369,6 +23958,9 @@ "smithy.api#xmlName": "transitGatewayConnectPeer" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayConnectRequest": { @@ -23406,6 +23998,9 @@ "smithy.api#xmlName": "transitGatewayConnect" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayMulticastDomain": { @@ -23455,6 +24050,9 @@ "smithy.api#xmlName": "transitGatewayMulticastDomain" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayPeeringAttachment": { @@ -23504,6 +24102,9 @@ "smithy.api#xmlName": "transitGatewayPeeringAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayPolicyTable": { @@ -23553,6 +24154,9 @@ "smithy.api#xmlName": "transitGatewayPolicyTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayPrefixListReference": { @@ -23610,6 +24214,9 @@ "smithy.api#xmlName": "transitGatewayPrefixListReference" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayRequest": { @@ -23647,6 +24254,9 @@ "smithy.api#xmlName": "transitGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayRoute": { @@ -23704,6 +24314,9 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayRouteTable": { @@ -23765,6 +24378,9 @@ "smithy.api#xmlName": "transitGatewayRouteTableAnnouncement" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayRouteTableRequest": { @@ -23802,6 +24418,9 @@ "smithy.api#xmlName": "transitGatewayRouteTable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteTransitGatewayVpcAttachment": { @@ -23851,6 +24470,9 @@ "smithy.api#xmlName": "transitGatewayVpcAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVerifiedAccessEndpoint": { @@ -23907,6 +24529,9 @@ "smithy.api#xmlName": "verifiedAccessEndpoint" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVerifiedAccessGroup": { @@ -23963,6 +24588,9 @@ "smithy.api#xmlName": "verifiedAccessGroup" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVerifiedAccessInstance": { @@ -24019,6 +24647,9 @@ "smithy.api#xmlName": "verifiedAccessInstance" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVerifiedAccessTrustProvider": { @@ -24075,6 +24706,9 @@ "smithy.api#xmlName": "verifiedAccessTrustProvider" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVolume": { @@ -24175,6 +24809,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVpcEndpointServiceConfigurations": { @@ -24225,6 +24862,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVpcEndpoints": { @@ -24275,6 +24915,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVpcPeeringConnection": { @@ -24330,6 +24973,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeleteVpcRequest": { @@ -24520,6 +25166,9 @@ "smithy.api#xmlName": "byoipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeprovisionIpamPoolCidr": { @@ -24575,6 +25224,9 @@ "smithy.api#xmlName": "ipamPoolCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeprovisionPublicIpv4PoolCidr": { @@ -24640,6 +25292,9 @@ "smithy.api#xmlName": "deprovisionedAddressSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeprovisionedAddressSet": { @@ -24716,7 +25371,9 @@ "InstanceTagAttribute": { "target": "com.amazonaws.ec2#DeregisterInstanceTagAttributeRequest", "traits": { - "smithy.api#documentation": "

Information about the tag keys to deregister.

" + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

Information about the tag keys to deregister.

", + "smithy.api#required": {} } } }, @@ -24735,6 +25392,9 @@ "smithy.api#xmlName": "instanceTagAttribute" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeregisterInstanceTagAttributeRequest": { @@ -24817,6 +25477,9 @@ "smithy.api#xmlName": "deregisteredMulticastGroupMembers" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DeregisterTransitGatewayMulticastGroupSources": { @@ -24876,6 +25539,9 @@ "smithy.api#xmlName": "deregisteredMulticastGroupSources" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAccountAttributes": { @@ -24927,6 +25593,9 @@ "smithy.api#xmlName": "accountAttributeSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAddressTransfers": { @@ -24938,7 +25607,7 @@ "target": "com.amazonaws.ec2#DescribeAddressTransfersResult" }, "traits": { - "smithy.api#documentation": "

Describes an Elastic IP address transfer. For more information, see Transfer Elastic IP addresses in the Amazon Virtual Private Cloud User Guide.

", + "smithy.api#documentation": "

Describes an Elastic IP address transfer. For more information, see Transfer Elastic IP addresses in the Amazon Virtual Private Cloud User Guide.

\n

When you transfer an Elastic IP address, there is a two-step handshake\n between the source and transfer Amazon Web Services accounts. When the source account starts the transfer,\n the transfer account has seven days to accept the Elastic IP address\n transfer. During those seven days, the source account can view the\n pending transfer by using this action. After seven days, the\n transfer expires and ownership of the Elastic IP\n address returns to the source\n account. Accepted transfers are visible to the source account for three days\n after the transfers have been accepted.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -25013,6 +25682,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAddresses": { @@ -25024,7 +25696,7 @@ "target": "com.amazonaws.ec2#DescribeAddressesResult" }, "traits": { - "smithy.api#documentation": "

Describes the specified Elastic IP addresses or all of your Elastic IP addresses.

\n

An Elastic IP address is for use in either the EC2-Classic platform or in a VPC.\n\t\t\t\tFor more information, see Elastic IP Addresses in the Amazon Elastic Compute Cloud User Guide.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "

Describes the specified Elastic IP addresses or all of your Elastic IP addresses.

" } }, "com.amazonaws.ec2#DescribeAddressesAttribute": { @@ -25107,6 +25779,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAddressesRequest": { @@ -25115,7 +25790,7 @@ "Filters": { "target": "com.amazonaws.ec2#FilterList", "traits": { - "smithy.api#documentation": "

One or more filters. Filter names and values are case-sensitive.

\n
    \n
  • \n

    \n allocation-id - [EC2-VPC] The allocation ID for the address.

    \n
  • \n
  • \n

    \n association-id - [EC2-VPC] The association ID for the address.

    \n
  • \n
  • \n

    \n domain - Indicates whether the address is for use in EC2-Classic (standard) \n or in a VPC (vpc).

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance the address is associated with, if any.

    \n
  • \n
  • \n

    \n network-border-group - A unique set of Availability Zones, Local Zones,\n or Wavelength Zones from where Amazon Web Services advertises IP addresses.

    \n
  • \n
  • \n

    \n network-interface-id - [EC2-VPC] The ID of the network interface that the address is associated with, if any.

    \n
  • \n
  • \n

    \n network-interface-owner-id - The Amazon Web Services account ID of the owner.

    \n
  • \n
  • \n

    \n private-ip-address - [EC2-VPC] The private IP address associated with the Elastic IP address.

    \n
  • \n
  • \n

    \n public-ip - The Elastic IP address, or the carrier IP address.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
", + "smithy.api#documentation": "

One or more filters. Filter names and values are case-sensitive.

\n
    \n
  • \n

    \n allocation-id - The allocation ID for the address.

    \n
  • \n
  • \n

    \n association-id - The association ID for the address.

    \n
  • \n
  • \n

    \n instance-id - The ID of the instance the address is associated with, if any.

    \n
  • \n
  • \n

    \n network-border-group - A unique set of Availability Zones, Local Zones,\n or Wavelength Zones from where Amazon Web Services advertises IP addresses.

    \n
  • \n
  • \n

    \n network-interface-id - The ID of the network interface that the address is associated with, if any.

    \n
  • \n
  • \n

    \n network-interface-owner-id - The Amazon Web Services account ID of the owner.

    \n
  • \n
  • \n

    \n private-ip-address - The private IP address associated with the Elastic IP address.

    \n
  • \n
  • \n

    \n public-ip - The Elastic IP address, or the carrier IP address.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
", "smithy.api#xmlName": "Filter" } }, @@ -25129,7 +25804,7 @@ "AllocationIds": { "target": "com.amazonaws.ec2#AllocationIdList", "traits": { - "smithy.api#documentation": "

[EC2-VPC] Information about the allocation IDs.

", + "smithy.api#documentation": "

Information about the allocation IDs.

", "smithy.api#xmlName": "AllocationId" } }, @@ -25159,6 +25834,9 @@ "smithy.api#xmlName": "addressesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAggregateIdFormat": { @@ -25210,6 +25888,9 @@ "smithy.api#xmlName": "statusSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAvailabilityZones": { @@ -25282,6 +25963,9 @@ "smithy.api#xmlName": "availabilityZoneInfo" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeAwsNetworkPerformanceMetricSubscriptions": { @@ -25358,6 +26042,9 @@ "smithy.api#xmlName": "subscriptionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeBundleTasks": { @@ -25442,6 +26129,9 @@ "smithy.api#xmlName": "bundleInstanceTasksSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeByoipCidrs": { @@ -25522,6 +26212,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeCapacityReservationFleets": { @@ -25615,6 +26308,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeCapacityReservations": { @@ -25708,6 +26404,9 @@ "smithy.api#xmlName": "capacityReservationSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeCarrierGateways": { @@ -25791,6 +26490,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClassicLinkInstances": { @@ -25890,6 +26592,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClientVpnAuthorizationRules": { @@ -25984,6 +26689,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClientVpnConnections": { @@ -26078,6 +26786,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClientVpnEndpointMaxResults": { @@ -26171,6 +26882,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClientVpnRoutes": { @@ -26265,6 +26979,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeClientVpnTargetNetworks": { @@ -26365,6 +27082,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeCoipPools": { @@ -26448,6 +27168,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeConversionTaskList": { @@ -26575,6 +27298,9 @@ "smithy.api#xmlName": "conversionTasks" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeCustomerGateways": { @@ -26672,7 +27398,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeCustomerGateways.

" + "smithy.api#documentation": "

Contains the output of DescribeCustomerGateways.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeDhcpOptions": { @@ -26768,6 +27495,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeEgressOnlyInternetGateways": { @@ -26861,6 +27591,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeElasticGpus": { @@ -26958,6 +27691,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeExportImageTasks": { @@ -27051,6 +27787,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeExportTasks": { @@ -27131,6 +27870,9 @@ "smithy.api#xmlName": "exportTaskSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFastLaunchImages": { @@ -27221,6 +27963,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFastLaunchImagesSuccessItem": { @@ -27500,6 +28245,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFleetError": { @@ -27649,6 +28397,9 @@ "smithy.api#xmlName": "startTime" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFleetInstances": { @@ -27735,6 +28486,9 @@ "smithy.api#xmlName": "fleetId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFleets": { @@ -27884,6 +28638,9 @@ "smithy.api#xmlName": "fleetSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFlowLogs": { @@ -27966,6 +28723,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFpgaImageAttribute": { @@ -28023,6 +28783,9 @@ "smithy.api#xmlName": "fpgaImageAttribute" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeFpgaImages": { @@ -28123,6 +28886,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeHostReservationOfferings": { @@ -28212,6 +28978,9 @@ "smithy.api#xmlName": "offeringSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeHostReservations": { @@ -28295,6 +29064,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeHosts": { @@ -28376,6 +29148,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIamInstanceProfileAssociations": { @@ -28461,6 +29236,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIdFormat": { @@ -28500,6 +29278,9 @@ "smithy.api#xmlName": "statusSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIdentityIdFormat": { @@ -28551,6 +29332,9 @@ "smithy.api#xmlName": "statusSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeImageAttribute": { @@ -28756,6 +29540,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeImportImageTasks": { @@ -28839,6 +29626,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeImportSnapshotTasks": { @@ -28949,6 +29739,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceAttribute": { @@ -29001,6 +29794,92 @@ "smithy.api#input": {} } }, + "com.amazonaws.ec2#DescribeInstanceConnectEndpoints": { + "type": "operation", + "input": { + "target": "com.amazonaws.ec2#DescribeInstanceConnectEndpointsRequest" + }, + "output": { + "target": "com.amazonaws.ec2#DescribeInstanceConnectEndpointsResult" + }, + "traits": { + "smithy.api#documentation": "

Describes the specified EC2 Instance Connect Endpoints or all EC2 Instance Connect Endpoints.

", + "smithy.api#paginated": { + "inputToken": "NextToken", + "outputToken": "NextToken", + "items": "InstanceConnectEndpoints", + "pageSize": "MaxResults" + } + } + }, + "com.amazonaws.ec2#DescribeInstanceConnectEndpointsRequest": { + "type": "structure", + "members": { + "DryRun": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Checks whether you have the required permissions for the action, without actually making the request, \n and provides an error response. If you have the required permissions, the error response is DryRunOperation. \n Otherwise, it is UnauthorizedOperation.

" + } + }, + "MaxResults": { + "target": "com.amazonaws.ec2#InstanceConnectEndpointMaxResults", + "traits": { + "smithy.api#clientOptional": {}, + "smithy.api#default": 0, + "smithy.api#documentation": "

The maximum number of items to return for this request.\n To get the next page of items, make another request with the token returned in the output.\n\t For more information, see Pagination.

" + } + }, + "NextToken": { + "target": "com.amazonaws.ec2#NextToken", + "traits": { + "smithy.api#documentation": "

The token returned from a previous paginated request. Pagination continues from the end of the items returned by the previous request.

" + } + }, + "Filters": { + "target": "com.amazonaws.ec2#FilterList", + "traits": { + "smithy.api#documentation": "

One or more filters.

\n
    \n
  • \n

    \n instance-connect-endpoint-id - The ID of the EC2 Instance Connect Endpoint.

    \n
  • \n
  • \n

    \n state - The state of the EC2 Instance Connect Endpoint (create-in-progress | create-complete | create-failed | \n delete-in-progress | delete-complete | delete-failed).

    \n
  • \n
  • \n

    \n subnet-id - The ID of the subnet in which the EC2 Instance\n Connect Endpoint was created.

    \n
  • \n
  • \n

    \n tag: - The key/value combination of a tag assigned to the resource. Use the tag key in the filter name and the tag value as the filter value.\n For example, to find all resources that have a tag with the key Owner and the value TeamA, specify tag:Owner for the filter name and TeamA for the filter value.

    \n
  • \n
  • \n

    \n tag-key - The key of a tag assigned to the resource. Use this filter to find all resources assigned a tag with a specific key, regardless of the tag value.

    \n
  • \n
  • \n

    \n tag-value - The value of a tag assigned to the resource. Use this filter to find all resources \n that have a tag with a specific value, regardless of tag key.

    \n
  • \n
  • \n

    \n vpc-id - The ID of the VPC in which the EC2 Instance Connect\n Endpoint was created.

    \n
  • \n
", + "smithy.api#xmlName": "Filter" + } + }, + "InstanceConnectEndpointIds": { + "target": "com.amazonaws.ec2#ValueStringList", + "traits": { + "smithy.api#documentation": "

One or more EC2 Instance Connect Endpoint IDs.

", + "smithy.api#xmlName": "InstanceConnectEndpointId" + } + } + }, + "traits": { + "smithy.api#input": {} + } + }, + "com.amazonaws.ec2#DescribeInstanceConnectEndpointsResult": { + "type": "structure", + "members": { + "InstanceConnectEndpoints": { + "target": "com.amazonaws.ec2#InstanceConnectEndpointSet", + "traits": { + "aws.protocols#ec2QueryName": "InstanceConnectEndpointSet", + "smithy.api#documentation": "

Information about the EC2 Instance Connect Endpoints.

", + "smithy.api#xmlName": "instanceConnectEndpointSet" + } + }, + "NextToken": { + "target": "com.amazonaws.ec2#NextToken", + "traits": { + "aws.protocols#ec2QueryName": "NextToken", + "smithy.api#documentation": "

The token to include in another request to get the next page of items. This value is null when there\n are no more items to return.

", + "smithy.api#xmlName": "nextToken" + } + } + }, + "traits": { + "smithy.api#output": {} + } + }, "com.amazonaws.ec2#DescribeInstanceCreditSpecifications": { "type": "operation", "input": { @@ -29092,6 +29971,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceEventNotificationAttributes": { @@ -29133,6 +30015,9 @@ "smithy.api#xmlName": "instanceTagAttribute" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceEventWindows": { @@ -29217,6 +30102,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceStatus": { @@ -29353,6 +30241,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceTypeOfferings": { @@ -29433,6 +30324,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstanceTypes": { @@ -29514,6 +30408,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInstances": { @@ -29750,6 +30647,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeInternetGateways": { @@ -29872,6 +30772,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpamPools": { @@ -29955,6 +30858,9 @@ "smithy.api#xmlName": "ipamPoolSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpamResourceDiscoveries": { @@ -30038,6 +30944,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpamResourceDiscoveryAssociations": { @@ -30121,6 +31030,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpamScopes": { @@ -30204,6 +31116,9 @@ "smithy.api#xmlName": "ipamScopeSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpams": { @@ -30287,6 +31202,9 @@ "smithy.api#xmlName": "ipamSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeIpv6Pools": { @@ -30370,6 +31288,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeKeyPairs": { @@ -30468,6 +31389,9 @@ "smithy.api#xmlName": "keySet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLaunchTemplateVersions": { @@ -30583,6 +31507,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLaunchTemplates": { @@ -30683,6 +31610,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGatewayRouteTableVirtualInterfaceGroupAssociations": { @@ -30766,6 +31696,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGatewayRouteTableVpcAssociations": { @@ -30849,6 +31782,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGatewayRouteTables": { @@ -30932,6 +31868,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGatewayVirtualInterfaceGroups": { @@ -31015,6 +31954,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGatewayVirtualInterfaces": { @@ -31098,6 +32040,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeLocalGateways": { @@ -31181,6 +32126,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeManagedPrefixLists": { @@ -31264,6 +32212,9 @@ "smithy.api#xmlName": "prefixListSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeMovingAddresses": { @@ -31275,7 +32226,7 @@ "target": "com.amazonaws.ec2#DescribeMovingAddressesResult" }, "traits": { - "smithy.api#documentation": "

Describes your Elastic IP addresses that are being moved to the EC2-VPC platform, or that are being restored to the EC2-Classic platform. This request does not return information about any other Elastic IP addresses in your account.

", + "smithy.api#documentation": "\n

This action is deprecated.

\n
\n

Describes your Elastic IP addresses that are being moved from or being restored to the EC2-Classic platform. \n This request does not return information about any other Elastic IP addresses in your account.

", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -31365,6 +32316,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNatGateways": { @@ -31534,6 +32488,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkAcls": { @@ -31629,6 +32586,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInsightsAccessScopeAnalyses": { @@ -31730,6 +32690,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInsightsAccessScopes": { @@ -31813,6 +32776,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInsightsAnalyses": { @@ -31914,6 +32880,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInsightsPaths": { @@ -31997,6 +32966,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInterfaceAttribute": { @@ -32093,7 +33065,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeNetworkInterfaceAttribute.

" + "smithy.api#documentation": "

Contains the output of DescribeNetworkInterfaceAttribute.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInterfacePermissions": { @@ -32182,7 +33155,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output for DescribeNetworkInterfacePermissions.

" + "smithy.api#documentation": "

Contains the output for DescribeNetworkInterfacePermissions.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeNetworkInterfaces": { @@ -32306,6 +33280,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribePlacementGroups": { @@ -32371,6 +33348,9 @@ "smithy.api#xmlName": "placementGroupSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribePrefixLists": { @@ -32454,6 +33434,9 @@ "smithy.api#xmlName": "prefixListSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribePrincipalIdFormat": { @@ -32540,6 +33523,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribePublicIpv4Pools": { @@ -32615,6 +33601,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeRegions": { @@ -32680,6 +33669,9 @@ "smithy.api#xmlName": "regionInfo" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeReplaceRootVolumeTasks": { @@ -32773,6 +33765,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeReservedInstances": { @@ -32844,7 +33839,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesListings.

" + "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesListings.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeReservedInstancesModifications": { @@ -32916,7 +33912,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesModifications.

" + "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesModifications.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeReservedInstancesOfferings": { @@ -33081,7 +34078,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesOfferings.

" + "smithy.api#documentation": "

Contains the output of DescribeReservedInstancesOfferings.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeReservedInstancesRequest": { @@ -33144,7 +34142,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output for DescribeReservedInstances.

" + "smithy.api#documentation": "

Contains the output for DescribeReservedInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeRouteTables": { @@ -33242,7 +34241,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeRouteTables.

" + "smithy.api#documentation": "

Contains the output of DescribeRouteTables.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeScheduledInstanceAvailability": { @@ -33364,7 +34364,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeScheduledInstanceAvailability.

" + "smithy.api#documentation": "

Contains the output of DescribeScheduledInstanceAvailability.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeScheduledInstances": { @@ -33457,7 +34458,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeScheduledInstances.

" + "smithy.api#documentation": "

Contains the output of DescribeScheduledInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSecurityGroupReferences": { @@ -33507,6 +34509,9 @@ "smithy.api#xmlName": "securityGroupReferenceSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSecurityGroupRules": { @@ -33600,6 +34605,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSecurityGroups": { @@ -33728,6 +34736,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSnapshotAttribute": { @@ -33803,6 +34814,9 @@ "smithy.api#xmlName": "snapshotId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSnapshotTierStatus": { @@ -33880,6 +34894,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSnapshots": { @@ -34006,6 +35023,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSpotDatafeedSubscription": { @@ -34052,7 +35072,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeSpotDatafeedSubscription.

" + "smithy.api#documentation": "

Contains the output of DescribeSpotDatafeedSubscription.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSpotFleetInstances": { @@ -34543,7 +35564,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeSpotInstanceRequests.

" + "smithy.api#documentation": "

Contains the output of DescribeSpotInstanceRequests.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSpotPriceHistory": { @@ -34667,7 +35689,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeSpotPriceHistory.

" + "smithy.api#documentation": "

Contains the output of DescribeSpotPriceHistory.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeStaleSecurityGroups": { @@ -34764,6 +35787,9 @@ "smithy.api#xmlName": "staleSecurityGroupSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeStoreImageTasks": { @@ -34857,6 +35883,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeSubnets": { @@ -34969,6 +35998,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTags": { @@ -35051,6 +36083,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTrafficMirrorFilters": { @@ -35134,6 +36169,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTrafficMirrorSessions": { @@ -35217,6 +36255,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTrafficMirrorTargets": { @@ -35300,6 +36341,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayAttachments": { @@ -35382,6 +36426,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayConnectPeers": { @@ -35464,6 +36511,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayConnects": { @@ -35546,6 +36596,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayMulticastDomains": { @@ -35628,6 +36681,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayPeeringAttachments": { @@ -35710,6 +36766,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayPolicyTables": { @@ -35792,6 +36851,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayRouteTableAnnouncements": { @@ -35874,6 +36936,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayRouteTables": { @@ -35956,6 +37021,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGatewayVpcAttachments": { @@ -36038,6 +37106,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTransitGateways": { @@ -36120,6 +37191,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeTrunkInterfaceAssociations": { @@ -36213,6 +37287,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVerifiedAccessEndpoints": { @@ -36318,6 +37395,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVerifiedAccessGroupMaxResults": { @@ -36417,6 +37497,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVerifiedAccessInstanceLoggingConfigurations": { @@ -36510,6 +37593,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVerifiedAccessInstances": { @@ -36603,6 +37689,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVerifiedAccessTrustProviders": { @@ -36696,6 +37785,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVolumeAttribute": { @@ -36771,6 +37863,9 @@ "smithy.api#xmlName": "volumeId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVolumeStatus": { @@ -36856,6 +37951,9 @@ "smithy.api#xmlName": "volumeStatusSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVolumes": { @@ -37033,6 +38131,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVolumesRequest": { @@ -37104,6 +38205,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcAttribute": { @@ -37187,6 +38291,9 @@ "smithy.api#xmlName": "enableNetworkAddressUsageMetrics" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcClassicLink": { @@ -37290,6 +38397,9 @@ "smithy.api#xmlName": "vpcs" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcClassicLinkRequest": { @@ -37335,6 +38445,9 @@ "smithy.api#xmlName": "vpcSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpointConnectionNotifications": { @@ -37417,6 +38530,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpointConnections": { @@ -37493,6 +38609,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpointServiceConfigurations": { @@ -37576,6 +38695,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpointServicePermissions": { @@ -37660,6 +38782,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpointServices": { @@ -37745,6 +38870,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcEndpoints": { @@ -37828,6 +38956,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcPeeringConnections": { @@ -37966,6 +39097,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpcs": { @@ -38098,6 +39232,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpnConnections": { @@ -38220,7 +39357,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeVpnConnections.

" + "smithy.api#documentation": "

Contains the output of DescribeVpnConnections.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DescribeVpnGateways": { @@ -38281,7 +39419,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of DescribeVpnGateways.

" + "smithy.api#documentation": "

Contains the output of DescribeVpnGateways.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#DestinationFileFormat": { @@ -38430,6 +39569,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DetachInternetGateway": { @@ -38603,6 +39745,9 @@ "smithy.api#xmlName": "verifiedAccessInstance" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DetachVolume": { @@ -38940,6 +40085,9 @@ "smithy.api#xmlName": "addressTransfer" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableAwsNetworkPerformanceMetricSubscription": { @@ -39007,6 +40155,9 @@ "smithy.api#xmlName": "output" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableEbsEncryptionByDefault": { @@ -39050,6 +40201,9 @@ "smithy.api#xmlName": "ebsEncryptionByDefault" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableFastLaunch": { @@ -39173,6 +40327,9 @@ "smithy.api#xmlName": "stateTransitionTime" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableFastSnapshotRestoreErrorItem": { @@ -39435,6 +40592,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableImageDeprecation": { @@ -39486,6 +40646,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableIpamOrganizationAdminAccount": { @@ -39537,6 +40700,9 @@ "smithy.api#xmlName": "success" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableSerialConsoleAccess": { @@ -39580,6 +40746,9 @@ "smithy.api#xmlName": "serialConsoleAccessEnabled" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableTransitGatewayRouteTablePropagation": { @@ -39641,6 +40810,9 @@ "smithy.api#xmlName": "propagation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableVgwRoutePropagation": { @@ -39740,6 +40912,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisableVpcClassicLinkRequest": { @@ -39783,6 +40958,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateAddress": { @@ -39794,7 +40972,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Disassociates an Elastic IP address from the instance or network interface it's associated with.

\n

An Elastic IP address is for use in either the EC2-Classic platform or in a VPC. For more\n\t\t\tinformation, see Elastic IP\n\t\t\t\tAddresses in the Amazon Elastic Compute Cloud User Guide.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
\n

This is an idempotent operation. If you perform the operation more than once, Amazon EC2 doesn't return an error.

" + "smithy.api#documentation": "

Disassociates an Elastic IP address from the instance or network interface it's associated with.

\n

This is an idempotent operation. If you perform the operation more than once, Amazon EC2 doesn't return an error.

" } }, "com.amazonaws.ec2#DisassociateAddressRequest": { @@ -39803,13 +40981,13 @@ "AssociationId": { "target": "com.amazonaws.ec2#ElasticIpAssociationId", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The association ID. Required for EC2-VPC.

" + "smithy.api#documentation": "

The association ID. This parameter is required.

" } }, "PublicIp": { "target": "com.amazonaws.ec2#EipAllocationPublicIp", "traits": { - "smithy.api#documentation": "

[EC2-Classic] The Elastic IP address. Required for EC2-Classic.

" + "smithy.api#documentation": "

Deprecated.

" } }, "DryRun": { @@ -39890,6 +41068,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateEnclaveCertificateIamRole": { @@ -39949,6 +41130,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateIamInstanceProfile": { @@ -39990,6 +41174,9 @@ "smithy.api#xmlName": "iamInstanceProfileAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateInstanceEventWindow": { @@ -40047,6 +41234,9 @@ "smithy.api#xmlName": "instanceEventWindow" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateIpamResourceDiscovery": { @@ -40096,6 +41286,9 @@ "smithy.api#xmlName": "ipamResourceDiscoveryAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateNatGatewayAddress": { @@ -40170,6 +41363,9 @@ "smithy.api#xmlName": "natGatewayAddressSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateRouteTable": { @@ -40261,6 +41457,9 @@ "smithy.api#xmlName": "subnetId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateTransitGatewayMulticastDomain": { @@ -40326,6 +41525,9 @@ "smithy.api#xmlName": "associations" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateTransitGatewayPolicyTable": { @@ -40383,6 +41585,9 @@ "smithy.api#xmlName": "association" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateTransitGatewayRouteTable": { @@ -40440,6 +41645,9 @@ "smithy.api#xmlName": "association" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateTrunkInterface": { @@ -40506,6 +41714,9 @@ "smithy.api#xmlName": "clientToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DisassociateVpcCidrBlock": { @@ -40565,6 +41776,9 @@ "smithy.api#xmlName": "vpcId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#DiskCount": { @@ -41341,6 +42555,177 @@ } } }, + "com.amazonaws.ec2#Ec2InstanceConnectEndpoint": { + "type": "structure", + "members": { + "OwnerId": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "OwnerId", + "smithy.api#documentation": "

The ID of the Amazon Web Services account that created the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "ownerId" + } + }, + "InstanceConnectEndpointId": { + "target": "com.amazonaws.ec2#InstanceConnectEndpointId", + "traits": { + "aws.protocols#ec2QueryName": "InstanceConnectEndpointId", + "smithy.api#documentation": "

The ID of the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "instanceConnectEndpointId" + } + }, + "InstanceConnectEndpointArn": { + "target": "com.amazonaws.ec2#ResourceArn", + "traits": { + "aws.protocols#ec2QueryName": "InstanceConnectEndpointArn", + "smithy.api#documentation": "

The Amazon Resource Name (ARN) of the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "instanceConnectEndpointArn" + } + }, + "State": { + "target": "com.amazonaws.ec2#Ec2InstanceConnectEndpointState", + "traits": { + "aws.protocols#ec2QueryName": "State", + "smithy.api#documentation": "

The current state of the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "state" + } + }, + "StateMessage": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "StateMessage", + "smithy.api#documentation": "

The message for the current state of the EC2 Instance Connect Endpoint. \n Can include a failure message.

", + "smithy.api#xmlName": "stateMessage" + } + }, + "DnsName": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "DnsName", + "smithy.api#documentation": "

The DNS name of the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "dnsName" + } + }, + "FipsDnsName": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "FipsDnsName", + "smithy.api#documentation": "

", + "smithy.api#xmlName": "fipsDnsName" + } + }, + "NetworkInterfaceIds": { + "target": "com.amazonaws.ec2#NetworkInterfaceIdSet", + "traits": { + "aws.protocols#ec2QueryName": "NetworkInterfaceIdSet", + "smithy.api#documentation": "

The ID of the elastic network interface that Amazon EC2 automatically created when creating the EC2\n Instance Connect Endpoint.

", + "smithy.api#xmlName": "networkInterfaceIdSet" + } + }, + "VpcId": { + "target": "com.amazonaws.ec2#VpcId", + "traits": { + "aws.protocols#ec2QueryName": "VpcId", + "smithy.api#documentation": "

The ID of the VPC in which the EC2 Instance Connect Endpoint was created.

", + "smithy.api#xmlName": "vpcId" + } + }, + "AvailabilityZone": { + "target": "com.amazonaws.ec2#String", + "traits": { + "aws.protocols#ec2QueryName": "AvailabilityZone", + "smithy.api#documentation": "

The Availability Zone of the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "availabilityZone" + } + }, + "CreatedAt": { + "target": "com.amazonaws.ec2#MillisecondDateTime", + "traits": { + "aws.protocols#ec2QueryName": "CreatedAt", + "smithy.api#documentation": "

The date and time that the EC2 Instance Connect Endpoint was created.

", + "smithy.api#xmlName": "createdAt" + } + }, + "SubnetId": { + "target": "com.amazonaws.ec2#SubnetId", + "traits": { + "aws.protocols#ec2QueryName": "SubnetId", + "smithy.api#documentation": "

The ID of the subnet in which the EC2 Instance Connect Endpoint was created.

", + "smithy.api#xmlName": "subnetId" + } + }, + "PreserveClientIp": { + "target": "com.amazonaws.ec2#Boolean", + "traits": { + "aws.protocols#ec2QueryName": "PreserveClientIp", + "smithy.api#clientOptional": {}, + "smithy.api#default": false, + "smithy.api#documentation": "

Indicates whether your client's IP address is preserved as the source. The value is true or false.

\n
    \n
  • \n

    If true, your client's IP address is used when you connect to a resource.

    \n
  • \n
  • \n

    If false, the elastic network interface IP address is used when you connect to a resource.

    \n
  • \n
\n

Default: true\n

", + "smithy.api#xmlName": "preserveClientIp" + } + }, + "SecurityGroupIds": { + "target": "com.amazonaws.ec2#SecurityGroupIdSet", + "traits": { + "aws.protocols#ec2QueryName": "SecurityGroupIdSet", + "smithy.api#documentation": "

The security groups associated with the endpoint. If you didn't specify a security group, \n the default security group for your VPC is associated with the endpoint.

", + "smithy.api#xmlName": "securityGroupIdSet" + } + }, + "Tags": { + "target": "com.amazonaws.ec2#TagList", + "traits": { + "aws.protocols#ec2QueryName": "TagSet", + "smithy.api#documentation": "

The tags assigned to the EC2 Instance Connect Endpoint.

", + "smithy.api#xmlName": "tagSet" + } + } + }, + "traits": { + "smithy.api#documentation": "

The EC2 Instance Connect Endpoint.

" + } + }, + "com.amazonaws.ec2#Ec2InstanceConnectEndpointState": { + "type": "enum", + "members": { + "create_in_progress": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "create-in-progress" + } + }, + "create_complete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "create-complete" + } + }, + "create_failed": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "create-failed" + } + }, + "delete_in_progress": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "delete-in-progress" + } + }, + "delete_complete": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "delete-complete" + } + }, + "delete_failed": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "delete-failed" + } + } + } + }, "com.amazonaws.ec2#EfaInfo": { "type": "structure", "members": { @@ -41875,6 +43260,9 @@ "smithy.api#xmlName": "addressTransfer" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableAwsNetworkPerformanceMetricSubscription": { @@ -41942,6 +43330,9 @@ "smithy.api#xmlName": "output" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableEbsEncryptionByDefault": { @@ -41985,6 +43376,9 @@ "smithy.api#xmlName": "ebsEncryptionByDefault" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableFastLaunch": { @@ -42126,6 +43520,9 @@ "smithy.api#xmlName": "stateTransitionTime" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableFastSnapshotRestoreErrorItem": { @@ -42388,6 +43785,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableImageDeprecation": { @@ -42447,6 +43847,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableIpamOrganizationAdminAccount": { @@ -42498,6 +43901,9 @@ "smithy.api#xmlName": "success" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableReachabilityAnalyzerOrganizationSharing": { @@ -42541,6 +43947,9 @@ "smithy.api#xmlName": "returnValue" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableSerialConsoleAccess": { @@ -42584,6 +43993,9 @@ "smithy.api#xmlName": "serialConsoleAccessEnabled" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableTransitGatewayRouteTablePropagation": { @@ -42645,6 +44057,9 @@ "smithy.api#xmlName": "propagation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableVgwRoutePropagation": { @@ -42784,6 +44199,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnableVpcClassicLinkRequest": { @@ -42827,6 +44245,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#EnclaveOptions": { @@ -43573,6 +44994,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ExportClientVpnClientConfiguration": { @@ -43622,6 +45046,9 @@ "smithy.api#xmlName": "clientConfiguration" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ExportEnvironment": { @@ -43808,6 +45235,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ExportImageTask": { @@ -44205,6 +45635,9 @@ "smithy.api#xmlName": "s3Location" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ExportVmTaskId": { @@ -46328,6 +47761,9 @@ "smithy.api#xmlName": "associatedRoleSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetAssociatedIpv6PoolCidrs": { @@ -46405,6 +47841,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetAwsNetworkPerformanceData": { @@ -46493,6 +47932,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetCapacityReservationUsage": { @@ -46618,6 +48060,9 @@ "smithy.api#xmlName": "instanceUsageSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetCoipPoolUsage": { @@ -46704,6 +48149,9 @@ "smithy.api#xmlName": "localGatewayRouteTableId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetConsoleOutput": { @@ -46779,6 +48227,9 @@ "smithy.api#xmlName": "timestamp" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetConsoleScreenshot": { @@ -46844,6 +48295,9 @@ "smithy.api#xmlName": "instanceId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetDefaultCreditSpecification": { @@ -46893,6 +48347,9 @@ "smithy.api#xmlName": "instanceFamilyCreditSpecification" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetEbsDefaultKmsKeyId": { @@ -46934,6 +48391,9 @@ "smithy.api#xmlName": "kmsKeyId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetEbsEncryptionByDefault": { @@ -46977,6 +48437,9 @@ "smithy.api#xmlName": "ebsEncryptionByDefault" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetFlowLogsIntegrationTemplate": { @@ -47043,6 +48506,9 @@ "smithy.api#xmlName": "result" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetGroupsForCapacityReservation": { @@ -47130,6 +48596,9 @@ "smithy.api#xmlName": "capacityReservationGroupSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetHostReservationPurchasePreview": { @@ -47203,6 +48672,9 @@ "smithy.api#xmlName": "totalUpfrontPrice" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetInstanceTypesFromInstanceRequirements": { @@ -47298,6 +48770,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetInstanceUefiData": { @@ -47356,6 +48831,9 @@ "smithy.api#xmlName": "uefiData" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamAddressHistory": { @@ -47459,6 +48937,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamDiscoveredAccounts": { @@ -47551,6 +49032,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamDiscoveredResourceCidrs": { @@ -47643,6 +49127,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamPoolAllocations": { @@ -47654,7 +49141,7 @@ "target": "com.amazonaws.ec2#GetIpamPoolAllocationsResult" }, "traits": { - "smithy.api#documentation": "

Get a list of all the CIDR allocations in an IPAM pool.

\n \n

If you use this action after AllocateIpamPoolCidr or ReleaseIpamPoolAllocation, note that all EC2 API actions follow an eventual consistency model.

\n
", + "smithy.api#documentation": "

Get a list of all the CIDR allocations in an IPAM pool. The Region you use should be the IPAM pool locale. The locale is the Amazon Web Services Region where this IPAM pool is available for allocations.

\n \n

If you use this action after AllocateIpamPoolCidr or ReleaseIpamPoolAllocation, note that all EC2 API actions follow an eventual consistency model.

\n
", "smithy.api#paginated": { "inputToken": "NextToken", "outputToken": "NextToken", @@ -47743,6 +49230,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamPoolCidrs": { @@ -47827,6 +49317,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetIpamResourceCidrs": { @@ -47941,6 +49434,9 @@ "smithy.api#xmlName": "ipamResourceCidrSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetLaunchTemplateData": { @@ -47990,6 +49486,9 @@ "smithy.api#xmlName": "launchTemplateData" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetManagedPrefixListAssociations": { @@ -48077,6 +49576,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetManagedPrefixListEntries": { @@ -48162,6 +49664,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetNetworkInsightsAccessScopeAnalysisFindings": { @@ -48255,6 +49760,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetNetworkInsightsAccessScopeContent": { @@ -48304,6 +49812,9 @@ "smithy.api#xmlName": "networkInsightsAccessScopeContent" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetPasswordData": { @@ -48388,6 +49899,9 @@ "smithy.api#xmlName": "timestamp" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetReservedInstancesExchangeQuote": { @@ -48514,7 +50028,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of GetReservedInstancesExchangeQuote.

" + "smithy.api#documentation": "

Contains the output of GetReservedInstancesExchangeQuote.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetSerialConsoleAccessStatus": { @@ -48558,6 +50073,9 @@ "smithy.api#xmlName": "serialConsoleAccessEnabled" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetSpotPlacementScores": { @@ -48670,6 +50188,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetSubnetCidrReservations": { @@ -48766,6 +50287,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayAttachmentPropagations": { @@ -48850,6 +50374,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayMulticastDomainAssociations": { @@ -48934,6 +50461,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayPolicyTableAssociations": { @@ -49018,6 +50548,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayPolicyTableEntries": { @@ -49088,6 +50621,9 @@ "smithy.api#xmlName": "transitGatewayPolicyTableEntries" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayPrefixListReferences": { @@ -49172,6 +50708,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayRouteTableAssociations": { @@ -49256,6 +50795,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetTransitGatewayRouteTablePropagations": { @@ -49340,6 +50882,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetVerifiedAccessEndpointPolicy": { @@ -49399,6 +50944,9 @@ "smithy.api#xmlName": "policyDocument" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetVerifiedAccessGroupPolicy": { @@ -49458,6 +51006,9 @@ "smithy.api#xmlName": "policyDocument" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetVpnConnectionDeviceSampleConfiguration": { @@ -49521,6 +51072,9 @@ "smithy.api#xmlName": "vpnConnectionDeviceSampleConfiguration" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetVpnConnectionDeviceTypes": { @@ -49588,6 +51142,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GetVpnTunnelReplacementStatus": { @@ -49685,6 +51242,9 @@ "smithy.api#xmlName": "maintenanceDetails" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#GpuDeviceCount": { @@ -51566,6 +53126,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ImportImage": { @@ -51871,6 +53434,9 @@ "smithy.api#xmlName": "usageOperation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ImportImageTask": { @@ -52202,6 +53768,9 @@ "smithy.api#xmlName": "conversionTask" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ImportInstanceTaskDetails": { @@ -52411,6 +53980,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ImportManifestUrl": { @@ -52533,6 +54105,9 @@ "smithy.api#xmlName": "tagSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ImportSnapshotTask": { @@ -52687,6 +54262,9 @@ "smithy.api#xmlName": "conversionTask" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ImportVolumeTaskDetails": { @@ -53680,6 +55258,28 @@ "smithy.api#documentation": "

Information about the number of instances that can be launched onto the Dedicated\n Host.

" } }, + "com.amazonaws.ec2#InstanceConnectEndpointId": { + "type": "string" + }, + "com.amazonaws.ec2#InstanceConnectEndpointMaxResults": { + "type": "integer", + "traits": { + "smithy.api#default": 0, + "smithy.api#range": { + "min": 1, + "max": 50 + } + } + }, + "com.amazonaws.ec2#InstanceConnectEndpointSet": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#Ec2InstanceConnectEndpoint", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#InstanceCount": { "type": "structure", "members": { @@ -65563,6 +67163,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ListSnapshotsInRecycleBin": { @@ -65646,6 +67249,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ListingState": { @@ -67074,6 +68680,9 @@ "smithy.api#xmlName": "address" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyAvailabilityZoneGroup": { @@ -67133,6 +68742,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyAvailabilityZoneOptInStatus": { @@ -67235,6 +68847,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyCapacityReservationRequest": { @@ -67308,6 +68923,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyClientVpnEndpoint": { @@ -67438,6 +69056,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyDefaultCreditSpecification": { @@ -67495,6 +69116,9 @@ "smithy.api#xmlName": "instanceFamilyCreditSpecification" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyEbsDefaultKmsKeyId": { @@ -67544,6 +69168,9 @@ "smithy.api#xmlName": "kmsKeyId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyFleet": { @@ -67620,6 +69247,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyFpgaImageAttribute": { @@ -67720,6 +69350,9 @@ "smithy.api#xmlName": "fpgaImageAttribute" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyHosts": { @@ -67803,6 +69436,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIdFormat": { @@ -68215,6 +69851,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstanceCreditSpecification": { @@ -68279,6 +69918,9 @@ "smithy.api#xmlName": "unsuccessfulInstanceCreditSpecificationSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstanceEventStartTime": { @@ -68344,6 +69986,9 @@ "smithy.api#xmlName": "event" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstanceEventWindow": { @@ -68412,6 +70057,9 @@ "smithy.api#xmlName": "instanceEventWindow" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstanceMaintenanceOptions": { @@ -68475,6 +70123,9 @@ "smithy.api#xmlName": "autoRecovery" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstanceMetadataOptions": { @@ -68564,6 +70215,9 @@ "smithy.api#xmlName": "instanceMetadataOptions" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyInstancePlacement": { @@ -68659,6 +70313,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIpam": { @@ -68780,6 +70437,9 @@ "smithy.api#xmlName": "ipamPool" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIpamRequest": { @@ -68912,6 +70572,9 @@ "smithy.api#xmlName": "ipamResourceCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIpamResourceDiscovery": { @@ -68981,6 +70644,9 @@ "smithy.api#xmlName": "ipamResourceDiscovery" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIpamResult": { @@ -68994,6 +70660,9 @@ "smithy.api#xmlName": "ipam" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyIpamScope": { @@ -69049,6 +70718,9 @@ "smithy.api#xmlName": "ipamScope" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyLaunchTemplate": { @@ -69115,6 +70787,9 @@ "smithy.api#xmlName": "launchTemplate" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyLocalGatewayRoute": { @@ -69188,6 +70863,9 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyManagedPrefixList": { @@ -69273,6 +70951,9 @@ "smithy.api#xmlName": "prefixList" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyNetworkInterfaceAttribute": { @@ -69424,6 +71105,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyReservedInstances": { @@ -69486,7 +71170,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of ModifyReservedInstances.

" + "smithy.api#documentation": "

Contains the output of ModifyReservedInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifySecurityGroupRules": { @@ -69547,6 +71232,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifySnapshotAttribute": { @@ -69680,6 +71368,9 @@ "smithy.api#xmlName": "tieringStartTime" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifySpotFleetRequest": { @@ -69924,6 +71615,9 @@ "smithy.api#xmlName": "trafficMirrorFilter" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyTrafficMirrorFilterRule": { @@ -70038,6 +71732,9 @@ "smithy.api#xmlName": "trafficMirrorFilterRule" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyTrafficMirrorSession": { @@ -70136,6 +71833,9 @@ "smithy.api#xmlName": "trafficMirrorSession" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyTransitGateway": { @@ -70289,6 +71989,9 @@ "smithy.api#xmlName": "transitGatewayPrefixListReference" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyTransitGatewayRequest": { @@ -70338,6 +72041,9 @@ "smithy.api#xmlName": "transitGateway" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyTransitGatewayVpcAttachment": { @@ -70431,6 +72137,9 @@ "smithy.api#xmlName": "transitGatewayVpcAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpoint": { @@ -70575,6 +72284,9 @@ "smithy.api#xmlName": "policyDocument" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpointRequest": { @@ -70643,6 +72355,9 @@ "smithy.api#xmlName": "verifiedAccessEndpoint" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessEndpointSubnetIdList": { @@ -70745,6 +72460,9 @@ "smithy.api#xmlName": "policyDocument" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessGroupRequest": { @@ -70801,6 +72519,9 @@ "smithy.api#xmlName": "verifiedAccessGroup" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessInstance": { @@ -70877,6 +72598,9 @@ "smithy.api#xmlName": "loggingConfiguration" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessInstanceRequest": { @@ -70927,6 +72651,9 @@ "smithy.api#xmlName": "verifiedAccessInstance" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVerifiedAccessTrustProvider": { @@ -71045,6 +72772,9 @@ "smithy.api#xmlName": "verifiedAccessTrustProvider" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVolume": { @@ -71176,6 +72906,9 @@ "smithy.api#xmlName": "volumeModification" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcAttribute": { @@ -71299,6 +73032,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcEndpointRequest": { @@ -71414,6 +73150,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcEndpointServiceConfiguration": { @@ -71529,6 +73268,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcEndpointServicePayerResponsibility": { @@ -71588,6 +73330,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcEndpointServicePermissions": { @@ -71659,6 +73404,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcPeeringConnectionOptions": { @@ -71728,6 +73476,9 @@ "smithy.api#xmlName": "requesterPeeringConnectionOptions" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpcTenancy": { @@ -71787,6 +73538,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpnConnection": { @@ -71872,6 +73626,9 @@ "smithy.api#xmlName": "vpnConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpnConnectionRequest": { @@ -71927,6 +73684,9 @@ "smithy.api#xmlName": "vpnConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpnTunnelCertificate": { @@ -71984,6 +73744,9 @@ "smithy.api#xmlName": "vpnConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpnTunnelOptions": { @@ -72057,6 +73820,9 @@ "smithy.api#xmlName": "vpnConnection" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ModifyVpnTunnelOptionsSpecification": { @@ -72258,6 +74024,9 @@ "smithy.api#xmlName": "instancesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#Monitoring": { @@ -72314,7 +74083,7 @@ "target": "com.amazonaws.ec2#MoveAddressToVpcResult" }, "traits": { - "smithy.api#documentation": "

Moves an Elastic IP address from the EC2-Classic platform to the EC2-VPC platform. The\n Elastic IP address must be allocated to your account for more than 24 hours, and it must not\n be associated with an instance. After the Elastic IP address is moved, it is no longer\n available for use in the EC2-Classic platform, unless you move it back using the\n RestoreAddressToClassic request. You cannot move an Elastic IP address that was\n originally allocated for use in the EC2-VPC platform to the EC2-Classic platform.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "\n

This action is deprecated.

\n
\n

Moves an Elastic IP address from the EC2-Classic platform to the EC2-VPC platform. The\n Elastic IP address must be allocated to your account for more than 24 hours, and it must not\n be associated with an instance. After the Elastic IP address is moved, it is no longer\n available for use in the EC2-Classic platform, unless you move it back using the\n RestoreAddressToClassic request. You cannot move an Elastic IP address that was\n originally allocated for use in the EC2-VPC platform to the EC2-Classic platform.

" } }, "com.amazonaws.ec2#MoveAddressToVpcRequest": { @@ -72364,6 +74133,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#MoveByoipCidrToIpam": { @@ -72429,6 +74201,9 @@ "smithy.api#xmlName": "byoipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#MoveStatus": { @@ -72455,7 +74230,7 @@ "target": "com.amazonaws.ec2#MoveStatus", "traits": { "aws.protocols#ec2QueryName": "MoveStatus", - "smithy.api#documentation": "

The status of the Elastic IP address that's being moved to the EC2-VPC platform, or restored to the EC2-Classic platform.

", + "smithy.api#documentation": "

The status of the Elastic IP address that's being moved or restored.

", "smithy.api#xmlName": "moveStatus" } }, @@ -72469,7 +74244,7 @@ } }, "traits": { - "smithy.api#documentation": "

Describes the status of a moving Elastic IP address.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "\n

This action is deprecated.

\n
\n

Describes the status of a moving Elastic IP address.

" } }, "com.amazonaws.ec2#MovingAddressStatusSet": { @@ -74263,6 +76038,15 @@ } } }, + "com.amazonaws.ec2#NetworkInterfaceIdSet": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#String", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#NetworkInterfaceIpv6Address": { "type": "structure", "members": { @@ -77333,6 +79117,9 @@ "smithy.api#xmlName": "byoipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ProvisionIpamPoolCidr": { @@ -77409,6 +79196,9 @@ "smithy.api#xmlName": "ipamPoolCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ProvisionPublicIpv4PoolCidr": { @@ -77483,6 +79273,9 @@ "smithy.api#xmlName": "poolAddressRange" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ProvisionedBandwidth": { @@ -77894,6 +79687,9 @@ "smithy.api#xmlName": "totalUpfrontPrice" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#PurchaseRequest": { @@ -78010,7 +79806,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of PurchaseReservedInstancesOffering.

" + "smithy.api#documentation": "

Contains the output of PurchaseReservedInstancesOffering.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#PurchaseScheduledInstances": { @@ -78071,7 +79868,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of PurchaseScheduledInstances.

" + "smithy.api#documentation": "

Contains the output of PurchaseScheduledInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#PurchaseSet": { @@ -78492,7 +80290,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of RegisterImage.

" + "smithy.api#documentation": "

Contains the output of RegisterImage.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#RegisterInstanceEventNotificationAttributes": { @@ -78521,7 +80320,9 @@ "InstanceTagAttribute": { "target": "com.amazonaws.ec2#RegisterInstanceTagAttributeRequest", "traits": { - "smithy.api#documentation": "

Information about the tag keys to register.

" + "smithy.api#clientOptional": {}, + "smithy.api#documentation": "

Information about the tag keys to register.

", + "smithy.api#required": {} } } }, @@ -78540,6 +80341,9 @@ "smithy.api#xmlName": "instanceTagAttribute" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RegisterInstanceTagAttributeRequest": { @@ -78626,6 +80430,9 @@ "smithy.api#xmlName": "registeredMulticastGroupMembers" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RegisterTransitGatewayMulticastGroupSources": { @@ -78689,6 +80496,9 @@ "smithy.api#xmlName": "registeredMulticastGroupSources" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RejectTransitGatewayMulticastDomainAssociations": { @@ -78748,6 +80558,9 @@ "smithy.api#xmlName": "associations" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RejectTransitGatewayPeeringAttachment": { @@ -78797,6 +80610,9 @@ "smithy.api#xmlName": "transitGatewayPeeringAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RejectTransitGatewayVpcAttachment": { @@ -78846,6 +80662,9 @@ "smithy.api#xmlName": "transitGatewayVpcAttachment" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RejectVpcEndpointConnections": { @@ -78904,6 +80723,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RejectVpcPeeringConnection": { @@ -78959,6 +80781,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReleaseAddress": { @@ -78970,7 +80795,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Releases the specified Elastic IP address.

\n

[EC2-Classic, default VPC] Releasing an Elastic IP address automatically disassociates it\n\t\t\t\tfrom any instance that it's associated with. To disassociate an Elastic IP address without\n\t\t\t\treleasing it, use DisassociateAddress.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
\n

[Nondefault VPC] You must use DisassociateAddress to disassociate the Elastic IP address\n\t\t\t before you can release it. Otherwise, Amazon EC2 returns an error (InvalidIPAddress.InUse).

\n

After releasing an Elastic IP address, it is released to the IP address pool. \n Be sure to update your DNS records and any servers or devices that communicate with the address. \n If you attempt to release an Elastic IP address that you already released, you'll get an\n AuthFailure error if the address is already allocated to another Amazon Web Services account.

\n

[EC2-VPC] After you release an Elastic IP address for use in a VPC, you might be able to recover it.\n For more information, see AllocateAddress.

\n

For more\n information, see Elastic IP\n Addresses in the Amazon Elastic Compute Cloud User Guide.

" + "smithy.api#documentation": "

Releases the specified Elastic IP address.

\n

[Default VPC] Releasing an Elastic IP address automatically disassociates it\n\t\t\t\tfrom any instance that it's associated with. To disassociate an Elastic IP address without\n\t\t\t\treleasing it, use DisassociateAddress.

\n

[Nondefault VPC] You must use DisassociateAddress to disassociate the Elastic IP address\n\t\t\t before you can release it. Otherwise, Amazon EC2 returns an error (InvalidIPAddress.InUse).

\n

After releasing an Elastic IP address, it is released to the IP address pool. \n Be sure to update your DNS records and any servers or devices that communicate with the address. \n If you attempt to release an Elastic IP address that you already released, you'll get an\n AuthFailure error if the address is already allocated to another Amazon Web Services account.

\n

After you release an Elastic IP address, you might be able to recover it.\n For more information, see AllocateAddress.

" } }, "com.amazonaws.ec2#ReleaseAddressRequest": { @@ -78979,13 +80804,13 @@ "AllocationId": { "target": "com.amazonaws.ec2#AllocationId", "traits": { - "smithy.api#documentation": "

[EC2-VPC] The allocation ID. Required for EC2-VPC.

" + "smithy.api#documentation": "

The allocation ID. This parameter is required.

" } }, "PublicIp": { "target": "com.amazonaws.ec2#String", "traits": { - "smithy.api#documentation": "

[EC2-Classic] The Elastic IP address. Required for EC2-Classic.

" + "smithy.api#documentation": "

Deprecated.

" } }, "NetworkBorderGroup": { @@ -79058,6 +80883,9 @@ "smithy.api#xmlName": "unsuccessful" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReleaseIpamPoolAllocation": { @@ -79069,7 +80897,7 @@ "target": "com.amazonaws.ec2#ReleaseIpamPoolAllocationResult" }, "traits": { - "smithy.api#documentation": "

Release an allocation within an IPAM pool. You can only use this action to release manual allocations. To remove an allocation for a resource without deleting the resource, set its monitored state to false using ModifyIpamResourceCidr. For more information, see Release an allocation in the Amazon VPC IPAM User Guide.\n

\n \n

All EC2 API actions follow an eventual consistency model.

\n
" + "smithy.api#documentation": "

Release an allocation within an IPAM pool. The Region you use should be the IPAM pool locale. The locale is the Amazon Web Services Region where this IPAM pool is available for allocations. You can only use this action to release manual allocations. To remove an allocation for a resource without deleting the resource, set its monitored state to false using ModifyIpamResourceCidr. For more information, see Release an allocation in the Amazon VPC IPAM User Guide.\n

\n \n

All EC2 API actions follow an eventual consistency model.

\n
" } }, "com.amazonaws.ec2#ReleaseIpamPoolAllocationRequest": { @@ -79125,6 +80953,9 @@ "smithy.api#xmlName": "success" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RemoveIpamOperatingRegion": { @@ -79228,6 +81059,9 @@ "smithy.api#xmlName": "iamInstanceProfileAssociation" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReplaceNetworkAclAssociation": { @@ -79291,6 +81125,9 @@ "smithy.api#xmlName": "newAssociationId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReplaceNetworkAclEntry": { @@ -79767,6 +81604,9 @@ "smithy.api#xmlName": "associationState" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReplaceTransitGatewayRoute": { @@ -79838,6 +81678,9 @@ "smithy.api#xmlName": "route" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReplaceVpnTunnel": { @@ -79905,6 +81748,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ReplacementStrategy": { @@ -80572,7 +82418,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of RequestSpotInstances.

" + "smithy.api#documentation": "

Contains the output of RequestSpotInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#RequestSpotLaunchSpecification": { @@ -81721,6 +83568,9 @@ "smithy.api#xmlName": "address" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ResetEbsDefaultKmsKeyId": { @@ -81762,6 +83612,9 @@ "smithy.api#xmlName": "kmsKeyId" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ResetFpgaImageAttribute": { @@ -81830,6 +83683,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ResetImageAttribute": { @@ -82617,6 +84473,12 @@ "traits": { "smithy.api#enumValue": "ipam-resource-discovery-association" } + }, + "instance_connect_endpoint": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "instance-connect-endpoint" + } } } }, @@ -82939,7 +84801,7 @@ "target": "com.amazonaws.ec2#RestoreAddressToClassicResult" }, "traits": { - "smithy.api#documentation": "

Restores an Elastic IP address that was previously moved to the EC2-VPC platform back to the EC2-Classic platform. You cannot move an Elastic IP address that was originally allocated for use in EC2-VPC. The Elastic IP address must not be associated with an instance or network interface.

\n \n

We are retiring EC2-Classic. We recommend that you migrate from EC2-Classic to a VPC. For more information, see Migrate from EC2-Classic to a VPC in the Amazon Elastic Compute Cloud User Guide.

\n
" + "smithy.api#documentation": "\n

This action is deprecated.

\n
\n

Restores an Elastic IP address that was previously moved to the EC2-VPC platform back to the EC2-Classic platform. You cannot move an Elastic IP address that was originally allocated for use in EC2-VPC. The Elastic IP address must not be associated with an instance or network interface.

" } }, "com.amazonaws.ec2#RestoreAddressToClassicRequest": { @@ -82989,6 +84851,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RestoreImageFromRecycleBin": { @@ -83040,6 +84905,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RestoreManagedPrefixListVersion": { @@ -83107,6 +84975,9 @@ "smithy.api#xmlName": "prefixList" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RestoreSnapshotFromRecycleBin": { @@ -83232,6 +85103,9 @@ "smithy.api#xmlName": "volumeSize" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RestoreSnapshotTier": { @@ -83326,6 +85200,9 @@ "smithy.api#xmlName": "isPermanentRestore" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ResultRange": { @@ -83407,6 +85284,9 @@ "smithy.api#xmlName": "status" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RevokeSecurityGroupEgress": { @@ -83537,6 +85417,9 @@ "smithy.api#xmlName": "unknownIpPermissionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RevokeSecurityGroupIngress": { @@ -83655,6 +85538,9 @@ "smithy.api#xmlName": "unknownIpPermissionSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#RoleId": { @@ -84604,7 +86490,8 @@ } }, "traits": { - "smithy.api#documentation": "

Contains the output of RunScheduledInstances.

" + "smithy.api#documentation": "

Contains the output of RunScheduledInstances.

", + "smithy.api#output": {} } }, "com.amazonaws.ec2#S3ObjectTag": { @@ -85548,6 +87435,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#SearchTransitGatewayMulticastGroups": { @@ -85632,6 +87522,9 @@ "smithy.api#xmlName": "nextToken" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#SearchTransitGatewayRoutes": { @@ -85708,6 +87601,9 @@ "smithy.api#xmlName": "additionalRoutesAvailable" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#SecurityGroup": { @@ -85794,6 +87690,15 @@ } } }, + "com.amazonaws.ec2#SecurityGroupIdSet": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#SecurityGroupId", + "traits": { + "smithy.api#xmlName": "item" + } + } + }, "com.amazonaws.ec2#SecurityGroupIdStringList": { "type": "list", "member": { @@ -85803,6 +87708,21 @@ } } }, + "com.amazonaws.ec2#SecurityGroupIdStringListRequest": { + "type": "list", + "member": { + "target": "com.amazonaws.ec2#SecurityGroupId", + "traits": { + "smithy.api#xmlName": "SecurityGroupId" + } + }, + "traits": { + "smithy.api#length": { + "min": 0, + "max": 16 + } + } + }, "com.amazonaws.ec2#SecurityGroupIdentifier": { "type": "structure", "members": { @@ -88890,6 +90810,9 @@ "smithy.api#xmlName": "instancesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#StartNetworkInsightsAccessScopeAnalysis": { @@ -88955,6 +90878,9 @@ "smithy.api#xmlName": "networkInsightsAccessScopeAnalysis" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#StartNetworkInsightsAnalysis": { @@ -89034,6 +90960,9 @@ "smithy.api#xmlName": "networkInsightsAnalysis" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#StartVpcEndpointServicePrivateDnsVerification": { @@ -89085,6 +91014,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#State": { @@ -89323,6 +91255,9 @@ "smithy.api#xmlName": "instancesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#Storage": { @@ -90684,6 +92619,9 @@ "smithy.api#xmlName": "connectionStatuses" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#TerminateConnectionStatus": { @@ -90777,6 +92715,9 @@ "smithy.api#xmlName": "instancesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ThreadsPerCore": { @@ -94882,6 +96823,9 @@ "smithy.api#xmlName": "unassignedIpv6PrefixSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#UnassignPrivateIpAddresses": { @@ -95002,6 +96946,9 @@ "smithy.api#xmlName": "natGatewayAddressSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#UnlimitedSupportedInstanceFamily": { @@ -95083,6 +97030,9 @@ "smithy.api#xmlName": "instancesSet" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#UnsuccessfulInstanceCreditSpecificationErrorCode": { @@ -95303,6 +97253,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#UpdateSecurityGroupRuleDescriptionsIngress": { @@ -95371,6 +97324,9 @@ "smithy.api#xmlName": "return" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#UsageClassType": { @@ -99699,6 +101655,9 @@ "smithy.api#xmlName": "byoipCidr" } } + }, + "traits": { + "smithy.api#output": {} } }, "com.amazonaws.ec2#ZoneIdStringList": { diff --git a/aws/sdk/aws-models/iam.json b/aws/sdk/aws-models/iam.json index 8453b370fc..3f222206c6 100644 --- a/aws/sdk/aws-models/iam.json +++ b/aws/sdk/aws-models/iam.json @@ -2576,6 +2576,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#EntityAlreadyExistsException" }, @@ -2587,7 +2590,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates an alias for your Amazon Web Services account. For information about using an Amazon Web Services account\n alias, see Using an\n alias for your Amazon Web Services account ID in the\n IAM User Guide.

" + "smithy.api#documentation": "

Creates an alias for your Amazon Web Services account. For information about using an Amazon Web Services account\n alias, see Creating, deleting, and\n listing an Amazon Web Services account alias in the Amazon Web Services Sign-In User\n Guide.

" } }, "com.amazonaws.iam#CreateAccountAliasRequest": { @@ -3574,6 +3577,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#EntityTemporarilyUnmodifiableException" }, @@ -3666,6 +3672,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#LimitExceededException" }, @@ -3677,7 +3686,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes the specified Amazon Web Services account alias. For information about using an Amazon Web Services\n account alias, see Using an alias for your Amazon Web Services account ID in the\n IAM User Guide.

" + "smithy.api#documentation": "

Deletes the specified Amazon Web Services account alias. For information about using an Amazon Web Services\n account alias, see Creating, deleting, and\n listing an Amazon Web Services account alias in the Amazon Web Services Sign-In User\n Guide.

" } }, "com.amazonaws.iam#DeleteAccountAliasRequest": { @@ -4390,6 +4399,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#LimitExceededException" }, @@ -4558,6 +4570,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#DeleteConflictException" }, @@ -4838,6 +4853,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#EntityAlreadyExistsException" }, @@ -7364,7 +7382,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the account alias associated with the Amazon Web Services account (Note: you can have only\n one). For information about using an Amazon Web Services account alias, see Using an alias for your\n Amazon Web Services account ID in the IAM User Guide.

", + "smithy.api#documentation": "

Lists the account alias associated with the Amazon Web Services account (Note: you can have only\n one). For information about using an Amazon Web Services account alias, see Creating,\n deleting, and listing an Amazon Web Services account alias in the Amazon Web Services Sign-In\n User Guide.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -9083,7 +9101,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the IAM roles that have the specified path prefix. If there are none, the\n operation returns an empty list. For more information about roles, see Working with\n roles.

\n \n

IAM resource-listing operations return a subset of the available \n attributes for the resource. For example, this operation does not return tags, even though they are an attribute of the returned object. To view all of the information for a role, see GetRole.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

", + "smithy.api#documentation": "

Lists the IAM roles that have the specified path prefix. If there are none, the\n operation returns an empty list. For more information about roles, see Working with\n roles.

\n \n

IAM resource-listing operations return a subset of the available \n attributes for the resource. This operation does not return the following attributes, even though they are an attribute of the returned object:

\n
    \n
  • \n

    PermissionsBoundary

    \n
  • \n
  • \n

    RoleLastUsed

    \n
  • \n
  • \n

    Tags

    \n
  • \n
\n

To view all of the information for a role, see GetRole.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -9806,7 +9824,7 @@ } ], "traits": { - "smithy.api#documentation": "

Lists the IAM users that have the specified path prefix. If no path prefix is\n specified, the operation returns all users in the Amazon Web Services account. If there are none, the\n operation returns an empty list.

\n \n

IAM resource-listing operations return a subset of the available \n attributes for the resource. For example, this operation does not return tags, even though they are an attribute of the returned object. To view all of the information for a user, see GetUser.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

", + "smithy.api#documentation": "

Lists the IAM users that have the specified path prefix. If no path prefix is\n specified, the operation returns all users in the Amazon Web Services account. If there are none, the\n operation returns an empty list.

\n \n

IAM resource-listing operations return a subset of the available \n attributes for the resource. This operation does not return the following attributes, even though they are an attribute of the returned object:

\n
    \n
  • \n

    PermissionsBoundary

    \n
  • \n
  • \n

    Tags

    \n
  • \n
\n

To view all of the information for a user, see GetUser.

\n
\n

You can paginate the results using the MaxItems and Marker\n parameters.

", "smithy.api#paginated": { "inputToken": "Marker", "outputToken": "Marker", @@ -11310,6 +11328,9 @@ "target": "smithy.api#Unit" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#InvalidAuthenticationCodeException" }, @@ -14237,6 +14258,9 @@ "target": "com.amazonaws.iam#UploadSigningCertificateResponse" }, "errors": [ + { + "target": "com.amazonaws.iam#ConcurrentModificationException" + }, { "target": "com.amazonaws.iam#DuplicateCertificateException" }, @@ -14439,7 +14463,7 @@ "Base32StringSeed": { "target": "com.amazonaws.iam#BootstrapDatum", "traits": { - "smithy.api#documentation": "

The base32 seed defined as specified in RFC3548. The Base32StringSeed is base64-encoded.

" + "smithy.api#documentation": "

The base32 seed defined as specified in RFC3548. The Base32StringSeed is base32-encoded.

" } }, "QRCodePNG": { @@ -14509,7 +14533,7 @@ "min": 3, "max": 63 }, - "smithy.api#pattern": "^[a-z0-9](([a-z0-9]|-(?!-))*[a-z0-9])?$" + "smithy.api#pattern": "^[a-z0-9]([a-z0-9]|-(?!-)){1,61}[a-z0-9]$" } }, "com.amazonaws.iam#arnType": { @@ -14963,7 +14987,7 @@ "min": 1, "max": 512 }, - "smithy.api#pattern": "^(\\u002F)|(\\u002F[\\u0021-\\u007F]+\\u002F)$" + "smithy.api#pattern": "^(\\u002F)|(\\u002F[\\u0021-\\u007E]+\\u002F)$" } }, "com.amazonaws.iam#policyDescriptionType": { @@ -15167,7 +15191,7 @@ "min": 0, "max": 1000 }, - "smithy.api#pattern": "^[\\p{L}\\p{M}\\p{Z}\\p{S}\\p{N}\\p{P}]*$" + "smithy.api#pattern": "^[\\u0009\\u000A\\u000D\\u0020-\\u007E\\u00A1-\\u00FF]*$" } }, "com.amazonaws.iam#roleDetailListType": { diff --git a/aws/sdk/aws-models/kms.json b/aws/sdk/aws-models/kms.json index c380116092..e161d67853 100644 --- a/aws/sdk/aws-models/kms.json +++ b/aws/sdk/aws-models/kms.json @@ -52,6 +52,18 @@ "traits": { "smithy.api#enumValue": "RSAES_OAEP_SHA_256" } + }, + "RSA_AES_KEY_WRAP_SHA_1": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RSA_AES_KEY_WRAP_SHA_1" + } + }, + "RSA_AES_KEY_WRAP_SHA_256": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RSA_AES_KEY_WRAP_SHA_256" + } } } }, @@ -877,7 +889,7 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a unique customer managed KMS key in your Amazon Web Services account and Region.\n You can use a KMS key in cryptographic operations, such as encryption and signing. Some Amazon Web Services\n services let you use KMS keys that you create and manage to protect your service\n resources.

\n

A KMS key is a logical representation of a cryptographic key. In addition to the key\n material used in cryptographic operations, a KMS key includes metadata, such as the key ID,\n key policy, creation date, description, and key state. For details, see Managing keys in the\n Key Management Service Developer Guide\n

\n

Use the parameters of CreateKey to specify the type of KMS key, the source of\n its key material, its key policy, description, tags, and other properties.

\n \n

KMS has replaced the term customer master key (CMK) with KMS key and KMS key. The concept has not changed. To prevent breaking changes, KMS is keeping some variations of this term.

\n
\n

To create different types of KMS keys, use the following guidance:

\n
\n
Symmetric encryption KMS key
\n
\n

By default, CreateKey creates a symmetric encryption KMS key with key\n material that KMS generates. This is the basic and most widely used type of KMS key, and\n provides the best performance.

\n

To create a symmetric encryption KMS key, you don't need to specify any parameters.\n The default value for KeySpec, SYMMETRIC_DEFAULT, the default\n value for KeyUsage, ENCRYPT_DECRYPT, and the default value for\n Origin, AWS_KMS, create a symmetric encryption KMS key with\n KMS key material.

\n

If you need a key for basic encryption and decryption or you are creating a KMS key\n to protect your resources in an Amazon Web Services service, create a symmetric encryption KMS key.\n The key material in a symmetric encryption key never leaves KMS unencrypted. You can\n use a symmetric encryption KMS key to encrypt and decrypt data up to 4,096 bytes, but\n they are typically used to generate data keys and data keys pairs. For details, see\n GenerateDataKey and GenerateDataKeyPair.

\n

\n
\n
Asymmetric KMS keys
\n
\n

To create an asymmetric KMS key, use the KeySpec parameter to specify\n the type of key material in the KMS key. Then, use the KeyUsage parameter\n to determine whether the KMS key will be used to encrypt and decrypt or sign and verify.\n You can't change these properties after the KMS key is created.

\n

Asymmetric KMS keys contain an RSA key pair, Elliptic Curve (ECC) key pair, or an SM2 key pair (China Regions only). The private key in an asymmetric \n KMS key never leaves KMS unencrypted. However, you can use the GetPublicKey operation to download the public key\n so it can be used outside of KMS. KMS keys with RSA or SM2 key pairs can be used to encrypt or decrypt data or sign and verify messages (but not both). \n KMS keys with ECC key pairs can be used only to sign and verify messages. \n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

\n
\n
HMAC KMS key
\n
\n

To create an HMAC KMS key, set the KeySpec parameter to a key spec\n value for HMAC KMS keys. Then set the KeyUsage parameter to\n GENERATE_VERIFY_MAC. You must set the key usage even though\n GENERATE_VERIFY_MAC is the only valid key usage value for HMAC KMS keys.\n You can't change these properties after the KMS key is created.

\n

HMAC KMS keys are symmetric keys that never leave KMS unencrypted. You can use\n HMAC keys to generate (GenerateMac) and verify (VerifyMac) HMAC codes for messages up to 4096 bytes.

\n

HMAC KMS keys are not supported in all Amazon Web Services Regions. If you try to create an HMAC\n KMS key in an Amazon Web Services Region in which HMAC keys are not supported, the\n CreateKey operation returns an\n UnsupportedOperationException. For a list of Regions in which HMAC KMS keys\n are supported, see HMAC keys in\n KMS in the Key Management Service Developer Guide.

\n

\n
\n
Multi-Region primary keys
\n
Imported key material
\n
\n

To create a multi-Region primary key in the local Amazon Web Services Region,\n use the MultiRegion parameter with a value of True. To create\n a multi-Region replica key, that is, a KMS key with the same key ID\n and key material as a primary key, but in a different Amazon Web Services Region, use the ReplicateKey operation. To change a replica key to a primary key, and its\n primary key to a replica key, use the UpdatePrimaryRegion\n operation.

\n

You can create multi-Region KMS keys for all supported KMS key types: symmetric\n encryption KMS keys, HMAC KMS keys, asymmetric encryption KMS keys, and asymmetric\n signing KMS keys. You can also create multi-Region keys with imported key material.\n However, you can't create multi-Region keys in a custom key store.

\n

This operation supports multi-Region keys, an KMS feature that lets you create multiple\n interoperable KMS keys in different Amazon Web Services Regions. Because these KMS keys have the same key ID, key\n material, and other metadata, you can use them interchangeably to encrypt data in one Amazon Web Services Region and decrypt\n it in a different Amazon Web Services Region without re-encrypting the data or making a cross-Region call. For more information about multi-Region keys, see Multi-Region keys in KMS in the Key Management Service Developer Guide.

\n

\n
\n
\n

To import your own key material into a KMS key, begin by creating a symmetric\n encryption KMS key with no key material. To do this, use the Origin\n parameter of CreateKey with a value of EXTERNAL. Next, use\n GetParametersForImport operation to get a public key and import\n token, and use the public key to encrypt your key material. Then, use ImportKeyMaterial with your import token to import the key material. For\n step-by-step instructions, see Importing Key Material in the \n Key Management Service Developer Guide\n .

\n

This feature supports only symmetric encryption KMS keys, including multi-Region\n symmetric encryption KMS keys. You cannot import key material into any other type of KMS\n key.

\n

To create a multi-Region primary key with imported key material, use the\n Origin parameter of CreateKey with a value of\n EXTERNAL and the MultiRegion parameter with a value of\n True. To create replicas of the multi-Region primary key, use the ReplicateKey operation. For instructions, see Importing key material into\n multi-Region keys. For more information about multi-Region keys, see Multi-Region keys in KMS in the Key Management Service Developer Guide.

\n

\n
\n
Custom key store
\n
\n

A custom key store lets you protect your Amazon Web Services resources using keys in a backing key\n store that you own and manage. When you request a cryptographic operation with a KMS key\n in a custom key store, the operation is performed in the backing key store using its\n cryptographic keys.

\n

KMS supports CloudHSM key stores backed by an CloudHSM cluster and external key stores backed by an\n external key manager outside of Amazon Web Services. When you create a KMS key in an CloudHSM key store,\n KMS generates an encryption key in the CloudHSM cluster and associates it with the KMS\n key. When you create a KMS key in an external key store, you specify an existing\n encryption key in the external key manager.

\n \n

Some external key managers provide a simpler method for creating a KMS key in an\n external key store. For details, see your external key manager documentation.

\n
\n

Before you create a KMS key in a custom key store, the ConnectionState\n of the key store must be CONNECTED. To connect the custom key store, use\n the ConnectCustomKeyStore operation. To find the\n ConnectionState, use the DescribeCustomKeyStores\n operation.

\n

To create a KMS key in a custom key store, use the CustomKeyStoreId.\n Use the default KeySpec value, SYMMETRIC_DEFAULT, and the\n default KeyUsage value, ENCRYPT_DECRYPT to create a symmetric\n encryption key. No other key type is supported in a custom key store.

\n

To create a KMS key in an CloudHSM key store, use the\n Origin parameter with a value of AWS_CLOUDHSM. The CloudHSM\n cluster that is associated with the custom key store must have at least two active HSMs\n in different Availability Zones in the Amazon Web Services Region.

\n

To create a KMS key in an external key store, use the Origin parameter\n with a value of EXTERNAL_KEY_STORE and an XksKeyId parameter\n that identifies an existing external key.

\n \n

Some external key managers provide a simpler method for creating a KMS key in an\n external key store. For details, see your external key manager documentation.

\n
\n
\n
\n

\n Cross-account use: No. You cannot use this operation to\n create a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:CreateKey (IAM policy). To use the\n Tags parameter, kms:TagResource (IAM policy). For examples and information about related\n permissions, see Allow a user to create\n KMS keys in the Key Management Service Developer Guide.

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Creates a unique customer managed KMS key in your Amazon Web Services account and Region.\n You can use a KMS key in cryptographic operations, such as encryption and signing. Some Amazon Web Services\n services let you use KMS keys that you create and manage to protect your service\n resources.

\n

A KMS key is a logical representation of a cryptographic key. In addition to the key\n material used in cryptographic operations, a KMS key includes metadata, such as the key ID,\n key policy, creation date, description, and key state. For details, see Managing keys in the\n Key Management Service Developer Guide\n

\n

Use the parameters of CreateKey to specify the type of KMS key, the source of\n its key material, its key policy, description, tags, and other properties.

\n \n

KMS has replaced the term customer master key (CMK) with KMS key and KMS key. The concept has not changed. To prevent breaking changes, KMS is keeping some variations of this term.

\n
\n

To create different types of KMS keys, use the following guidance:

\n
\n
Symmetric encryption KMS key
\n
\n

By default, CreateKey creates a symmetric encryption KMS key with key\n material that KMS generates. This is the basic and most widely used type of KMS key, and\n provides the best performance.

\n

To create a symmetric encryption KMS key, you don't need to specify any parameters.\n The default value for KeySpec, SYMMETRIC_DEFAULT, the default\n value for KeyUsage, ENCRYPT_DECRYPT, and the default value for\n Origin, AWS_KMS, create a symmetric encryption KMS key with\n KMS key material.

\n

If you need a key for basic encryption and decryption or you are creating a KMS key\n to protect your resources in an Amazon Web Services service, create a symmetric encryption KMS key.\n The key material in a symmetric encryption key never leaves KMS unencrypted. You can\n use a symmetric encryption KMS key to encrypt and decrypt data up to 4,096 bytes, but\n they are typically used to generate data keys and data keys pairs. For details, see\n GenerateDataKey and GenerateDataKeyPair.

\n

\n
\n
Asymmetric KMS keys
\n
\n

To create an asymmetric KMS key, use the KeySpec parameter to specify\n the type of key material in the KMS key. Then, use the KeyUsage parameter\n to determine whether the KMS key will be used to encrypt and decrypt or sign and verify.\n You can't change these properties after the KMS key is created.

\n

Asymmetric KMS keys contain an RSA key pair, Elliptic Curve (ECC) key pair, or an SM2 key pair (China Regions only). The private key in an asymmetric \n KMS key never leaves KMS unencrypted. However, you can use the GetPublicKey operation to download the public key\n so it can be used outside of KMS. KMS keys with RSA or SM2 key pairs can be used to encrypt or decrypt data or sign and verify messages (but not both). \n KMS keys with ECC key pairs can be used only to sign and verify messages. \n For information about asymmetric KMS keys, see Asymmetric KMS keys in the Key Management Service Developer Guide.

\n

\n
\n
HMAC KMS key
\n
\n

To create an HMAC KMS key, set the KeySpec parameter to a key spec\n value for HMAC KMS keys. Then set the KeyUsage parameter to\n GENERATE_VERIFY_MAC. You must set the key usage even though\n GENERATE_VERIFY_MAC is the only valid key usage value for HMAC KMS keys.\n You can't change these properties after the KMS key is created.

\n

HMAC KMS keys are symmetric keys that never leave KMS unencrypted. You can use\n HMAC keys to generate (GenerateMac) and verify (VerifyMac) HMAC codes for messages up to 4096 bytes.

\n

\n
\n
Multi-Region primary keys
\n
Imported key material
\n
\n

To create a multi-Region primary key in the local Amazon Web Services Region,\n use the MultiRegion parameter with a value of True. To create\n a multi-Region replica key, that is, a KMS key with the same key ID\n and key material as a primary key, but in a different Amazon Web Services Region, use the ReplicateKey operation. To change a replica key to a primary key, and its\n primary key to a replica key, use the UpdatePrimaryRegion\n operation.

\n

You can create multi-Region KMS keys for all supported KMS key types: symmetric\n encryption KMS keys, HMAC KMS keys, asymmetric encryption KMS keys, and asymmetric\n signing KMS keys. You can also create multi-Region keys with imported key material.\n However, you can't create multi-Region keys in a custom key store.

\n

This operation supports multi-Region keys, an KMS feature that lets you create multiple\n interoperable KMS keys in different Amazon Web Services Regions. Because these KMS keys have the same key ID, key\n material, and other metadata, you can use them interchangeably to encrypt data in one Amazon Web Services Region and decrypt\n it in a different Amazon Web Services Region without re-encrypting the data or making a cross-Region call. For more information about multi-Region keys, see Multi-Region keys in KMS in the Key Management Service Developer Guide.

\n

\n
\n
\n

To import your own key material into a KMS key, begin by creating a KMS key with no\n key material. To do this, use the Origin parameter of\n CreateKey with a value of EXTERNAL. Next, use GetParametersForImport operation to get a public key and import token. Use\n the wrapping public key to encrypt your key material. Then, use ImportKeyMaterial with your import token to import the key material. For step-by-step instructions, see\n Importing Key Material in the \n Key Management Service Developer Guide\n .

\n

You can import key material into KMS keys of all supported KMS key types: symmetric\n encryption KMS keys, HMAC KMS keys, asymmetric encryption KMS keys, and asymmetric\n signing KMS keys. You can also create multi-Region keys with imported key material.\n However, you can't import key material into a KMS key in a custom key store.

\n

To create a multi-Region primary key with imported key material, use the\n Origin parameter of CreateKey with a value of\n EXTERNAL and the MultiRegion parameter with a value of\n True. To create replicas of the multi-Region primary key, use the ReplicateKey operation. For instructions, see Importing key material into\n multi-Region keys. For more information about multi-Region keys, see Multi-Region keys in KMS in the Key Management Service Developer Guide.

\n

\n
\n
Custom key store
\n
\n

A custom key store lets you protect your Amazon Web Services resources using keys in a backing key\n store that you own and manage. When you request a cryptographic operation with a KMS key\n in a custom key store, the operation is performed in the backing key store using its\n cryptographic keys.

\n

KMS supports CloudHSM key stores backed by an CloudHSM cluster and external key stores backed by an\n external key manager outside of Amazon Web Services. When you create a KMS key in an CloudHSM key store,\n KMS generates an encryption key in the CloudHSM cluster and associates it with the KMS\n key. When you create a KMS key in an external key store, you specify an existing\n encryption key in the external key manager.

\n \n

Some external key managers provide a simpler method for creating a KMS key in an\n external key store. For details, see your external key manager documentation.

\n
\n

Before you create a KMS key in a custom key store, the ConnectionState\n of the key store must be CONNECTED. To connect the custom key store, use\n the ConnectCustomKeyStore operation. To find the\n ConnectionState, use the DescribeCustomKeyStores\n operation.

\n

To create a KMS key in a custom key store, use the CustomKeyStoreId.\n Use the default KeySpec value, SYMMETRIC_DEFAULT, and the\n default KeyUsage value, ENCRYPT_DECRYPT to create a symmetric\n encryption key. No other key type is supported in a custom key store.

\n

To create a KMS key in an CloudHSM key store, use the\n Origin parameter with a value of AWS_CLOUDHSM. The CloudHSM\n cluster that is associated with the custom key store must have at least two active HSMs\n in different Availability Zones in the Amazon Web Services Region.

\n

To create a KMS key in an external key store, use the Origin parameter\n with a value of EXTERNAL_KEY_STORE and an XksKeyId parameter\n that identifies an existing external key.

\n \n

Some external key managers provide a simpler method for creating a KMS key in an\n external key store. For details, see your external key manager documentation.

\n
\n
\n
\n

\n Cross-account use: No. You cannot use this operation to\n create a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:CreateKey (IAM policy). To use the\n Tags parameter, kms:TagResource (IAM policy). For examples and information about related\n permissions, see Allow a user to create\n KMS keys in the Key Management Service Developer Guide.

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#CreateKeyRequest": { @@ -1543,7 +1555,7 @@ } ], "traits": { - "smithy.api#documentation": "

Deletes key material that you previously imported. This operation makes the specified KMS\n key unusable. For more information about importing key material into KMS, see Importing Key Material\n in the Key Management Service Developer Guide.

\n

When the specified KMS key is in the PendingDeletion state, this operation\n does not change the KMS key's state. Otherwise, it changes the KMS key's state to\n PendingImport.

\n

After you delete key material, you can use ImportKeyMaterial to reimport\n the same key material into the KMS key.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:DeleteImportedKeyMaterial (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Deletes key material that was previously imported. This operation makes the specified KMS\n key temporarily unusable. To restore the usability of the KMS key, reimport the same key\n material. For more information about importing key material into KMS, see Importing Key Material\n in the Key Management Service Developer Guide.

\n

When the specified KMS key is in the PendingDeletion state, this operation\n does not change the KMS key's state. Otherwise, it changes the KMS key's state to\n PendingImport.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:DeleteImportedKeyMaterial (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#DeleteImportedKeyMaterialRequest": { @@ -2951,7 +2963,7 @@ } ], "traits": { - "smithy.api#documentation": "

Returns the items you need to import key material into a symmetric encryption KMS key. For\n more information about importing key material into KMS, see Importing key material in the\n Key Management Service Developer Guide.

\n

This operation returns a public key and an import token. Use the public key to encrypt the\n symmetric key material. Store the import token to send with a subsequent ImportKeyMaterial request.

\n

You must specify the key ID of the symmetric encryption KMS key into which you will import\n key material. The KMS key Origin must be EXTERNAL. You must also\n specify the wrapping algorithm and type of wrapping key (public key) that you will use to\n encrypt the key material. You cannot perform this operation on an asymmetric KMS key, an HMAC KMS key, or on any KMS key in a different Amazon Web Services account.

\n

To import key material, you must use the public key and import token from the same\n response. These items are valid for 24 hours. The expiration date and time appear in the\n GetParametersForImport response. You cannot use an expired token in an ImportKeyMaterial request. If your key and token expire, send another\n GetParametersForImport request.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:GetParametersForImport (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Returns the public key and an import token you need to import or reimport key material for\n a KMS key.

\n

By default, KMS keys are created with key material that KMS generates. This operation\n supports Importing key\n material, an advanced feature that lets you generate and import the cryptographic\n key material for a KMS key. For more information about importing key material into KMS, see\n Importing key\n material in the Key Management Service Developer Guide.

\n

Before calling GetParametersForImport, use the CreateKey\n operation with an Origin value of EXTERNAL to create a KMS key with\n no key material. You can import key material for a symmetric encryption KMS key, HMAC KMS key,\n asymmetric encryption KMS key, or asymmetric signing KMS key. You can also import key material\n into a multi-Region key of\n any supported type. However, you can't import key material into a KMS key in a custom key store. You can also use\n GetParametersForImport to get a public key and import token to reimport the original key material into a KMS key whose key material expired or was\n deleted.

\n

\n GetParametersForImport returns the items that you need to import your key\n material.

\n
    \n
  • \n

    The public key (or \"wrapping key\") of an RSA key pair that KMS generates.

    \n

    You will use this public key to encrypt (\"wrap\") your key material while it's in\n transit to KMS.

    \n
  • \n
  • \n

    A import token that ensures that KMS can decrypt your key material and associate it with the correct KMS key.

    \n
  • \n
\n

The public key and its import token are permanently linked and must be used together. Each\n public key and import token set is valid for 24 hours. The expiration date and time appear in\n the ParametersValidTo field in the GetParametersForImport response.\n You cannot use an expired public key or import token in an ImportKeyMaterial\n request. If your key and token expire, send another GetParametersForImport\n request.

\n

\n GetParametersForImport requires the following information:

\n
    \n
  • \n

    The key ID of the KMS key for which you are importing the key material.

    \n
  • \n
  • \n

    The key spec of the public key (\"wrapping key\") that you will use to encrypt your key\n material during import.

    \n
  • \n
  • \n

    The wrapping algorithm that you will use with the public key to encrypt your key\n material.

    \n
  • \n
\n

You can use the same or a different public key spec and wrapping algorithm each time you\n import or reimport the same key material.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:GetParametersForImport (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#GetParametersForImportRequest": { @@ -2960,21 +2972,21 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

The identifier of the symmetric encryption KMS key into which you will import key\n material. The Origin of the KMS key must be EXTERNAL.

\n

Specify the key ID or key ARN of the KMS key.

\n

For example:

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

", + "smithy.api#documentation": "

The identifier of the KMS key that will be associated with the imported key material. The\n Origin of the KMS key must be EXTERNAL.

\n

All KMS key types are supported, including multi-Region keys. However, you cannot import\n key material into a KMS key in a custom key store.

\n

Specify the key ID or key ARN of the KMS key.

\n

For example:

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

", "smithy.api#required": {} } }, "WrappingAlgorithm": { "target": "com.amazonaws.kms#AlgorithmSpec", "traits": { - "smithy.api#documentation": "

The algorithm you will use to encrypt the key material before using the ImportKeyMaterial operation to import it. For more information, see Encrypt the\n key material in the Key Management Service Developer Guide.

\n \n

The RSAES_PKCS1_V1_5 wrapping algorithm is deprecated. We recommend that\n you begin using a different wrapping algorithm immediately. KMS will end support for\n RSAES_PKCS1_V1_5 by October 1, 2023 pursuant to cryptographic key management guidance from the National Institute of Standards\n and Technology (NIST).

\n
", + "smithy.api#documentation": "

The algorithm you will use with the RSA public key (PublicKey) in the\n response to protect your key material during import. For more information, see Select a wrapping algorithm in the Key Management Service Developer Guide.

\n

For RSA_AES wrapping algorithms, you encrypt your key material with an AES key that you\n generate, then encrypt your AES key with the RSA public key from KMS. For RSAES wrapping\n algorithms, you encrypt your key material directly with the RSA public key from KMS.

\n

The wrapping algorithms that you can use depend on the type of key material that you are\n importing. To import an RSA private key, you must use an RSA_AES wrapping algorithm.

\n
    \n
  • \n

    \n RSA_AES_KEY_WRAP_SHA_256 — Supported for wrapping RSA and ECC key\n material.

    \n
  • \n
  • \n

    \n RSA_AES_KEY_WRAP_SHA_1 — Supported for wrapping RSA and ECC key material.

    \n
  • \n
  • \n

    \n RSAES_OAEP_SHA_256 — Supported for all types of key material, except RSA key material (private key).

    \n

    You cannot use the RSAES_OAEP_SHA_256 wrapping algorithm with the RSA_2048 wrapping key spec to wrap \n ECC_NIST_P521 key material.

    \n
  • \n
  • \n

    \n RSAES_OAEP_SHA_1 — Supported for all types of key material, except RSA key material (private\n key).

    \n

    You cannot use the RSAES_OAEP_SHA_1 wrapping algorithm with the RSA_2048 wrapping key spec to wrap \n ECC_NIST_P521 key material.

    \n
  • \n
  • \n

    \n RSAES_PKCS1_V1_5 (Deprecated) — Supported only for symmetric encryption key\n material (and only in legacy mode).

    \n
  • \n
", "smithy.api#required": {} } }, "WrappingKeySpec": { "target": "com.amazonaws.kms#WrappingKeySpec", "traits": { - "smithy.api#documentation": "

The type of wrapping key (public key) to return in the response. Only 2048-bit RSA public\n keys are supported.

", + "smithy.api#documentation": "

The type of RSA public key to return in the response. You will use this wrapping key with\n the specified wrapping algorithm to protect your key material during import.

\n

Use the longest RSA wrapping key that is practical.

\n

You cannot use an RSA_2048 public key to directly wrap an ECC_NIST_P521 private key.\n Instead, use an RSA_AES wrapping algorithm or choose a longer RSA public key.

", "smithy.api#required": {} } } @@ -3409,7 +3421,7 @@ } ], "traits": { - "smithy.api#documentation": "

Imports key material into an existing symmetric encryption KMS key that was created\n without key material. After you successfully import key material into a KMS key, you can\n reimport the same key material into that KMS key, but you cannot import different\n key material.

\n

You cannot perform this operation on an asymmetric KMS key, an HMAC KMS key, or on any KMS key in a different Amazon Web Services account. For more information about creating KMS keys with no key material\n and then importing key material, see Importing Key Material in the\n Key Management Service Developer Guide.

\n

Before using this operation, call GetParametersForImport. Its response\n includes a public key and an import token. Use the public key to encrypt the key material.\n Then, submit the import token from the same GetParametersForImport\n response.

\n

When calling this operation, you must specify the following values:

\n
    \n
  • \n

    The key ID or key ARN of a KMS key with no key material. Its Origin must\n be EXTERNAL.

    \n

    To create a KMS key with no key material, call CreateKey and set the\n value of its Origin parameter to EXTERNAL. To get the\n Origin of a KMS key, call DescribeKey.)

    \n
  • \n
  • \n

    The encrypted key material. To get the public key to encrypt the key material, call\n GetParametersForImport.

    \n
  • \n
  • \n

    The import token that GetParametersForImport returned. You must use\n a public key and token from the same GetParametersForImport response.

    \n
  • \n
  • \n

    Whether the key material expires (ExpirationModel) and, if so, when\n (ValidTo). If you set an expiration date, on the specified date, KMS\n deletes the key material from the KMS key, making the KMS key unusable. To use the KMS key\n in cryptographic operations again, you must reimport the same key material. The only way\n to change the expiration model or expiration date is by reimporting the same key material\n and specifying a new expiration date.

    \n
  • \n
\n

When this operation is successful, the key state of the KMS key changes from\n PendingImport to Enabled, and you can use the KMS key.

\n

If this operation fails, use the exception to help determine the problem. If the error is\n related to the key material, the import token, or wrapping key, use GetParametersForImport to get a new public key and import token for the KMS key\n and repeat the import procedure. For help, see How To Import Key\n Material in the Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:ImportKeyMaterial (key policy)

\n

\n Related operations:\n

\n " + "smithy.api#documentation": "

Imports or reimports key material into an existing KMS key that was created without key\n material. ImportKeyMaterial also sets the expiration model and expiration date of\n the imported key material.

\n

By default, KMS keys are created with key material that KMS generates. This operation\n supports Importing key\n material, an advanced feature that lets you generate and import the cryptographic\n key material for a KMS key. For more information about importing key material into KMS, see\n Importing key\n material in the Key Management Service Developer Guide.

\n

After you successfully import key material into a KMS key, you can reimport\n the same key material into that KMS key, but you cannot import different key\n material. You might reimport key material to replace key material that expired or key material\n that you deleted. You might also reimport key material to change the expiration model or\n expiration date of the key material. Before reimporting key material, if necessary, call DeleteImportedKeyMaterial to delete the current imported key material.

\n

Each time you import key material into KMS, you can determine whether\n (ExpirationModel) and when (ValidTo) the key material expires. To\n change the expiration of your key material, you must import it again, either by calling\n ImportKeyMaterial or using the import features of the\n KMS console.

\n

Before calling ImportKeyMaterial:

\n
    \n
  • \n

    Create or identify a KMS key with no key material. The KMS key must have an\n Origin value of EXTERNAL, which indicates that the KMS key is\n designed for imported key material.

    \n

    To create an new KMS key for imported key material, call the CreateKey operation with an Origin value of EXTERNAL. You can create a\n symmetric encryption KMS key, HMAC KMS key, asymmetric encryption KMS key, or asymmetric\n signing KMS key. You can also import key material into a multi-Region key of any\n supported type. However, you can't import key material into a KMS key in a custom key store.

    \n
  • \n
  • \n

    Use the DescribeKey operation to verify that the\n KeyState of the KMS key is PendingImport, which indicates that\n the KMS key has no key material.

    \n

    If you are reimporting the same key material into an existing KMS key, you might need\n to call the DeleteImportedKeyMaterial to delete its existing key\n material.

    \n
  • \n
  • \n

    Call the GetParametersForImport operation to get a public key and\n import token set for importing key material.

    \n
  • \n
  • \n

    Use the public key in the GetParametersForImport response to encrypt\n your key material.

    \n
  • \n
\n

Then, in an ImportKeyMaterial request, you submit your encrypted key\n material and import token. When calling this operation, you must specify the following\n values:

\n
    \n
  • \n

    The key ID or key ARN of the KMS key to associate with the imported key material. Its\n Origin must be EXTERNAL and its KeyState must be\n PendingImport. You cannot perform this operation on a KMS key in a custom key store, or on a KMS\n key in a different Amazon Web Services account. To get the Origin and KeyState\n of a KMS key, call DescribeKey.

    \n
  • \n
  • \n

    The encrypted key material.

    \n
  • \n
  • \n

    The import token that GetParametersForImport returned. You must use\n a public key and token from the same GetParametersForImport response.

    \n
  • \n
  • \n

    Whether the key material expires (ExpirationModel) and, if so, when\n (ValidTo). For help with this choice, see Setting an expiration time in the Key Management Service Developer Guide.

    \n

    If you set an expiration date, KMS deletes the key material from the KMS key on the\n specified date, making the KMS key unusable. To use the KMS key in cryptographic\n operations again, you must reimport the same key material. However, you can delete and\n reimport the key material at any time, including before the key material expires. Each\n time you reimport, you can eliminate or reset the expiration time.

    \n
  • \n
\n

When this operation is successful, the key state of the KMS key changes from\n PendingImport to Enabled, and you can use the KMS key in\n cryptographic operations.

\n

If this operation fails, use the exception to help determine the problem. If the error is\n related to the key material, the import token, or wrapping key, use GetParametersForImport to get a new public key and import token for the KMS key\n and repeat the import procedure. For help, see How To Import Key\n Material in the Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:ImportKeyMaterial (key policy)

\n

\n Related operations:\n

\n " } }, "com.amazonaws.kms#ImportKeyMaterialRequest": { @@ -3418,7 +3430,7 @@ "KeyId": { "target": "com.amazonaws.kms#KeyIdType", "traits": { - "smithy.api#documentation": "

The identifier of the symmetric encryption KMS key that receives the imported key\n material. This must be the same KMS key specified in the KeyID parameter of the\n corresponding GetParametersForImport request. The Origin of the\n KMS key must be EXTERNAL. You cannot perform this operation on an asymmetric KMS\n key, an HMAC KMS key, a KMS key in a custom key store, or on a KMS key in a different\n Amazon Web Services account

\n

Specify the key ID or key ARN of the KMS key.

\n

For example:

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

", + "smithy.api#documentation": "

The identifier of the KMS key that will be associated with the imported key material. This\n must be the same KMS key specified in the KeyID parameter of the corresponding\n GetParametersForImport request. The Origin of the KMS key\n must be EXTERNAL and its KeyState must be\n PendingImport.

\n

The KMS key can be a symmetric encryption KMS key, HMAC KMS key, asymmetric encryption KMS\n key, or asymmetric signing KMS key, including a multi-Region key of any supported\n type. You cannot perform this operation on a KMS key in a custom key store, or on a KMS key in\n a different Amazon Web Services account.

\n

Specify the key ID or key ARN of the KMS key.

\n

For example:

\n
    \n
  • \n

    Key ID: 1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
  • \n

    Key ARN: arn:aws:kms:us-east-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\n

    \n
  • \n
\n

To get the key ID and key ARN for a KMS key, use ListKeys or DescribeKey.

", "smithy.api#required": {} } }, @@ -3432,7 +3444,7 @@ "EncryptedKeyMaterial": { "target": "com.amazonaws.kms#CiphertextType", "traits": { - "smithy.api#documentation": "

The encrypted key material to import. The key material must be encrypted with the public\n wrapping key that GetParametersForImport returned, using the wrapping\n algorithm that you specified in the same GetParametersForImport request.

", + "smithy.api#documentation": "

The encrypted key material to import. The key material must be encrypted under the public\n wrapping key that GetParametersForImport returned, using the wrapping\n algorithm that you specified in the same GetParametersForImport request.

", "smithy.api#required": {} } }, @@ -3445,7 +3457,7 @@ "ExpirationModel": { "target": "com.amazonaws.kms#ExpirationModelType", "traits": { - "smithy.api#documentation": "

Specifies whether the key material expires. The default is\n KEY_MATERIAL_EXPIRES.

\n

When the value of ExpirationModel is KEY_MATERIAL_EXPIRES, you\n must specify a value for the ValidTo parameter. When value is\n KEY_MATERIAL_DOES_NOT_EXPIRE, you must omit the ValidTo\n parameter.

\n

You cannot change the ExpirationModel or ValidTo values for the\n current import after the request completes. To change either value, you must delete (DeleteImportedKeyMaterial) and reimport the key material.

" + "smithy.api#documentation": "

Specifies whether the key material expires. The default is\n KEY_MATERIAL_EXPIRES. For help with this choice, see Setting an expiration time in the Key Management Service Developer Guide.

\n

When the value of ExpirationModel is KEY_MATERIAL_EXPIRES, you\n must specify a value for the ValidTo parameter. When value is\n KEY_MATERIAL_DOES_NOT_EXPIRE, you must omit the ValidTo\n parameter.

\n

You cannot change the ExpirationModel or ValidTo values for the\n current import after the request completes. To change either value, you must reimport the key\n material.

" } } }, @@ -5424,7 +5436,7 @@ } ], "traits": { - "smithy.api#documentation": "

Schedules the deletion of a KMS key. By default, KMS applies a waiting period of 30\n days, but you can specify a waiting period of 7-30 days. When this operation is successful,\n the key state of the KMS key changes to PendingDeletion and the key can't be used\n in any cryptographic operations. It remains in this state for the duration of the waiting\n period. Before the waiting period ends, you can use CancelKeyDeletion to\n cancel the deletion of the KMS key. After the waiting period ends, KMS deletes the KMS key,\n its key material, and all KMS data associated with it, including all aliases that refer to\n it.

\n \n

Deleting a KMS key is a destructive and potentially dangerous operation. When a KMS key\n is deleted, all data that was encrypted under the KMS key is unrecoverable. (The only\n exception is a multi-Region replica key.) To prevent the use of a KMS key without deleting\n it, use DisableKey.

\n
\n

You can schedule the deletion of a multi-Region primary key and its replica keys at any\n time. However, KMS will not delete a multi-Region primary key with existing replica keys. If\n you schedule the deletion of a primary key with replicas, its key state changes to\n PendingReplicaDeletion and it cannot be replicated or used in cryptographic\n operations. This status can continue indefinitely. When the last of its replicas keys is\n deleted (not just scheduled), the key state of the primary key changes to\n PendingDeletion and its waiting period (PendingWindowInDays)\n begins. For details, see Deleting multi-Region keys in the\n Key Management Service Developer Guide.

\n

When KMS deletes\n a KMS key from an CloudHSM key store, it makes a best effort to delete the associated\n key material from the associated CloudHSM cluster. However, you might need to manually delete\n the orphaned key material from the cluster and its backups. Deleting a KMS key from an\n external key store has no effect on the associated external key. However, for both\n types of custom key stores, deleting a KMS key is destructive and irreversible. You cannot\n decrypt ciphertext encrypted under the KMS key by using only its associated external key or\n CloudHSM key. Also, you cannot recreate a KMS key in an external key store by creating a new KMS\n key with the same key material.

\n

For more information about scheduling a KMS key for deletion, see Deleting KMS keys in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:ScheduleKeyDeletion (key\n policy)

\n

\n Related operations\n

\n " + "smithy.api#documentation": "

Schedules the deletion of a KMS key. By default, KMS applies a waiting period of 30\n days, but you can specify a waiting period of 7-30 days. When this operation is successful,\n the key state of the KMS key changes to PendingDeletion and the key can't be used\n in any cryptographic operations. It remains in this state for the duration of the waiting\n period. Before the waiting period ends, you can use CancelKeyDeletion to\n cancel the deletion of the KMS key. After the waiting period ends, KMS deletes the KMS key,\n its key material, and all KMS data associated with it, including all aliases that refer to\n it.

\n \n

Deleting a KMS key is a destructive and potentially dangerous operation. When a KMS key\n is deleted, all data that was encrypted under the KMS key is unrecoverable. (The only\n exception is a multi-Region replica\n key, or an asymmetric or HMAC KMS key with imported key material[BUGBUG-link to\n importing-keys-managing.html#import-delete-key.) To prevent the use of a KMS key without\n deleting it, use DisableKey.

\n
\n

You can schedule the deletion of a multi-Region primary key and its replica keys at any\n time. However, KMS will not delete a multi-Region primary key with existing replica keys. If\n you schedule the deletion of a primary key with replicas, its key state changes to\n PendingReplicaDeletion and it cannot be replicated or used in cryptographic\n operations. This status can continue indefinitely. When the last of its replicas keys is\n deleted (not just scheduled), the key state of the primary key changes to\n PendingDeletion and its waiting period (PendingWindowInDays)\n begins. For details, see Deleting multi-Region keys in the\n Key Management Service Developer Guide.

\n

When KMS deletes\n a KMS key from an CloudHSM key store, it makes a best effort to delete the associated\n key material from the associated CloudHSM cluster. However, you might need to manually delete\n the orphaned key material from the cluster and its backups. Deleting a KMS key from an\n external key store has no effect on the associated external key. However, for both\n types of custom key stores, deleting a KMS key is destructive and irreversible. You cannot\n decrypt ciphertext encrypted under the KMS key by using only its associated external key or\n CloudHSM key. Also, you cannot recreate a KMS key in an external key store by creating a new KMS\n key with the same key material.

\n

For more information about scheduling a KMS key for deletion, see Deleting KMS keys in the\n Key Management Service Developer Guide.

\n

The KMS key that you use for this operation must be in a compatible key state. For\ndetails, see Key states of KMS keys in the Key Management Service Developer Guide.

\n

\n Cross-account use: No. You cannot perform this operation on a KMS key in a different Amazon Web Services account.

\n

\n Required permissions: kms:ScheduleKeyDeletion (key\n policy)

\n

\n Related operations\n

\n " } }, "com.amazonaws.kms#ScheduleKeyDeletionRequest": { @@ -5440,7 +5452,7 @@ "PendingWindowInDays": { "target": "com.amazonaws.kms#PendingWindowInDaysType", "traits": { - "smithy.api#documentation": "

The waiting period, specified in number of days. After the waiting period ends, KMS\n deletes the KMS key.

\n

If the KMS key is a multi-Region primary key with replica keys, the waiting period begins\n when the last of its replica keys is deleted. Otherwise, the waiting period begins\n immediately.

\n

This value is optional. If you include a value, it must be between 7 and 30, inclusive. If\n you do not include a value, it defaults to 30.

" + "smithy.api#documentation": "

The waiting period, specified in number of days. After the waiting period ends, KMS\n deletes the KMS key.

\n

If the KMS key is a multi-Region primary key with replica keys, the waiting period begins\n when the last of its replica keys is deleted. Otherwise, the waiting period begins\n immediately.

\n

This value is optional. If you include a value, it must be between 7 and 30, inclusive. If\n you do not include a value, it defaults to 30. You can use the \n kms:ScheduleKeyDeletionPendingWindowInDays\n \n condition key to further constrain the values that principals can specify in the \n PendingWindowInDays parameter.

" } } }, @@ -5571,7 +5583,7 @@ "Signature": { "target": "com.amazonaws.kms#CiphertextType", "traits": { - "smithy.api#documentation": "

The cryptographic signature that was generated for the message.

\n
    \n
  • \n

    When used with the supported RSA signing algorithms, the encoding of this value is\n defined by PKCS #1 in RFC\n 8017.

    \n
  • \n
  • \n

    When used with the ECDSA_SHA_256, ECDSA_SHA_384, or\n ECDSA_SHA_512 signing algorithms, this value is a DER-encoded object as\n defined by ANS X9.62–2005 and RFC 3279 Section 2.2.3.\n This is the most commonly used signature format and is appropriate for most uses.\n

    \n
  • \n
\n

When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

" + "smithy.api#documentation": "

The cryptographic signature that was generated for the message.

\n
    \n
  • \n

    When used with the supported RSA signing algorithms, the encoding of this value is\n defined by PKCS #1 in RFC\n 8017.

    \n
  • \n
  • \n

    When used with the ECDSA_SHA_256, ECDSA_SHA_384, or\n ECDSA_SHA_512 signing algorithms, this value is a DER-encoded object as\n defined by ANSI X9.62–2005 and RFC 3279 Section 2.2.3.\n This is the most commonly used signature format and is appropriate for most uses.\n

    \n
  • \n
\n

When you use the HTTP API or the Amazon Web Services CLI, the value is Base64-encoded. Otherwise, it is not Base64-encoded.

" } }, "SigningAlgorithm": { @@ -7827,6 +7839,18 @@ "traits": { "smithy.api#enumValue": "RSA_2048" } + }, + "RSA_3072": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RSA_3072" + } + }, + "RSA_4096": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "RSA_4096" + } } } }, diff --git a/aws/sdk/aws-models/lambda.json b/aws/sdk/aws-models/lambda.json index b7c3fb34ca..df1e421639 100644 --- a/aws/sdk/aws-models/lambda.json +++ b/aws/sdk/aws-models/lambda.json @@ -2799,7 +2799,7 @@ "KMSKeyArn": { "target": "com.amazonaws.lambda#KMSKeyArn", "traits": { - "smithy.api#documentation": "

The ARN of the Key Management Service (KMS) customer managed key that's used to encrypt your function's environment variables. When Lambda SnapStart is activated, this key is also used to encrypt your function's snapshot. If you don't provide a customer managed key, Lambda uses a default service key.

" + "smithy.api#documentation": "

The ARN of the Key Management Service (KMS) customer managed key that's used to encrypt your function's \nenvironment variables. When \nLambda SnapStart is activated, Lambda also uses \nthis key is to encrypt your function's snapshot. If you deploy your function using a container image, Lambda also uses this key to \nencrypt your function when it's deployed. Note that this is not the same key that's used to protect your container image in the Amazon Elastic Container Registry (Amazon ECR).\nIf you don't provide a customer managed key, Lambda uses a default service key.

" } }, "TracingConfig": { @@ -4014,7 +4014,7 @@ "MaximumRecordAgeInSeconds": { "target": "com.amazonaws.lambda#MaximumRecordAgeInSeconds", "traits": { - "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records older than the specified age. The default value is -1,\nwhich sets the maximum age to infinite. When the value is set to infinite, Lambda never discards old records.

\n \n

The minimum value that can be set is 60 seconds.

\n
" + "smithy.api#documentation": "

(Kinesis and DynamoDB Streams only) Discard records older than the specified age. The default value is -1,\nwhich sets the maximum age to infinite. When the value is set to infinite, Lambda never discards old records.

\n \n

The minimum valid value for maximum record age is 60s. Although values less than 60 and greater than -1 fall within the parameter's absolute range, they are not allowed

\n
" } }, "BisectBatchOnFunctionError": { @@ -5762,7 +5762,7 @@ "CompatibleRuntimes": { "target": "com.amazonaws.lambda#CompatibleRuntimes", "traits": { - "smithy.api#documentation": "

The layer's compatible runtimes.

" + "smithy.api#documentation": "

The layer's compatible runtimes.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

" } }, "LicenseInfo": { @@ -7270,7 +7270,7 @@ "CompatibleRuntimes": { "target": "com.amazonaws.lambda#CompatibleRuntimes", "traits": { - "smithy.api#documentation": "

The layer's compatible runtimes.

" + "smithy.api#documentation": "

The layer's compatible runtimes.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

" } }, "LicenseInfo": { @@ -7990,7 +7990,7 @@ "CompatibleRuntime": { "target": "com.amazonaws.lambda#Runtime", "traits": { - "smithy.api#documentation": "

A runtime identifier. For example, go1.x.

", + "smithy.api#documentation": "

A runtime identifier. For example, go1.x.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

", "smithy.api#httpQuery": "CompatibleRuntime" } }, @@ -8088,7 +8088,7 @@ "CompatibleRuntime": { "target": "com.amazonaws.lambda#Runtime", "traits": { - "smithy.api#documentation": "

A runtime identifier. For example, go1.x.

", + "smithy.api#documentation": "

A runtime identifier. For example, go1.x.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

", "smithy.api#httpQuery": "CompatibleRuntime" } }, @@ -8875,7 +8875,7 @@ "CompatibleRuntimes": { "target": "com.amazonaws.lambda#CompatibleRuntimes", "traits": { - "smithy.api#documentation": "

A list of compatible function\n runtimes. Used for filtering with ListLayers and ListLayerVersions.

" + "smithy.api#documentation": "

A list of compatible function\n runtimes. Used for filtering with ListLayers and ListLayerVersions.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

" } }, "LicenseInfo": { @@ -8938,7 +8938,7 @@ "CompatibleRuntimes": { "target": "com.amazonaws.lambda#CompatibleRuntimes", "traits": { - "smithy.api#documentation": "

The layer's compatible runtimes.

" + "smithy.api#documentation": "

The layer's compatible runtimes.

\n

The following list includes deprecated runtimes. For more information, see Runtime deprecation policy.

" } }, "LicenseInfo": { @@ -9942,6 +9942,12 @@ "traits": { "smithy.api#enumValue": "java17" } + }, + "ruby32": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "ruby3.2" + } } } }, @@ -11426,7 +11432,7 @@ "KMSKeyArn": { "target": "com.amazonaws.lambda#KMSKeyArn", "traits": { - "smithy.api#documentation": "

The ARN of the Key Management Service (KMS) customer managed key that's used to encrypt your function's environment variables. When Lambda SnapStart is activated, this key is also used to encrypt your function's snapshot. If you don't provide a customer managed key, Lambda uses a default service key.

" + "smithy.api#documentation": "

The ARN of the Key Management Service (KMS) customer managed key that's used to encrypt your function's \nenvironment variables. When \nLambda SnapStart is activated, Lambda also uses \nthis key is to encrypt your function's snapshot. If you deploy your function using a container image, Lambda also uses this key to \nencrypt your function when it's deployed. Note that this is not the same key that's used to protect your container image in the Amazon Elastic Container Registry (Amazon ECR).\nIf you don't provide a customer managed key, Lambda uses a default service key.

" } }, "TracingConfig": { diff --git a/aws/sdk/aws-models/polly.json b/aws/sdk/aws-models/polly.json index 70ddbca064..219c1a0a92 100644 --- a/aws/sdk/aws-models/polly.json +++ b/aws/sdk/aws-models/polly.json @@ -677,6 +677,12 @@ "traits": { "smithy.api#enumValue": "fi-FI" } + }, + "en_IE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "en-IE" + } } } }, @@ -3371,6 +3377,18 @@ "traits": { "smithy.api#enumValue": "Tomoko" } + }, + "Niamh": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Niamh" + } + }, + "Sofie": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "Sofie" + } } } }, diff --git a/aws/sdk/aws-models/s3.json b/aws/sdk/aws-models/s3.json index a08417723f..f0227a4af8 100644 --- a/aws/sdk/aws-models/s3.json +++ b/aws/sdk/aws-models/s3.json @@ -62,6 +62,18 @@ ], "traits": { "smithy.api#documentation": "

This action aborts a multipart upload. After a multipart upload is aborted, no\n additional parts can be uploaded using that upload ID. The storage consumed by any\n previously uploaded parts will be freed. However, if any part uploads are currently in\n progress, those part uploads might or might not succeed. As a result, it might be necessary\n to abort a given multipart upload multiple times in order to completely free all storage\n consumed by all parts.

\n

To verify that all parts have been removed, so you don't get charged for the part\n storage, you should call the ListParts action and ensure that\n the parts list is empty.

\n

For information about permissions required to use the multipart upload, see Multipart Upload\n and Permissions.

\n

The following operations are related to AbortMultipartUpload:

\n ", + "smithy.api#examples": [ + { + "title": "To abort a multipart upload", + "documentation": "The following example aborts a multipart upload.", + "input": { + "Bucket": "examplebucket", + "Key": "bigobject", + "UploadId": "xadcOB_7YPBOJuoFiQ9cz4P3Pe6FIZwO4f7wN93uHsNBEw97pl5eNwzExg0LAT2dUN91cOmrEQHDsP3WA60CEg--" + }, + "output": {} + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}/{Key+}?x-id=AbortMultipartUpload", @@ -15277,7 +15289,7 @@ } }, { - "documentation": "vanilla virtual addressing@us-west-2", + "documentation": "non-bucket endpoint with FIPS: TODO(descriptive)", "expect": { "endpoint": { "properties": { @@ -15290,33 +15302,18 @@ } ] }, - "url": "https://bucket-name.s3.us-west-2.amazonaws.com" + "url": "http://beta.example.com:1234/path" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "us-west-2" - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": false, "Region": "us-west-2", - "UseDualStack": false, - "UseFIPS": false, - "___key": "key" + "Endpoint": "http://beta.example.com:1234/path", + "UseFIPS": true, + "UseDualStack": false } }, { - "documentation": "virtual addressing + dualstack@us-west-2", + "documentation": "FIPS + dualstack + custom endpoint TODO(descriptive)", "expect": { "endpoint": { "properties": { @@ -15329,34 +15326,18 @@ } ] }, - "url": "https://bucket-name.s3.dualstack.us-west-2.amazonaws.com" + "url": "http://beta.example.com:1234/path" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "us-west-2", - "AWS::UseDualStack": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": false, "Region": "us-west-2", - "UseDualStack": true, - "UseFIPS": false, - "___key": "key" + "Endpoint": "http://beta.example.com:1234/path", + "UseFIPS": true, + "UseDualStack": true } }, { - "documentation": "accelerate + dualstack@us-west-2", + "documentation": "dualstack + custom endpoint TODO(descriptive)", "expect": { "endpoint": { "properties": { @@ -15369,35 +15350,18 @@ } ] }, - "url": "https://bucket-name.s3-accelerate.dualstack.amazonaws.com" + "url": "http://beta.example.com:1234/path" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "us-west-2", - "AWS::UseDualStack": true, - "AWS::S3::Accelerate": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": true, - "Bucket": "bucket-name", - "ForcePathStyle": false, "Region": "us-west-2", - "UseDualStack": true, + "Endpoint": "http://beta.example.com:1234/path", "UseFIPS": false, - "___key": "key" + "UseDualStack": true } }, { - "documentation": "accelerate (dualstack=false)@us-west-2", + "documentation": "custom endpoint without FIPS/dualstack", "expect": { "endpoint": { "properties": { @@ -15410,34 +15374,29 @@ } ] }, - "url": "https://bucket-name.s3-accelerate.amazonaws.com" + "url": "http://beta.example.com:1234/path" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "us-west-2", - "AWS::S3::Accelerate": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": true, - "Bucket": "bucket-name", - "ForcePathStyle": false, "Region": "us-west-2", - "UseDualStack": false, + "Endpoint": "http://beta.example.com:1234/path", "UseFIPS": false, - "___key": "key" + "UseDualStack": false } }, { - "documentation": "virtual addressing + fips@us-west-2", + "documentation": "s3 object lambda with access points disabled", + "expect": { + "error": "Access points are not supported for this operation" + }, + "params": { + "Region": "us-west-2", + "Bucket": "arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint:myendpoint", + "DisableAccessPoints": true + } + }, + { + "documentation": "non bucket + FIPS", "expect": { "endpoint": { "properties": { @@ -15450,34 +15409,17 @@ } ] }, - "url": "https://bucket-name.s3-fips.us-west-2.amazonaws.com" + "url": "https://s3-fips.us-west-2.amazonaws.com" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "us-west-2", - "AWS::UseFIPS": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": false, "Region": "us-west-2", - "UseDualStack": false, "UseFIPS": true, - "___key": "key" + "UseDualStack": false } }, { - "documentation": "virtual addressing + dualstack + fips@us-west-2", + "documentation": "standard non bucket endpoint", "expect": { "endpoint": { "properties": { @@ -15490,64 +15432,40 @@ } ] }, - "url": "https://bucket-name.s3-fips.dualstack.us-west-2.amazonaws.com" + "url": "https://s3.us-west-2.amazonaws.com" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "us-west-2", - "AWS::UseFIPS": true, - "AWS::UseDualStack": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": false, "Region": "us-west-2", - "UseDualStack": true, - "UseFIPS": true, - "___key": "key" + "UseFIPS": false, + "UseDualStack": false } }, { - "documentation": "accelerate + fips = error@us-west-2", + "documentation": "non bucket endpoint with FIPS + Dualstack", "expect": { - "error": "Accelerate cannot be used with FIPS" - }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "us-west-2", - "AWS::UseFIPS": true, - "AWS::S3::Accelerate": true + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } + "url": "https://s3-fips.dualstack.us-west-2.amazonaws.com" } - ], + }, "params": { - "Accelerate": true, - "Bucket": "bucket-name", - "ForcePathStyle": false, "Region": "us-west-2", - "UseDualStack": false, "UseFIPS": true, - "___key": "key" + "UseDualStack": true } }, { - "documentation": "vanilla virtual addressing@cn-north-1", + "documentation": "non bucket endpoint with dualstack", "expect": { "endpoint": { "properties": { @@ -15555,38 +15473,22 @@ { "name": "sigv4", "signingName": "s3", - "signingRegion": "cn-north-1", + "signingRegion": "us-west-2", "disableDoubleEncoding": true } ] }, - "url": "https://bucket-name.s3.cn-north-1.amazonaws.com.cn" + "url": "https://s3.dualstack.us-west-2.amazonaws.com" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "cn-north-1" - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": false, - "Region": "cn-north-1", - "UseDualStack": false, + "Region": "us-west-2", "UseFIPS": false, - "___key": "key" + "UseDualStack": true } }, { - "documentation": "virtual addressing + dualstack@cn-north-1", + "documentation": "use global endpoint + IP address endpoint override", "expect": { "endpoint": { "properties": { @@ -15594,67 +15496,76 @@ { "name": "sigv4", "signingName": "s3", - "signingRegion": "cn-north-1", - "disableDoubleEncoding": true + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" } ] }, - "url": "https://bucket-name.s3.dualstack.cn-north-1.amazonaws.com.cn" + "url": "http://127.0.0.1/bucket" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "cn-north-1", - "AWS::UseDualStack": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": false, - "Region": "cn-north-1", - "UseDualStack": true, + "Region": "us-east-1", + "Bucket": "bucket", "UseFIPS": false, - "___key": "key" + "UseDualStack": false, + "Endpoint": "http://127.0.0.1", + "UseGlobalEndpoint": true } }, { - "documentation": "accelerate (dualstack=false)@cn-north-1", + "documentation": "non-dns endpoint + global endpoint", "expect": { - "error": "S3 Accelerate cannot be used in this region" + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3.amazonaws.com/bucket%21" + } }, "params": { - "Accelerate": true, - "Bucket": "bucket-name", - "ForcePathStyle": false, - "Region": "cn-north-1", + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": false, "UseDualStack": false, - "UseFIPS": false + "UseGlobalEndpoint": true } }, { - "documentation": "virtual addressing + fips@cn-north-1", + "documentation": "endpoint override + use global endpoint", "expect": { - "error": "Partition does not support FIPS" + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com/bucket%21" + } }, "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": false, - "Region": "cn-north-1", + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": false, "UseDualStack": false, - "UseFIPS": true + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" } }, { - "documentation": "vanilla virtual addressing@af-south-1", + "documentation": "FIPS + dualstack + non-bucket endpoint", "expect": { "endpoint": { "properties": { @@ -15662,38 +15573,23 @@ { "name": "sigv4", "signingName": "s3", - "signingRegion": "af-south-1", - "disableDoubleEncoding": true + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" } ] }, - "url": "https://bucket-name.s3.af-south-1.amazonaws.com" + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket%21" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "af-south-1" - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": false, - "Region": "af-south-1", - "UseDualStack": false, - "UseFIPS": false, - "___key": "key" + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": true, + "UseDualStack": true } }, { - "documentation": "virtual addressing + dualstack@af-south-1", + "documentation": "FIPS + dualstack + non-DNS endpoint", "expect": { "endpoint": { "properties": { @@ -15701,39 +15597,24 @@ { "name": "sigv4", "signingName": "s3", - "signingRegion": "af-south-1", - "disableDoubleEncoding": true + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" } ] }, - "url": "https://bucket-name.s3.dualstack.af-south-1.amazonaws.com" + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket%21" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "af-south-1", - "AWS::UseDualStack": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": false, - "Region": "af-south-1", - "UseDualStack": true, - "UseFIPS": false, - "___key": "key" + "Region": "us-east-1", + "Bucket": "bucket!", + "ForcePathStyle": true, + "UseFIPS": true, + "UseDualStack": true } }, { - "documentation": "accelerate + dualstack@af-south-1", + "documentation": "endpoint override + FIPS + dualstack (BUG)", "expect": { "endpoint": { "properties": { @@ -15741,40 +15622,25 @@ { "name": "sigv4", "signingName": "s3", - "signingRegion": "af-south-1", - "disableDoubleEncoding": true + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" } ] }, - "url": "https://bucket-name.s3-accelerate.dualstack.amazonaws.com" + "url": "http://foo.com/bucket%21" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "af-south-1", - "AWS::UseDualStack": true, - "AWS::S3::Accelerate": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": true, - "Bucket": "bucket-name", - "ForcePathStyle": false, - "Region": "af-south-1", - "UseDualStack": true, - "UseFIPS": false, - "___key": "key" + "Region": "us-east-1", + "Bucket": "bucket!", + "ForcePathStyle": true, + "UseFIPS": true, + "UseDualStack": false, + "Endpoint": "http://foo.com" } }, { - "documentation": "accelerate (dualstack=false)@af-south-1", + "documentation": "endpoint override + non-dns bucket + FIPS (BUG)", "expect": { "endpoint": { "properties": { @@ -15782,39 +15648,24 @@ { "name": "sigv4", "signingName": "s3", - "signingRegion": "af-south-1", - "disableDoubleEncoding": true + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" } ] }, - "url": "https://bucket-name.s3-accelerate.amazonaws.com" + "url": "http://foo.com/bucket%21" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "af-south-1", - "AWS::S3::Accelerate": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": true, - "Bucket": "bucket-name", - "ForcePathStyle": false, - "Region": "af-south-1", + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": true, "UseDualStack": false, - "UseFIPS": false, - "___key": "key" + "Endpoint": "http://foo.com" } }, { - "documentation": "virtual addressing + fips@af-south-1", + "documentation": "FIPS + bucket endpoint + force path style", "expect": { "endpoint": { "properties": { @@ -15822,39 +15673,25 @@ { "name": "sigv4", "signingName": "s3", - "signingRegion": "af-south-1", - "disableDoubleEncoding": true + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" } ] }, - "url": "https://bucket-name.s3-fips.af-south-1.amazonaws.com" + "url": "https://s3-fips.us-east-1.amazonaws.com/bucket%21" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "af-south-1", - "AWS::UseFIPS": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": false, - "Region": "af-south-1", - "UseDualStack": false, + "Region": "us-east-1", + "Bucket": "bucket!", + "ForcePathStyle": true, "UseFIPS": true, - "___key": "key" + "UseDualStack": false, + "UseGlobalEndpoint": true } }, { - "documentation": "virtual addressing + dualstack + fips@af-south-1", + "documentation": "bucket + FIPS + force path style", "expect": { "endpoint": { "properties": { @@ -15862,69 +15699,50 @@ { "name": "sigv4", "signingName": "s3", - "signingRegion": "af-south-1", - "disableDoubleEncoding": true + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" } ] }, - "url": "https://bucket-name.s3-fips.dualstack.af-south-1.amazonaws.com" + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "af-south-1", - "AWS::UseFIPS": true, - "AWS::UseDualStack": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": false, - "Region": "af-south-1", - "UseDualStack": true, + "Region": "us-east-1", + "Bucket": "bucket", + "ForcePathStyle": true, "UseFIPS": true, - "___key": "key" + "UseDualStack": true, + "UseGlobalEndpoint": true } }, { - "documentation": "accelerate + fips = error@af-south-1", + "documentation": "FIPS + dualstack + use global endpoint", "expect": { - "error": "Accelerate cannot be used with FIPS" - }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "af-south-1", - "AWS::UseFIPS": true, - "AWS::S3::Accelerate": true + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } + "url": "https://bucket.s3-fips.dualstack.us-east-1.amazonaws.com" } - ], + }, "params": { - "Accelerate": true, - "Bucket": "bucket-name", - "ForcePathStyle": false, - "Region": "af-south-1", - "UseDualStack": false, + "Region": "us-east-1", + "Bucket": "bucket", "UseFIPS": true, - "___key": "key" + "UseDualStack": true, + "UseGlobalEndpoint": true } }, { - "documentation": "vanilla path style@us-west-2", + "documentation": "URI encoded bucket + use global endpoint", "expect": { "endpoint": { "properties": { @@ -15932,52 +15750,1606 @@ { "name": "sigv4", "signingName": "s3", - "signingRegion": "us-west-2", - "disableDoubleEncoding": true + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" } ] }, - "url": "https://s3.us-west-2.amazonaws.com/bucket-name" + "url": "https://foo.com/bucket%21" } }, - "operationInputs": [ - { - "builtInParams": { - "AWS::Region": "us-west-2", - "AWS::S3::ForcePathStyle": true - }, - "operationName": "GetObject", - "operationParams": { - "Bucket": "bucket-name", - "Key": "key" - } - } - ], "params": { - "Accelerate": false, - "Bucket": "bucket-name", - "ForcePathStyle": true, - "Region": "us-west-2", + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": true, "UseDualStack": false, - "UseFIPS": false, - "___key": "key" + "UseGlobalEndpoint": true, + "Endpoint": "https://foo.com" } }, { - "documentation": "fips@us-gov-west-2, bucket is not S3-dns-compatible (subdomains)", + "documentation": "FIPS + path based endpoint", "expect": { "endpoint": { "properties": { "authSchemes": [ { + "name": "sigv4", "signingName": "s3", - "signingRegion": "us-gov-west-1", "disableDoubleEncoding": true, - "name": "sigv4" + "signingRegion": "us-east-1" } ] }, - "url": "https://s3-fips.us-gov-west-1.amazonaws.com/bucket.with.dots" + "url": "https://s3-fips.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "UseFIPS": true, + "UseDualStack": false, + "Accelerate": false, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "accelerate + dualstack + global endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://bucket.s3-accelerate.dualstack.amazonaws.com" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket", + "UseFIPS": false, + "UseDualStack": true, + "Accelerate": true, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "dualstack + global endpoint + non URI safe bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "Accelerate": false, + "UseDualStack": true, + "UseFIPS": false, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "FIPS + uri encoded bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3-fips.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "ForcePathStyle": true, + "Accelerate": false, + "UseDualStack": false, + "UseFIPS": true, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "endpoint override + non-uri safe endpoint + force path style", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "ForcePathStyle": true, + "Accelerate": false, + "UseDualStack": false, + "UseFIPS": true, + "Endpoint": "http://foo.com", + "UseGlobalEndpoint": true + } + }, + { + "documentation": "FIPS + Dualstack + global endpoint + non-dns bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-1", + "Bucket": "bucket!", + "Accelerate": false, + "UseDualStack": true, + "UseFIPS": true, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "endpoint override + FIPS + dualstack (this is wrong—it's a bug in the UseGlobalEndpoint branch)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "us-east-1", + "UseDualStack": true, + "UseFIPS": true, + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "non-bucket endpoint override + dualstack + global endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "Endpoint override + UseGlobalEndpoint + us-east-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true, + "signingRegion": "us-east-1" + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "non-FIPS partition with FIPS set + custom endpoint", + "expect": { + "error": "Partition does not support FIPS" + }, + "params": { + "Region": "cn-north-1", + "UseFIPS": true, + "UseDualStack": false, + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "aws-global signs as us-east-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "UseFIPS": true, + "Accelerate": false, + "UseDualStack": true + } + }, + { + "documentation": "aws-global signs as us-east-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket.foo.com" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket", + "UseDualStack": false, + "UseFIPS": false, + "Accelerate": false, + "Endpoint": "https://foo.com" + } + }, + { + "documentation": "aws-global + dualstack + path-only bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "UseDualStack": true, + "UseFIPS": false, + "Accelerate": false + } + }, + { + "documentation": "aws-global + path-only bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!" + } + }, + { + "documentation": "aws-global + fips + custom endpoint (TODO: should be an error)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "UseDualStack": false, + "UseFIPS": true, + "Accelerate": false, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "aws-global, endpoint override & path only-bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "UseDualStack": false, + "UseFIPS": false, + "Accelerate": false, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "aws-global + dualstack + custom endpoint (TODO: should be an error)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "aws-global", + "UseDualStack": true, + "UseFIPS": false, + "Accelerate": false, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "accelerate, dualstack + aws-global", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket.s3-accelerate.dualstack.us-east-1.amazonaws.com" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket", + "UseDualStack": true, + "UseFIPS": false, + "Accelerate": true + } + }, + { + "documentation": "FIPS + aws-global + path only bucket. TODO: this should be an error", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3-fips.dualstack.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "ForcePathStyle": true, + "UseDualStack": true, + "UseFIPS": true, + "Accelerate": false + } + }, + { + "documentation": "aws-global + FIPS + endpoint override. TODO: should this be an error?", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "aws-global", + "UseFIPS": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "force path style, aws-global & endpoint override", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "ForcePathStyle": true, + "UseFIPS": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "ip address causes path style to be forced", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://192.168.1.1/bucket" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket", + "Endpoint": "http://192.168.1.1" + } + }, + { + "documentation": "endpoint override with aws-global region", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com" + } + }, + "params": { + "Region": "aws-global", + "UseFIPS": true, + "UseDualStack": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "FIPS + path-only (TODO: consider making this an error)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-1", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3-fips.us-east-1.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "aws-global", + "Bucket": "bucket!", + "UseFIPS": true + } + }, + { + "documentation": "empty arn type", + "expect": { + "error": "Invalid ARN: No ARN type specified" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:not-s3:us-west-2:123456789012::myendpoint" + } + }, + { + "documentation": "path style can't be used with accelerate", + "expect": { + "error": "Path-style addressing cannot be used with S3 Accelerate" + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket!", + "Accelerate": true + } + }, + { + "documentation": "invalid region", + "expect": { + "error": "Invalid region: region was not a valid DNS name." + }, + "params": { + "Region": "us-east-2!", + "Bucket": "bucket.subdomain", + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "invalid region", + "expect": { + "error": "Invalid region: region was not a valid DNS name." + }, + "params": { + "Region": "us-east-2!", + "Bucket": "bucket", + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "empty arn type", + "expect": { + "error": "Invalid Access Point Name" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3::123456789012:accesspoint:my_endpoint" + } + }, + { + "documentation": "empty arn type", + "expect": { + "error": "Client was configured for partition `aws` but ARN (`arn:aws:s3:cn-north-1:123456789012:accesspoint:my-endpoint`) has `aws-cn`" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3:cn-north-1:123456789012:accesspoint:my-endpoint", + "UseArnRegion": true + } + }, + { + "documentation": "invalid arn region", + "expect": { + "error": "Invalid region in ARN: `us-east_2` (invalid DNS name)" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-object-lambda:us-east_2:123456789012:accesspoint:my-endpoint", + "UseArnRegion": true + } + }, + { + "documentation": "invalid ARN outpost", + "expect": { + "error": "Invalid ARN: The outpost Id may only contain a-z, A-Z, 0-9 and `-`. Found: `op_01234567890123456`" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:123456789012:outpost/op_01234567890123456/accesspoint/reports", + "UseArnRegion": true + } + }, + { + "documentation": "invalid ARN", + "expect": { + "error": "Invalid ARN: expected an access point name" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:123456789012:outpost/op-01234567890123456/reports" + } + }, + { + "documentation": "invalid ARN", + "expect": { + "error": "Invalid ARN: Expected a 4-component resource" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:123456789012:outpost/op-01234567890123456" + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "error": "Expected an outpost type `accesspoint`, found not-accesspoint" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:123456789012:outpost/op-01234567890123456/not-accesspoint/reports" + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "error": "Invalid region in ARN: `us-east_1` (invalid DNS name)" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east_1:123456789012:outpost/op-01234567890123456/not-accesspoint/reports" + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "error": "Invalid ARN: The account id may only contain a-z, A-Z, 0-9 and `-`. Found: `12345_789012`" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:12345_789012:outpost/op-01234567890123456/not-accesspoint/reports" + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "error": "Invalid ARN: The Outpost Id was not set" + }, + "params": { + "Region": "us-east-2", + "Bucket": "arn:aws:s3-outposts:us-east-1:12345789012:outpost" + } + }, + { + "documentation": "use global endpoint virtual addressing", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://bucket.example.com" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket", + "Endpoint": "http://example.com", + "UseGlobalEndpoint": true + } + }, + { + "documentation": "global endpoint + ip address", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://192.168.0.1/bucket" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket", + "Endpoint": "http://192.168.0.1", + "UseGlobalEndpoint": true + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3.us-east-2.amazonaws.com/bucket%21" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket!", + "UseGlobalEndpoint": true + } + }, + { + "documentation": "invalid outpost type", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket.s3-accelerate.amazonaws.com" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket", + "Accelerate": true, + "UseGlobalEndpoint": true + } + }, + { + "documentation": "use global endpoint + custom endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket!", + "UseGlobalEndpoint": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "use global endpoint, not us-east-1, force path style", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingRegion": "us-east-2", + "name": "sigv4", + "signingName": "s3", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://foo.com/bucket%21" + } + }, + "params": { + "Region": "us-east-2", + "Bucket": "bucket!", + "UseGlobalEndpoint": true, + "ForcePathStyle": true, + "Endpoint": "http://foo.com" + } + }, + { + "documentation": "vanilla virtual addressing@us-west-2", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3.us-west-2.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "us-west-2" + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "us-west-2", + "UseDualStack": false, + "UseFIPS": false, + "___key": "key" + } + }, + { + "documentation": "virtual addressing + dualstack@us-west-2", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3.dualstack.us-west-2.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "us-west-2", + "AWS::UseDualStack": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "us-west-2", + "UseDualStack": true, + "UseFIPS": false, + "___key": "key" + } + }, + { + "documentation": "accelerate + dualstack@us-west-2", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3-accelerate.dualstack.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "us-west-2", + "AWS::UseDualStack": true, + "AWS::S3::Accelerate": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": true, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "us-west-2", + "UseDualStack": true, + "UseFIPS": false, + "___key": "key" + } + }, + { + "documentation": "accelerate (dualstack=false)@us-west-2", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3-accelerate.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "us-west-2", + "AWS::S3::Accelerate": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": true, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "us-west-2", + "UseDualStack": false, + "UseFIPS": false, + "___key": "key" + } + }, + { + "documentation": "virtual addressing + fips@us-west-2", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3-fips.us-west-2.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "us-west-2", + "AWS::UseFIPS": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "us-west-2", + "UseDualStack": false, + "UseFIPS": true, + "___key": "key" + } + }, + { + "documentation": "virtual addressing + dualstack + fips@us-west-2", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3-fips.dualstack.us-west-2.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "us-west-2", + "AWS::UseFIPS": true, + "AWS::UseDualStack": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "us-west-2", + "UseDualStack": true, + "UseFIPS": true, + "___key": "key" + } + }, + { + "documentation": "accelerate + fips = error@us-west-2", + "expect": { + "error": "Accelerate cannot be used with FIPS" + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "us-west-2", + "AWS::UseFIPS": true, + "AWS::S3::Accelerate": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": true, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "us-west-2", + "UseDualStack": false, + "UseFIPS": true, + "___key": "key" + } + }, + { + "documentation": "vanilla virtual addressing@cn-north-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "cn-north-1", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3.cn-north-1.amazonaws.com.cn" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "cn-north-1" + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "cn-north-1", + "UseDualStack": false, + "UseFIPS": false, + "___key": "key" + } + }, + { + "documentation": "virtual addressing + dualstack@cn-north-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "cn-north-1", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3.dualstack.cn-north-1.amazonaws.com.cn" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "cn-north-1", + "AWS::UseDualStack": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "cn-north-1", + "UseDualStack": true, + "UseFIPS": false, + "___key": "key" + } + }, + { + "documentation": "accelerate (dualstack=false)@cn-north-1", + "expect": { + "error": "S3 Accelerate cannot be used in this region" + }, + "params": { + "Accelerate": true, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "cn-north-1", + "UseDualStack": false, + "UseFIPS": false + } + }, + { + "documentation": "virtual addressing + fips@cn-north-1", + "expect": { + "error": "Partition does not support FIPS" + }, + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "cn-north-1", + "UseDualStack": false, + "UseFIPS": true + } + }, + { + "documentation": "vanilla virtual addressing@af-south-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "af-south-1", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3.af-south-1.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "af-south-1" + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "af-south-1", + "UseDualStack": false, + "UseFIPS": false, + "___key": "key" + } + }, + { + "documentation": "virtual addressing + dualstack@af-south-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "af-south-1", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3.dualstack.af-south-1.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "af-south-1", + "AWS::UseDualStack": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "af-south-1", + "UseDualStack": true, + "UseFIPS": false, + "___key": "key" + } + }, + { + "documentation": "accelerate + dualstack@af-south-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "af-south-1", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3-accelerate.dualstack.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "af-south-1", + "AWS::UseDualStack": true, + "AWS::S3::Accelerate": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": true, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "af-south-1", + "UseDualStack": true, + "UseFIPS": false, + "___key": "key" + } + }, + { + "documentation": "accelerate (dualstack=false)@af-south-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "af-south-1", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3-accelerate.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "af-south-1", + "AWS::S3::Accelerate": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": true, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "af-south-1", + "UseDualStack": false, + "UseFIPS": false, + "___key": "key" + } + }, + { + "documentation": "virtual addressing + fips@af-south-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "af-south-1", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3-fips.af-south-1.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "af-south-1", + "AWS::UseFIPS": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "af-south-1", + "UseDualStack": false, + "UseFIPS": true, + "___key": "key" + } + }, + { + "documentation": "virtual addressing + dualstack + fips@af-south-1", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "af-south-1", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://bucket-name.s3-fips.dualstack.af-south-1.amazonaws.com" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "af-south-1", + "AWS::UseFIPS": true, + "AWS::UseDualStack": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "af-south-1", + "UseDualStack": true, + "UseFIPS": true, + "___key": "key" + } + }, + { + "documentation": "accelerate + fips = error@af-south-1", + "expect": { + "error": "Accelerate cannot be used with FIPS" + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "af-south-1", + "AWS::UseFIPS": true, + "AWS::S3::Accelerate": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": true, + "Bucket": "bucket-name", + "ForcePathStyle": false, + "Region": "af-south-1", + "UseDualStack": false, + "UseFIPS": true, + "___key": "key" + } + }, + { + "documentation": "vanilla path style@us-west-2", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "us-west-2", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://s3.us-west-2.amazonaws.com/bucket-name" + } + }, + "operationInputs": [ + { + "builtInParams": { + "AWS::Region": "us-west-2", + "AWS::S3::ForcePathStyle": true + }, + "operationName": "GetObject", + "operationParams": { + "Bucket": "bucket-name", + "Key": "key" + } + } + ], + "params": { + "Accelerate": false, + "Bucket": "bucket-name", + "ForcePathStyle": true, + "Region": "us-west-2", + "UseDualStack": false, + "UseFIPS": false, + "___key": "key" + } + }, + { + "documentation": "fips@us-gov-west-2, bucket is not S3-dns-compatible (subdomains)", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "signingName": "s3", + "signingRegion": "us-gov-west-1", + "disableDoubleEncoding": true, + "name": "sigv4" + } + ] + }, + "url": "https://s3-fips.us-gov-west-1.amazonaws.com/bucket.with.dots" } }, "operationInputs": [ @@ -19143,6 +20515,122 @@ "UseDualStack": false, "Accelerate": false } + }, + { + "documentation": "S3 Outposts Abba - No endpoint set for beta", + "expect": { + "error": "Expected a endpoint to be specified but no endpoint was found" + }, + "params": { + "Region": "us-east-1", + "Bucket": "test-accessp-e0b1d075431d83bebde8xz5w8ijx1qzlbp3i3ebeta0--op-s3", + "UseFIPS": false, + "UseDualStack": false, + "Accelerate": false + } + }, + { + "documentation": "S3 Snow with bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://10.0.1.12:433/bucketName" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "http://10.0.1.12:433", + "UseFIPS": false, + "UseDualStack": false, + "Accelerate": false + } + }, + { + "documentation": "S3 Snow without bucket", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://10.0.1.12:433" + } + }, + "params": { + "Region": "snow", + "Endpoint": "https://10.0.1.12:433", + "UseFIPS": false, + "UseDualStack": false, + "Accelerate": false + } + }, + { + "documentation": "S3 Snow no port", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "http://10.0.1.12/bucketName" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "http://10.0.1.12", + "UseFIPS": false, + "UseDualStack": false, + "Accelerate": false + } + }, + { + "documentation": "S3 Snow dns endpoint", + "expect": { + "endpoint": { + "properties": { + "authSchemes": [ + { + "name": "sigv4", + "signingName": "s3", + "signingRegion": "snow", + "disableDoubleEncoding": true + } + ] + }, + "url": "https://amazonaws.com/bucketName" + } + }, + "params": { + "Region": "snow", + "Bucket": "bucketName", + "Endpoint": "https://amazonaws.com", + "UseFIPS": false, + "UseDualStack": false, + "Accelerate": false + } } ], "version": "1.0" @@ -20031,7 +21519,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -20045,7 +21533,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -20053,7 +21541,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -20335,7 +21823,24 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a copy of an object that is already stored in Amazon S3.

\n \n

You can store individual objects of up to 5 TB in Amazon S3. You create a copy of your\n object up to 5 GB in size in a single atomic action using this API. However, to copy an\n object greater than 5 GB, you must use the multipart upload Upload Part - Copy\n (UploadPartCopy) API. For more information, see Copy Object Using the\n REST Multipart Upload API.

\n
\n

All copy requests must be authenticated. Additionally, you must have\n read access to the source object and write\n access to the destination bucket. For more information, see REST Authentication. Both the\n Region that you want to copy the object from and the Region that you want to copy the\n object to must be enabled for your account.

\n

A copy request might return an error when Amazon S3 receives the copy request or while Amazon S3\n is copying the files. If the error occurs before the copy action starts, you receive a\n standard Amazon S3 error. If the error occurs during the copy operation, the error response is\n embedded in the 200 OK response. This means that a 200 OK\n response can contain either a success or an error. If you call the S3 API directly, make\n sure to design your application to parse the contents of the response and handle it\n appropriately. If you use Amazon Web Services SDKs, SDKs handle this condition. The SDKs detect the\n embedded error and apply error handling per your configuration settings (including\n automatically retrying the request as appropriate). If the condition persists, the SDKs\n throws an exception (or, for the SDKs that don't use exceptions, they return the\n error).

\n

If the copy is successful, you receive a response with information about the copied\n object.

\n \n

If the request is an HTTP 1.1 request, the response is chunk encoded. If it were not,\n it would not contain the content-length, and you would need to read the entire\n body.

\n
\n

The copy request charge is based on the storage class and Region that you specify for\n the destination object. For pricing information, see Amazon S3 pricing.

\n \n

Amazon S3 transfer acceleration does not support cross-Region copies. If you request a\n cross-Region copy using a transfer acceleration endpoint, you get a 400 Bad\n Request error. For more information, see Transfer\n Acceleration.

\n
\n
\n
Metadata
\n
\n

When copying an object, you can preserve all metadata (default) or specify new metadata.\n However, the ACL is not preserved and is set to private for the user making the request. To\n override the default ACL setting, specify a new ACL when generating a copy request. For\n more information, see Using ACLs.

\n

To specify whether you want the object metadata copied from the source object or\n replaced with metadata provided in the request, you can optionally add the\n x-amz-metadata-directive header. When you grant permissions, you can use\n the s3:x-amz-metadata-directive condition key to enforce certain metadata\n behavior when objects are uploaded. For more information, see Specifying Conditions in a\n Policy in the Amazon S3 User Guide. For a complete list of\n Amazon S3-specific condition keys, see Actions, Resources, and Condition Keys for\n Amazon S3.

\n \n

\n x-amz-website-redirect-location is unique to each object and must be\n specified in the request headers to copy the value.

\n
\n
\n
x-amz-copy-source-if Headers
\n
\n

To only copy an object under certain conditions, such as whether the Etag\n matches or whether the object was modified before or after a specified date, use the\n following request parameters:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-none-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since\n

    \n
  • \n
\n

If both the x-amz-copy-source-if-match and\n x-amz-copy-source-if-unmodified-since headers are present in the request\n and evaluate as follows, Amazon S3 returns 200 OK and copies the data:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match condition evaluates to true

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since condition evaluates to\n false

    \n
  • \n
\n

If both the x-amz-copy-source-if-none-match and\n x-amz-copy-source-if-modified-since headers are present in the request and\n evaluate as follows, Amazon S3 returns the 412 Precondition Failed response\n code:

\n
    \n
  • \n

    \n x-amz-copy-source-if-none-match condition evaluates to false

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since condition evaluates to\n true

    \n
  • \n
\n \n

All headers with the x-amz- prefix, including\n x-amz-copy-source, must be signed.

\n
\n
\n
Server-side encryption
\n
\n

Amazon S3 automatically encrypts all new objects that are copied to an S3 bucket. When\n copying an object, if you don't specify encryption information in your copy request, the\n encryption setting of the target object is set to the default encryption configuration of\n the destination bucket. By default, all buckets have a base level of encryption\n configuration that uses server-side encryption with Amazon S3 managed keys (SSE-S3). If the\n destination bucket has a default encryption configuration that uses server-side encryption\n with an Key Management Service (KMS) key (SSE-KMS), or a customer-provided encryption key (SSE-C),\n Amazon S3 uses the corresponding KMS key, or a customer-provided key to encrypt the target\n object copy.

\n

When you perform a CopyObject operation, if you want to use a different type\n of encryption setting for the target object, you can use other appropriate\n encryption-related headers to encrypt the target object with a KMS key, an Amazon S3 managed\n key, or a customer-provided key. With server-side encryption, Amazon S3 encrypts your data as it\n writes it to disks in its data centers and decrypts the data when you access it. If the\n encryption setting in your request is different from the default encryption configuration\n of the destination bucket, the encryption setting in your request takes precedence. If the\n source object for the copy is stored in Amazon S3 using SSE-C, you must provide the necessary\n encryption information in your request so that Amazon S3 can decrypt the object for copying. For\n more information about server-side encryption, see Using Server-Side\n Encryption.

\n

If a target object uses SSE-KMS, you can enable an S3 Bucket Key for the object. For\n more information, see Amazon S3 Bucket Keys in the Amazon S3 User Guide.

\n
\n
Access Control List (ACL)-Specific Request\n Headers
\n
\n

When copying an object, you can optionally use headers to grant ACL-based permissions.\n By default, all objects are private. Only the owner has full access control. When adding a\n new object, you can grant permissions to individual Amazon Web Services accounts or to predefined groups\n defined by Amazon S3. These permissions are then added to the ACL on the object. For more\n information, see Access Control List (ACL) Overview and Managing ACLs Using the REST\n API.

\n

If the bucket that you're copying objects to uses the bucket owner enforced setting for\n S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that use\n this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control\n canned ACL or an equivalent form of this ACL expressed in the XML format.

\n

For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, all\n objects written to the bucket by any account will be owned by the bucket owner.

\n
\n
\n
Checksums
\n
\n

When copying an object, if it has a checksum, that checksum will be copied to the new\n object by default. When you copy the object over, you may optionally specify a different\n checksum algorithm to use with the x-amz-checksum-algorithm header.

\n
\n
Storage Class Options
\n
\n

You can use the CopyObject action to change the storage class of an object\n that is already stored in Amazon S3 using the StorageClass parameter. For more\n information, see Storage Classes in the\n Amazon S3 User Guide.

\n

If the source object's storage class is GLACIER, you must restore a copy of\n this object before you can use it as a source object for the copy operation. For\n more information, see RestoreObject. For\n more information, see Copying\n Objects.

\n
\n
Versioning
\n
\n

By default, x-amz-copy-source identifies the current version of an object\n to copy. If the current version is a delete marker, Amazon S3 behaves as if the object was\n deleted. To copy a different version, use the versionId subresource.

\n

If you enable versioning on the target bucket, Amazon S3 generates a unique version ID for\n the object being copied. This version ID is different from the version ID of the source\n object. Amazon S3 returns the version ID of the copied object in the\n x-amz-version-id response header in the response.

\n

If you do not enable versioning or suspend it on the target bucket, the version ID that\n Amazon S3 generates is always null.

\n
\n
\n

The following operations are related to CopyObject:

\n ", + "smithy.api#documentation": "

Creates a copy of an object that is already stored in Amazon S3.

\n \n

You can store individual objects of up to 5 TB in Amazon S3. You create a copy of your\n object up to 5 GB in size in a single atomic action using this API. However, to copy an\n object greater than 5 GB, you must use the multipart upload Upload Part - Copy\n (UploadPartCopy) API. For more information, see Copy Object Using the\n REST Multipart Upload API.

\n
\n

All copy requests must be authenticated. Additionally, you must have\n read access to the source object and write\n access to the destination bucket. For more information, see REST Authentication. Both the\n Region that you want to copy the object from and the Region that you want to copy the\n object to must be enabled for your account.

\n

A copy request might return an error when Amazon S3 receives the copy request or while Amazon S3\n is copying the files. If the error occurs before the copy action starts, you receive a\n standard Amazon S3 error. If the error occurs during the copy operation, the error response is\n embedded in the 200 OK response. This means that a 200 OK\n response can contain either a success or an error. If you call the S3 API directly, make\n sure to design your application to parse the contents of the response and handle it\n appropriately. If you use Amazon Web Services SDKs, SDKs handle this condition. The SDKs detect the\n embedded error and apply error handling per your configuration settings (including\n automatically retrying the request as appropriate). If the condition persists, the SDKs\n throws an exception (or, for the SDKs that don't use exceptions, they return the\n error).

\n

If the copy is successful, you receive a response with information about the copied\n object.

\n \n

If the request is an HTTP 1.1 request, the response is chunk encoded. If it were not,\n it would not contain the content-length, and you would need to read the entire\n body.

\n
\n

The copy request charge is based on the storage class and Region that you specify for\n the destination object. For pricing information, see Amazon S3 pricing.

\n \n

Amazon S3 transfer acceleration does not support cross-Region copies. If you request a\n cross-Region copy using a transfer acceleration endpoint, you get a 400 Bad\n Request error. For more information, see Transfer\n Acceleration.

\n
\n
\n
Metadata
\n
\n

When copying an object, you can preserve all metadata (the default) or specify new metadata.\n However, the access control list (ACL) is not preserved and is set to private for the user making the request. To\n override the default ACL setting, specify a new ACL when generating a copy request. For\n more information, see Using ACLs.

\n

To specify whether you want the object metadata copied from the source object or\n replaced with metadata provided in the request, you can optionally add the\n x-amz-metadata-directive header. When you grant permissions, you can use\n the s3:x-amz-metadata-directive condition key to enforce certain metadata\n behavior when objects are uploaded. For more information, see Specifying Conditions in a\n Policy in the Amazon S3 User Guide. For a complete list of\n Amazon S3-specific condition keys, see Actions, Resources, and Condition Keys for\n Amazon S3.

\n \n

\n x-amz-website-redirect-location is unique to each object and must be\n specified in the request headers to copy the value.

\n
\n
\n
x-amz-copy-source-if Headers
\n
\n

To only copy an object under certain conditions, such as whether the Etag\n matches or whether the object was modified before or after a specified date, use the\n following request parameters:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-none-match\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since\n

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since\n

    \n
  • \n
\n

If both the x-amz-copy-source-if-match and\n x-amz-copy-source-if-unmodified-since headers are present in the request\n and evaluate as follows, Amazon S3 returns 200 OK and copies the data:

\n
    \n
  • \n

    \n x-amz-copy-source-if-match condition evaluates to true

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-unmodified-since condition evaluates to\n false

    \n
  • \n
\n

If both the x-amz-copy-source-if-none-match and\n x-amz-copy-source-if-modified-since headers are present in the request and\n evaluate as follows, Amazon S3 returns the 412 Precondition Failed response\n code:

\n
    \n
  • \n

    \n x-amz-copy-source-if-none-match condition evaluates to false

    \n
  • \n
  • \n

    \n x-amz-copy-source-if-modified-since condition evaluates to\n true

    \n
  • \n
\n \n

All headers with the x-amz- prefix, including\n x-amz-copy-source, must be signed.

\n
\n
\n
Server-side encryption
\n
\n

Amazon S3 automatically encrypts all new objects that are copied to an S3 bucket. When\n copying an object, if you don't specify encryption information in your copy\n request, the encryption setting of the target object is set to the default\n encryption configuration of the destination bucket. By default, all buckets have a\n base level of encryption configuration that uses server-side encryption with Amazon S3\n managed keys (SSE-S3). If the destination bucket has a default encryption\n configuration that uses server-side encryption with Key Management Service (KMS) keys\n (SSE-KMS), dual-layer server-side encryption with Amazon Web Services KMS keys (DSSE-KMS), or\n server-side encryption with customer-provided encryption keys (SSE-C), Amazon S3 uses\n the corresponding KMS key, or a customer-provided key to encrypt the target\n object copy.

\n

When you perform a CopyObject operation, if you want to use a different type\n of encryption setting for the target object, you can use other appropriate\n encryption-related headers to encrypt the target object with a KMS key, an Amazon S3 managed\n key, or a customer-provided key. With server-side encryption, Amazon S3 encrypts your data as it\n writes your data to disks in its data centers and decrypts the data when you access it. If the\n encryption setting in your request is different from the default encryption configuration\n of the destination bucket, the encryption setting in your request takes precedence. If the\n source object for the copy is stored in Amazon S3 using SSE-C, you must provide the necessary\n encryption information in your request so that Amazon S3 can decrypt the object for copying. For\n more information about server-side encryption, see Using Server-Side\n Encryption.

\n

If a target object uses SSE-KMS, you can enable an S3 Bucket Key for the\n object. For more information, see Amazon S3 Bucket Keys in the\n Amazon S3 User Guide.

\n
\n
Access Control List (ACL)-Specific Request\n Headers
\n
\n

When copying an object, you can optionally use headers to grant ACL-based permissions.\n By default, all objects are private. Only the owner has full access control. When adding a\n new object, you can grant permissions to individual Amazon Web Services accounts or to predefined groups\n that are defined by Amazon S3. These permissions are then added to the ACL on the object. For more\n information, see Access Control List (ACL) Overview and Managing ACLs Using the REST\n API.

\n

If the bucket that you're copying objects to uses the bucket owner enforced setting for\n S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that use\n this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control\n canned ACL or an equivalent form of this ACL expressed in the XML format.

\n

For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, all\n objects written to the bucket by any account will be owned by the bucket owner.

\n
\n
\n
Checksums
\n
\n

When copying an object, if it has a checksum, that checksum will be copied to the new\n object by default. When you copy the object over, you can optionally specify a different\n checksum algorithm to use with the x-amz-checksum-algorithm header.

\n
\n
Storage Class Options
\n
\n

You can use the CopyObject action to change the storage class of an object\n that is already stored in Amazon S3 by using the StorageClass parameter. For more\n information, see Storage Classes in the\n Amazon S3 User Guide.

\n

If the source object's storage class is GLACIER, you must restore a copy of\n this object before you can use it as a source object for the copy operation. For\n more information, see RestoreObject. For\n more information, see Copying\n Objects.

\n
\n
Versioning
\n
\n

By default, x-amz-copy-source header identifies the current version of an object\n to copy. If the current version is a delete marker, Amazon S3 behaves as if the object was\n deleted. To copy a different version, use the versionId subresource.

\n

If you enable versioning on the target bucket, Amazon S3 generates a unique version ID for\n the object being copied. This version ID is different from the version ID of the source\n object. Amazon S3 returns the version ID of the copied object in the\n x-amz-version-id response header in the response.

\n

If you do not enable versioning or suspend it on the target bucket, the version ID that\n Amazon S3 generates is always null.

\n
\n
\n

The following operations are related to CopyObject:

\n ", + "smithy.api#examples": [ + { + "title": "To copy an object", + "documentation": "The following example copies an object from one bucket to another.", + "input": { + "Bucket": "destinationbucket", + "CopySource": "/sourcebucket/HappyFacejpg", + "Key": "HappyFaceCopyjpg" + }, + "output": { + "CopyObjectResult": { + "LastModified": "2016-12-15T17:38:53.000Z", + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"" + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?x-id=CopyObject", @@ -20377,7 +21882,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -20398,7 +21903,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -20413,7 +21918,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the copied object uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the copied object uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -20594,7 +22099,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -20636,7 +22141,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

Specifies the Amazon Web Services KMS key ID to use for object encryption. All GET and PUT requests\n for an object protected by Amazon Web Services KMS will fail if not made via SSL or using SigV4. For\n information about configuring using any of the officially supported Amazon Web Services SDKs and Amazon Web Services\n CLI, see Specifying the\n Signature Version in Request Authentication in the\n Amazon S3 User Guide.

", + "smithy.api#documentation": "

Specifies the KMS key ID to use for object encryption. All GET and PUT requests for an\n object protected by KMS will fail if they're not made via SSL or using SigV4. For\n information about configuring any of the officially supported Amazon Web Services SDKs and Amazon Web Services CLI, see\n Specifying the\n Signature Version in Request Authentication in the\n Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -20651,7 +22156,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using AWS KMS (SSE-KMS). Setting this header to true\n causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with a COPY action doesn’t affect bucket-level settings for S3\n Bucket Key.

", + "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using Key Management Service (KMS) keys (SSE-KMS). Setting this header to\n true causes Amazon S3 to use an S3 Bucket Key for object encryption with\n SSE-KMS.

\n

Specifying this header with a COPY action doesn’t affect bucket-level settings for S3\n Bucket Key.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -20870,7 +22375,19 @@ } ], "traits": { - "smithy.api#documentation": "

Creates a new S3 bucket. To create a bucket, you must register with Amazon S3 and have a\n valid Amazon Web Services Access Key ID to authenticate requests. Anonymous requests are never allowed to\n create buckets. By creating the bucket, you become the bucket owner.

\n

Not every string is an acceptable bucket name. For information about bucket naming\n restrictions, see Bucket naming\n rules.

\n

If you want to create an Amazon S3 on Outposts bucket, see Create Bucket.

\n

By default, the bucket is created in the US East (N. Virginia) Region. You can\n optionally specify a Region in the request body. You might choose a Region to optimize\n latency, minimize costs, or address regulatory requirements. For example, if you reside in\n Europe, you will probably find it advantageous to create buckets in the Europe (Ireland)\n Region. For more information, see Accessing a\n bucket.

\n \n

If you send your create bucket request to the s3.amazonaws.com endpoint,\n the request goes to the us-east-1 Region. Accordingly, the signature calculations in\n Signature Version 4 must use us-east-1 as the Region, even if the location constraint in\n the request specifies another Region where the bucket is to be created. If you create a\n bucket in a Region other than US East (N. Virginia), your application must be able to\n handle 307 redirect. For more information, see Virtual hosting of\n buckets.

\n
\n
\n
Access control lists (ACLs)
\n
\n

When creating a bucket using this operation, you can optionally configure the bucket ACL\n to specify the accounts or groups that should be granted specific permissions on the\n bucket.

\n \n

If your CreateBucket request sets bucket owner enforced for S3 Object Ownership and\n specifies a bucket ACL that provides access to an external Amazon Web Services account, your request\n fails with a 400 error and returns the\n InvalidBucketAclWithObjectOwnership error code. For more information,\n see Controlling object\n ownership in the Amazon S3 User Guide.

\n
\n

There are two ways to grant the appropriate permissions using the request\n headers.

\n
    \n
  • \n

    Specify a canned ACL using the x-amz-acl request header. Amazon S3\n supports a set of predefined ACLs, known as canned ACLs. Each\n canned ACL has a predefined set of grantees and permissions. For more information,\n see Canned ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly using the x-amz-grant-read,\n x-amz-grant-write, x-amz-grant-read-acp,\n x-amz-grant-write-acp, and x-amz-grant-full-control\n headers. These headers map to the set of permissions Amazon S3 supports in an ACL. For\n more information, see Access control list (ACL)\n overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an\n Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants the Amazon Web Services accounts identified by account IDs permissions to read object data and its metadata:

    \n

    \n x-amz-grant-read: id=\"11112222333\", id=\"444455556666\" \n

    \n
  • \n
\n \n

You can use either a canned ACL or specify access permissions explicitly. You cannot\n do both.

\n
\n
\n
Permissions
\n
\n

In addition to s3:CreateBucket, the following permissions are required when\n your CreateBucket includes specific headers:

\n
    \n
  • \n

    \n ACLs - If your CreateBucket request\n specifies ACL permissions and the ACL is public-read, public-read-write,\n authenticated-read, or if you specify access permissions explicitly through any other\n ACL, both s3:CreateBucket and s3:PutBucketAcl permissions\n are needed. If the ACL the CreateBucket request is private or doesn't\n specify any ACLs, only s3:CreateBucket permission is needed.

    \n
  • \n
  • \n

    \n Object Lock - If\n ObjectLockEnabledForBucket is set to true in your\n CreateBucket request,\n s3:PutBucketObjectLockConfiguration and\n s3:PutBucketVersioning permissions are required.

    \n
  • \n
  • \n

    \n S3 Object Ownership - If your CreateBucket\n request includes the x-amz-object-ownership header,\n s3:PutBucketOwnershipControls permission is required.

    \n
  • \n
\n
\n
\n

The following operations are related to CreateBucket:

\n ", + "smithy.api#documentation": "

Creates a new S3 bucket. To create a bucket, you must register with Amazon S3 and have a\n valid Amazon Web Services Access Key ID to authenticate requests. Anonymous requests are never allowed to\n create buckets. By creating the bucket, you become the bucket owner.

\n

Not every string is an acceptable bucket name. For information about bucket naming\n restrictions, see Bucket naming\n rules.

\n

If you want to create an Amazon S3 on Outposts bucket, see Create Bucket.

\n

By default, the bucket is created in the US East (N. Virginia) Region. You can\n optionally specify a Region in the request body. You might choose a Region to optimize\n latency, minimize costs, or address regulatory requirements. For example, if you reside in\n Europe, you will probably find it advantageous to create buckets in the Europe (Ireland)\n Region. For more information, see Accessing a\n bucket.

\n \n

If you send your create bucket request to the s3.amazonaws.com endpoint,\n the request goes to the us-east-1 Region. Accordingly, the signature calculations in\n Signature Version 4 must use us-east-1 as the Region, even if the location constraint in\n the request specifies another Region where the bucket is to be created. If you create a\n bucket in a Region other than US East (N. Virginia), your application must be able to\n handle 307 redirect. For more information, see Virtual hosting of\n buckets.

\n
\n
\n
Permissions
\n
\n

In addition to s3:CreateBucket, the following permissions are required when\n your CreateBucket request includes specific headers:

\n
    \n
  • \n

    \n Access control lists (ACLs) - If your CreateBucket request\n specifies access control list (ACL) permissions and the ACL is public-read, public-read-write,\n authenticated-read, or if you specify access permissions explicitly through any other\n ACL, both s3:CreateBucket and s3:PutBucketAcl permissions\n are needed. If the ACL for the CreateBucket request is private or if the request doesn't\n specify any ACLs, only s3:CreateBucket permission is needed.

    \n
  • \n
  • \n

    \n Object Lock - If ObjectLockEnabledForBucket is set to true in your\n CreateBucket request,\n s3:PutBucketObjectLockConfiguration and\n s3:PutBucketVersioning permissions are required.

    \n
  • \n
  • \n

    \n S3 Object Ownership - If your CreateBucket request includes the x-amz-object-ownership header, then the\n s3:PutBucketOwnershipControls permission is required. By default, ObjectOwnership is set to BucketOWnerEnforced and ACLs are disabled. We recommend keeping\n ACLs disabled, except in uncommon use cases where you must control access for each object individually. If you want to change the ObjectOwnership setting, you can use the \n x-amz-object-ownership header in your CreateBucket request to set the ObjectOwnership setting of your choice.\n For more information about S3 Object Ownership, see Controlling object\n ownership in the Amazon S3 User Guide.

    \n
  • \n
  • \n

    \n S3 Block Public Access - If your specific use case requires granting public access to your S3 resources, you can disable Block Public Access. You can create a new bucket with Block Public Access enabled, then separately call the \n DeletePublicAccessBlock\n API. To use this operation, you must have the\n s3:PutBucketPublicAccessBlock permission. By default, all Block\n Public Access settings are enabled for new buckets. To avoid inadvertent exposure of\n your resources, we recommend keeping the S3 Block Public Access settings enabled. For more information about S3 Block Public Access, see Blocking public\n access to your Amazon S3 storage in the Amazon S3 User Guide.

    \n
  • \n
\n
\n
\n \n

If your CreateBucket request sets BucketOwnerEnforced for Amazon S3 Object Ownership\n and specifies a bucket ACL that provides access to an external Amazon Web Services account, your request fails with a 400 error and returns the InvalidBucketAcLWithObjectOwnership error code. For more information,\n see Setting Object\n Ownership on an existing bucket in the Amazon S3 User Guide.

\n
\n

The following operations are related to CreateBucket:

\n ", + "smithy.api#examples": [ + { + "title": "To create a bucket ", + "documentation": "The following example creates a bucket.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Location": "/examplebucket" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}", @@ -21005,6 +22522,21 @@ }, "traits": { "smithy.api#documentation": "

This action initiates a multipart upload and returns an upload ID. This upload ID is\n used to associate all of the parts in the specific multipart upload. You specify this\n upload ID in each of your subsequent upload part requests (see UploadPart). You also include this\n upload ID in the final request to either complete or abort the multipart upload\n request.

\n

For more information about multipart uploads, see Multipart Upload Overview.

\n

If you have configured a lifecycle rule to abort incomplete multipart uploads, the\n upload must complete within the number of days specified in the bucket lifecycle\n configuration. Otherwise, the incomplete multipart upload becomes eligible for an abort\n action and Amazon S3 aborts the multipart upload. For more information, see Aborting Incomplete Multipart Uploads Using a Bucket Lifecycle Configuration.

\n

For information about the permissions required to use the multipart upload API, see\n Multipart\n Upload and Permissions.

\n

For request signing, multipart upload is just a series of regular requests. You initiate\n a multipart upload, send one or more requests to upload parts, and then complete the\n multipart upload process. You sign each request individually. There is nothing special\n about signing multipart upload requests. For more information about signing, see Authenticating Requests (Amazon Web Services Signature Version 4).

\n \n

After you initiate a multipart upload and upload one or more parts, to stop being\n charged for storing the uploaded parts, you must either complete or abort the multipart\n upload. Amazon S3 frees up the space used to store the parts and stop charging you for\n storing them only after you either complete or abort a multipart upload.

\n
\n

Server-side encryption is for data encryption at rest. Amazon S3 encrypts your data as it\n writes it to disks in its data centers and decrypts it when you access it. Amazon S3\n automatically encrypts all new objects that are uploaded to an S3 bucket. When doing a\n multipart upload, if you don't specify encryption information in your request, the\n encryption setting of the uploaded parts is set to the default encryption configuration of\n the destination bucket. By default, all buckets have a base level of encryption\n configuration that uses server-side encryption with Amazon S3 managed keys (SSE-S3). If the\n destination bucket has a default encryption configuration that uses server-side encryption\n with an Key Management Service (KMS) key (SSE-KMS), or a customer-provided encryption key (SSE-C),\n Amazon S3 uses the corresponding KMS key, or a customer-provided key to encrypt the uploaded\n parts. When you perform a CreateMultipartUpload operation, if you want to use a different\n type of encryption setting for the uploaded parts, you can request that Amazon S3 encrypts the\n object with a KMS key, an Amazon S3 managed key, or a customer-provided key. If the encryption\n setting in your request is different from the default encryption configuration of the\n destination bucket, the encryption setting in your request takes precedence. If you choose\n to provide your own encryption key, the request headers you provide in UploadPart\n and UploadPartCopy requests must match the headers you used in the request to\n initiate the upload by using CreateMultipartUpload. You can request that Amazon S3\n save the uploaded parts encrypted with server-side encryption with an Amazon S3 managed key\n (SSE-S3), an Key Management Service (KMS) key (SSE-KMS), or a customer-provided encryption key\n (SSE-C).

\n

To perform a multipart upload with encryption by using an Amazon Web Services KMS key, the requester\n must have permission to the kms:Decrypt and kms:GenerateDataKey*\n actions on the key. These permissions are required because Amazon S3 must decrypt and read data\n from the encrypted file parts before it completes the multipart upload. For more\n information, see Multipart upload API\n and permissions and Protecting data using\n server-side encryption with Amazon Web Services KMS in the\n Amazon S3 User Guide.

\n

If your Identity and Access Management (IAM) user or role is in the same Amazon Web Services account as the KMS key,\n then you must have these permissions on the key policy. If your IAM user or role belongs\n to a different account than the key, then you must have the permissions on both the key\n policy and your IAM user or role.

\n

For more information, see Protecting Data Using Server-Side\n Encryption.

\n
\n
Access Permissions
\n
\n

When copying an object, you can optionally specify the accounts or groups that\n should be granted specific permissions on the new object. There are two ways to\n grant the permissions using the request headers:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. For\n more information, see Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the\n x-amz-grant-read, x-amz-grant-read-acp,\n x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. These parameters map to\n the set of permissions that Amazon S3 supports in an ACL. For more information,\n see Access Control List (ACL) Overview.

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You\n cannot do both.

\n
\n
Server-Side- Encryption-Specific Request Headers
\n
\n

Amazon S3 encrypts data\n by using server-side encryption with an Amazon S3 managed key (SSE-S3) by default. Server-side encryption is for data encryption at rest. Amazon S3 encrypts\n your data as it writes it to disks in its data centers and decrypts it when you\n access it. You can request that Amazon S3 encrypts\n data at rest by using server-side encryption with other key options. The option you use depends on\n whether you want to use KMS keys (SSE-KMS) or provide your own encryption keys\n (SSE-C).

\n
    \n
  • \n

    Use KMS keys (SSE-KMS) that include the Amazon Web Services managed key\n (aws/s3) and KMS customer managed keys stored in Key Management Service (KMS) – If you\n want Amazon Web Services to manage the keys used to encrypt data, specify the following\n headers in the request.

    \n
      \n
    • \n

      \n x-amz-server-side-encryption\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-aws-kms-key-id\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-context\n

      \n
    • \n
    \n \n

    If you specify x-amz-server-side-encryption:aws:kms, but\n don't provide x-amz-server-side-encryption-aws-kms-key-id,\n Amazon S3 uses the Amazon Web Services managed key (aws/s3 key) in KMS to\n protect the data.

    \n
    \n \n

    All GET and PUT requests for an object protected\n by KMS fail if you don't make them by using Secure Sockets Layer (SSL),\n Transport Layer Security (TLS), or Signature Version 4.

    \n
    \n

    For more information about server-side encryption with KMS keys\n (SSE-KMS), see Protecting Data\n Using Server-Side Encryption with KMS keys.

    \n
  • \n
  • \n

    Use customer-provided encryption keys (SSE-C) – If you want to manage\n your own encryption keys, provide all the following headers in the\n request.

    \n
      \n
    • \n

      \n x-amz-server-side-encryption-customer-algorithm\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-customer-key\n

      \n
    • \n
    • \n

      \n x-amz-server-side-encryption-customer-key-MD5\n

      \n
    • \n
    \n

    For more information about server-side encryption with customer-provided\n encryption keys (SSE-C), see \n Protecting data using server-side encryption with customer-provided\n encryption keys (SSE-C).

    \n
  • \n
\n
\n
Access-Control-List (ACL)-Specific Request Headers
\n
\n

You also can use the following access control–related headers with this\n operation. By default, all objects are private. Only the owner has full access\n control. When adding a new object, you can grant permissions to individual\n Amazon Web Services accounts or to predefined groups defined by Amazon S3. These permissions are then\n added to the access control list (ACL) on the object. For more information, see\n Using ACLs. With this operation, you can grant access permissions\n using one of the following two methods:

\n
    \n
  • \n

    Specify a canned ACL (x-amz-acl) — Amazon S3 supports a set of\n predefined ACLs, known as canned ACLs. Each canned ACL\n has a predefined set of grantees and permissions. For more information, see\n Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly — To explicitly grant access\n permissions to specific Amazon Web Services accounts or groups, use the following headers.\n Each header maps to specific permissions that Amazon S3 supports in an ACL. For\n more information, see Access Control List (ACL)\n Overview. In the header, you specify a list of grantees who get\n the specific permission. To grant permissions explicitly, use:

    \n
      \n
    • \n

      \n x-amz-grant-read\n

      \n
    • \n
    • \n

      \n x-amz-grant-write\n

      \n
    • \n
    • \n

      \n x-amz-grant-read-acp\n

      \n
    • \n
    • \n

      \n x-amz-grant-write-acp\n

      \n
    • \n
    • \n

      \n x-amz-grant-full-control\n

      \n
    • \n
    \n

    You specify each grantee as a type=value pair, where the type is one of\n the following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID\n of an Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email\n address of an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants the Amazon Web Services accounts identified by account IDs permissions to read object data and its metadata:

    \n

    \n x-amz-grant-read: id=\"11112222333\", id=\"444455556666\" \n

    \n
  • \n
\n
\n
\n

The following operations are related to CreateMultipartUpload:

\n ", + "smithy.api#examples": [ + { + "title": "To initiate a multipart upload", + "documentation": "The following example initiates a multipart upload.", + "input": { + "Bucket": "examplebucket", + "Key": "largeobject" + }, + "output": { + "Bucket": "examplebucket", + "UploadId": "ibZBv_75gd9r8lH_gqXatLdxMVpAlj6ZQjEs.OwyF3953YdwbcQnMA2BLGn8Lx12fQNICtMw5KyteFeHw.Sjng--", + "Key": "largeobject" + } + } + ], "smithy.api#http": { "method": "POST", "uri": "/{Bucket}/{Key+}?uploads&x-id=CreateMultipartUpload", @@ -21051,7 +22583,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -21072,7 +22604,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -21087,7 +22619,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -21219,7 +22751,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -21261,7 +22793,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

Specifies the ID of the symmetric encryption customer managed key to use for object encryption.\n All GET and PUT requests for an object protected by Amazon Web Services KMS will fail if not made via SSL\n or using SigV4. For information about configuring using any of the officially supported\n Amazon Web Services SDKs and Amazon Web Services CLI, see Specifying the Signature Version in Request Authentication\n in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Specifies the ID of the symmetric encryption customer managed key to use for object encryption.\n All GET and PUT requests for an object protected by KMS will fail if they're not made via\n SSL or using SigV4. For information about configuring any of the officially supported Amazon Web Services\n SDKs and Amazon Web Services CLI, see Specifying the Signature Version in Request Authentication\n in the Amazon S3 User Guide.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -21276,7 +22808,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using AWS KMS (SSE-KMS). Setting this header to true\n causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with an object action doesn’t affect bucket-level settings for S3\n Bucket Key.

", + "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using Key Management Service (KMS) keys (SSE-KMS). Setting this header to\n true causes Amazon S3 to use an S3 Bucket Key for object encryption with\n SSE-KMS.

\n

Specifying this header with an object action doesn’t affect bucket-level settings for S3\n Bucket Key.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -21388,7 +22920,7 @@ "Objects": { "target": "com.amazonaws.s3#ObjectIdentifierList", "traits": { - "smithy.api#documentation": "

The objects to delete.

", + "smithy.api#documentation": "

The object to delete.

", "smithy.api#required": {}, "smithy.api#xmlFlattened": {}, "smithy.api#xmlName": "Object" @@ -21416,6 +22948,15 @@ }, "traits": { "smithy.api#documentation": "

Deletes the S3 bucket. All objects (including all object versions and delete markers) in\n the bucket must be deleted before the bucket itself can be deleted.

\n

The following operations are related to DeleteBucket:

\n ", + "smithy.api#examples": [ + { + "title": "To delete a bucket", + "documentation": "The following example deletes the specified bucket.", + "input": { + "Bucket": "forrandall2" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}", @@ -21483,7 +23024,16 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Deletes the cors configuration information set for the bucket.

\n

To use this operation, you must have permission to perform the\n s3:PutBucketCORS action. The bucket owner has this permission by default\n and can grant this permission to others.

\n

For information about cors, see Enabling Cross-Origin Resource Sharing in\n the Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketCors:

\n ", + "smithy.api#documentation": "

Deletes the cors configuration information set for the bucket.

\n

To use this operation, you must have permission to perform the\n s3:PutBucketCORS action. The bucket owner has this permission by default\n and can grant this permission to others.

\n

For information about cors, see Enabling Cross-Origin Resource Sharing in\n the Amazon S3 User Guide.

\n

\n Related Resources\n

\n ", + "smithy.api#examples": [ + { + "title": "To delete cors configuration on a bucket.", + "documentation": "The following example deletes CORS configuration on a bucket.", + "input": { + "Bucket": "examplebucket" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?cors", @@ -21665,6 +23215,15 @@ }, "traits": { "smithy.api#documentation": "

Deletes the lifecycle configuration from the specified bucket. Amazon S3 removes all the\n lifecycle configuration rules in the lifecycle subresource associated with the bucket. Your\n objects never expire, and Amazon S3 no longer automatically deletes any objects on the basis of\n rules contained in the deleted lifecycle configuration.

\n

To use this operation, you must have permission to perform the\n s3:PutLifecycleConfiguration action. By default, the bucket owner has this\n permission and the bucket owner can grant this permission to others.

\n

There is usually some time lag before lifecycle configuration deletion is fully\n propagated to all the Amazon S3 systems.

\n

For more information about the object expiration, see Elements to Describe Lifecycle Actions.

\n

Related actions include:

\n ", + "smithy.api#examples": [ + { + "title": "To delete lifecycle configuration on a bucket.", + "documentation": "The following example deletes lifecycle configuration on a bucket.", + "input": { + "Bucket": "examplebucket" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?lifecycle", @@ -21802,6 +23361,15 @@ }, "traits": { "smithy.api#documentation": "

This implementation of the DELETE action uses the policy subresource to delete the\n policy of a specified bucket. If you are using an identity other than the root user of the\n Amazon Web Services account that owns the bucket, the calling identity must have the\n DeleteBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account to use this operation.

\n

If you don't have DeleteBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

To ensure that bucket owners don't inadvertently lock themselves out of their own\n buckets, the root principal in a bucket owner's Amazon Web Services account can perform the\n GetBucketPolicy, PutBucketPolicy, and\n DeleteBucketPolicy API actions, even if their bucket policy explicitly\n denies the root principal's access. Bucket owner root principals can only be blocked from performing \n these API actions by VPC endpoint policies and Amazon Web Services Organizations policies.

\n
\n

For more information about bucket policies, see Using Bucket Policies and\n UserPolicies.

\n

The following operations are related to DeleteBucketPolicy\n

\n ", + "smithy.api#examples": [ + { + "title": "To delete bucket policy", + "documentation": "The following example deletes bucket policy on the specified bucket.", + "input": { + "Bucket": "examplebucket" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?policy", @@ -21845,6 +23413,15 @@ }, "traits": { "smithy.api#documentation": "

Deletes the replication configuration from the bucket.

\n

To use this operation, you must have permissions to perform the\n s3:PutReplicationConfiguration action. The bucket owner has these\n permissions by default and can grant it to others. For more information about permissions,\n see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n \n

It can take a while for the deletion of a replication configuration to fully\n propagate.

\n
\n

For information about replication configuration, see Replication in the\n Amazon S3 User Guide.

\n

The following operations are related to DeleteBucketReplication:

\n ", + "smithy.api#examples": [ + { + "title": "To delete bucket replication configuration", + "documentation": "The following example deletes replication configuration set on bucket.", + "input": { + "Bucket": "example" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?replication", @@ -21914,6 +23491,15 @@ }, "traits": { "smithy.api#documentation": "

Deletes the tags from the bucket.

\n

To use this operation, you must have permission to perform the\n s3:PutBucketTagging action. By default, the bucket owner has this\n permission and can grant this permission to others.

\n

The following operations are related to DeleteBucketTagging:

\n ", + "smithy.api#examples": [ + { + "title": "To delete bucket tags", + "documentation": "The following example deletes bucket tags.", + "input": { + "Bucket": "examplebucket" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?tagging", @@ -21957,6 +23543,15 @@ }, "traits": { "smithy.api#documentation": "

This action removes the website configuration for a bucket. Amazon S3 returns a 200\n OK response upon successfully deleting a website configuration on the specified\n bucket. You will get a 200 OK response if the website configuration you are\n trying to delete does not exist on the bucket. Amazon S3 returns a 404 response if\n the bucket specified in the request does not exist.

\n

This DELETE action requires the S3:DeleteBucketWebsite permission. By\n default, only the bucket owner can delete the website configuration attached to a bucket.\n However, bucket owners can grant other users permission to delete the website configuration\n by writing a bucket policy granting them the S3:DeleteBucketWebsite\n permission.

\n

For more information about hosting websites, see Hosting Websites on Amazon S3.

\n

The following operations are related to DeleteBucketWebsite:

\n ", + "smithy.api#examples": [ + { + "title": "To delete bucket website configuration", + "documentation": "The following example deletes bucket website configuration.", + "input": { + "Bucket": "examplebucket" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}?website", @@ -22085,6 +23680,17 @@ }, "traits": { "smithy.api#documentation": "

Removes the null version (if there is one) of an object and inserts a delete marker,\n which becomes the latest version of the object. If there isn't a null version, Amazon S3 does\n not remove any objects but will still respond that the command was successful.

\n

To remove a specific version, you must use the version Id subresource. Using this\n subresource permanently deletes the version. If the object deleted is a delete marker, Amazon S3\n sets the response header, x-amz-delete-marker, to true.

\n

If the object you want to delete is in a bucket where the bucket versioning\n configuration is MFA Delete enabled, you must include the x-amz-mfa request\n header in the DELETE versionId request. Requests that include\n x-amz-mfa must use HTTPS.

\n

For more information about MFA Delete, see Using MFA Delete. To see sample\n requests that use versioning, see Sample\n Request.

\n

You can delete objects by explicitly calling DELETE Object or configure its lifecycle\n (PutBucketLifecycle) to enable Amazon S3 to remove them for you. If you want to block\n users or accounts from removing or deleting objects from your bucket, you must deny them\n the s3:DeleteObject, s3:DeleteObjectVersion, and\n s3:PutLifeCycleConfiguration actions.

\n

The following action is related to DeleteObject:

\n ", + "smithy.api#examples": [ + { + "title": "To delete an object", + "documentation": "The following example deletes an object from an S3 bucket.", + "input": { + "Bucket": "examplebucket", + "Key": "objectkey.jpg" + }, + "output": {} + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}/{Key+}?x-id=DeleteObject", @@ -22193,6 +23799,20 @@ }, "traits": { "smithy.api#documentation": "

Removes the entire tag set from the specified object. For more information about\n managing object tags, see Object Tagging.

\n

To use this operation, you must have permission to perform the\n s3:DeleteObjectTagging action.

\n

To delete tags of a specific object version, add the versionId query\n parameter in the request. You will need permission for the\n s3:DeleteObjectVersionTagging action.

\n

The following operations are related to DeleteObjectTagging:

\n ", + "smithy.api#examples": [ + { + "title": "To remove tag set from an object version", + "documentation": "The following example removes tag set associated with the specified object version. The request specifies both the object key and object version.", + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg", + "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" + }, + "output": { + "VersionId": "ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI" + } + } + ], "smithy.api#http": { "method": "DELETE", "uri": "/{Bucket}/{Key+}?tagging", @@ -22551,7 +24171,7 @@ "KMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If the encryption type is aws:kms, this optional value specifies the ID of\n the symmetric encryption customer managed key to use for encryption of job results. Amazon S3 only\n supports symmetric encryption KMS keys. For more information, see Asymmetric keys in Amazon Web Services KMS in the Amazon Web Services Key Management Service\n Developer Guide.

" + "smithy.api#documentation": "

If the encryption type is aws:kms, this optional value specifies the ID of\n the symmetric encryption customer managed key to use for encryption of job results. Amazon S3 only\n supports symmetric encryption KMS keys. For more information, see Asymmetric keys in KMS in the Amazon Web Services Key Management Service\n Developer Guide.

" } }, "KMSContext": { @@ -23024,6 +24644,12 @@ "traits": { "smithy.api#documentation": "

The accelerate configuration of the bucket.

" } + }, + "RequestCharged": { + "target": "com.amazonaws.s3#RequestCharged", + "traits": { + "smithy.api#httpHeader": "x-amz-request-charged" + } } }, "traits": { @@ -23051,6 +24677,12 @@ "smithy.api#documentation": "

The account ID of the expected bucket owner. If the bucket is owned by a different account, the request fails with the HTTP status code 403 Forbidden (access denied).

", "smithy.api#httpHeader": "x-amz-expected-bucket-owner" } + }, + "RequestPayer": { + "target": "com.amazonaws.s3#RequestPayer", + "traits": { + "smithy.api#httpHeader": "x-amz-request-payer" + } } }, "traits": { @@ -23198,6 +24830,31 @@ }, "traits": { "smithy.api#documentation": "

Returns the Cross-Origin Resource Sharing (CORS) configuration information set for the\n bucket.

\n

To use this operation, you must have permission to perform the\n s3:GetBucketCORS action. By default, the bucket owner has this permission\n and can grant it to others.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n

For more information about CORS, see Enabling Cross-Origin Resource\n Sharing.

\n

The following operations are related to GetBucketCors:

\n ", + "smithy.api#examples": [ + { + "title": "To get cors configuration set on a bucket", + "documentation": "The following example returns cross-origin resource sharing (CORS) configuration set on a bucket.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "CORSRules": [ + { + "AllowedHeaders": [ + "Authorization" + ], + "MaxAgeSeconds": 3000, + "AllowedMethods": [ + "GET" + ], + "AllowedOrigins": [ + "*" + ] + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?cors", @@ -23257,7 +24914,7 @@ "target": "com.amazonaws.s3#GetBucketEncryptionOutput" }, "traits": { - "smithy.api#documentation": "

Returns the default encryption configuration for an Amazon S3 bucket. By default, all buckets have a default encryption configuration that\n uses server-side encryption with Amazon S3 managed keys (SSE-S3). For information\n about the bucket default encryption feature, see Amazon S3 Bucket\n Default Encryption in the Amazon S3 User Guide.

\n

To use this operation, you must have permission to perform the\n s3:GetEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

The following operations are related to GetBucketEncryption:

\n ", + "smithy.api#documentation": "

Returns the default encryption configuration for an Amazon S3 bucket. By default, all buckets\n have a default encryption configuration that uses server-side encryption with Amazon S3 managed\n keys (SSE-S3). For information about the bucket default encryption feature, see Amazon S3 Bucket\n Default Encryption in the Amazon S3 User Guide.

\n

To use this operation, you must have permission to perform the\n s3:GetEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

The following operations are related to GetBucketEncryption:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?encryption", @@ -23440,6 +25097,30 @@ }, "traits": { "smithy.api#documentation": "\n

Bucket lifecycle configuration now supports specifying a lifecycle rule using an\n object key name prefix, one or more object tags, or a combination of both. Accordingly,\n this section describes the latest API. The response describes the new filter element\n that you can use to specify a filter to select a subset of objects to which the rule\n applies. If you are using a previous version of the lifecycle configuration, it still\n works. For the earlier action, see GetBucketLifecycle.

\n
\n

Returns the lifecycle configuration information set on the bucket. For information about\n lifecycle configuration, see Object Lifecycle\n Management.

\n

To use this operation, you must have permission to perform the\n s3:GetLifecycleConfiguration action. The bucket owner has this permission,\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

\n GetBucketLifecycleConfiguration has the following special error:

\n
    \n
  • \n

    Error code: NoSuchLifecycleConfiguration\n

    \n
      \n
    • \n

      Description: The lifecycle configuration does not exist.

      \n
    • \n
    • \n

      HTTP Status Code: 404 Not Found

      \n
    • \n
    • \n

      SOAP Fault Code Prefix: Client

      \n
    • \n
    \n
  • \n
\n

The following operations are related to\n GetBucketLifecycleConfiguration:

\n ", + "smithy.api#examples": [ + { + "title": "To get lifecycle configuration on a bucket", + "documentation": "The following example retrieves lifecycle configuration on set on a bucket. ", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Rules": [ + { + "Prefix": "TaxDocs", + "Status": "Enabled", + "Transitions": [ + { + "Days": 365, + "StorageClass": "STANDARD_IA" + } + ], + "ID": "Rule for TaxDocs/" + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?lifecycle", @@ -23501,6 +25182,18 @@ "traits": { "aws.customizations#s3UnwrappedXmlOutput": {}, "smithy.api#documentation": "

Returns the Region the bucket resides in. You set the bucket's Region using the\n LocationConstraint request parameter in a CreateBucket\n request. For more information, see CreateBucket.

\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n \n

We recommend that you use HeadBucket to return the Region\n that a bucket resides in. For backward compatibility, Amazon S3 continues to support\n GetBucketLocation.

\n
\n

The following operations are related to GetBucketLocation:

\n ", + "smithy.api#examples": [ + { + "title": "To get bucket location", + "documentation": "The following example returns bucket location.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "LocationConstraint": "us-west-2" + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?location", @@ -23781,6 +25474,18 @@ }, "traits": { "smithy.api#documentation": "

Returns the policy of a specified bucket. If you are using an identity other than the\n root user of the Amazon Web Services account that owns the bucket, the calling identity must have the\n GetBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account in order to use this operation.

\n

If you don't have GetBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

To ensure that bucket owners don't inadvertently lock themselves out of their own\n buckets, the root principal in a bucket owner's Amazon Web Services account can perform the\n GetBucketPolicy, PutBucketPolicy, and\n DeleteBucketPolicy API actions, even if their bucket policy explicitly\n denies the root principal's access. Bucket owner root principals can only be blocked from performing \n these API actions by VPC endpoint policies and Amazon Web Services Organizations policies.

\n
\n

To use this API operation against an access point, provide the alias of the access point in place of the bucket name.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

\n

For more information about bucket policies, see Using Bucket Policies and User\n Policies.

\n

The following action is related to GetBucketPolicy:

\n ", + "smithy.api#examples": [ + { + "title": "To get bucket policy", + "documentation": "The following example returns bucket policy associated with a bucket.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Policy": "{\"Version\":\"2008-10-17\",\"Id\":\"LogPolicy\",\"Statement\":[{\"Sid\":\"Enables the log delivery group to publish logs to your bucket \",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"111122223333\"},\"Action\":[\"s3:GetBucketAcl\",\"s3:GetObjectAcl\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::policytest1/*\",\"arn:aws:s3:::policytest1\"]}]}" + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?policy", @@ -23897,6 +25602,30 @@ }, "traits": { "smithy.api#documentation": "

Returns the replication configuration of a bucket.

\n \n

It can take a while to propagate the put or delete a replication configuration to\n all Amazon S3 systems. Therefore, a get request soon after put or delete can return a wrong\n result.

\n
\n

For information about replication configuration, see Replication in the\n Amazon S3 User Guide.

\n

This action requires permissions for the s3:GetReplicationConfiguration\n action. For more information about permissions, see Using Bucket Policies and User\n Policies.

\n

If you include the Filter element in a replication configuration, you must\n also include the DeleteMarkerReplication and Priority elements.\n The response also returns those elements.

\n

For information about GetBucketReplication errors, see List of\n replication-related error codes\n

\n

The following operations are related to GetBucketReplication:

\n ", + "smithy.api#examples": [ + { + "title": "To get replication configuration set on a bucket", + "documentation": "The following example returns replication configuration set on a bucket.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "ReplicationConfiguration": { + "Rules": [ + { + "Status": "Enabled", + "Prefix": "Tax", + "Destination": { + "Bucket": "arn:aws:s3:::destination-bucket" + }, + "ID": "MWIwNTkwZmItMTE3MS00ZTc3LWJkZDEtNzRmODQwYzc1OTQy" + } + ], + "Role": "arn:aws:iam::acct-id:role/example-role" + } + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?replication", @@ -23954,6 +25683,18 @@ }, "traits": { "smithy.api#documentation": "

Returns the request payment configuration of a bucket. To use this version of the\n operation, you must be the bucket owner. For more information, see Requester Pays\n Buckets.

\n

The following operations are related to GetBucketRequestPayment:

\n ", + "smithy.api#examples": [ + { + "title": "To get bucket versioning configuration", + "documentation": "The following example retrieves bucket versioning configuration.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Payer": "BucketOwner" + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?requestPayment", @@ -24012,6 +25753,27 @@ }, "traits": { "smithy.api#documentation": "

Returns the tag set associated with the bucket.

\n

To use this operation, you must have permission to perform the\n s3:GetBucketTagging action. By default, the bucket owner has this\n permission and can grant this permission to others.

\n

\n GetBucketTagging has the following special error:

\n
    \n
  • \n

    Error code: NoSuchTagSet\n

    \n
      \n
    • \n

      Description: There is no tag set associated with the bucket.

      \n
    • \n
    \n
  • \n
\n

The following operations are related to GetBucketTagging:

\n ", + "smithy.api#examples": [ + { + "title": "To get tag set associated with a bucket", + "documentation": "The following example returns tag set associated with a bucket", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "TagSet": [ + { + "Value": "value1", + "Key": "key1" + }, + { + "Value": "value2", + "Key": "key2" + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?tagging", @@ -24071,6 +25833,19 @@ }, "traits": { "smithy.api#documentation": "

Returns the versioning state of a bucket.

\n

To retrieve the versioning state of a bucket, you must be the bucket owner.

\n

This implementation also returns the MFA Delete status of the versioning state. If the\n MFA Delete status is enabled, the bucket owner must use an authentication\n device to change the versioning state of the bucket.

\n

The following operations are related to GetBucketVersioning:

\n ", + "smithy.api#examples": [ + { + "title": "To get bucket versioning configuration", + "documentation": "The following example retrieves bucket versioning configuration.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "Status": "Enabled", + "MFADelete": "Disabled" + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?versioning", @@ -24136,6 +25911,23 @@ }, "traits": { "smithy.api#documentation": "

Returns the website configuration for a bucket. To host website on Amazon S3, you can\n configure a bucket as website by adding a website configuration. For more information about\n hosting websites, see Hosting Websites on Amazon S3.

\n

This GET action requires the S3:GetBucketWebsite permission. By default,\n only the bucket owner can read the bucket website configuration. However, bucket owners can\n allow other users to read the website configuration by writing a bucket policy granting\n them the S3:GetBucketWebsite permission.

\n

The following operations are related to GetBucketWebsite:

\n ", + "smithy.api#examples": [ + { + "title": "To get bucket website configuration", + "documentation": "The following example retrieves website configuration of a bucket.", + "input": { + "Bucket": "examplebucket" + }, + "output": { + "IndexDocument": { + "Suffix": "index.html" + }, + "ErrorDocument": { + "Key": "error.html" + } + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?website", @@ -24228,7 +26020,7 @@ "SHA1" ] }, - "smithy.api#documentation": "

Retrieves objects from Amazon S3. To use GET, you must have READ\n access to the object. If you grant READ access to the anonymous user, you can\n return the object without using an authorization header.

\n

An Amazon S3 bucket has no directory hierarchy such as you would find in a typical computer\n file system. You can, however, create a logical hierarchy by using object key names that\n imply a folder structure. For example, instead of naming an object sample.jpg,\n you can name it photos/2006/February/sample.jpg.

\n

To get an object from such a logical hierarchy, specify the full key name for the object\n in the GET operation. For a virtual hosted-style request example, if you have\n the object photos/2006/February/sample.jpg, specify the resource as\n /photos/2006/February/sample.jpg. For a path-style request example, if you\n have the object photos/2006/February/sample.jpg in the bucket named\n examplebucket, specify the resource as\n /examplebucket/photos/2006/February/sample.jpg. For more information about\n request types, see HTTP Host\n Header Bucket Specification.

\n

For more information about returning the ACL of an object, see GetObjectAcl.

\n

If the object you are retrieving is stored in the S3 Glacier or\n S3 Glacier Deep Archive storage class, or S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers, before you can retrieve the object you must first restore a\n copy using RestoreObject. Otherwise, this action returns an\n InvalidObjectState error. For information about restoring archived objects,\n see Restoring\n Archived Objects.

\n

Encryption request headers, like x-amz-server-side-encryption, should not\n be sent for GET requests if your object uses server-side encryption with KMS keys\n (SSE-KMS) or server-side encryption with Amazon S3–managed encryption keys (SSE-S3). If your\n object does use these types of keys, you’ll get an HTTP 400 BadRequest error.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you GET the object,\n you must use the following headers:

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys).

\n

Assuming you have the relevant permission to read object tags, the response also returns\n the x-amz-tagging-count header that provides the count of number of tags\n associated with the object. You can use GetObjectTagging to retrieve\n the tag set associated with an object.

\n
\n
Permissions
\n
\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Specifying Permissions in a\n Policy. If the object you request does not exist, the error Amazon S3 returns depends\n on whether you also have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 will\n return an HTTP status code 404 (\"no such key\") error.

    \n
  • \n
  • \n

    If you don’t have the s3:ListBucket permission, Amazon S3 will return an\n HTTP status code 403 (\"access denied\") error.

    \n
  • \n
\n
\n
Versioning
\n
\n

By default, the GET action returns the current version of an object. To return a\n different version, use the versionId subresource.

\n \n
    \n
  • \n

    If you supply a versionId, you need the\n s3:GetObjectVersion permission to access a specific version of an\n object. If you request a specific version, you do not need to have the\n s3:GetObject permission. If you request the current version\n without a specific version ID, only s3:GetObject permission is\n required. s3:GetObjectVersion permission won't be required.

    \n
  • \n
  • \n

    If the current version of the object is a delete marker, Amazon S3 behaves as if the\n object was deleted and includes x-amz-delete-marker: true in the\n response.

    \n
  • \n
\n
\n

For more information about versioning, see PutBucketVersioning.

\n
\n
Overriding Response Header Values
\n
\n

There are times when you want to override certain response header values in a GET\n response. For example, you might override the Content-Disposition response\n header value in your GET request.

\n

You can override values for a set of response headers using the following query\n parameters. These response header values are sent only on a successful request, that is,\n when status code 200 OK is returned. The set of headers you can override using these\n parameters is a subset of the headers that Amazon S3 accepts when you create an object. The\n response headers that you can override for the GET response are Content-Type,\n Content-Language, Expires, Cache-Control,\n Content-Disposition, and Content-Encoding. To override these\n header values in the GET response, you use the following request parameters.

\n \n

You must sign the request, either using an Authorization header or a presigned URL,\n when using these parameters. They cannot be used with an unsigned (anonymous)\n request.

\n
\n
    \n
  • \n

    \n response-content-type\n

    \n
  • \n
  • \n

    \n response-content-language\n

    \n
  • \n
  • \n

    \n response-expires\n

    \n
  • \n
  • \n

    \n response-cache-control\n

    \n
  • \n
  • \n

    \n response-content-disposition\n

    \n
  • \n
  • \n

    \n response-content-encoding\n

    \n
  • \n
\n
\n
Overriding Response Header Values
\n
\n

If both of the If-Match and If-Unmodified-Since headers are\n present in the request as follows: If-Match condition evaluates to\n true, and; If-Unmodified-Since condition evaluates to\n false; then, S3 returns 200 OK and the data requested.

\n

If both of the If-None-Match and If-Modified-Since headers are\n present in the request as follows: If-None-Match condition evaluates to\n false, and; If-Modified-Since condition evaluates to\n true; then, S3 returns 304 Not Modified response code.

\n

For more information about conditional requests, see RFC 7232.

\n
\n
\n

The following operations are related to GetObject:

\n ", + "smithy.api#documentation": "

Retrieves objects from Amazon S3. To use GET, you must have READ\n access to the object. If you grant READ access to the anonymous user, you can\n return the object without using an authorization header.

\n

An Amazon S3 bucket has no directory hierarchy such as you would find in a typical computer\n file system. You can, however, create a logical hierarchy by using object key names that\n imply a folder structure. For example, instead of naming an object sample.jpg,\n you can name it photos/2006/February/sample.jpg.

\n

To get an object from such a logical hierarchy, specify the full key name for the object\n in the GET operation. For a virtual hosted-style request example, if you have\n the object photos/2006/February/sample.jpg, specify the resource as\n /photos/2006/February/sample.jpg. For a path-style request example, if you\n have the object photos/2006/February/sample.jpg in the bucket named\n examplebucket, specify the resource as\n /examplebucket/photos/2006/February/sample.jpg. For more information about\n request types, see HTTP Host\n Header Bucket Specification.

\n

For more information about returning the ACL of an object, see GetObjectAcl.

\n

If the object you are retrieving is stored in the S3 Glacier Flexible Retrieval or\n S3 Glacier Deep Archive storage class, or S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers, before you can retrieve the object you must first restore a\n copy using RestoreObject. Otherwise, this action returns an\n InvalidObjectState error. For information about restoring archived objects,\n see Restoring\n Archived Objects.

\n

Encryption request headers, like x-amz-server-side-encryption, should not\n be sent for GET requests if your object uses server-side encryption with Key Management Service (KMS)\n keys (SSE-KMS), dual-layer server-side encryption with Amazon Web Services KMS keys (DSSE-KMS), or\n server-side encryption with Amazon S3 managed encryption keys (SSE-S3). If your object does use\n these types of keys, you’ll get an HTTP 400 Bad Request error.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you GET the object,\n you must use the following headers:

\n
    \n
  • \n

    \n x-amz-server-side-encryption-customer-algorithm\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key-MD5\n

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys).

\n

Assuming you have the relevant permission to read object tags, the response also returns\n the x-amz-tagging-count header that provides the count of number of tags\n associated with the object. You can use GetObjectTagging to retrieve\n the tag set associated with an object.

\n
\n
Permissions
\n
\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Specifying Permissions in a\n Policy. If the object that you request doesn’t exist, the error that Amazon S3 returns depends\n on whether you also have the s3:ListBucket permission.

\n

If you have the s3:ListBucket permission on the bucket, Amazon S3\n returns an HTTP status code 404 (Not Found) error.

\n

If you don’t have the s3:ListBucket permission, Amazon S3 returns an\n HTTP status code 403 (\"access denied\") error.

\n
\n
Versioning
\n
\n

By default, the GET action returns the current version of an object. To return a\n different version, use the versionId subresource.

\n \n
    \n
  • \n

    If you supply a versionId, you need the\n s3:GetObjectVersion permission to access a specific version of an\n object. If you request a specific version, you do not need to have the\n s3:GetObject permission. If you request the current version\n without a specific version ID, only s3:GetObject permission is\n required. s3:GetObjectVersion permission won't be required.

    \n
  • \n
  • \n

    If the current version of the object is a delete marker, Amazon S3 behaves as if the\n object was deleted and includes x-amz-delete-marker: true in the\n response.

    \n
  • \n
\n
\n

For more information about versioning, see PutBucketVersioning.

\n
\n
Overriding Response Header Values
\n
\n

There are times when you want to override certain response header values in a GET\n response. For example, you might override the Content-Disposition response\n header value in your GET request.

\n

You can override values for a set of response headers using the following query\n parameters. These response header values are sent only on a successful request, that is,\n when status code 200 OK is returned. The set of headers you can override using these\n parameters is a subset of the headers that Amazon S3 accepts when you create an object. The\n response headers that you can override for the GET response are Content-Type,\n Content-Language, Expires, Cache-Control,\n Content-Disposition, and Content-Encoding. To override these\n header values in the GET response, you use the following request parameters.

\n \n

You must sign the request, either using an Authorization header or a presigned URL,\n when using these parameters. They cannot be used with an unsigned (anonymous)\n request.

\n
\n
    \n
  • \n

    \n response-content-type\n

    \n
  • \n
  • \n

    \n response-content-language\n

    \n
  • \n
  • \n

    \n response-expires\n

    \n
  • \n
  • \n

    \n response-cache-control\n

    \n
  • \n
  • \n

    \n response-content-disposition\n

    \n
  • \n
  • \n

    \n response-content-encoding\n

    \n
  • \n
\n
\n
Overriding Response Header Values
\n
\n

If both of the If-Match and If-Unmodified-Since headers are\n present in the request as follows: If-Match condition evaluates to\n true, and; If-Unmodified-Since condition evaluates to\n false; then, S3 returns 200 OK and the data requested.

\n

If both of the If-None-Match and If-Modified-Since headers are\n present in the request as follows: If-None-Match condition evaluates to\n false, and; If-Modified-Since condition evaluates to\n true; then, S3 returns 304 Not Modified response code.

\n

For more information about conditional requests, see RFC 7232.

\n
\n
\n

The following operations are related to GetObject:

\n ", "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?x-id=GetObject", @@ -24251,6 +26043,56 @@ ], "traits": { "smithy.api#documentation": "

Returns the access control list (ACL) of an object. To use this operation, you must have\n s3:GetObjectAcl permissions or READ_ACP access to the object.\n For more information, see Mapping of ACL permissions and access policy permissions in the Amazon S3\n User Guide\n

\n

This action is not supported by Amazon S3 on Outposts.

\n

By default, GET returns ACL information about the current version of an object. To\n return ACL information about a different version, use the versionId subresource.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership,\n requests to read ACLs are still supported and return the\n bucket-owner-full-control ACL with the owner being the account that\n created the bucket. For more information, see Controlling object\n ownership and disabling ACLs in the\n Amazon S3 User Guide.

\n
\n

The following operations are related to GetObjectAcl:

\n ", + "smithy.api#examples": [ + { + "title": "To retrieve object ACL", + "documentation": "The following example retrieves access control list (ACL) of an object.", + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": { + "Owner": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Grants": [ + { + "Grantee": { + "Type": "CanonicalUser", + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Permission": "WRITE" + }, + { + "Grantee": { + "Type": "CanonicalUser", + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Permission": "WRITE_ACP" + }, + { + "Grantee": { + "Type": "CanonicalUser", + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Permission": "READ" + }, + { + "Grantee": { + "Type": "CanonicalUser", + "DisplayName": "owner-display-name", + "ID": "852b113eexamplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "Permission": "READ_ACP" + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?acl", @@ -24864,7 +26706,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -24892,7 +26734,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -24900,7 +26742,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with Amazon Web Services\n KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with\n Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -25221,6 +27063,29 @@ }, "traits": { "smithy.api#documentation": "

Returns the tag-set of an object. You send the GET request against the tagging\n subresource associated with the object.

\n

To use this operation, you must have permission to perform the\n s3:GetObjectTagging action. By default, the GET action returns information\n about current version of an object. For a versioned bucket, you can have multiple versions\n of an object in your bucket. To retrieve tags of any other version, use the versionId query\n parameter. You also need permission for the s3:GetObjectVersionTagging\n action.

\n

By default, the bucket owner has this permission and can grant this permission to\n others.

\n

For information about the Amazon S3 object tagging feature, see Object Tagging.

\n

The following actions are related to GetObjectTagging:

\n ", + "smithy.api#examples": [ + { + "title": "To retrieve tag set of an object", + "documentation": "The following example retrieves tag set of an object.", + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": { + "VersionId": "null", + "TagSet": [ + { + "Value": "Value4", + "Key": "Key4" + }, + { + "Value": "Value3", + "Key": "Key3" + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?tagging", @@ -25308,6 +27173,17 @@ }, "traits": { "smithy.api#documentation": "

Returns torrent files from a bucket. BitTorrent can save you bandwidth when you're\n distributing large files.

\n \n

You can get torrent only for objects that are less than 5 GB in size, and that are\n not encrypted using server-side encryption with a customer-provided encryption\n key.

\n
\n

To use GET, you must have READ access to the object.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following action is related to GetObjectTorrent:

\n ", + "smithy.api#examples": [ + { + "title": "To retrieve torrent files for an object", + "documentation": "The following example retrieves torrent files of an object.", + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg" + }, + "output": {} + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}/{Key+}?torrent", @@ -25554,6 +27430,15 @@ ], "traits": { "smithy.api#documentation": "

This action is useful to determine if a bucket exists and you have permission to access\n it. The action returns a 200 OK if the bucket exists and you have permission\n to access it.

\n

If the bucket does not exist or you do not have permission to access it, the\n HEAD request returns a generic 400 Bad Request, 403\n Forbidden or 404 Not Found code. A message body is not included, so\n you cannot determine the exception beyond these error codes.

\n

To use this operation, you must have permissions to perform the\n s3:ListBucket action. The bucket owner has this permission by default and\n can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

To use this API operation against an access point, you must provide the alias of the access point in place of the\n bucket name or specify the access point ARN. When using the access point ARN, you must direct requests to\n the access point hostname. The access point hostname takes the form\n AccessPointName-AccountId.s3-accesspoint.Region.amazonaws.com.\n When using the Amazon Web Services SDKs, you provide the ARN in place of the bucket name. For more\n information, see Using access points.

\n

To use this API operation against an Object Lambda access point, provide the alias of the Object Lambda access point in place of the bucket name. \nIf the Object Lambda access point alias in a request is not valid, the error code InvalidAccessPointAliasError is returned. \nFor more information about InvalidAccessPointAliasError, see List of\n Error Codes.

", + "smithy.api#examples": [ + { + "title": "To determine if bucket exists", + "documentation": "This operation checks to see if a bucket exists.", + "input": { + "Bucket": "acl1" + } + } + ], "smithy.api#http": { "method": "HEAD", "uri": "/{Bucket}", @@ -25631,7 +27516,7 @@ } ], "traits": { - "smithy.api#documentation": "

The HEAD action retrieves metadata from an object without returning the object itself.\n This action is useful if you're only interested in an object's metadata. To use HEAD, you\n must have READ access to the object.

\n

A HEAD request has the same options as a GET action on an\n object. The response is identical to the GET response except that there is no\n response body. Because of this, if the HEAD request generates an error, it\n returns a generic 400 Bad Request, 403 Forbidden or 404 Not\n Found code. It is not possible to retrieve the exact exception beyond these error\n codes.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you retrieve the\n metadata from the object, you must use the following headers:

\n
    \n
  • \n

    x-amz-server-side-encryption-customer-algorithm

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key

    \n
  • \n
  • \n

    x-amz-server-side-encryption-customer-key-MD5

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys).

\n \n
    \n
  • \n

    Encryption request headers, like x-amz-server-side-encryption,\n should not be sent for GET requests if your object uses server-side encryption\n with KMS keys (SSE-KMS) or server-side encryption with Amazon S3–managed encryption\n keys (SSE-S3). If your object does use these types of keys, you’ll get an HTTP 400\n BadRequest error.

    \n
  • \n
  • \n

    The last modified property in this case is the creation date of the\n object.

    \n
  • \n
\n
\n

Request headers are limited to 8 KB in size. For more information, see Common\n Request Headers.

\n

Consider the following when using request headers:

\n
    \n
  • \n

    Consideration 1 – If both of the If-Match and\n If-Unmodified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-Match condition evaluates to true, and;

      \n
    • \n
    • \n

      \n If-Unmodified-Since condition evaluates to\n false;

      \n
    • \n
    \n

    Then Amazon S3 returns 200 OK and the data requested.

    \n
  • \n
  • \n

    Consideration 2 – If both of the If-None-Match and\n If-Modified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-None-Match condition evaluates to false,\n and;

      \n
    • \n
    • \n

      \n If-Modified-Since condition evaluates to\n true;

      \n
    • \n
    \n

    Then Amazon S3 returns the 304 Not Modified response code.

    \n
  • \n
\n

For more information about conditional requests, see RFC 7232.

\n
\n
Permissions
\n
\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Actions, resources, and condition keys for Amazon S3. \n If the object you request does not exist, the error Amazon S3 returns depends\n on whether you also have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 returns\n an HTTP status code 404 (\"no such key\") error.

    \n
  • \n
  • \n

    If you don’t have the s3:ListBucket permission, Amazon S3 returns an HTTP\n status code 403 (\"access denied\") error.

    \n
  • \n
\n
\n
\n

The following actions are related to HeadObject:

\n ", + "smithy.api#documentation": "

The HEAD action retrieves metadata from an object without returning the object itself.\n This action is useful if you're only interested in an object's metadata. To use HEAD, you\n must have READ access to the object.

\n

A HEAD request has the same options as a GET action on an\n object. The response is identical to the GET response except that there is no\n response body. Because of this, if the HEAD request generates an error, it\n returns a generic 400 Bad Request, 403 Forbidden or 404 Not\n Found code. It is not possible to retrieve the exact exception beyond these error\n codes.

\n

If you encrypt an object by using server-side encryption with customer-provided\n encryption keys (SSE-C) when you store the object in Amazon S3, then when you retrieve the\n metadata from the object, you must use the following headers:

\n
    \n
  • \n

    \n x-amz-server-side-encryption-customer-algorithm\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key\n

    \n
  • \n
  • \n

    \n x-amz-server-side-encryption-customer-key-MD5\n

    \n
  • \n
\n

For more information about SSE-C, see Server-Side Encryption\n (Using Customer-Provided Encryption Keys).

\n \n
    \n
  • \n

    Encryption request headers, like x-amz-server-side-encryption,\n should not be sent for GET requests if your object uses server-side\n encryption with Key Management Service (KMS) keys (SSE-KMS), dual-layer server-side\n encryption with Amazon Web Services KMS keys (DSSE-KMS), or server-side encryption with Amazon S3\n managed encryption keys (SSE-S3). If your object does use these types of keys,\n you’ll get an HTTP 400 Bad Request error.

    \n
  • \n
  • \n

    The last modified property in this case is the creation date of the\n object.

    \n
  • \n
\n
\n

Request headers are limited to 8 KB in size. For more information, see Common\n Request Headers.

\n

Consider the following when using request headers:

\n
    \n
  • \n

    Consideration 1 – If both of the If-Match and\n If-Unmodified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-Match condition evaluates to true, and;

      \n
    • \n
    • \n

      \n If-Unmodified-Since condition evaluates to\n false;

      \n
    • \n
    \n

    Then Amazon S3 returns 200 OK and the data requested.

    \n
  • \n
  • \n

    Consideration 2 – If both of the If-None-Match and\n If-Modified-Since headers are present in the request as\n follows:

    \n
      \n
    • \n

      \n If-None-Match condition evaluates to false,\n and;

      \n
    • \n
    • \n

      \n If-Modified-Since condition evaluates to\n true;

      \n
    • \n
    \n

    Then Amazon S3 returns the 304 Not Modified response code.

    \n
  • \n
\n

For more information about conditional requests, see RFC 7232.

\n
\n
Permissions
\n
\n

You need the relevant read object (or version) permission for this operation. For more\n information, see Actions, resources, and condition keys for Amazon S3. \n If the object you request doesn't exist, the error that Amazon S3 returns depends\n on whether you also have the s3:ListBucket permission.

\n
    \n
  • \n

    If you have the s3:ListBucket permission on the bucket, Amazon S3 returns\n an HTTP status code 404 error.

    \n
  • \n
  • \n

    If you don’t have the s3:ListBucket permission, Amazon S3 returns an HTTP\n status code 403 error.

    \n
  • \n
\n
\n
\n

The following actions are related to HeadObject:

\n ", "smithy.api#http": { "method": "HEAD", "uri": "/{Bucket}/{Key+}", @@ -25825,7 +27710,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -25853,7 +27738,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -25861,7 +27746,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with Amazon Web Services\n KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the object uses an S3 Bucket Key for server-side encryption with\n Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -27238,6 +29123,32 @@ }, "traits": { "smithy.api#documentation": "

Returns a list of all buckets owned by the authenticated sender of the request. To use\n this operation, you must have the s3:ListAllMyBuckets permission.

\n

For information about Amazon S3 buckets, see Creating, configuring, and\n working with Amazon S3 buckets.

", + "smithy.api#examples": [ + { + "title": "To list all buckets", + "documentation": "The following example returns all the buckets owned by the sender of this request.", + "output": { + "Owner": { + "DisplayName": "own-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31" + }, + "Buckets": [ + { + "CreationDate": "2012-02-15T21:03:02.000Z", + "Name": "examplebucket" + }, + { + "CreationDate": "2011-07-24T19:33:50.000Z", + "Name": "examplebucket2" + }, + { + "CreationDate": "2010-12-17T00:56:49.000Z", + "Name": "examplebucket3" + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/", @@ -27362,6 +29273,12 @@ "traits": { "smithy.api#documentation": "

Encoding type used by Amazon S3 to encode object keys in the response.

\n

If you specify encoding-type request parameter, Amazon S3 includes this element\n in the response, and returns encoded key name values in the following response\n elements:

\n

\n Delimiter, KeyMarker, Prefix,\n NextKeyMarker, Key.

" } + }, + "RequestCharged": { + "target": "com.amazonaws.s3#RequestCharged", + "traits": { + "smithy.api#httpHeader": "x-amz-request-charged" + } } }, "traits": { @@ -27431,6 +29348,12 @@ "smithy.api#documentation": "

The account ID of the expected bucket owner. If the bucket is owned by a different account, the request fails with the HTTP status code 403 Forbidden (access denied).

", "smithy.api#httpHeader": "x-amz-expected-bucket-owner" } + }, + "RequestPayer": { + "target": "com.amazonaws.s3#RequestPayer", + "traits": { + "smithy.api#httpHeader": "x-amz-request-payer" + } } }, "traits": { @@ -27447,6 +29370,46 @@ }, "traits": { "smithy.api#documentation": "

Returns metadata about all versions of the objects in a bucket. You can also use request\n parameters as selection criteria to return metadata about a subset of all the object\n versions.

\n \n

To use this operation, you must have permissions to perform the\n s3:ListBucketVersions action. Be aware of the name difference.

\n
\n \n

A 200 OK response can contain valid or invalid XML. Make sure to design your\n application to parse the contents of the response and handle it appropriately.

\n
\n

To use this operation, you must have READ access to the bucket.

\n

This action is not supported by Amazon S3 on Outposts.

\n

The following operations are related to ListObjectVersions:

\n ", + "smithy.api#examples": [ + { + "title": "To list object versions", + "documentation": "The following example return versions of an object with specific key name prefix. The request limits the number of items returned to two. If there are are more than two object version, S3 returns NextToken in the response. You can specify this token value in your next request to fetch next set of object versions.", + "input": { + "Bucket": "examplebucket", + "Prefix": "HappyFace.jpg" + }, + "output": { + "Versions": [ + { + "LastModified": "2016-12-15T01:19:41.000Z", + "VersionId": "null", + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "StorageClass": "STANDARD", + "Key": "HappyFace.jpg", + "Owner": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "IsLatest": true, + "Size": 3191 + }, + { + "LastModified": "2016-12-13T00:58:26.000Z", + "VersionId": "PHtexPGjH2y.zBgT8LmB7wwLI2mpbz.k", + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"", + "StorageClass": "STANDARD", + "Key": "HappyFace.jpg", + "Owner": { + "DisplayName": "owner-display-name", + "ID": "examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484be31bebcc" + }, + "IsLatest": false, + "Size": 3191 + } + ] + } + } + ], "smithy.api#http": { "method": "GET", "uri": "/{Bucket}?versions", @@ -27541,6 +29504,12 @@ "traits": { "smithy.api#documentation": "

Encoding type used by Amazon S3 to encode object key names in the XML response.

\n

If you specify encoding-type request parameter, Amazon S3 includes this element in the\n response, and returns encoded key name values in the following response elements:

\n

\n KeyMarker, NextKeyMarker, Prefix, Key, and Delimiter.

" } + }, + "RequestCharged": { + "target": "com.amazonaws.s3#RequestCharged", + "traits": { + "smithy.api#httpHeader": "x-amz-request-charged" + } } }, "traits": { @@ -27610,6 +29579,12 @@ "smithy.api#documentation": "

The account ID of the expected bucket owner. If the bucket is owned by a different account, the request fails with the HTTP status code 403 Forbidden (access denied).

", "smithy.api#httpHeader": "x-amz-expected-bucket-owner" } + }, + "RequestPayer": { + "target": "com.amazonaws.s3#RequestPayer", + "traits": { + "smithy.api#httpHeader": "x-amz-request-payer" + } } }, "traits": { @@ -27704,6 +29679,12 @@ "traits": { "smithy.api#documentation": "

Encoding type used by Amazon S3 to encode object keys in the response.

" } + }, + "RequestCharged": { + "target": "com.amazonaws.s3#RequestCharged", + "traits": { + "smithy.api#httpHeader": "x-amz-request-charged" + } } }, "traits": { @@ -27885,6 +29866,12 @@ "traits": { "smithy.api#documentation": "

If StartAfter was sent with the request, it is included in the response.

" } + }, + "RequestCharged": { + "target": "com.amazonaws.s3#RequestCharged", + "traits": { + "smithy.api#httpHeader": "x-amz-request-charged" + } } }, "traits": { @@ -29736,7 +31723,18 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Sets the permissions on an existing bucket using access control lists (ACL). For more\n information, see Using ACLs. To set the ACL of a\n bucket, you must have WRITE_ACP permission.

\n

You can use one of the following two ways to set a bucket's permissions:

\n
    \n
  • \n

    Specify the ACL in the request body

    \n
  • \n
  • \n

    Specify permissions using request headers

    \n
  • \n
\n \n

You cannot specify access permission using both the body and the request\n headers.

\n
\n

Depending on your application needs, you may choose to set the ACL on a bucket using\n either the request body or the headers. For example, if you have an existing application\n that updates a bucket ACL using the request body, then you can continue to use that\n approach.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, ACLs\n are disabled and no longer affect permissions. You must use policies to grant access to\n your bucket and the objects in it. Requests to set ACLs or update ACLs fail and return\n the AccessControlListNotSupported error code. Requests to read ACLs are\n still supported. For more information, see Controlling object\n ownership in the Amazon S3 User Guide.

\n
\n
\n
Permissions
\n
\n

You can set access permissions using one of the following methods:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports\n a set of predefined ACLs, known as canned ACLs. Each canned ACL\n has a predefined set of grantees and permissions. Specify the canned ACL name as the\n value of x-amz-acl. If you use this header, you cannot use other access\n control-specific headers in your request. For more information, see Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the x-amz-grant-read,\n x-amz-grant-read-acp, x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. When using these headers, you\n specify explicit access permissions and grantees (Amazon Web Services accounts or Amazon S3 groups) who\n will receive the permission. If you use these ACL-specific headers, you cannot use\n the x-amz-acl header to set a canned ACL. These parameters map to the\n set of permissions that Amazon S3 supports in an ACL. For more information, see Access Control\n List (ACL) Overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an\n Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-write header grants create,\n overwrite, and delete objects permission to LogDelivery group predefined by Amazon S3 and\n two Amazon Web Services accounts identified by their email addresses.

    \n

    \n x-amz-grant-write: uri=\"http://acs.amazonaws.com/groups/s3/LogDelivery\",\n id=\"111122223333\", id=\"555566667777\" \n

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You cannot do\n both.

\n
\n
Grantee Values
\n
\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>&\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n \n

    Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

    \n
      \n
    • \n

      US East (N. Virginia)

      \n
    • \n
    • \n

      US West (N. California)

      \n
    • \n
    • \n

      US West (Oregon)

      \n
    • \n
    • \n

      Asia Pacific (Singapore)

      \n
    • \n
    • \n

      Asia Pacific (Sydney)

      \n
    • \n
    • \n

      Asia Pacific (Tokyo)

      \n
    • \n
    • \n

      Europe (Ireland)

      \n
    • \n
    • \n

      South America (São Paulo)

      \n
    • \n
    \n

    For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

    \n
    \n
  • \n
\n
\n
\n

The following operations are related to PutBucketAcl:

\n ", + "smithy.api#documentation": "

Sets the permissions on an existing bucket using access control lists (ACL). For more\n information, see Using ACLs. To set the ACL of a\n bucket, you must have WRITE_ACP permission.

\n

You can use one of the following two ways to set a bucket's permissions:

\n
    \n
  • \n

    Specify the ACL in the request body

    \n
  • \n
  • \n

    Specify permissions using request headers

    \n
  • \n
\n \n

You cannot specify access permission using both the body and the request\n headers.

\n
\n

Depending on your application needs, you may choose to set the ACL on a bucket using\n either the request body or the headers. For example, if you have an existing application\n that updates a bucket ACL using the request body, then you can continue to use that\n approach.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, ACLs\n are disabled and no longer affect permissions. You must use policies to grant access to\n your bucket and the objects in it. Requests to set ACLs or update ACLs fail and return\n the AccessControlListNotSupported error code. Requests to read ACLs are\n still supported. For more information, see Controlling object\n ownership in the Amazon S3 User Guide.

\n
\n
\n
Permissions
\n
\n

You can set access permissions by using one of the following methods:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports\n a set of predefined ACLs, known as canned ACLs. Each canned ACL\n has a predefined set of grantees and permissions. Specify the canned ACL name as the\n value of x-amz-acl. If you use this header, you cannot use other access\n control-specific headers in your request. For more information, see Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the x-amz-grant-read,\n x-amz-grant-read-acp, x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. When using these headers, you\n specify explicit access permissions and grantees (Amazon Web Services accounts or Amazon S3 groups) who\n will receive the permission. If you use these ACL-specific headers, you cannot use\n the x-amz-acl header to set a canned ACL. These parameters map to the\n set of permissions that Amazon S3 supports in an ACL. For more information, see Access Control\n List (ACL) Overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an\n Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-write header grants create,\n overwrite, and delete objects permission to LogDelivery group predefined by Amazon S3 and\n two Amazon Web Services accounts identified by their email addresses.

    \n

    \n x-amz-grant-write: uri=\"http://acs.amazonaws.com/groups/s3/LogDelivery\",\n id=\"111122223333\", id=\"555566667777\" \n

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You cannot do\n both.

\n
\n
Grantee Values
\n
\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>&\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n \n

    Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

    \n
      \n
    • \n

      US East (N. Virginia)

      \n
    • \n
    • \n

      US West (N. California)

      \n
    • \n
    • \n

      US West (Oregon)

      \n
    • \n
    • \n

      Asia Pacific (Singapore)

      \n
    • \n
    • \n

      Asia Pacific (Sydney)

      \n
    • \n
    • \n

      Asia Pacific (Tokyo)

      \n
    • \n
    • \n

      Europe (Ireland)

      \n
    • \n
    • \n

      South America (São Paulo)

      \n
    • \n
    \n

    For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

    \n
    \n
  • \n
\n
\n
\n

The following operations are related to PutBucketAcl:

\n ", + "smithy.api#examples": [ + { + "title": "Put bucket acl", + "documentation": "The following example replaces existing ACL on a bucket. The ACL grants the bucket owner (specified using the owner ID) and write permission to the LogDelivery group. Because this is a replace operation, you must specify all the grants in your request. To incrementally add or remove ACL grants, you might use the console.", + "input": { + "Bucket": "examplebucket", + "GrantFullControl": "id=examplee7a2f25102679df27bb0ae12b3f85be6f290b936c4393484", + "GrantWrite": "uri=http://acs.amazonaws.com/groups/s3/LogDelivery" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?acl", @@ -29908,6 +31906,49 @@ "requestChecksumRequired": true }, "smithy.api#documentation": "

Sets the cors configuration for your bucket. If the configuration exists,\n Amazon S3 replaces it.

\n

To use this operation, you must be allowed to perform the s3:PutBucketCORS\n action. By default, the bucket owner has this permission and can grant it to others.

\n

You set this configuration on a bucket so that the bucket can service cross-origin\n requests. For example, you might want to enable a request whose origin is\n http://www.example.com to access your Amazon S3 bucket at\n my.example.bucket.com by using the browser's XMLHttpRequest\n capability.

\n

To enable cross-origin resource sharing (CORS) on a bucket, you add the\n cors subresource to the bucket. The cors subresource is an XML\n document in which you configure rules that identify origins and the HTTP methods that can\n be executed on your bucket. The document is limited to 64 KB in size.

\n

When Amazon S3 receives a cross-origin request (or a pre-flight OPTIONS request) against a\n bucket, it evaluates the cors configuration on the bucket and uses the first\n CORSRule rule that matches the incoming browser request to enable a\n cross-origin request. For a rule to match, the following conditions must be met:

\n
    \n
  • \n

    The request's Origin header must match AllowedOrigin\n elements.

    \n
  • \n
  • \n

    The request method (for example, GET, PUT, HEAD, and so on) or the\n Access-Control-Request-Method header in case of a pre-flight\n OPTIONS request must be one of the AllowedMethod\n elements.

    \n
  • \n
  • \n

    Every header specified in the Access-Control-Request-Headers request\n header of a pre-flight request must match an AllowedHeader element.\n

    \n
  • \n
\n

For more information about CORS, go to Enabling Cross-Origin Resource Sharing in\n the Amazon S3 User Guide.

\n

The following operations are related to PutBucketCors:

\n ", + "smithy.api#examples": [ + { + "title": "To set cors configuration on a bucket.", + "documentation": "The following example enables PUT, POST, and DELETE requests from www.example.com, and enables GET requests from any domain.", + "input": { + "Bucket": "", + "CORSConfiguration": { + "CORSRules": [ + { + "AllowedOrigins": [ + "http://www.example.com" + ], + "AllowedHeaders": [ + "*" + ], + "AllowedMethods": [ + "PUT", + "POST", + "DELETE" + ], + "MaxAgeSeconds": 3000, + "ExposeHeaders": [ + "x-amz-server-side-encryption" + ] + }, + { + "AllowedOrigins": [ + "*" + ], + "AllowedHeaders": [ + "Authorization" + ], + "AllowedMethods": [ + "GET" + ], + "MaxAgeSeconds": 3000 + } + ] + }, + "ContentMD5": "" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?cors", @@ -29977,7 +32018,7 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

This action uses the encryption subresource to configure default encryption\n and Amazon S3 Bucket Keys for an existing bucket.

\n

By default, all buckets have a default encryption configuration that\n uses server-side encryption with Amazon S3 managed keys (SSE-S3).\n You can optionally configure default encryption for a bucket by using server-side\n encryption with an Amazon Web Services KMS key (SSE-KMS) or a customer-provided key (SSE-C). If you specify default encryption by using\n SSE-KMS, you can also configure Amazon S3 Bucket Keys. For information about bucket default encryption,\n see Amazon S3\n bucket default encryption in the Amazon S3 User Guide. For more\n information about S3 Bucket Keys, see Amazon S3 Bucket Keys in the\n Amazon S3 User Guide.

\n \n

This action requires Amazon Web Services Signature Version 4. For more information, see \n Authenticating Requests (Amazon Web Services Signature Version 4).

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n

The following operations are related to PutBucketEncryption:

\n ", + "smithy.api#documentation": "

This action uses the encryption subresource to configure default encryption\n and Amazon S3 Bucket Keys for an existing bucket.

\n

By default, all buckets have a default encryption configuration that uses server-side\n encryption with Amazon S3 managed keys (SSE-S3). You can optionally configure default encryption\n for a bucket by using server-side encryption with Key Management Service (KMS) keys (SSE-KMS),\n dual-layer server-side encryption with Amazon Web Services KMS keys (DSSE-KMS), or server-side\n encryption with customer-provided keys (SSE-C). If you specify default encryption by using\n SSE-KMS, you can also configure Amazon S3 Bucket Keys. For information about bucket default\n encryption, see Amazon S3 bucket default encryption\n in the Amazon S3 User Guide. For more information about S3 Bucket Keys, see\n Amazon S3 Bucket\n Keys in the Amazon S3 User Guide.

\n \n

This action requires Amazon Web Services Signature Version 4. For more information, see \n Authenticating Requests (Amazon Web Services Signature Version 4).

\n
\n

To use this operation, you must have permission to perform the\n s3:PutEncryptionConfiguration action. The bucket owner has this permission\n by default. The bucket owner can grant this permission to others. For more information\n about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n

The following operations are related to PutBucketEncryption:

\n ", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?encryption", @@ -29991,7 +32032,7 @@ "Bucket": { "target": "com.amazonaws.s3#BucketName", "traits": { - "smithy.api#documentation": "

Specifies default encryption for a bucket using server-side encryption with different\n key options. By default, all buckets have a default encryption configuration that\n uses server-side encryption with Amazon S3 managed keys (SSE-S3). You can optionally configure default encryption for a bucket by using server-side\n encryption with an Amazon Web Services KMS key (SSE-KMS) or a customer-provided key (SSE-C). For information about the bucket default\n encryption feature, see Amazon S3 Bucket Default Encryption\n in the Amazon S3 User Guide.

", + "smithy.api#documentation": "

Specifies default encryption for a bucket using server-side encryption with different\n key options. By default, all buckets have a default encryption configuration that uses\n server-side encryption with Amazon S3 managed keys (SSE-S3). You can optionally configure\n default encryption for a bucket by using server-side encryption with an Amazon Web Services KMS key\n (SSE-KMS) or a customer-provided key (SSE-C). For information about the bucket default\n encryption feature, see Amazon S3 Bucket Default Encryption\n in the Amazon S3 User Guide.

", "smithy.api#httpLabel": {}, "smithy.api#required": {}, "smithy.rules#contextParam": { @@ -30159,7 +32200,36 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Creates a new lifecycle configuration for the bucket or replaces an existing lifecycle\n configuration. Keep in mind that this will overwrite an existing lifecycle configuration,\n so if you want to retain any configuration details, they must be included in the new\n lifecycle configuration. For information about lifecycle configuration, see Managing\n your storage lifecycle.

\n \n

Bucket lifecycle configuration now supports specifying a lifecycle rule using an\n object key name prefix, one or more object tags, or a combination of both. Accordingly,\n this section describes the latest API. The previous version of the API supported\n filtering based only on an object key name prefix, which is supported for backward\n compatibility. For the related API description, see PutBucketLifecycle.

\n
\n
\n
Rules
\n
\n

You specify the lifecycle configuration in your request body. The lifecycle\n configuration is specified as XML consisting of one or more rules. An Amazon S3 Lifecycle\n configuration can have up to 1,000 rules. This limit is not adjustable. Each rule consists\n of the following:

\n
    \n
  • \n

    Filter identifying a subset of objects to which the rule applies. The filter can\n be based on a key name prefix, object tags, or a combination of both.

    \n
  • \n
  • \n

    Status whether the rule is in effect.

    \n
  • \n
  • \n

    One or more lifecycle transition and expiration actions that you want Amazon S3 to\n perform on the objects identified by the filter. If the state of your bucket is\n versioning-enabled or versioning-suspended, you can have many versions of the same\n object (one current version and zero or more noncurrent versions). Amazon S3 provides\n predefined actions that you can specify for current and noncurrent object\n versions.

    \n
  • \n
\n

For more information, see Object Lifecycle Management\n and Lifecycle Configuration Elements.

\n
\n
Permissions
\n
\n

By default, all Amazon S3 resources are private, including buckets, objects, and related\n subresources (for example, lifecycle configuration and website configuration). Only the\n resource owner (that is, the Amazon Web Services account that created it) can access the resource. The\n resource owner can optionally grant access permissions to others by writing an access\n policy. For this operation, a user must get the s3:PutLifecycleConfiguration\n permission.

\n

You can also explicitly deny permissions. Explicit deny also supersedes any other\n permissions. If you want to block users or accounts from removing or deleting objects from\n your bucket, you must deny them permissions for the following actions:

\n
    \n
  • \n

    \n s3:DeleteObject\n

    \n
  • \n
  • \n

    \n s3:DeleteObjectVersion\n

    \n
  • \n
  • \n

    \n s3:PutLifecycleConfiguration\n

    \n
  • \n
\n

For more information about permissions, see Managing Access Permissions to\n Your Amazon S3 Resources.

\n
\n
\n

The following operations are related to PutBucketLifecycleConfiguration:

\n ", + "smithy.api#documentation": "

Creates a new lifecycle configuration for the bucket or replaces an existing lifecycle\n configuration. Keep in mind that this will overwrite an existing lifecycle configuration,\n so if you want to retain any configuration details, they must be included in the new\n lifecycle configuration. For information about lifecycle configuration, see Managing\n your storage lifecycle.

\n \n

Bucket lifecycle configuration now supports specifying a lifecycle rule using an\n object key name prefix, one or more object tags, or a combination of both. Accordingly,\n this section describes the latest API. The previous version of the API supported\n filtering based only on an object key name prefix, which is supported for backward\n compatibility. For the related API description, see PutBucketLifecycle.

\n
\n
\n
Rules
\n
\n

You specify the lifecycle configuration in your request body. The lifecycle\n configuration is specified as XML consisting of one or more rules. An Amazon S3 Lifecycle\n configuration can have up to 1,000 rules. This limit is not adjustable. Each rule consists\n of the following:

\n
    \n
  • \n

    A filter identifying a subset of objects to which the rule applies. The filter can\n be based on a key name prefix, object tags, or a combination of both.

    \n
  • \n
  • \n

    A status indicating whether the rule is in effect.

    \n
  • \n
  • \n

    One or more lifecycle transition and expiration actions that you want Amazon S3 to\n perform on the objects identified by the filter. If the state of your bucket is\n versioning-enabled or versioning-suspended, you can have many versions of the same\n object (one current version and zero or more noncurrent versions). Amazon S3 provides\n predefined actions that you can specify for current and noncurrent object\n versions.

    \n
  • \n
\n

For more information, see Object Lifecycle Management\n and Lifecycle Configuration Elements.

\n
\n
Permissions
\n
\n

By default, all Amazon S3 resources are private, including buckets, objects, and related\n subresources (for example, lifecycle configuration and website configuration). Only the\n resource owner (that is, the Amazon Web Services account that created it) can access the resource. The\n resource owner can optionally grant access permissions to others by writing an access\n policy. For this operation, a user must get the s3:PutLifecycleConfiguration\n permission.

\n

You can also explicitly deny permissions. An explicit deny also supersedes any other\n permissions. If you want to block users or accounts from removing or deleting objects from\n your bucket, you must deny them permissions for the following actions:

\n
    \n
  • \n

    \n s3:DeleteObject\n

    \n
  • \n
  • \n

    \n s3:DeleteObjectVersion\n

    \n
  • \n
  • \n

    \n s3:PutLifecycleConfiguration\n

    \n
  • \n
\n

For more information about permissions, see Managing Access Permissions to\n Your Amazon S3 Resources.

\n
\n
\n

The following operations are related to PutBucketLifecycleConfiguration:

\n ", + "smithy.api#examples": [ + { + "title": "Put bucket lifecycle", + "documentation": "The following example replaces existing lifecycle configuration, if any, on the specified bucket. ", + "input": { + "Bucket": "examplebucket", + "LifecycleConfiguration": { + "Rules": [ + { + "Filter": { + "Prefix": "documents/" + }, + "Status": "Enabled", + "Transitions": [ + { + "Days": 365, + "StorageClass": "GLACIER" + } + ], + "Expiration": { + "Days": 3650 + }, + "ID": "TestOnly" + } + ] + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?lifecycle", @@ -30221,7 +32291,31 @@ "requestAlgorithmMember": "ChecksumAlgorithm", "requestChecksumRequired": true }, - "smithy.api#documentation": "

Set the logging parameters for a bucket and to specify permissions for who can view and\n modify the logging parameters. All logs are saved to buckets in the same Amazon Web Services Region as\n the source bucket. To set the logging status of a bucket, you must be the bucket\n owner.

\n

The bucket owner is automatically granted FULL_CONTROL to all logs. You use the\n Grantee request element to grant access to other people. The\n Permissions request element specifies the kind of access the grantee has to\n the logs.

\n \n

If the target bucket for log delivery uses the bucket owner enforced setting for S3\n Object Ownership, you can't use the Grantee request element to grant access\n to others. Permissions can only be granted using policies. For more information, see\n Permissions for server access log delivery in the\n Amazon S3 User Guide.

\n
\n
\n
Grantee Values
\n
\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request.

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
\n
\n
\n

To enable logging, you use LoggingEnabled and its children request elements. To disable\n logging, you use an empty BucketLoggingStatus request element:

\n

\n \n

\n

For more information about server access logging, see Server Access Logging in the\n Amazon S3 User Guide.

\n

For more information about creating a bucket, see CreateBucket. For more\n information about returning the logging status of a bucket, see GetBucketLogging.

\n

The following operations are related to PutBucketLogging:

\n ", + "smithy.api#documentation": "

Set the logging parameters for a bucket and to specify permissions for who can view and\n modify the logging parameters. All logs are saved to buckets in the same Amazon Web Services Region as\n the source bucket. To set the logging status of a bucket, you must be the bucket\n owner.

\n

The bucket owner is automatically granted FULL_CONTROL to all logs. You use the\n Grantee request element to grant access to other people. The\n Permissions request element specifies the kind of access the grantee has to\n the logs.

\n \n

If the target bucket for log delivery uses the bucket owner enforced setting for S3\n Object Ownership, you can't use the Grantee request element to grant access\n to others. Permissions can only be granted using policies. For more information, see\n Permissions for server access log delivery in the\n Amazon S3 User Guide.

\n
\n
\n
Grantee Values
\n
\n

You can specify the person (grantee) to whom you're assigning access rights (by using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    \n DisplayName is optional and ignored in the request.

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GETObjectAcl\n request, appears as the CanonicalUser.

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
\n
\n
\n

To enable logging, you use LoggingEnabled and its children request elements. To disable\n logging, you use an empty BucketLoggingStatus request element:

\n

\n \n

\n

For more information about server access logging, see Server Access Logging in the\n Amazon S3 User Guide.

\n

For more information about creating a bucket, see CreateBucket. For more\n information about returning the logging status of a bucket, see GetBucketLogging.

\n

The following operations are related to PutBucketLogging:

\n ", + "smithy.api#examples": [ + { + "title": "Set logging configuration for a bucket", + "documentation": "The following example sets logging policy on a bucket. For the Log Delivery group to deliver logs to the destination bucket, it needs permission for the READ_ACP action which the policy grants.", + "input": { + "Bucket": "sourcebucket", + "BucketLoggingStatus": { + "LoggingEnabled": { + "TargetBucket": "targetbucket", + "TargetPrefix": "MyBucketLogs/", + "TargetGrants": [ + { + "Grantee": { + "Type": "Group", + "URI": "http://acs.amazonaws.com/groups/global/AllUsers" + }, + "Permission": "READ" + } + ] + } + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?logging", @@ -30287,7 +32381,7 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Sets a metrics configuration (specified by the metrics configuration ID) for the bucket.\n You can have up to 1,000 metrics configurations per bucket. If you're updating an existing\n metrics configuration, note that this is a full replacement of the existing metrics\n configuration. If you don't include the elements you want to keep, they are erased.

\n

To use this operation, you must have permissions to perform the\n s3:PutMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring\n Metrics with Amazon CloudWatch.

\n

The following operations are related to\n PutBucketMetricsConfiguration:

\n \n

\n GetBucketLifecycle has the following special error:

\n
    \n
  • \n

    Error code: TooManyConfigurations\n

    \n
      \n
    • \n

      Description: You are attempting to create a new configuration but have\n already reached the 1,000-configuration limit.

      \n
    • \n
    • \n

      HTTP Status Code: HTTP 400 Bad Request

      \n
    • \n
    \n
  • \n
", + "smithy.api#documentation": "

Sets a metrics configuration (specified by the metrics configuration ID) for the bucket.\n You can have up to 1,000 metrics configurations per bucket. If you're updating an existing\n metrics configuration, note that this is a full replacement of the existing metrics\n configuration. If you don't include the elements you want to keep, they are erased.

\n

To use this operation, you must have permissions to perform the\n s3:PutMetricsConfiguration action. The bucket owner has this permission by\n default. The bucket owner can grant this permission to others. For more information about\n permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

For information about CloudWatch request metrics for Amazon S3, see Monitoring\n Metrics with Amazon CloudWatch.

\n

The following operations are related to\n PutBucketMetricsConfiguration:

\n \n

\n PutBucketMetricsConfiguration has the following special error:

\n
    \n
  • \n

    Error code: TooManyConfigurations\n

    \n
      \n
    • \n

      Description: You are attempting to create a new configuration but have\n already reached the 1,000-configuration limit.

      \n
    • \n
    • \n

      HTTP Status Code: HTTP 400 Bad Request

      \n
    • \n
    \n
  • \n
", "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?metrics", @@ -30347,7 +32441,26 @@ "target": "smithy.api#Unit" }, "traits": { - "smithy.api#documentation": "

Enables notifications of specified events for a bucket. For more information about event\n notifications, see Configuring Event\n Notifications.

\n

Using this API, you can replace an existing notification configuration. The\n configuration is an XML file that defines the event types that you want Amazon S3 to publish and\n the destination where you want Amazon S3 to publish an event notification when it detects an\n event of the specified type.

\n

By default, your bucket has no event notifications configured. That is, the notification\n configuration will be an empty NotificationConfiguration.

\n

\n \n

\n

\n \n

\n

This action replaces the existing notification configuration with the configuration you\n include in the request body.

\n

After Amazon S3 receives this request, it first verifies that any Amazon Simple Notification\n Service (Amazon SNS) or Amazon Simple Queue Service (Amazon SQS) destination exists, and\n that the bucket owner has permission to publish to it by sending a test notification. In\n the case of Lambda destinations, Amazon S3 verifies that the Lambda function permissions\n grant Amazon S3 permission to invoke the function from the Amazon S3 bucket. For more information,\n see Configuring Notifications for Amazon S3 Events.

\n

You can disable notifications by adding the empty NotificationConfiguration\n element.

\n

For more information about the number of event notification configurations that you can\n create per bucket, see Amazon S3 service quotas in Amazon Web Services\n General Reference.

\n

By default, only the bucket owner can configure notifications on a bucket. However,\n bucket owners can use a bucket policy to grant permission to other users to set this\n configuration with s3:PutBucketNotification permission.

\n \n

The PUT notification is an atomic operation. For example, suppose your notification\n configuration includes SNS topic, SQS queue, and Lambda function configurations. When\n you send a PUT request with this configuration, Amazon S3 sends test messages to your SNS\n topic. If the message fails, the entire PUT action will fail, and Amazon S3 will not add the\n configuration to your bucket.

\n
\n

If the configuration in the request body includes only one\n TopicConfiguration specifying only the\n s3:ReducedRedundancyLostObject event type, the response will also include\n the x-amz-sns-test-message-id header containing the message ID of the test\n notification sent to the topic.

\n

The following action is related to\n PutBucketNotificationConfiguration:

\n ", + "smithy.api#documentation": "

Enables notifications of specified events for a bucket. For more information about event\n notifications, see Configuring Event\n Notifications.

\n

Using this API, you can replace an existing notification configuration. The\n configuration is an XML file that defines the event types that you want Amazon S3 to publish and\n the destination where you want Amazon S3 to publish an event notification when it detects an\n event of the specified type.

\n

By default, your bucket has no event notifications configured. That is, the notification\n configuration will be an empty NotificationConfiguration.

\n

\n \n

\n

\n \n

\n

This action replaces the existing notification configuration with the configuration you\n include in the request body.

\n

After Amazon S3 receives this request, it first verifies that any Amazon Simple Notification\n Service (Amazon SNS) or Amazon Simple Queue Service (Amazon SQS) destination exists, and\n that the bucket owner has permission to publish to it by sending a test notification. In\n the case of Lambda destinations, Amazon S3 verifies that the Lambda function permissions\n grant Amazon S3 permission to invoke the function from the Amazon S3 bucket. For more information,\n see Configuring Notifications for Amazon S3 Events.

\n

You can disable notifications by adding the empty NotificationConfiguration\n element.

\n

For more information about the number of event notification configurations that you can\n create per bucket, see Amazon S3 service quotas in Amazon Web Services\n General Reference.

\n

By default, only the bucket owner can configure notifications on a bucket. However,\n bucket owners can use a bucket policy to grant permission to other users to set this\n configuration with the required s3:PutBucketNotification permission.

\n \n

The PUT notification is an atomic operation. For example, suppose your notification\n configuration includes SNS topic, SQS queue, and Lambda function configurations. When\n you send a PUT request with this configuration, Amazon S3 sends test messages to your SNS\n topic. If the message fails, the entire PUT action will fail, and Amazon S3 will not add the\n configuration to your bucket.

\n
\n

If the configuration in the request body includes only one\n TopicConfiguration specifying only the\n s3:ReducedRedundancyLostObject event type, the response will also include\n the x-amz-sns-test-message-id header containing the message ID of the test\n notification sent to the topic.

\n

The following action is related to\n PutBucketNotificationConfiguration:

\n ", + "smithy.api#examples": [ + { + "title": "Set notification configuration for a bucket", + "documentation": "The following example sets notification configuration on a bucket to publish the object created events to an SNS topic.", + "input": { + "Bucket": "examplebucket", + "NotificationConfiguration": { + "TopicConfigurations": [ + { + "TopicArn": "arn:aws:sns:us-west-2:123456789012:s3-notification-topic", + "Events": [ + "s3:ObjectCreated:*" + ] + } + ] + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?notification", @@ -30473,6 +32586,16 @@ "requestChecksumRequired": true }, "smithy.api#documentation": "

Applies an Amazon S3 bucket policy to an Amazon S3 bucket. If you are using an identity other than\n the root user of the Amazon Web Services account that owns the bucket, the calling identity must have the\n PutBucketPolicy permissions on the specified bucket and belong to the\n bucket owner's account in order to use this operation.

\n

If you don't have PutBucketPolicy permissions, Amazon S3 returns a 403\n Access Denied error. If you have the correct permissions, but you're not using an\n identity that belongs to the bucket owner's account, Amazon S3 returns a 405 Method Not\n Allowed error.

\n \n

To ensure that bucket owners don't inadvertently lock themselves out of their own\n buckets, the root principal in a bucket owner's Amazon Web Services account can perform the\n GetBucketPolicy, PutBucketPolicy, and\n DeleteBucketPolicy API actions, even if their bucket policy explicitly\n denies the root principal's access. Bucket owner root principals can only be blocked from performing \n these API actions by VPC endpoint policies and Amazon Web Services Organizations policies.

\n
\n

For more information, see Bucket policy\n examples.

\n

The following operations are related to PutBucketPolicy:

\n ", + "smithy.api#examples": [ + { + "title": "Set bucket policy", + "documentation": "The following example sets a permission policy on a bucket.", + "input": { + "Bucket": "examplebucket", + "Policy": "{\"Version\": \"2012-10-17\", \"Statement\": [{ \"Sid\": \"id-1\",\"Effect\": \"Allow\",\"Principal\": {\"AWS\": \"arn:aws:iam::123456789012:root\"}, \"Action\": [ \"s3:PutObject\",\"s3:PutObjectAcl\"], \"Resource\": [\"arn:aws:s3:::acl3/*\" ] } ]}" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?policy", @@ -30550,6 +32673,28 @@ "requestChecksumRequired": true }, "smithy.api#documentation": "

Creates a replication configuration or replaces an existing one. For more information,\n see Replication in the Amazon S3 User Guide.

\n

Specify the replication configuration in the request body. In the replication\n configuration, you provide the name of the destination bucket or buckets where you want\n Amazon S3 to replicate objects, the IAM role that Amazon S3 can assume to replicate objects on your\n behalf, and other relevant information.

\n

A replication configuration must include at least one rule, and can contain a maximum of\n 1,000. Each rule identifies a subset of objects to replicate by filtering the objects in\n the source bucket. To choose additional subsets of objects to replicate, add a rule for\n each subset.

\n

To specify a subset of the objects in the source bucket to apply a replication rule to,\n add the Filter element as a child of the Rule element. You can filter objects based on an\n object key prefix, one or more object tags, or both. When you add the Filter element in the\n configuration, you must also add the following elements:\n DeleteMarkerReplication, Status, and\n Priority.

\n \n

If you are using an earlier version of the replication configuration, Amazon S3 handles\n replication of delete markers differently. For more information, see Backward Compatibility.

\n
\n

For information about enabling versioning on a bucket, see Using Versioning.

\n
\n
Handling Replication of Encrypted Objects
\n
\n

By default, Amazon S3 doesn't replicate objects that are stored at rest using server-side\n encryption with KMS keys. To replicate Amazon Web Services KMS-encrypted objects, add the following:\n SourceSelectionCriteria, SseKmsEncryptedObjects,\n Status, EncryptionConfiguration, and\n ReplicaKmsKeyID. For information about replication configuration, see\n Replicating Objects\n Created with SSE Using KMS keys.

\n

For information on PutBucketReplication errors, see List of\n replication-related error codes\n

\n
\n
Permissions
\n
\n

To create a PutBucketReplication request, you must have\n s3:PutReplicationConfiguration permissions for the bucket.\n \n

\n

By default, a resource owner, in this case the Amazon Web Services account that created the bucket,\n can perform this operation. The resource owner can also grant others permissions to perform\n the operation. For more information about permissions, see Specifying Permissions in a\n Policy and Managing Access Permissions to\n Your Amazon S3 Resources.

\n \n

To perform this operation, the user or role performing the action must have the\n iam:PassRole permission.

\n
\n
\n
\n

The following operations are related to PutBucketReplication:

\n ", + "smithy.api#examples": [ + { + "title": "Set replication configuration on a bucket", + "documentation": "The following example sets replication configuration on a bucket.", + "input": { + "Bucket": "examplebucket", + "ReplicationConfiguration": { + "Role": "arn:aws:iam::123456789012:role/examplerole", + "Rules": [ + { + "Prefix": "", + "Status": "Enabled", + "Destination": { + "Bucket": "arn:aws:s3:::destinationbucket", + "StorageClass": "STANDARD" + } + } + ] + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?replication", @@ -30626,6 +32771,18 @@ "requestChecksumRequired": true }, "smithy.api#documentation": "

Sets the request payment configuration for a bucket. By default, the bucket owner pays\n for downloads from the bucket. This configuration parameter enables the bucket owner (only)\n to specify that the person requesting the download will be charged for the download. For\n more information, see Requester Pays\n Buckets.

\n

The following operations are related to PutBucketRequestPayment:

\n ", + "smithy.api#examples": [ + { + "title": "Set request payment configuration on a bucket.", + "documentation": "The following example sets request payment configuration on a bucket so that person requesting the download is charged.", + "input": { + "Bucket": "examplebucket", + "RequestPaymentConfiguration": { + "Payer": "Requester" + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?requestPayment", @@ -30696,6 +32853,27 @@ "requestChecksumRequired": true }, "smithy.api#documentation": "

Sets the tags for a bucket.

\n

Use tags to organize your Amazon Web Services bill to reflect your own cost structure. To do this,\n sign up to get your Amazon Web Services account bill with tag key values included. Then, to see the cost\n of combined resources, organize your billing information according to resources with the\n same tag key values. For example, you can tag several resources with a specific application\n name, and then organize your billing information to see the total cost of that application\n across several services. For more information, see Cost Allocation and\n Tagging and Using Cost Allocation in Amazon S3 Bucket\n Tags.

\n \n

When this operation sets the tags for a bucket, it will overwrite any current tags\n the bucket already has. You cannot use this operation to add tags to an existing list of\n tags.

\n
\n

To use this operation, you must have permissions to perform the\n s3:PutBucketTagging action. The bucket owner has this permission by default\n and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources.

\n

\n PutBucketTagging has the following special errors:

\n
    \n
  • \n

    Error code: InvalidTagError\n

    \n \n
  • \n
  • \n

    Error code: MalformedXMLError\n

    \n
      \n
    • \n

      Description: The XML provided does not match the schema.

      \n
    • \n
    \n
  • \n
  • \n

    Error code: OperationAbortedError \n

    \n
      \n
    • \n

      Description: A conflicting conditional action is currently in progress\n against this resource. Please try again.

      \n
    • \n
    \n
  • \n
  • \n

    Error code: InternalError\n

    \n
      \n
    • \n

      Description: The service was unable to apply the provided tag to the\n bucket.

      \n
    • \n
    \n
  • \n
\n

The following operations are related to PutBucketTagging:

\n ", + "smithy.api#examples": [ + { + "title": "Set tags on a bucket", + "documentation": "The following example sets tags on a bucket. Any existing tags are replaced.", + "input": { + "Bucket": "examplebucket", + "Tagging": { + "TagSet": [ + { + "Key": "Key1", + "Value": "Value1" + }, + { + "Key": "Key2", + "Value": "Value2" + } + ] + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?tagging", @@ -30766,6 +32944,19 @@ "requestChecksumRequired": true }, "smithy.api#documentation": "

Sets the versioning state of an existing bucket.

\n

You can set the versioning state with one of the following values:

\n

\n Enabled—Enables versioning for the objects in the\n bucket. All objects added to the bucket receive a unique version ID.

\n

\n Suspended—Disables versioning for the objects in the\n bucket. All objects added to the bucket receive the version ID null.

\n

If the versioning state has never been set on a bucket, it has no versioning state; a\n GetBucketVersioning request does not return a versioning state value.

\n

In order to enable MFA Delete, you must be the bucket owner. If you are the bucket owner\n and want to enable MFA Delete in the bucket versioning configuration, you must include the\n x-amz-mfa request header and the Status and the\n MfaDelete request elements in a request to set the versioning state of the\n bucket.

\n \n

If you have an object expiration lifecycle configuration in your non-versioned bucket and\n you want to maintain the same permanent delete behavior when you enable versioning, you\n must add a noncurrent expiration policy. The noncurrent expiration lifecycle configuration will\n manage the deletes of the noncurrent object versions in the version-enabled bucket. (A\n version-enabled bucket maintains one current and zero or more noncurrent object\n versions.) For more information, see Lifecycle and Versioning.

\n
\n

The following operations are related to PutBucketVersioning:

\n ", + "smithy.api#examples": [ + { + "title": "Set versioning configuration on a bucket", + "documentation": "The following example sets versioning configuration on bucket. The configuration enables versioning on the bucket.", + "input": { + "Bucket": "examplebucket", + "VersioningConfiguration": { + "MFADelete": "Disabled", + "Status": "Enabled" + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?versioning", @@ -30843,6 +33034,24 @@ "requestChecksumRequired": true }, "smithy.api#documentation": "

Sets the configuration of the website that is specified in the website\n subresource. To configure a bucket as a website, you can add this subresource on the bucket\n with website configuration information such as the file name of the index document and any\n redirect rules. For more information, see Hosting Websites on Amazon S3.

\n

This PUT action requires the S3:PutBucketWebsite permission. By default,\n only the bucket owner can configure the website attached to a bucket; however, bucket\n owners can allow other users to set the website configuration by writing a bucket policy\n that grants them the S3:PutBucketWebsite permission.

\n

To redirect all website requests sent to the bucket's website endpoint, you add a\n website configuration with the following elements. Because all requests are sent to another\n website, you don't need to provide index document name for the bucket.

\n
    \n
  • \n

    \n WebsiteConfiguration\n

    \n
  • \n
  • \n

    \n RedirectAllRequestsTo\n

    \n
  • \n
  • \n

    \n HostName\n

    \n
  • \n
  • \n

    \n Protocol\n

    \n
  • \n
\n

If you want granular control over redirects, you can use the following elements to add\n routing rules that describe conditions for redirecting requests and information about the\n redirect destination. In this case, the website configuration must provide an index\n document for the bucket, because some requests might not be redirected.

\n
    \n
  • \n

    \n WebsiteConfiguration\n

    \n
  • \n
  • \n

    \n IndexDocument\n

    \n
  • \n
  • \n

    \n Suffix\n

    \n
  • \n
  • \n

    \n ErrorDocument\n

    \n
  • \n
  • \n

    \n Key\n

    \n
  • \n
  • \n

    \n RoutingRules\n

    \n
  • \n
  • \n

    \n RoutingRule\n

    \n
  • \n
  • \n

    \n Condition\n

    \n
  • \n
  • \n

    \n HttpErrorCodeReturnedEquals\n

    \n
  • \n
  • \n

    \n KeyPrefixEquals\n

    \n
  • \n
  • \n

    \n Redirect\n

    \n
  • \n
  • \n

    \n Protocol\n

    \n
  • \n
  • \n

    \n HostName\n

    \n
  • \n
  • \n

    \n ReplaceKeyPrefixWith\n

    \n
  • \n
  • \n

    \n ReplaceKeyWith\n

    \n
  • \n
  • \n

    \n HttpRedirectCode\n

    \n
  • \n
\n

Amazon S3 has a limitation of 50 routing rules per website configuration. If you require more\n than 50 routing rules, you can use object redirect. For more information, see Configuring an\n Object Redirect in the Amazon S3 User Guide.

", + "smithy.api#examples": [ + { + "title": "Set website configuration on a bucket", + "documentation": "The following example adds website configuration to a bucket.", + "input": { + "Bucket": "examplebucket", + "ContentMD5": "", + "WebsiteConfiguration": { + "IndexDocument": { + "Suffix": "index.html" + }, + "ErrorDocument": { + "Key": "error.html" + } + } + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}?website", @@ -30911,7 +33120,23 @@ "aws.protocols#httpChecksum": { "requestAlgorithmMember": "ChecksumAlgorithm" }, - "smithy.api#documentation": "

Adds an object to a bucket. You must have WRITE permissions on a bucket to add an object\n to it.

\n \n

Amazon S3 never adds partial objects; if you receive a success response, Amazon S3 added the\n entire object to the bucket. You cannot use PutObject to only update a\n single piece of metadata for an existing object. You must put the entire object with\n updated metadata if you want to update some values.

\n
\n

Amazon S3 is a distributed system. If it receives multiple write requests for the same object\n simultaneously, it overwrites all but the last object written. To prevent objects from\n being deleted or overwritten, you can use Amazon S3 Object\n Lock.

\n

To ensure that data is not corrupted traversing the network, use the\n Content-MD5 header. When you use this header, Amazon S3 checks the object\n against the provided MD5 value and, if they do not match, returns an error. Additionally,\n you can calculate the MD5 while putting an object to Amazon S3 and compare the returned ETag to\n the calculated MD5 value.

\n \n
    \n
  • \n

    To successfully complete the PutObject request, you must have the\n s3:PutObject in your IAM permissions.

    \n
  • \n
  • \n

    To successfully change the objects acl of your PutObject request,\n you must have the s3:PutObjectAcl in your IAM permissions.

    \n
  • \n
  • \n

    To successfully set the tag-set with your PutObject request, you\n must have the s3:PutObjectTagging in your IAM permissions.

    \n
  • \n
  • \n

    The Content-MD5 header is required for any request to upload an\n object with a retention period configured using Amazon S3 Object Lock. For more\n information about Amazon S3 Object Lock, see Amazon S3 Object Lock\n Overview in the Amazon S3 User Guide.

    \n
  • \n
\n
\n

You have three mutually exclusive options to protect data using server-side encryption\n in Amazon S3, depending on how you choose to manage the encryption keys. Specifically, the\n encryption key options are Amazon S3 managed keys (SSE-S3), Amazon Web Services KMS keys (SSE-KMS), and\n customer-provided keys (SSE-C). Amazon S3 encrypts data with server-side encryption by using\n Amazon S3 managed keys (SSE-S3) by default. You can optionally tell Amazon S3 to encrypt data at by\n rest using server-side encryption with other key options. For more information, see Using\n Server-Side Encryption.

\n

When adding a new object, you can use headers to grant ACL-based permissions to\n individual Amazon Web Services accounts or to predefined groups defined by Amazon S3. These permissions are\n then added to the ACL on the object. By default, all objects are private. Only the owner\n has full access control. For more information, see Access Control List (ACL) Overview\n and Managing\n ACLs Using the REST API.

\n

If the bucket that you're uploading objects to uses the bucket owner enforced setting\n for S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that\n use this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control\n canned ACL or an equivalent form of this ACL expressed in the XML format. PUT requests that\n contain other ACLs (for example, custom grants to certain Amazon Web Services accounts) fail and return a\n 400 error with the error code AccessControlListNotSupported.\n For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, all\n objects written to the bucket by any account will be owned by the bucket owner.

\n
\n

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

\n

If you enable versioning for a bucket, Amazon S3 automatically generates a unique version ID\n for the object being stored. Amazon S3 returns this ID in the response. When you enable\n versioning for a bucket, if Amazon S3 receives multiple write requests for the same object\n simultaneously, it stores all of the objects. For more information about versioning, see\n Adding Objects to\n Versioning-Enabled Buckets. For information about returning the versioning state\n of a bucket, see GetBucketVersioning.

\n

For more information about related Amazon S3 APIs, see the following:

\n ", + "smithy.api#documentation": "

Adds an object to a bucket. You must have WRITE permissions on a bucket to add an object\n to it.

\n \n

Amazon S3 never adds partial objects; if you receive a success response, Amazon S3 added the\n entire object to the bucket. You cannot use PutObject to only update a\n single piece of metadata for an existing object. You must put the entire object with\n updated metadata if you want to update some values.

\n
\n

Amazon S3 is a distributed system. If it receives multiple write requests for the same object\n simultaneously, it overwrites all but the last object written. To prevent objects from\n being deleted or overwritten, you can use Amazon S3 Object\n Lock.

\n

To ensure that data is not corrupted traversing the network, use the\n Content-MD5 header. When you use this header, Amazon S3 checks the object\n against the provided MD5 value and, if they do not match, returns an error. Additionally,\n you can calculate the MD5 while putting an object to Amazon S3 and compare the returned ETag to\n the calculated MD5 value.

\n \n
    \n
  • \n

    To successfully complete the PutObject request, you must have the\n s3:PutObject in your IAM permissions.

    \n
  • \n
  • \n

    To successfully change the objects acl of your PutObject request,\n you must have the s3:PutObjectAcl in your IAM permissions.

    \n
  • \n
  • \n

    To successfully set the tag-set with your PutObject request, you\n must have the s3:PutObjectTagging in your IAM permissions.

    \n
  • \n
  • \n

    The Content-MD5 header is required for any request to upload an\n object with a retention period configured using Amazon S3 Object Lock. For more\n information about Amazon S3 Object Lock, see Amazon S3 Object Lock\n Overview in the Amazon S3 User Guide.

    \n
  • \n
\n
\n

You have four mutually exclusive options to protect data using server-side encryption in\n Amazon S3, depending on how you choose to manage the encryption keys. Specifically, the\n encryption key options are Amazon S3 managed keys (SSE-S3), Amazon Web Services KMS keys (SSE-KMS or\n DSSE-KMS), and customer-provided keys (SSE-C). Amazon S3 encrypts data with server-side\n encryption by using Amazon S3 managed keys (SSE-S3) by default. You can optionally tell Amazon S3 to\n encrypt data at rest by using server-side encryption with other key options. For more\n information, see Using Server-Side\n Encryption.

\n

When adding a new object, you can use headers to grant ACL-based permissions to\n individual Amazon Web Services accounts or to predefined groups defined by Amazon S3. These permissions are\n then added to the ACL on the object. By default, all objects are private. Only the owner\n has full access control. For more information, see Access Control List (ACL) Overview\n and Managing\n ACLs Using the REST API.

\n

If the bucket that you're uploading objects to uses the bucket owner enforced setting\n for S3 Object Ownership, ACLs are disabled and no longer affect permissions. Buckets that\n use this setting only accept PUT requests that don't specify an ACL or PUT requests that\n specify bucket owner full control ACLs, such as the bucket-owner-full-control\n canned ACL or an equivalent form of this ACL expressed in the XML format. PUT requests that\n contain other ACLs (for example, custom grants to certain Amazon Web Services accounts) fail and return a\n 400 error with the error code AccessControlListNotSupported.\n For more information, see Controlling ownership of\n objects and disabling ACLs in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for Object Ownership, all\n objects written to the bucket by any account will be owned by the bucket owner.

\n
\n

By default, Amazon S3 uses the STANDARD Storage Class to store newly created objects. The\n STANDARD storage class provides high durability and high availability. Depending on\n performance needs, you can specify a different Storage Class. Amazon S3 on Outposts only uses\n the OUTPOSTS Storage Class. For more information, see Storage Classes in the\n Amazon S3 User Guide.

\n

If you enable versioning for a bucket, Amazon S3 automatically generates a unique version ID\n for the object being stored. Amazon S3 returns this ID in the response. When you enable\n versioning for a bucket, if Amazon S3 receives multiple write requests for the same object\n simultaneously, it stores all of the objects. For more information about versioning, see\n Adding Objects to\n Versioning-Enabled Buckets. For information about returning the versioning state\n of a bucket, see GetBucketVersioning.

\n

For more information about related Amazon S3 APIs, see the following:

\n ", + "smithy.api#examples": [ + { + "title": "To upload an object and specify optional tags", + "documentation": "The following example uploads an object. The request specifies optional object tags. The bucket is versioned, therefore S3 returns version ID of the newly created object.", + "input": { + "Body": "c:\\HappyFace.jpg", + "Bucket": "examplebucket", + "Key": "HappyFace.jpg", + "Tagging": "key1=value1&key2=value2" + }, + "output": { + "VersionId": "psM2sYY4.o1501dSx8wMvnkOzSBB.V4a", + "ETag": "\"6805f2cfc46c0f04559748bb039d69ae\"" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?x-id=PutObject", @@ -30938,6 +33163,20 @@ "requestChecksumRequired": true }, "smithy.api#documentation": "

Uses the acl subresource to set the access control list (ACL) permissions\n for a new or existing object in an S3 bucket. You must have WRITE_ACP\n permission to set the ACL of an object. For more information, see What\n permissions can I grant? in the Amazon S3 User Guide.

\n

This action is not supported by Amazon S3 on Outposts.

\n

Depending on your application needs, you can choose to set the ACL on an object using\n either the request body or the headers. For example, if you have an existing application\n that updates a bucket ACL using the request body, you can continue to use that approach.\n For more information, see Access Control List (ACL) Overview\n in the Amazon S3 User Guide.

\n \n

If your bucket uses the bucket owner enforced setting for S3 Object Ownership, ACLs\n are disabled and no longer affect permissions. You must use policies to grant access to\n your bucket and the objects in it. Requests to set ACLs or update ACLs fail and return\n the AccessControlListNotSupported error code. Requests to read ACLs are\n still supported. For more information, see Controlling object\n ownership in the Amazon S3 User Guide.

\n
\n
\n
Permissions
\n
\n

You can set access permissions using one of the following methods:

\n
    \n
  • \n

    Specify a canned ACL with the x-amz-acl request header. Amazon S3 supports\n a set of predefined ACLs, known as canned ACLs. Each canned ACL has a predefined set\n of grantees and permissions. Specify the canned ACL name as the value of\n x-amz-acl. If you use this header, you cannot use other access\n control-specific headers in your request. For more information, see Canned\n ACL.

    \n
  • \n
  • \n

    Specify access permissions explicitly with the x-amz-grant-read,\n x-amz-grant-read-acp, x-amz-grant-write-acp, and\n x-amz-grant-full-control headers. When using these headers, you\n specify explicit access permissions and grantees (Amazon Web Services accounts or Amazon S3 groups) who\n will receive the permission. If you use these ACL-specific headers, you cannot use\n x-amz-acl header to set a canned ACL. These parameters map to the set\n of permissions that Amazon S3 supports in an ACL. For more information, see Access Control\n List (ACL) Overview.

    \n

    You specify each grantee as a type=value pair, where the type is one of the\n following:

    \n
      \n
    • \n

      \n id – if the value specified is the canonical user ID of an\n Amazon Web Services account

      \n
    • \n
    • \n

      \n uri – if you are granting permissions to a predefined\n group

      \n
    • \n
    • \n

      \n emailAddress – if the value specified is the email address of\n an Amazon Web Services account

      \n \n

      Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

      \n
        \n
      • \n

        US East (N. Virginia)

        \n
      • \n
      • \n

        US West (N. California)

        \n
      • \n
      • \n

        US West (Oregon)

        \n
      • \n
      • \n

        Asia Pacific (Singapore)

        \n
      • \n
      • \n

        Asia Pacific (Sydney)

        \n
      • \n
      • \n

        Asia Pacific (Tokyo)

        \n
      • \n
      • \n

        Europe (Ireland)

        \n
      • \n
      • \n

        South America (São Paulo)

        \n
      • \n
      \n

      For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

      \n
      \n
    • \n
    \n

    For example, the following x-amz-grant-read header grants list\n objects permission to the two Amazon Web Services accounts identified by their email\n addresses.

    \n

    \n x-amz-grant-read: emailAddress=\"xyz@amazon.com\",\n emailAddress=\"abc@amazon.com\" \n

    \n
  • \n
\n

You can use either a canned ACL or specify access permissions explicitly. You cannot do\n both.

\n
\n
Grantee Values
\n
\n

You can specify the person (grantee) to whom you're assigning access rights (using\n request elements) in the following ways:

\n
    \n
  • \n

    By the person's ID:

    \n

    \n <>ID<><>GranteesEmail<>\n \n

    \n

    DisplayName is optional and ignored in the request.

    \n
  • \n
  • \n

    By URI:

    \n

    \n <>http://acs.amazonaws.com/groups/global/AuthenticatedUsers<>\n

    \n
  • \n
  • \n

    By Email address:

    \n

    \n <>Grantees@email.com<>lt;/Grantee>\n

    \n

    The grantee is resolved to the CanonicalUser and, in a response to a GET Object\n acl request, appears as the CanonicalUser.

    \n \n

    Using email addresses to specify a grantee is only supported in the following Amazon Web Services Regions:

    \n
      \n
    • \n

      US East (N. Virginia)

      \n
    • \n
    • \n

      US West (N. California)

      \n
    • \n
    • \n

      US West (Oregon)

      \n
    • \n
    • \n

      Asia Pacific (Singapore)

      \n
    • \n
    • \n

      Asia Pacific (Sydney)

      \n
    • \n
    • \n

      Asia Pacific (Tokyo)

      \n
    • \n
    • \n

      Europe (Ireland)

      \n
    • \n
    • \n

      South America (São Paulo)

      \n
    • \n
    \n

    For a list of all the Amazon S3 supported Regions and endpoints, see Regions and Endpoints in the Amazon Web Services General Reference.

    \n
    \n
  • \n
\n
\n
Versioning
\n
\n

The ACL of an object is set at the object version level. By default, PUT sets the ACL of\n the current version of an object. To set the ACL of a different version, use the\n versionId subresource.

\n
\n
\n

The following operations are related to PutObjectAcl:

\n ", + "smithy.api#examples": [ + { + "title": "To grant permissions using object ACL", + "documentation": "The following example adds grants to an object ACL. The first permission grants user1 and user2 FULL_CONTROL and the AllUsers group READ permission.", + "input": { + "AccessControlPolicy": {}, + "Bucket": "examplebucket", + "GrantFullControl": "emailaddress=user1@example.com,emailaddress=user2@example.com", + "GrantRead": "uri=http://acs.amazonaws.com/groups/global/AllUsers", + "Key": "HappyFace.jpg" + }, + "output": {} + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?acl", @@ -31318,7 +33557,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -31346,7 +33585,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If x-amz-server-side-encryption is has a valid value of\n aws:kms, this header specifies the ID of the Amazon Web Services Key Management Service\n (Amazon Web Services KMS) symmetric encryption customer managed key that was used for the object.

", + "smithy.api#documentation": "

If x-amz-server-side-encryption has a valid value of aws:kms\n or aws:kms:dsse, this header specifies the ID of the Key Management Service (KMS)\n symmetric encryption customer managed key that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -31361,7 +33600,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the uploaded object uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the uploaded object uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -31543,7 +33782,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms, aws:kms:dsse).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -31585,7 +33824,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If x-amz-server-side-encryption has a valid value of aws:kms,\n this header specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object. If you specify\n x-amz-server-side-encryption:aws:kms, but do not provide\n x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the Amazon Web Services managed key to\n protect the data. If the KMS key does not exist in the same account issuing the command,\n you must use the full ARN and not just the ID.

", + "smithy.api#documentation": "

If x-amz-server-side-encryption has a valid value of aws:kms\n or aws:kms:dsse, this header specifies the ID of the Key Management Service (KMS)\n symmetric encryption customer managed key that was used for the object. If you specify\n x-amz-server-side-encryption:aws:kms or\n x-amz-server-side-encryption:aws:kms:dsse, but do not provide\n x-amz-server-side-encryption-aws-kms-key-id, Amazon S3 uses the Amazon Web Services managed key\n (aws/s3) to protect the data. If the KMS key does not exist in the same\n account that's issuing the command, you must use the full ARN and not just the ID.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -31600,7 +33839,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using AWS KMS (SSE-KMS). Setting this header to true\n causes Amazon S3 to use an S3 Bucket Key for object encryption with SSE-KMS.

\n

Specifying this header with a PUT action doesn’t affect bucket-level settings for S3\n Bucket Key.

", + "smithy.api#documentation": "

Specifies whether Amazon S3 should use an S3 Bucket Key for object encryption with\n server-side encryption using Key Management Service (KMS) keys (SSE-KMS). Setting this header to\n true causes Amazon S3 to use an S3 Bucket Key for object encryption with\n SSE-KMS.

\n

Specifying this header with a PUT action doesn’t affect bucket-level settings for S3\n Bucket Key.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -31776,6 +34015,31 @@ "requestChecksumRequired": true }, "smithy.api#documentation": "

Sets the supplied tag-set to an object that already exists in a bucket.

\n

A tag is a key-value pair. You can associate tags with an object by sending a PUT\n request against the tagging subresource that is associated with the object. You can\n retrieve tags by sending a GET request. For more information, see GetObjectTagging.

\n

For tagging-related restrictions related to characters and encodings, see Tag\n Restrictions. Note that Amazon S3 limits the maximum number of tags to 10 tags per\n object.

\n

To use this operation, you must have permission to perform the\n s3:PutObjectTagging action. By default, the bucket owner has this\n permission and can grant this permission to others.

\n

To put tags of any other version, use the versionId query parameter. You\n also need permission for the s3:PutObjectVersionTagging action.

\n

For information about the Amazon S3 object tagging feature, see Object Tagging.

\n

\n PutObjectTagging has the following special errors:

\n
    \n
  • \n
      \n
    • \n

      \n Code: InvalidTagError \n

      \n
    • \n
    • \n

      \n Cause: The tag provided was not a valid tag. This error can occur\n if the tag did not pass input validation. For more information, see Object\n Tagging.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: MalformedXMLError \n

      \n
    • \n
    • \n

      \n Cause: The XML provided does not match the schema.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: OperationAbortedError \n

      \n
    • \n
    • \n

      \n Cause: A conflicting conditional action is currently in progress\n against this resource. Please try again.\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: InternalError\n

      \n
    • \n
    • \n

      \n Cause: The service was unable to apply the provided tag to the\n object.\n

      \n
    • \n
    \n
  • \n
\n

The following operations are related to PutObjectTagging:

\n ", + "smithy.api#examples": [ + { + "title": "To add tags to an existing object", + "documentation": "The following example adds tags to an existing object.", + "input": { + "Bucket": "examplebucket", + "Key": "HappyFace.jpg", + "Tagging": { + "TagSet": [ + { + "Key": "Key3", + "Value": "Value3" + }, + { + "Key": "Key4", + "Value": "Value4" + } + ] + } + }, + "output": { + "VersionId": "null" + } + } + ], "smithy.api#http": { "method": "PUT", "uri": "/{Bucket}/{Key+}?tagging", @@ -32471,7 +34735,24 @@ "aws.protocols#httpChecksum": { "requestAlgorithmMember": "ChecksumAlgorithm" }, - "smithy.api#documentation": "

Restores an archived copy of an object back into Amazon S3

\n

This action is not supported by Amazon S3 on Outposts.

\n

This action performs the following types of requests:

\n
    \n
  • \n

    \n select - Perform a select query on an archived object

    \n
  • \n
  • \n

    \n restore an archive - Restore an archived object

    \n
  • \n
\n

For more information about the S3 structure in the request body, see the\n following:

\n \n

Define the SQL expression for the SELECT type of restoration for your\n query in the request body's SelectParameters structure. You can use\n expressions like the following examples.

\n
    \n
  • \n

    The following expression returns all records from the specified\n object.

    \n

    \n SELECT * FROM Object\n

    \n
  • \n
  • \n

    Assuming that you are not using any headers for data stored in the object,\n you can specify columns with positional headers.

    \n

    \n SELECT s._1, s._2 FROM Object s WHERE s._3 > 100\n

    \n
  • \n
  • \n

    If you have headers and you set the fileHeaderInfo in the\n CSV structure in the request body to USE, you can\n specify headers in the query. (If you set the fileHeaderInfo field\n to IGNORE, the first row is skipped for the query.) You cannot mix\n ordinal positions with header column names.

    \n

    \n SELECT s.Id, s.FirstName, s.SSN FROM S3Object s\n

    \n
  • \n
\n

When making a select request, you can also do the following:

\n
    \n
  • \n

    To expedite your queries, specify the Expedited tier. For more\n information about tiers, see \"Restoring Archives,\" later in this topic.

    \n
  • \n
  • \n

    Specify details about the data serialization format of both the input object that\n is being queried and the serialization of the CSV-encoded query results.

    \n
  • \n
\n

The following are additional important facts about the select feature:

\n
    \n
  • \n

    The output results are new Amazon S3 objects. Unlike archive retrievals, they are\n stored until explicitly deleted-manually or through a lifecycle configuration.

    \n
  • \n
  • \n

    You can issue more than one select request on the same Amazon S3 object. Amazon S3 doesn't\n duplicate requests, so avoid issuing duplicate requests.

    \n
  • \n
  • \n

    Amazon S3 accepts a select request even if the object has already been restored. A\n select request doesn’t return error response 409.

    \n
  • \n
\n
\n
Permissions
\n
\n

To use this operation, you must have permissions to perform the\n s3:RestoreObject action. The bucket owner has this permission by default\n and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n
\n
Restoring objects
\n
\n

Objects that you archive to the S3 Glacier Flexible Retrieval or\n S3 Glacier Deep Archive storage class, and S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers, are not accessible in real time. For objects in the\n S3 Glacier Flexible Retrieval or S3 Glacier Deep Archive storage\n classes, you must first initiate a restore request, and then wait until a temporary copy of\n the object is available. If you want a permanent copy of the object, create a copy of it in\n the Amazon S3 Standard storage class in your S3 bucket. To access an archived object, you must\n restore the object for the duration (number of days) that you specify. For objects in the\n Archive Access or Deep Archive Access tiers of S3 Intelligent-Tiering, you must first\n initiate a restore request, and then wait until the object is moved into the Frequent\n Access tier.

\n

To restore a specific object version, you can provide a version ID. If you don't provide\n a version ID, Amazon S3 restores the current version.

\n

When restoring an archived object, you can specify one of the following data access tier\n options in the Tier element of the request body:

\n
    \n
  • \n

    \n Expedited - Expedited retrievals allow you to quickly access your\n data stored in the S3 Glacier Flexible Retrieval storage class or\n S3 Intelligent-Tiering Archive tier when occasional urgent requests for restoring archives\n are required. For all but the largest archived objects (250 MB+), data accessed using\n Expedited retrievals is typically made available within 1–5 minutes. Provisioned\n capacity ensures that retrieval capacity for Expedited retrievals is available when\n you need it. Expedited retrievals and provisioned capacity are not available for\n objects stored in the S3 Glacier Deep Archive storage class or\n S3 Intelligent-Tiering Deep Archive tier.

    \n
  • \n
  • \n

    \n Standard - Standard retrievals allow you to access any of your\n archived objects within several hours. This is the default option for retrieval\n requests that do not specify the retrieval option. Standard retrievals typically\n finish within 3–5 hours for objects stored in the S3 Glacier Flexible\n Retrieval storage class or S3 Intelligent-Tiering Archive tier. They typically finish within\n 12 hours for objects stored in the S3 Glacier Deep Archive storage class or\n S3 Intelligent-Tiering Deep Archive tier. Standard retrievals are free for objects stored in\n S3 Intelligent-Tiering.

    \n
  • \n
  • \n

    \n Bulk - Bulk retrievals free for objects stored in the S3 Glacier\n Flexible Retrieval and S3 Intelligent-Tiering storage classes, enabling you to\n retrieve large amounts, even petabytes, of data at no cost. Bulk retrievals typically\n finish within 5–12 hours for objects stored in the S3 Glacier\n Flexible Retrieval storage class or S3 Intelligent-Tiering Archive tier. Bulk retrievals are\n also the lowest-cost retrieval option when restoring objects from\n S3 Glacier Deep Archive. They typically finish within 48 hours for objects\n stored in the S3 Glacier Deep Archive storage class or S3 Intelligent-Tiering Deep Archive\n tier.

    \n
  • \n
\n

For more information about archive retrieval options and provisioned capacity for\n Expedited data access, see Restoring Archived Objects in\n the Amazon S3 User Guide.

\n

You can use Amazon S3 restore speed upgrade to change the restore speed to a faster speed\n while it is in progress. For more information, see Upgrading the speed of an in-progress restore in the\n Amazon S3 User Guide.

\n

To get the status of object restoration, you can send a HEAD request.\n Operations return the x-amz-restore header, which provides information about\n the restoration status, in the response. You can use Amazon S3 event notifications to notify you\n when a restore is initiated or completed. For more information, see Configuring Amazon S3\n Event Notifications in the Amazon S3 User Guide.

\n

After restoring an archived object, you can update the restoration period by reissuing\n the request with a new period. Amazon S3 updates the restoration period relative to the current\n time and charges only for the request-there are no data transfer charges. You cannot\n update the restoration period when Amazon S3 is actively processing your current restore request\n for the object.

\n

If your bucket has a lifecycle configuration with a rule that includes an expiration\n action, the object expiration overrides the life span that you specify in a restore\n request. For example, if you restore an object copy for 10 days, but the object is\n scheduled to expire in 3 days, Amazon S3 deletes the object in 3 days. For more information\n about lifecycle configuration, see PutBucketLifecycleConfiguration and Object Lifecycle Management\n in Amazon S3 User Guide.

\n
\n
Responses
\n
\n

A successful action returns either the 200 OK or 202 Accepted\n status code.

\n
    \n
  • \n

    If the object is not previously restored, then Amazon S3 returns 202\n Accepted in the response.

    \n
  • \n
  • \n

    If the object is previously restored, Amazon S3 returns 200 OK in the\n response.

    \n
  • \n
\n
    \n
  • \n

    Special errors:

    \n
      \n
    • \n

      \n Code: RestoreAlreadyInProgress\n

      \n
    • \n
    • \n

      \n Cause: Object restore is already in progress. (This error does not\n apply to SELECT type requests.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: GlacierExpeditedRetrievalNotAvailable\n

      \n
    • \n
    • \n

      \n Cause: expedited retrievals are currently not available. Try again\n later. (Returned if there is insufficient capacity to process the Expedited\n request. This error applies only to Expedited retrievals and not to\n S3 Standard or Bulk retrievals.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 503\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: N/A\n

      \n
    • \n
    \n
  • \n
\n
\n
\n

The following operations are related to RestoreObject:

\n ", + "smithy.api#documentation": "

Restores an archived copy of an object back into Amazon S3

\n

This action is not supported by Amazon S3 on Outposts.

\n

This action performs the following types of requests:

\n
    \n
  • \n

    \n select - Perform a select query on an archived object

    \n
  • \n
  • \n

    \n restore an archive - Restore an archived object

    \n
  • \n
\n

For more information about the S3 structure in the request body, see the\n following:

\n \n

Define the SQL expression for the SELECT type of restoration for your\n query in the request body's SelectParameters structure. You can use\n expressions like the following examples.

\n
    \n
  • \n

    The following expression returns all records from the specified\n object.

    \n

    \n SELECT * FROM Object\n

    \n
  • \n
  • \n

    Assuming that you are not using any headers for data stored in the object,\n you can specify columns with positional headers.

    \n

    \n SELECT s._1, s._2 FROM Object s WHERE s._3 > 100\n

    \n
  • \n
  • \n

    If you have headers and you set the fileHeaderInfo in the\n CSV structure in the request body to USE, you can\n specify headers in the query. (If you set the fileHeaderInfo field\n to IGNORE, the first row is skipped for the query.) You cannot mix\n ordinal positions with header column names.

    \n

    \n SELECT s.Id, s.FirstName, s.SSN FROM S3Object s\n

    \n
  • \n
\n

When making a select request, you can also do the following:

\n
    \n
  • \n

    To expedite your queries, specify the Expedited tier. For more\n information about tiers, see \"Restoring Archives,\" later in this topic.

    \n
  • \n
  • \n

    Specify details about the data serialization format of both the input object that\n is being queried and the serialization of the CSV-encoded query results.

    \n
  • \n
\n

The following are additional important facts about the select feature:

\n
    \n
  • \n

    The output results are new Amazon S3 objects. Unlike archive retrievals, they are\n stored until explicitly deleted-manually or through a lifecycle configuration.

    \n
  • \n
  • \n

    You can issue more than one select request on the same Amazon S3 object. Amazon S3 doesn't\n duplicate requests, so avoid issuing duplicate requests.

    \n
  • \n
  • \n

    Amazon S3 accepts a select request even if the object has already been restored. A\n select request doesn’t return error response 409.

    \n
  • \n
\n
\n
Permissions
\n
\n

To use this operation, you must have permissions to perform the\n s3:RestoreObject action. The bucket owner has this permission by default\n and can grant this permission to others. For more information about permissions, see Permissions Related to Bucket Subresource Operations and Managing\n Access Permissions to Your Amazon S3 Resources in the\n Amazon S3 User Guide.

\n
\n
Restoring objects
\n
\n

Objects that you archive to the S3 Glacier Flexible Retrieval Flexible Retrieval or\n S3 Glacier Deep Archive storage class, and S3 Intelligent-Tiering Archive or\n S3 Intelligent-Tiering Deep Archive tiers, are not accessible in real time. For objects in the\n S3 Glacier Flexible Retrieval Flexible Retrieval or S3 Glacier Deep Archive storage\n classes, you must first initiate a restore request, and then wait until a temporary copy of\n the object is available. If you want a permanent copy of the object, create a copy of it in\n the Amazon S3 Standard storage class in your S3 bucket. To access an archived object, you must\n restore the object for the duration (number of days) that you specify. For objects in the\n Archive Access or Deep Archive Access tiers of S3 Intelligent-Tiering, you must first\n initiate a restore request, and then wait until the object is moved into the Frequent\n Access tier.

\n

To restore a specific object version, you can provide a version ID. If you don't provide\n a version ID, Amazon S3 restores the current version.

\n

When restoring an archived object, you can specify one of the following data access tier\n options in the Tier element of the request body:

\n
    \n
  • \n

    \n Expedited - Expedited retrievals allow you to quickly access your\n data stored in the S3 Glacier Flexible Retrieval Flexible Retrieval storage class or\n S3 Intelligent-Tiering Archive tier when occasional urgent requests for restoring archives\n are required. For all but the largest archived objects (250 MB+), data accessed using\n Expedited retrievals is typically made available within 1–5 minutes. Provisioned\n capacity ensures that retrieval capacity for Expedited retrievals is available when\n you need it. Expedited retrievals and provisioned capacity are not available for\n objects stored in the S3 Glacier Deep Archive storage class or\n S3 Intelligent-Tiering Deep Archive tier.

    \n
  • \n
  • \n

    \n Standard - Standard retrievals allow you to access any of your\n archived objects within several hours. This is the default option for retrieval\n requests that do not specify the retrieval option. Standard retrievals typically\n finish within 3–5 hours for objects stored in the S3 Glacier Flexible Retrieval Flexible\n Retrieval storage class or S3 Intelligent-Tiering Archive tier. They typically finish within\n 12 hours for objects stored in the S3 Glacier Deep Archive storage class or\n S3 Intelligent-Tiering Deep Archive tier. Standard retrievals are free for objects stored in\n S3 Intelligent-Tiering.

    \n
  • \n
  • \n

    \n Bulk - Bulk retrievals free for objects stored in the S3 Glacier\n Flexible Retrieval and S3 Intelligent-Tiering storage classes, enabling you to\n retrieve large amounts, even petabytes, of data at no cost. Bulk retrievals typically\n finish within 5–12 hours for objects stored in the S3 Glacier Flexible Retrieval\n Flexible Retrieval storage class or S3 Intelligent-Tiering Archive tier. Bulk retrievals are\n also the lowest-cost retrieval option when restoring objects from\n S3 Glacier Deep Archive. They typically finish within 48 hours for objects\n stored in the S3 Glacier Deep Archive storage class or S3 Intelligent-Tiering Deep Archive\n tier.

    \n
  • \n
\n

For more information about archive retrieval options and provisioned capacity for\n Expedited data access, see Restoring Archived Objects in\n the Amazon S3 User Guide.

\n

You can use Amazon S3 restore speed upgrade to change the restore speed to a faster speed\n while it is in progress. For more information, see Upgrading the speed of an in-progress restore in the\n Amazon S3 User Guide.

\n

To get the status of object restoration, you can send a HEAD request.\n Operations return the x-amz-restore header, which provides information about\n the restoration status, in the response. You can use Amazon S3 event notifications to notify you\n when a restore is initiated or completed. For more information, see Configuring Amazon S3\n Event Notifications in the Amazon S3 User Guide.

\n

After restoring an archived object, you can update the restoration period by reissuing\n the request with a new period. Amazon S3 updates the restoration period relative to the current\n time and charges only for the request-there are no data transfer charges. You cannot\n update the restoration period when Amazon S3 is actively processing your current restore request\n for the object.

\n

If your bucket has a lifecycle configuration with a rule that includes an expiration\n action, the object expiration overrides the life span that you specify in a restore\n request. For example, if you restore an object copy for 10 days, but the object is\n scheduled to expire in 3 days, Amazon S3 deletes the object in 3 days. For more information\n about lifecycle configuration, see PutBucketLifecycleConfiguration and Object Lifecycle Management\n in Amazon S3 User Guide.

\n
\n
Responses
\n
\n

A successful action returns either the 200 OK or 202 Accepted\n status code.

\n
    \n
  • \n

    If the object is not previously restored, then Amazon S3 returns 202\n Accepted in the response.

    \n
  • \n
  • \n

    If the object is previously restored, Amazon S3 returns 200 OK in the\n response.

    \n
  • \n
\n
    \n
  • \n

    Special errors:

    \n
      \n
    • \n

      \n Code: RestoreAlreadyInProgress\n

      \n
    • \n
    • \n

      \n Cause: Object restore is already in progress. (This error does not\n apply to SELECT type requests.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 409 Conflict\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: Client\n

      \n
    • \n
    \n
  • \n
  • \n
      \n
    • \n

      \n Code: GlacierExpeditedRetrievalNotAvailable\n

      \n
    • \n
    • \n

      \n Cause: expedited retrievals are currently not available. Try again\n later. (Returned if there is insufficient capacity to process the Expedited\n request. This error applies only to Expedited retrievals and not to\n S3 Standard or Bulk retrievals.)\n

      \n
    • \n
    • \n

      \n HTTP Status Code: 503\n

      \n
    • \n
    • \n

      \n SOAP Fault Code Prefix: N/A\n

      \n
    • \n
    \n
  • \n
\n
\n
\n

The following operations are related to RestoreObject:

\n ", + "smithy.api#examples": [ + { + "title": "To restore an archived object", + "documentation": "The following example restores for one day an archived copy of an object back into Amazon S3 bucket.", + "input": { + "Bucket": "examplebucket", + "Key": "archivedobjectkey", + "RestoreRequest": { + "Days": 1, + "GlacierJobParameters": { + "Tier": "Expedited" + } + } + }, + "output": {} + } + ], "smithy.api#http": { "method": "POST", "uri": "/{Bucket}/{Key+}?restore&x-id=RestoreObject", @@ -32747,7 +35028,7 @@ "KeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

Specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric encryption\n customer managed key to use for encrypting inventory reports.

", + "smithy.api#documentation": "

Specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key to use for\n encrypting inventory reports.

", "smithy.api#required": {} } } @@ -32808,7 +35089,7 @@ "target": "com.amazonaws.s3#SelectObjectContentOutput" }, "traits": { - "smithy.api#documentation": "

This action filters the contents of an Amazon S3 object based on a simple structured query\n language (SQL) statement. In the request, along with the SQL expression, you must also\n specify a data serialization format (JSON, CSV, or Apache Parquet) of the object. Amazon S3 uses\n this format to parse object data into records, and returns only records that match the\n specified SQL expression. You must also specify the data serialization format for the\n response.

\n

This action is not supported by Amazon S3 on Outposts.

\n

For more information about Amazon S3 Select, see Selecting Content from\n Objects and SELECT\n Command in the Amazon S3 User Guide.

\n

\n
\n
Permissions
\n
\n

You must have s3:GetObject permission for this operation. Amazon S3 Select does\n not support anonymous access. For more information about permissions, see Specifying\n Permissions in a Policy in the Amazon S3 User Guide.

\n
\n
Object Data Formats
\n
\n

You can use Amazon S3 Select to query objects that have the following format\n properties:

\n
    \n
  • \n

    \n CSV, JSON, and Parquet - Objects must be in CSV, JSON, or\n Parquet format.

    \n
  • \n
  • \n

    \n UTF-8 - UTF-8 is the only encoding type Amazon S3 Select\n supports.

    \n
  • \n
  • \n

    \n GZIP or BZIP2 - CSV and JSON files can be compressed using\n GZIP or BZIP2. GZIP and BZIP2 are the only compression formats that Amazon S3 Select\n supports for CSV and JSON files. Amazon S3 Select supports columnar compression for\n Parquet using GZIP or Snappy. Amazon S3 Select does not support whole-object compression\n for Parquet objects.

    \n
  • \n
  • \n

    \n Server-side encryption - Amazon S3 Select supports querying\n objects that are protected with server-side encryption.

    \n

    For objects that are encrypted with customer-provided encryption keys (SSE-C), you\n must use HTTPS, and you must use the headers that are documented in the GetObject. For more information about SSE-C, see Server-Side\n Encryption (Using Customer-Provided Encryption Keys) in the\n Amazon S3 User Guide.

    \n

    For objects that are encrypted with Amazon S3 managed keys (SSE-S3) and Amazon Web Services KMS keys\n (SSE-KMS), server-side encryption is handled transparently, so you don't need to\n specify anything. For more information about server-side encryption, including SSE-S3\n and SSE-KMS, see Protecting Data Using\n Server-Side Encryption in the Amazon S3 User Guide.

    \n
  • \n
\n
\n
Working with the Response Body
\n
\n

Given the response size is unknown, Amazon S3 Select streams the response as a series of\n messages and includes a Transfer-Encoding header with chunked as\n its value in the response. For more information, see Appendix: SelectObjectContent\n Response.

\n
\n
GetObject Support
\n
\n

The SelectObjectContent action does not support the following\n GetObject functionality. For more information, see GetObject.

\n
    \n
  • \n

    \n Range: Although you can specify a scan range for an Amazon S3 Select request\n (see SelectObjectContentRequest - ScanRange in the request parameters),\n you cannot specify the range of bytes of an object to return.

    \n
  • \n
  • \n

    GLACIER, DEEP_ARCHIVE and REDUCED_REDUNDANCY storage classes: You cannot specify\n the GLACIER, DEEP_ARCHIVE, or REDUCED_REDUNDANCY storage classes. For\n more information, about storage classes see Storage\n Classes in the Amazon S3 User Guide.

    \n
  • \n
\n
\n
Special Errors
\n
\n

For a list of special errors for this operation, see List of\n SELECT Object Content Error Codes\n

\n
\n
\n

The following operations are related to SelectObjectContent:

\n ", + "smithy.api#documentation": "

This action filters the contents of an Amazon S3 object based on a simple structured query\n language (SQL) statement. In the request, along with the SQL expression, you must also\n specify a data serialization format (JSON, CSV, or Apache Parquet) of the object. Amazon S3 uses\n this format to parse object data into records, and returns only records that match the\n specified SQL expression. You must also specify the data serialization format for the\n response.

\n

This action is not supported by Amazon S3 on Outposts.

\n

For more information about Amazon S3 Select, see Selecting Content from\n Objects and SELECT\n Command in the Amazon S3 User Guide.

\n

\n
\n
Permissions
\n
\n

You must have s3:GetObject permission for this operation. Amazon S3 Select does\n not support anonymous access. For more information about permissions, see Specifying\n Permissions in a Policy in the Amazon S3 User Guide.

\n
\n
Object Data Formats
\n
\n

You can use Amazon S3 Select to query objects that have the following format\n properties:

\n
    \n
  • \n

    \n CSV, JSON, and Parquet - Objects must be in CSV, JSON, or\n Parquet format.

    \n
  • \n
  • \n

    \n UTF-8 - UTF-8 is the only encoding type Amazon S3 Select\n supports.

    \n
  • \n
  • \n

    \n GZIP or BZIP2 - CSV and JSON files can be compressed using\n GZIP or BZIP2. GZIP and BZIP2 are the only compression formats that Amazon S3 Select\n supports for CSV and JSON files. Amazon S3 Select supports columnar compression for\n Parquet using GZIP or Snappy. Amazon S3 Select does not support whole-object compression\n for Parquet objects.

    \n
  • \n
  • \n

    \n Server-side encryption - Amazon S3 Select supports querying\n objects that are protected with server-side encryption.

    \n

    For objects that are encrypted with customer-provided encryption keys (SSE-C), you\n must use HTTPS, and you must use the headers that are documented in the GetObject. For more information about SSE-C, see Server-Side\n Encryption (Using Customer-Provided Encryption Keys) in the\n Amazon S3 User Guide.

    \n

    For objects that are encrypted with Amazon S3 managed keys (SSE-S3) and Amazon Web Services KMS keys\n (SSE-KMS), server-side encryption is handled transparently, so you don't need to\n specify anything. For more information about server-side encryption, including SSE-S3\n and SSE-KMS, see Protecting Data Using\n Server-Side Encryption in the Amazon S3 User Guide.

    \n
  • \n
\n
\n
Working with the Response Body
\n
\n

Given the response size is unknown, Amazon S3 Select streams the response as a series of\n messages and includes a Transfer-Encoding header with chunked as\n its value in the response. For more information, see Appendix: SelectObjectContent\n Response.

\n
\n
GetObject Support
\n
\n

The SelectObjectContent action does not support the following\n GetObject functionality. For more information, see GetObject.

\n
    \n
  • \n

    \n Range: Although you can specify a scan range for an Amazon S3 Select request\n (see SelectObjectContentRequest - ScanRange in the request parameters),\n you cannot specify the range of bytes of an object to return.

    \n
  • \n
  • \n

    The GLACIER, DEEP_ARCHIVE, and REDUCED_REDUNDANCY storage classes, or the ARCHIVE_ACCESS and \n DEEP_ARCHIVE_ACCESS access tiers of \n the INTELLIGENT_TIERING storage class: You cannot query objects in \n the GLACIER, DEEP_ARCHIVE, or REDUCED_REDUNDANCY storage classes, nor objects in the \n ARCHIVE_ACCESS or \n DEEP_ARCHIVE_ACCESS access tiers of \n the INTELLIGENT_TIERING storage class. For\n more information about storage classes, see Using Amazon S3 storage\n classes in the Amazon S3 User Guide.

    \n
  • \n
\n
\n
Special Errors
\n
\n

For a list of special errors for this operation, see List of\n SELECT Object Content Error Codes\n

\n
\n
\n

The following operations are related to SelectObjectContent:

\n ", "smithy.api#http": { "method": "POST", "uri": "/{Bucket}/{Key+}?select&select-type=2&x-id=SelectObjectContent", @@ -33016,6 +35297,12 @@ "traits": { "smithy.api#enumValue": "aws:kms" } + }, + "aws_kms_dsse": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "aws:kms:dsse" + } } } }, @@ -33694,7 +35981,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -33715,7 +36002,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key that was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n that was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -33723,7 +36010,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, @@ -33893,7 +36180,7 @@ "ServerSideEncryption": { "target": "com.amazonaws.s3#ServerSideEncryption", "traits": { - "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", + "smithy.api#documentation": "

The server-side encryption algorithm used when storing this object in Amazon S3 (for example,\n AES256, aws:kms).

", "smithy.api#httpHeader": "x-amz-server-side-encryption" } }, @@ -33949,7 +36236,7 @@ "SSEKMSKeyId": { "target": "com.amazonaws.s3#SSEKMSKeyId", "traits": { - "smithy.api#documentation": "

If present, specifies the ID of the Amazon Web Services Key Management Service (Amazon Web Services KMS) symmetric\n encryption customer managed key was used for the object.

", + "smithy.api#documentation": "

If present, specifies the ID of the Key Management Service (KMS) symmetric encryption customer managed key\n was used for the object.

", "smithy.api#httpHeader": "x-amz-server-side-encryption-aws-kms-key-id" } }, @@ -33957,7 +36244,7 @@ "target": "com.amazonaws.s3#BucketKeyEnabled", "traits": { "smithy.api#default": false, - "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Amazon Web Services KMS (SSE-KMS).

", + "smithy.api#documentation": "

Indicates whether the multipart upload uses an S3 Bucket Key for server-side encryption\n with Key Management Service (KMS) keys (SSE-KMS).

", "smithy.api#httpHeader": "x-amz-server-side-encryption-bucket-key-enabled" } }, diff --git a/aws/sdk/aws-models/sdk-endpoints.json b/aws/sdk/aws-models/sdk-endpoints.json index b94964b8f8..b74e1ac35d 100644 --- a/aws/sdk/aws-models/sdk-endpoints.json +++ b/aws/sdk/aws-models/sdk-endpoints.json @@ -1076,6 +1076,7 @@ "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-central-2" : { }, @@ -2212,6 +2213,7 @@ "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-central-2" : { }, @@ -2836,6 +2838,7 @@ "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-central-2" : { }, @@ -3211,6 +3214,7 @@ "ca-central-1" : { }, "eu-central-1" : { }, "eu-north-1" : { }, + "eu-south-1" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -3271,6 +3275,13 @@ "deprecated" : true, "hostname" : "cognito-identity-fips.us-east-2.amazonaws.com" }, + "fips-us-west-1" : { + "credentialScope" : { + "region" : "us-west-1" + }, + "deprecated" : true, + "hostname" : "cognito-identity-fips.us-west-1.amazonaws.com" + }, "fips-us-west-2" : { "credentialScope" : { "region" : "us-west-2" @@ -3292,7 +3303,12 @@ "tags" : [ "fips" ] } ] }, - "us-west-1" : { }, + "us-west-1" : { + "variants" : [ { + "hostname" : "cognito-identity-fips.us-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "us-west-2" : { "variants" : [ { "hostname" : "cognito-identity-fips.us-west-2.amazonaws.com", @@ -4250,6 +4266,7 @@ "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "datasync-fips.ca-central-1.amazonaws.com", @@ -6612,6 +6629,7 @@ } ] }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "fms-fips.ca-central-1.amazonaws.com", @@ -6945,6 +6963,7 @@ "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "fsx-fips.ca-central-1.amazonaws.com", @@ -7955,11 +7974,6 @@ } }, "iot" : { - "defaults" : { - "credentialScope" : { - "service" : "execute-api" - } - }, "endpoints" : { "ap-east-1" : { }, "ap-northeast-1" : { }, @@ -7979,37 +7993,22 @@ "eu-west-2" : { }, "eu-west-3" : { }, "fips-ca-central-1" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.ca-central-1.amazonaws.com" }, "fips-us-east-1" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-east-1.amazonaws.com" }, "fips-us-east-2" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-east-2.amazonaws.com" }, "fips-us-west-1" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-west-1.amazonaws.com" }, "fips-us-west-2" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-west-2.amazonaws.com" }, @@ -8529,6 +8528,7 @@ "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { "variants" : [ { "hostname" : "kafka-fips.ca-central-1.amazonaws.com", @@ -8635,6 +8635,7 @@ "ap-southeast-2" : { }, "ca-central-1" : { }, "eu-west-1" : { }, + "eu-west-2" : { }, "fips-us-east-1" : { "credentialScope" : { "region" : "us-east-1" @@ -8720,7 +8721,11 @@ "hostname" : "kendra-ranking.ap-southeast-4.api.aws" }, "ca-central-1" : { - "hostname" : "kendra-ranking.ca-central-1.api.aws" + "hostname" : "kendra-ranking.ca-central-1.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.ca-central-1.api.aws", + "tags" : [ "fips" ] + } ] }, "eu-central-2" : { "hostname" : "kendra-ranking.eu-central-2.api.aws" @@ -8750,16 +8755,28 @@ "hostname" : "kendra-ranking.sa-east-1.api.aws" }, "us-east-1" : { - "hostname" : "kendra-ranking.us-east-1.api.aws" + "hostname" : "kendra-ranking.us-east-1.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.us-east-1.api.aws", + "tags" : [ "fips" ] + } ] }, "us-east-2" : { - "hostname" : "kendra-ranking.us-east-2.api.aws" + "hostname" : "kendra-ranking.us-east-2.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.us-east-2.api.aws", + "tags" : [ "fips" ] + } ] }, "us-west-1" : { "hostname" : "kendra-ranking.us-west-1.api.aws" }, "us-west-2" : { - "hostname" : "kendra-ranking.us-west-2.api.aws" + "hostname" : "kendra-ranking.us-west-2.api.aws", + "variants" : [ { + "hostname" : "kendra-ranking-fips.us-west-2.api.aws", + "tags" : [ "fips" ] + } ] } } }, @@ -10301,6 +10318,25 @@ "us-west-2" : { } } }, + "mediapackagev2" : { + "endpoints" : { + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "eu-central-1" : { }, + "eu-north-1" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "mediastore" : { "endpoints" : { "ap-northeast-1" : { }, @@ -10449,6 +10485,7 @@ "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, + "eu-west-3" : { }, "me-central-1" : { }, "me-south-1" : { }, "sa-east-1" : { }, @@ -10477,13 +10514,17 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -13041,6 +13082,7 @@ "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-central-2" : { }, @@ -13908,6 +13950,7 @@ "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, + "ap-southeast-4" : { }, "ca-central-1" : { }, "eu-central-1" : { }, "eu-central-2" : { }, @@ -13977,6 +14020,8 @@ "securitylake" : { "endpoints" : { "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-south-1" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "eu-central-1" : { }, @@ -13985,6 +14030,7 @@ "sa-east-1" : { }, "us-east-1" : { }, "us-east-2" : { }, + "us-west-1" : { }, "us-west-2" : { } } }, @@ -16056,8 +16102,11 @@ }, "transcribestreaming" : { "endpoints" : { + "af-south-1" : { }, "ap-northeast-1" : { }, "ap-northeast-2" : { }, + "ap-south-1" : { }, + "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ca-central-1" : { }, "eu-central-1" : { }, @@ -16145,6 +16194,7 @@ "ap-northeast-2" : { }, "ap-northeast-3" : { }, "ap-south-1" : { }, + "ap-south-2" : { }, "ap-southeast-1" : { }, "ap-southeast-2" : { }, "ap-southeast-3" : { }, @@ -16155,8 +16205,10 @@ } ] }, "eu-central-1" : { }, + "eu-central-2" : { }, "eu-north-1" : { }, "eu-south-1" : { }, + "eu-south-2" : { }, "eu-west-1" : { }, "eu-west-2" : { }, "eu-west-3" : { }, @@ -16283,6 +16335,37 @@ } } }, + "verifiedpermissions" : { + "endpoints" : { + "af-south-1" : { }, + "ap-east-1" : { }, + "ap-northeast-1" : { }, + "ap-northeast-2" : { }, + "ap-northeast-3" : { }, + "ap-south-1" : { }, + "ap-south-2" : { }, + "ap-southeast-1" : { }, + "ap-southeast-2" : { }, + "ap-southeast-3" : { }, + "ap-southeast-4" : { }, + "ca-central-1" : { }, + "eu-central-1" : { }, + "eu-central-2" : { }, + "eu-north-1" : { }, + "eu-south-1" : { }, + "eu-south-2" : { }, + "eu-west-1" : { }, + "eu-west-2" : { }, + "eu-west-3" : { }, + "me-central-1" : { }, + "me-south-1" : { }, + "sa-east-1" : { }, + "us-east-1" : { }, + "us-east-2" : { }, + "us-west-1" : { }, + "us-west-2" : { } + } + }, "voice-chime" : { "endpoints" : { "ap-northeast-1" : { }, @@ -18234,11 +18317,6 @@ } }, "iot" : { - "defaults" : { - "credentialScope" : { - "service" : "execute-api" - } - }, "endpoints" : { "cn-north-1" : { }, "cn-northwest-1" : { } @@ -21028,23 +21106,12 @@ } }, "iot" : { - "defaults" : { - "credentialScope" : { - "service" : "execute-api" - } - }, "endpoints" : { "fips-us-gov-east-1" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-gov-east-1.amazonaws.com" }, "fips-us-gov-west-1" : { - "credentialScope" : { - "service" : "execute-api" - }, "deprecated" : true, "hostname" : "iot-fips.us-gov-west-1.amazonaws.com" }, @@ -21519,6 +21586,36 @@ "us-gov-west-1" : { } } }, + "mgn" : { + "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-gov-east-1.amazonaws.com" + }, + "fips-us-gov-west-1" : { + "credentialScope" : { + "region" : "us-gov-west-1" + }, + "deprecated" : true, + "hostname" : "mgn-fips.us-gov-west-1.amazonaws.com" + }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, + "us-gov-west-1" : { + "variants" : [ { + "hostname" : "mgn-fips.us-gov-west-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + } + } + }, "models.lex" : { "defaults" : { "credentialScope" : { @@ -22516,6 +22613,12 @@ } } }, + "simspaceweaver" : { + "endpoints" : { + "us-gov-east-1" : { }, + "us-gov-west-1" : { } + } + }, "sms" : { "endpoints" : { "fips-us-gov-east-1" : { @@ -23151,6 +23254,13 @@ }, "workspaces" : { "endpoints" : { + "fips-us-gov-east-1" : { + "credentialScope" : { + "region" : "us-gov-east-1" + }, + "deprecated" : true, + "hostname" : "workspaces-fips.us-gov-east-1.amazonaws.com" + }, "fips-us-gov-west-1" : { "credentialScope" : { "region" : "us-gov-west-1" @@ -23158,7 +23268,12 @@ "deprecated" : true, "hostname" : "workspaces-fips.us-gov-west-1.amazonaws.com" }, - "us-gov-east-1" : { }, + "us-gov-east-1" : { + "variants" : [ { + "hostname" : "workspaces-fips.us-gov-east-1.amazonaws.com", + "tags" : [ "fips" ] + } ] + }, "us-gov-west-1" : { "variants" : [ { "hostname" : "workspaces-fips.us-gov-west-1.amazonaws.com", @@ -23331,6 +23446,12 @@ "us-iso-west-1" : { } } }, + "dlm" : { + "endpoints" : { + "us-iso-east-1" : { }, + "us-iso-west-1" : { } + } + }, "dms" : { "defaults" : { "variants" : [ { @@ -23659,7 +23780,8 @@ }, "route53resolver" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } }, "runtime.sagemaker" : { @@ -23760,7 +23882,8 @@ }, "tagging" : { "endpoints" : { - "us-iso-east-1" : { } + "us-iso-east-1" : { }, + "us-iso-west-1" : { } } }, "transcribe" : { @@ -24252,6 +24375,23 @@ "regionRegex" : "^eu\\-isoe\\-\\w+\\-\\d+$", "regions" : { }, "services" : { } + }, { + "defaults" : { + "hostname" : "{service}.{region}.{dnsSuffix}", + "protocols" : [ "https" ], + "signatureVersions" : [ "v4" ], + "variants" : [ { + "dnsSuffix" : "csp.hci.ic.gov", + "hostname" : "{service}-fips.{region}.{dnsSuffix}", + "tags" : [ "fips" ] + } ] + }, + "dnsSuffix" : "csp.hci.ic.gov", + "partition" : "aws-iso-f", + "partitionName" : "AWS ISOF", + "regionRegex" : "^us\\-isof\\-\\w+\\-\\d+$", + "regions" : { }, + "services" : { } } ], "version" : 3 } diff --git a/aws/sdk/aws-models/timestream-query.json b/aws/sdk/aws-models/timestream-query.json index 9545cf1b2b..29edeadd2a 100644 --- a/aws/sdk/aws-models/timestream-query.json +++ b/aws/sdk/aws-models/timestream-query.json @@ -2637,8 +2637,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2650,8 +2650,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2663,8 +2663,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2676,8 +2676,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2689,8 +2689,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2702,8 +2702,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2715,8 +2715,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2728,8 +2728,8 @@ }, "params": { "Region": "cn-north-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2741,8 +2741,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2754,8 +2754,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false } }, { @@ -2767,8 +2767,8 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": true, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2780,8 +2780,19 @@ }, "params": { "Region": "us-gov-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2793,8 +2804,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2806,8 +2828,19 @@ }, "params": { "Region": "us-iso-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -2819,8 +2852,19 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": true + "UseFIPS": true, + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -2832,8 +2876,8 @@ }, "params": { "Region": "us-isob-east-1", - "UseDualStack": false, - "UseFIPS": false + "UseFIPS": false, + "UseDualStack": false } }, { @@ -2845,8 +2889,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2858,8 +2902,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2870,8 +2914,8 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": false, "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -2882,10 +2926,16 @@ }, "params": { "Region": "us-east-1", - "UseDualStack": true, "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" diff --git a/aws/sdk/aws-models/timestream-write.json b/aws/sdk/aws-models/timestream-write.json index 568ab520a0..a3378d042b 100644 --- a/aws/sdk/aws-models/timestream-write.json +++ b/aws/sdk/aws-models/timestream-write.json @@ -236,13 +236,13 @@ "ReportConfiguration": { "target": "com.amazonaws.timestreamwrite#ReportConfiguration", "traits": { - "smithy.api#documentation": "

Report configuration for a batch load task. This contains details about where error reports are stored.

" + "smithy.api#documentation": "

Report configuration for a batch load task. This contains details about where error\n reports are stored.

" } }, "DataModelConfiguration": { "target": "com.amazonaws.timestreamwrite#DataModelConfiguration", "traits": { - "smithy.api#documentation": "

Data model configuration for a batch load task. This contains details about where a data model for a batch load task is stored.

" + "smithy.api#documentation": "

Data model configuration for a batch load task. This contains details about where a data\n model for a batch load task is stored.

" } }, "TargetDatabaseName": { @@ -376,7 +376,7 @@ "aws.api#clientDiscoveredEndpoint": { "required": true }, - "smithy.api#documentation": "

Creates a new Timestream batch load task. A batch load task processes data from\n a CSV source in an S3 location and writes to a Timestream table. A mapping from\n source to target is defined in a batch load task. Errors and events are written to a report\n at an S3 location. For the report, if the KMS key is not specified, the\n batch load task will be encrypted with a Timestream managed KMS key\n located in your account. For more information, see Amazon Web Services managed\n keys. Service quotas apply. For\n details, see code\n sample.

" + "smithy.api#documentation": "

Creates a new Timestream batch load task. A batch load task processes data from\n a CSV source in an S3 location and writes to a Timestream table. A mapping from\n source to target is defined in a batch load task. Errors and events are written to a report\n at an S3 location. For the report, if the KMS key is not specified, the\n report will be encrypted with an S3 managed key when SSE_S3 is the option.\n Otherwise an error is thrown. For more information, see Amazon Web Services managed\n keys. Service quotas apply. For\n details, see code\n sample.

" } }, "com.amazonaws.timestreamwrite#CreateBatchLoadTaskRequest": { @@ -600,6 +600,12 @@ "traits": { "smithy.api#documentation": "

Contains properties to set on the table when enabling magnetic store writes.

" } + }, + "Schema": { + "target": "com.amazonaws.timestreamwrite#Schema", + "traits": { + "smithy.api#documentation": "

The schema of the table.

" + } } }, "traits": { @@ -1709,7 +1715,7 @@ "Value": { "target": "com.amazonaws.timestreamwrite#StringValue2048", "traits": { - "smithy.api#documentation": "

The value for the MeasureValue.

", + "smithy.api#documentation": "

The value for the MeasureValue. For information, see Data\n types.

", "smithy.api#required": {} } }, @@ -1909,6 +1915,78 @@ } } }, + "com.amazonaws.timestreamwrite#PartitionKey": { + "type": "structure", + "members": { + "Type": { + "target": "com.amazonaws.timestreamwrite#PartitionKeyType", + "traits": { + "smithy.api#documentation": "

The type of the partition key. Options are DIMENSION (dimension key) and MEASURE\n (measure key).

", + "smithy.api#required": {} + } + }, + "Name": { + "target": "com.amazonaws.timestreamwrite#SchemaName", + "traits": { + "smithy.api#documentation": "

The name of the attribute used for a dimension key.

" + } + }, + "EnforcementInRecord": { + "target": "com.amazonaws.timestreamwrite#PartitionKeyEnforcementLevel", + "traits": { + "smithy.api#documentation": "

The level of enforcement for the specification of a dimension key in ingested records.\n Options are REQUIRED (dimension key must be specified) and OPTIONAL (dimension key does not\n have to be specified).

" + } + } + }, + "traits": { + "smithy.api#documentation": "

An attribute used in partitioning data in a table. A dimension key partitions data\n using the values of the dimension specified by the dimension-name as partition key, while a\n measure key partitions data using measure names (values of the 'measure_name' column).\n

" + } + }, + "com.amazonaws.timestreamwrite#PartitionKeyEnforcementLevel": { + "type": "enum", + "members": { + "REQUIRED": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "REQUIRED" + } + }, + "OPTIONAL": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "OPTIONAL" + } + } + } + }, + "com.amazonaws.timestreamwrite#PartitionKeyList": { + "type": "list", + "member": { + "target": "com.amazonaws.timestreamwrite#PartitionKey" + }, + "traits": { + "smithy.api#length": { + "min": 1 + } + } + }, + "com.amazonaws.timestreamwrite#PartitionKeyType": { + "type": "enum", + "members": { + "DIMENSION": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "DIMENSION" + } + }, + "MEASURE": { + "target": "smithy.api#Unit", + "traits": { + "smithy.api#enumValue": "MEASURE" + } + } + } + }, "com.amazonaws.timestreamwrite#Record": { "type": "structure", "members": { @@ -1933,7 +2011,7 @@ "MeasureValueType": { "target": "com.amazonaws.timestreamwrite#MeasureValueType", "traits": { - "smithy.api#documentation": "

Contains the data type of the measure value for the time-series data point. Default\n type is DOUBLE.

" + "smithy.api#documentation": "

Contains the data type of the measure value for the time-series data point. Default\n type is DOUBLE. For more information, see Data\n types.

" } }, "Time": { @@ -2083,7 +2161,7 @@ } }, "traits": { - "smithy.api#documentation": "

Report configuration for a batch load task. This contains details about where error reports are stored.

" + "smithy.api#documentation": "

Report configuration for a batch load task. This contains details about where error\n reports are stored.

" } }, "com.amazonaws.timestreamwrite#ReportS3Configuration": { @@ -2336,6 +2414,20 @@ } } }, + "com.amazonaws.timestreamwrite#Schema": { + "type": "structure", + "members": { + "CompositePartitionKey": { + "target": "com.amazonaws.timestreamwrite#PartitionKeyList", + "traits": { + "smithy.api#documentation": "

A non-empty list of partition keys defining the attributes used to partition the table\n data. The order of the list determines the partition hierarchy. The name and type of each\n partition key as well as the partition key order cannot be changed after the table is\n created. However, the enforcement level of each partition key can be changed.

" + } + } + }, + "traits": { + "smithy.api#documentation": "

A Schema specifies the expected data model of the table.

" + } + }, "com.amazonaws.timestreamwrite#SchemaName": { "type": "string", "traits": { @@ -2440,6 +2532,12 @@ "traits": { "smithy.api#documentation": "

Contains properties to set on the table when enabling magnetic store writes.

" } + }, + "Schema": { + "target": "com.amazonaws.timestreamwrite#Schema", + "traits": { + "smithy.api#documentation": "

The schema of the table.

" + } } }, "traits": { @@ -3085,9 +3183,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -3098,9 +3196,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": true, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -3111,9 +3209,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": true } }, { @@ -3124,9 +3222,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-east-1", "UseFIPS": false, - "Region": "us-east-1" + "UseDualStack": false } }, { @@ -3137,9 +3235,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -3150,9 +3248,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": true, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -3163,9 +3261,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": true } }, { @@ -3176,9 +3274,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "cn-north-1", "UseFIPS": false, - "Region": "cn-north-1" + "UseDualStack": false } }, { @@ -3189,9 +3287,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -3202,9 +3300,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": true, - "Region": "us-gov-east-1" + "UseDualStack": false } }, { @@ -3215,9 +3313,9 @@ } }, "params": { - "UseDualStack": true, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": true } }, { @@ -3228,9 +3326,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-gov-east-1", "UseFIPS": false, - "Region": "us-gov-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -3241,9 +3350,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": true, - "Region": "us-iso-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-iso-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-iso-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -3254,9 +3374,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-iso-east-1", "UseFIPS": false, - "Region": "us-iso-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS enabled and DualStack enabled", + "expect": { + "error": "FIPS and DualStack are enabled, but this partition does not support one or both" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": true, + "UseDualStack": true } }, { @@ -3267,9 +3398,20 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": true, - "Region": "us-isob-east-1" + "UseDualStack": false + } + }, + { + "documentation": "For region us-isob-east-1 with FIPS disabled and DualStack enabled", + "expect": { + "error": "DualStack is enabled but this partition does not support DualStack" + }, + "params": { + "Region": "us-isob-east-1", + "UseFIPS": false, + "UseDualStack": true } }, { @@ -3280,9 +3422,9 @@ } }, "params": { - "UseDualStack": false, + "Region": "us-isob-east-1", "UseFIPS": false, - "Region": "us-isob-east-1" + "UseDualStack": false } }, { @@ -3293,9 +3435,9 @@ } }, "params": { - "UseDualStack": false, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -3307,8 +3449,8 @@ } }, "params": { - "UseDualStack": false, "UseFIPS": false, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -3318,9 +3460,9 @@ "error": "Invalid Configuration: FIPS and custom endpoint are not supported" }, "params": { - "UseDualStack": false, - "UseFIPS": true, "Region": "us-east-1", + "UseFIPS": true, + "UseDualStack": false, "Endpoint": "https://example.com" } }, @@ -3330,11 +3472,17 @@ "error": "Invalid Configuration: Dualstack and custom endpoint are not supported" }, "params": { - "UseDualStack": true, - "UseFIPS": false, "Region": "us-east-1", + "UseFIPS": false, + "UseDualStack": true, "Endpoint": "https://example.com" } + }, + { + "documentation": "Missing region", + "expect": { + "error": "Invalid Configuration: Missing Region" + } } ], "version": "1.0" @@ -3536,6 +3684,12 @@ "traits": { "smithy.api#documentation": "

Contains properties to set on the table when enabling magnetic store writes.

" } + }, + "Schema": { + "target": "com.amazonaws.timestreamwrite#Schema", + "traits": { + "smithy.api#documentation": "

The schema of the table.

" + } } }, "traits": { From bb8e19a4185855a0bfcc0bbc11e30e62015eeced Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 23 Jun 2023 11:32:11 -0500 Subject: [PATCH 179/253] Remove HACK that used to put `Handle` in config bag (#2806) ## Motivation and Context Removes `HACK` that used to put `Handle` in config bag. ## Description The `HACK` was previously placed in `ServiceRuntimePlugin::config` because later in the orchestrator execution, we needed to access service config fields (through `Handle`) to build endpoint `Params` within `EndpointParamsInterceptor`. Now that service config fields are baked into a frozen layer, `EndpointParamsInterceptor` can load those fields directly from the config bag (to which the frozen layer has been added) when constructing endpoint `Params`. We can therefore remove `HACK` at this point. ## Testing - [x] Passed tests in CI ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- .../rustsdk/EndpointBuiltInsDecorator.kt | 38 +++++++----- .../amazon/smithy/rustsdk/RegionDecorator.kt | 5 +- .../EndpointParamsInterceptorGenerator.kt | 23 ++++--- .../ServiceRuntimePluginGenerator.kt | 6 +- .../config/ServiceConfigGenerator.kt | 60 ++++++++++--------- .../testutil/TestConfigCustomization.kt | 3 + .../ClientContextConfigCustomizationTest.kt | 46 +++++++++++--- 7 files changed, 118 insertions(+), 63 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt index 02922234e3..3cc4783a17 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt @@ -20,18 +20,23 @@ import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegen import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointRulesetIndex import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.symbol import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigParam import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.loadFromConfigBag import software.amazon.smithy.rust.codegen.client.smithy.generators.config.standardConfigParam +import software.amazon.smithy.rust.codegen.core.rustlang.RustType import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs 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.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization +import software.amazon.smithy.rust.codegen.core.smithy.mapRustType import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.extendIf @@ -54,22 +59,20 @@ fun ClientCodegenContext.getBuiltIn(builtIn: String): Parameter? { private fun promotedBuiltins(parameter: Parameter) = parameter == Builtins.FIPS || parameter == Builtins.DUALSTACK || parameter == Builtins.SDK_ENDPOINT +private fun configParamNewtype(parameter: Parameter, name: String, runtimeConfig: RuntimeConfig): RuntimeType { + val type = parameter.symbol().mapRustType { t -> t.stripOuter() } + return when (promotedBuiltins(parameter)) { + true -> AwsRuntimeType.awsTypes(runtimeConfig) + .resolve("endpoint_config::${name.toPascalCase()}") + + false -> configParamNewtype(name.toPascalCase(), type, runtimeConfig) + } +} + private fun ConfigParam.Builder.toConfigParam(parameter: Parameter, runtimeConfig: RuntimeConfig): ConfigParam = this.name(this.name ?: parameter.name.rustName()) - .type( - when (parameter.type!!) { - ParameterType.STRING -> RuntimeType.String.toSymbol() - ParameterType.BOOLEAN -> RuntimeType.Bool.toSymbol() - }, - ) - .newtype( - when (promotedBuiltins(parameter)) { - true -> AwsRuntimeType.awsTypes(runtimeConfig) - .resolve("endpoint_config::${this.name!!.toPascalCase()}") - - false -> configParamNewtype(this.name!!.toPascalCase(), this.type!!, runtimeConfig) - }, - ) + .type(parameter.symbol().mapRustType { t -> t.stripOuter() }) + .newtype(configParamNewtype(parameter, this.name!!, runtimeConfig)) .setterDocs(this.setterDocs ?: parameter.documentation.orNull()?.let { writable { docs(it) } }) .build() @@ -139,7 +142,12 @@ fun decoratorForBuiltIn( when (parameter.builtIn) { builtIn.builtIn -> writable { if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { - rust("$configRef.$name()") + val newtype = configParamNewtype(parameter, name, codegenContext.runtimeConfig) + val symbol = parameter.symbol().mapRustType { t -> t.stripOuter() } + rustTemplate( + """$configRef.#{load_from_service_config_layer}""", + "load_from_service_config_layer" to loadFromConfigBag(symbol.name, newtype), + ) } else { rust("$configRef.$name") } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt index 7e38722e3f..494a08f8ed 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt @@ -132,7 +132,10 @@ class RegionDecorator : ClientCodegenDecorator { return when (parameter.builtIn) { Builtins.REGION.builtIn -> writable { if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { - rust("$configRef.region().as_ref().map(|r|r.as_ref().to_owned())") + rustTemplate( + "$configRef.load::<#{Region}>().map(|r|r.as_ref().to_owned())", + "Region" to region(codegenContext.runtimeConfig).resolve("Region"), + ) } else { rust("$configRef.region.as_ref().map(|r|r.as_ref().to_owned())") } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 44245bdabe..9d282e4b27 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -13,9 +13,12 @@ import software.amazon.smithy.model.traits.EndpointTrait import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters import software.amazon.smithy.rulesengine.traits.ContextIndex import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.ClientContextConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointTypesGenerator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName import software.amazon.smithy.rust.codegen.client.smithy.generators.EndpointTraitBindings +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.configParamNewtype +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.loadFromConfigBag import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable @@ -83,11 +86,6 @@ class EndpointParamsInterceptorGenerator( #{endpoint_prefix:W} - // HACK: pull the handle out of the config bag until config is implemented right - let handle = cfg.get::>() - .expect("the handle is hacked into the config bag"); - let _config = &handle.conf; - let params = #{Params}::builder() #{param_setters} .build() @@ -109,15 +107,24 @@ class EndpointParamsInterceptorGenerator( val builtInParams = params.toList().filter { it.isBuiltIn } // first load builtins and their defaults builtInParams.forEach { param -> - endpointTypesGenerator.builtInFor(param, "_config")?.also { defaultValue -> + val config = if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + "cfg" + } else { + "_config" + } + endpointTypesGenerator.builtInFor(param, config)?.also { defaultValue -> rust(".set_${param.name.rustName()}(#W)", defaultValue) } } idx.getClientContextParams(codegenContext.serviceShape).orNull()?.parameters?.forEach { (name, param) -> - val paramName = EndpointParamsGenerator.memberName(name) val setterName = EndpointParamsGenerator.setterName(name) - rust(".$setterName(_config.$paramName())") + val inner = ClientContextConfigCustomization.toSymbol(param.type, symbolProvider) + val newtype = configParamNewtype(name, inner, codegenContext.runtimeConfig) + rustTemplate( + ".$setterName(cfg.#{load_from_service_config_layer})", + "load_from_service_config_layer" to loadFromConfigBag(inner.name, newtype), + ) } idx.getStaticContextParams(operationShape).orNull()?.parameters?.forEach { (name, param) -> diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index 0a73a283b0..dcc3373f26 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -111,6 +111,9 @@ class ServiceRuntimePluginGenerator( fun render(writer: RustWriter, customizations: List) { writer.rustTemplate( """ + // TODO(enableNewSmithyRuntimeLaunch) Remove `allow(dead_code)` as well as a field `handle` when + // the field is no longer used. + ##[allow(dead_code)] ##[derive(Debug)] pub(crate) struct ServiceRuntimePlugin { handle: #{Arc}, @@ -127,9 +130,6 @@ class ServiceRuntimePluginGenerator( use #{ConfigBagAccessors}; let mut cfg = #{Layer}::new(${codegenContext.serviceShape.id.name.dq()}); - // HACK: Put the handle into the config bag to work around config not being fully implemented yet - cfg.put(self.handle.clone()); - let http_auth_schemes = #{HttpAuthSchemes}::builder() #{http_auth_scheme_customizations} .build(); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 6e899a5e99..1278541ad0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.traits.IdempotencyTokenTrait 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.customizations.codegenScope import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -159,15 +160,37 @@ fun configParamNewtype(newtypeName: String, inner: Symbol, runtimeConfig: Runtim rustTemplate( """ ##[derive(Debug, Clone)] - pub(crate) struct $newtypeName($inner); + pub(crate) struct $newtypeName(pub(crate) $inner); impl #{Storable} for $newtypeName { - type Storer = #{StoreReplace}<$newtypeName>; + type Storer = #{StoreReplace}; } """, *codegenScope, ) } +/** + * Render an expression that loads a value from a config bag. + * + * The expression to be rendered handles a case where a newtype is stored in the config bag, but the user expects + * the underlying raw type after the newtype has been loaded from the bag. + */ +fun loadFromConfigBag(innerTypeName: String, newtype: RuntimeType): Writable = writable { + rustTemplate( + """ + load::<#{newtype}>().map(#{f}) + """, + "newtype" to newtype, + "f" to writable { + if (innerTypeName == "bool") { + rust("|ty| ty.0") + } else { + rust("|ty| ty.0.clone()") + } + }, + ) +} + /** * Config customization for a config param with no special behavior: * 1. `pub(crate)` field @@ -190,31 +213,6 @@ fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext } } - ServiceConfig.ConfigImpl -> writable { - if (runtimeMode.defaultToOrchestrator) { - rustTemplate( - """ - pub(crate) fn ${param.name}(&self) -> #{output} { - self.inner.load::<#{newtype}>().map(#{f}) - } - """, - "f" to writable { - if (param.type.name == "bool") { - rust("|ty| ty.0") - } else { - rust("|ty| ty.0.clone()") - } - }, - "newtype" to param.newtype!!, - "output" to if (param.optional) { - param.type.makeOptional() - } else { - param.type - }, - ) - } - } - ServiceConfig.BuilderStruct -> writable { if (runtimeMode.defaultToMiddleware) { rust("${param.name}: #T,", param.type.makeOptional()) @@ -430,6 +428,7 @@ class ServiceConfigGenerator( rustBlock("pub fn build(mut self) -> Config") { rustTemplate( """ + ##[allow(unused_imports)] use #{ConfigBagAccessors}; // The builder is being turned into a service config. While doing so, we'd like to avoid // requiring that items created and stored _during_ the build method be `Clone`, since they @@ -472,14 +471,17 @@ class ServiceConfigGenerator( #{Some}(self.inner.clone()) } - fn interceptors(&self, interceptors: &mut #{InterceptorRegistrar}) { - interceptors.extend(self.interceptors.iter().cloned()); + fn interceptors(&self, _interceptors: &mut #{InterceptorRegistrar}) { + #{interceptors} } } """, *codegenScope, "config" to writable { writeCustomizations(customizations, ServiceConfig.RuntimePluginConfig("cfg")) }, + "interceptors" to writable { + writeCustomizations(customizations, ServiceConfig.RuntimePluginInterceptors("_interceptors")) + }, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt index f37a925ae1..e449fe448c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt @@ -129,6 +129,9 @@ fun stubConfigProject(codegenContext: ClientCodegenContext, customization: Confi val generator = ServiceConfigGenerator(codegenContext, customizations = customizations.toList()) project.withModule(ClientRustModule.config) { generator.render(this) + if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + generator.renderRuntimePluginImplForSelf(this) + } unitTest( "config_send_sync", """ diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt index 509a86e845..c467189c24 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt @@ -12,6 +12,8 @@ import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenCont import software.amazon.smithy.rust.codegen.client.testutil.validateConfigCustomizations import software.amazon.smithy.rust.codegen.client.testutil.withSmithyRuntimeMode 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.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 @@ -37,14 +39,30 @@ class ClientContextConfigCustomizationTest { fun `client params generate a valid customization`(smithyRuntimeModeStr: String) { val project = TestWorkspace.testProject() val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) + val context = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode) project.unitTest { if (smithyRuntimeMode.defaultToOrchestrator) { - rust( + rustTemplate( """ + use #{RuntimePlugin}; let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build(); - assert_eq!(conf.a_string_param().unwrap(), "hello!"); - assert_eq!(conf.a_bool_param(), Some(true)); + assert_eq!( + conf.config() + .unwrap() + .load::() + .map(|u| u.0.clone()) + .unwrap(), + "hello!" + ); + assert_eq!( + conf.config() + .unwrap() + .load::() + .map(|u| u.0), + Some(true) + ); """, + "RuntimePlugin" to RuntimeType.runtimePlugin(context.runtimeConfig), ) } else { rust( @@ -59,12 +77,27 @@ class ClientContextConfigCustomizationTest { // unset fields project.unitTest { if (smithyRuntimeMode.defaultToOrchestrator) { - rust( + rustTemplate( """ + use #{RuntimePlugin}; let conf = crate::Config::builder().a_string_param("hello!").build(); - assert_eq!(conf.a_string_param().unwrap(), "hello!"); - assert_eq!(conf.a_bool_param(), None); + assert_eq!( + conf.config() + .unwrap() + .load::() + .map(|u| u.0.clone()) + .unwrap(), + "hello!" + ); + assert_eq!( + conf.config() + .unwrap() + .load::() + .map(|u| u.0), + None, + ); """, + "RuntimePlugin" to RuntimeType.runtimePlugin(context.runtimeConfig), ) } else { rust( @@ -76,7 +109,6 @@ class ClientContextConfigCustomizationTest { ) } } - val context = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode) validateConfigCustomizations(context, ClientContextConfigCustomization(context), project) } } From fbeaab9d3886318ac898e7605e52a9d767c21e07 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 23 Jun 2023 16:13:12 -0700 Subject: [PATCH 180/253] Remove `get`/`put` methods from `ConfigBag` and `Layer` (#2807) This PR removes the non-storable `get`/`put` methods from `ConfigBag` and `Layer`. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-http/src/user_agent.rs | 9 + .../src/glacier_interceptors.rs | 6 +- .../src/http_request_checksum.rs | 7 +- .../src/presigning_interceptors.rs | 24 +- .../aws-runtime/src/auth/sigv4.rs | 18 +- .../aws-runtime/src/invocation_id.rs | 35 ++- .../aws-runtime/src/request_info.rs | 12 +- .../aws-runtime/src/user_agent.rs | 16 +- aws/rust-runtime/aws-types/src/lib.rs | 5 + aws/rust-runtime/aws-types/src/region.rs | 4 + .../smithy/rustsdk/AwsPresigningDecorator.kt | 4 +- .../smithy/rustsdk/SigV4AuthDecorator.kt | 7 +- .../smithy/rustsdk/SigV4SigningDecorator.kt | 4 +- .../smithy/rustsdk/UserAgentDecorator.kt | 2 +- .../customizations/HttpAuthDecorator.kt | 5 +- .../HttpConnectorConfigDecorator.kt | 5 +- .../ResiliencyConfigCustomization.kt | 4 +- .../endpoint/EndpointConfigCustomization.kt | 24 +- .../EndpointParamsInterceptorGenerator.kt | 2 +- .../OperationRuntimePluginGenerator.kt | 10 +- .../ServiceRuntimePluginGenerator.kt | 7 +- .../protocol/RequestSerializerGenerator.kt | 2 +- .../protocols/HttpBoundProtocolGenerator.kt | 2 +- .../codegen/core/rustlang/CargoDependency.kt | 1 + .../aws-smithy-eventstream/src/frame.rs | 5 + rust-runtime/aws-smithy-http/src/endpoint.rs | 4 + .../aws-smithy-runtime-api/src/client/auth.rs | 27 +- .../src/client/interceptors.rs | 14 +- .../src/client/orchestrator.rs | 249 +++++++++++++----- .../src/client/request_attempts.rs | 6 + .../src/client/retries.rs | 33 ++- .../src/client/orchestrator.rs | 29 +- .../src/client/orchestrator/auth.rs | 20 +- .../src/client/orchestrator/endpoints.rs | 4 +- .../interceptors/service_clock_skew.rs | 8 +- .../client/retries/strategy/fixed_delay.rs | 6 +- .../src/client/retries/strategy/standard.rs | 83 +++--- .../client/runtime_plugin/anonymous_auth.rs | 9 +- .../src/client/test_util/deserializer.rs | 8 +- .../src/client/test_util/serializer.rs | 6 +- .../aws-smithy-runtime/src/client/timeout.rs | 6 +- .../aws-smithy-types/src/config_bag.rs | 117 ++++---- rust-runtime/aws-smithy-types/src/endpoint.rs | 5 + .../inlineable/src/serialization_settings.rs | 5 + 44 files changed, 566 insertions(+), 293 deletions(-) diff --git a/aws/rust-runtime/aws-http/src/user_agent.rs b/aws/rust-runtime/aws-http/src/user_agent.rs index 72a2e2105d..59edb2a5c3 100644 --- a/aws/rust-runtime/aws-http/src/user_agent.rs +++ b/aws/rust-runtime/aws-http/src/user_agent.rs @@ -5,6 +5,7 @@ use aws_smithy_http::middleware::MapRequest; use aws_smithy_http::operation::Request; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use aws_types::app_name::AppName; use aws_types::build_metadata::{OsFamily, BUILD_METADATA}; use aws_types::os_shim_internal::Env; @@ -211,6 +212,10 @@ impl AwsUserAgent { } } +impl Storable for AwsUserAgent { + type Storer = StoreReplace; +} + #[derive(Clone, Copy, Debug)] struct SdkMetadata { name: &'static str, @@ -246,6 +251,10 @@ impl fmt::Display for ApiMetadata { } } +impl Storable for ApiMetadata { + type Storer = StoreReplace; +} + /// Error for when an user agent metadata doesn't meet character requirements. /// /// Metadata may only have alphanumeric characters and any of these characters: diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs index 55aa9fd822..e165dad882 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -130,18 +130,18 @@ impl Interceptor for GlacierTreeHashHeaderInterceptor { context: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let maybe_loaded_body = cfg.get::(); + let maybe_loaded_body = cfg.load::(); if let Some(LoadedRequestBody::Loaded(body)) = maybe_loaded_body { let content_sha256 = add_checksum_treehash(context.request_mut(), body)?; // Override the signing payload with this precomputed hash let mut signing_config = cfg - .get::() + .load::() .ok_or("SigV4OperationSigningConfig not found")? .clone(); signing_config.signing_options.payload_override = Some(SignableBody::Precomputed(content_sha256)); - cfg.interceptor_state().put(signing_config); + cfg.interceptor_state().store_put(signing_config); } else { return Err( "the request body wasn't loaded into memory before the retry loop, \ diff --git a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs index 2cb87a04af..e438032bdc 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs @@ -132,13 +132,10 @@ fn add_checksum_for_request_body( // Body is streaming: wrap the body so it will emit a checksum as a trailer. None => { tracing::debug!("applying {checksum_algorithm:?} of the request body as a trailer"); - if let Some(mut signing_config) = cfg.get::().cloned() { + if let Some(mut signing_config) = cfg.load::().cloned() { signing_config.signing_options.payload_override = Some(SignableBody::StreamingUnsignedPayloadTrailer); - - let mut layer = Layer::new("http_body_checksum_sigv4_payload_override"); - layer.put(signing_config); - cfg.push_layer(layer); + cfg.interceptor_state().store_put(signing_config); } wrap_streaming_request_body_in_checksum_calculating_body(request, checksum_algorithm)?; } diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs index c3151503b6..2a193c08c0 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs @@ -22,6 +22,7 @@ use aws_smithy_runtime_api::client::interceptors::{ disable_interceptor, Interceptor, InterceptorRegistrar, SharedInterceptor, }; use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; +use aws_smithy_runtime_api::client::retries::DynRetryStrategy; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; @@ -48,11 +49,12 @@ impl Interceptor for SigV4PresigningInterceptor { _context: &mut BeforeSerializationInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - cfg.interceptor_state().put::( - HeaderSerializationSettings::new() - .omit_default_content_length() - .omit_default_content_type(), - ); + cfg.interceptor_state() + .store_put::( + HeaderSerializationSettings::new() + .omit_default_content_length() + .omit_default_content_type(), + ); cfg.interceptor_state() .set_request_time(SharedTimeSource::new(StaticTimeSource::new( self.config.start_time(), @@ -65,12 +67,12 @@ impl Interceptor for SigV4PresigningInterceptor { _context: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - if let Some(mut config) = cfg.get::().cloned() { + if let Some(mut config) = cfg.load::().cloned() { config.signing_options.expires_in = Some(self.config.expires()); config.signing_options.signature_type = HttpSignatureType::HttpRequestQueryParams; config.signing_options.payload_override = Some(self.payload_override.clone()); cfg.interceptor_state() - .put::(config); + .store_put::(config); Ok(()) } else { Err( @@ -101,10 +103,10 @@ impl SigV4PresigningRuntimePlugin { impl RuntimePlugin for SigV4PresigningRuntimePlugin { fn config(&self) -> Option { let mut layer = Layer::new("Presigning"); - layer.set_retry_strategy(NeverRetryStrategy::new()); - layer.put(disable_interceptor::("presigning")); - layer.put(disable_interceptor::("presigning")); - layer.put(disable_interceptor::("presigning")); + layer.set_retry_strategy(DynRetryStrategy::new(NeverRetryStrategy::new())); + layer.store_put(disable_interceptor::("presigning")); + layer.store_put(disable_interceptor::("presigning")); + layer.store_put(disable_interceptor::("presigning")); Some(layer.freeze()) } diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs index 0e64408830..ef127eb0d9 100644 --- a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -14,7 +14,7 @@ use aws_smithy_runtime_api::client::auth::{ }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpRequest}; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::Document; use aws_types::region::{Region, SigningRegion}; use aws_types::SigningService; @@ -169,6 +169,10 @@ pub struct SigV4OperationSigningConfig { pub signing_options: SigningOptions, } +impl Storable for SigV4OperationSigningConfig { + type Storer = StoreReplace; +} + /// SigV4 HTTP request signer. #[derive(Debug, Default)] pub struct SigV4HttpRequestSigner; @@ -253,11 +257,11 @@ impl SigV4HttpRequestSigner { config_bag: &'a ConfigBag, ) -> Result, SigV4SigningError> { let operation_config = config_bag - .get::() + .load::() .ok_or(SigV4SigningError::MissingOperationSigningConfig)?; - let signing_region = config_bag.get::(); - let signing_service = config_bag.get::(); + let signing_region = config_bag.load::(); + let signing_service = config_bag.load::(); let EndpointAuthSchemeConfig { signing_region_override, @@ -365,7 +369,7 @@ impl HttpRequestSigner for SigV4HttpRequestSigner { use aws_smithy_eventstream::frame::DeferredSignerSender; use event_stream::SigV4MessageSigner; - if let Some(signer_sender) = config_bag.get::() { + if let Some(signer_sender) = config_bag.load::() { let time_source = config_bag.request_time().unwrap_or_default(); signer_sender .send(Box::new(SigV4MessageSigner::new( @@ -559,7 +563,7 @@ mod tests { #[test] fn endpoint_config_overrides_region_and_service() { let mut layer = Layer::new("test"); - layer.put(SigV4OperationSigningConfig { + layer.store_put(SigV4OperationSigningConfig { region: Some(SigningRegion::from(Region::new("override-this-region"))), service: Some(SigningService::from_static("override-this-service")), signing_options: Default::default(), @@ -597,7 +601,7 @@ mod tests { #[test] fn endpoint_config_supports_fallback_when_region_or_service_are_unset() { let mut layer = Layer::new("test"); - layer.put(SigV4OperationSigningConfig { + layer.store_put(SigV4OperationSigningConfig { region: Some(SigningRegion::from(Region::new("us-east-1"))), service: Some(SigningService::from_static("qldb")), signing_options: Default::default(), diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 49d9b56c7f..6a5c178757 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -6,7 +6,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use http::{HeaderName, HeaderValue}; use std::fmt::Debug; use uuid::Uuid; @@ -24,6 +24,27 @@ pub trait InvocationIdGenerator: Debug + Send + Sync { fn generate(&self) -> Result, BoxError>; } +/// Dynamic dispatch implementation of [`InvocationIdGenerator`] +#[derive(Debug)] +pub struct DynInvocationIdGenerator(Box); + +impl DynInvocationIdGenerator { + /// Creates a new [`DynInvocationIdGenerator`]. + pub fn new(gen: impl InvocationIdGenerator + 'static) -> Self { + Self(Box::new(gen)) + } +} + +impl InvocationIdGenerator for DynInvocationIdGenerator { + fn generate(&self) -> Result, BoxError> { + self.0.generate() + } +} + +impl Storable for DynInvocationIdGenerator { + type Storer = StoreReplace; +} + /// This interceptor generates a UUID and attaches it to all request attempts made as part of this operation. #[non_exhaustive] #[derive(Debug, Default)] @@ -43,12 +64,12 @@ impl Interceptor for InvocationIdInterceptor { cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let id = cfg - .get::>() + .load::() .map(|gen| gen.generate()) .transpose()? .flatten(); cfg.interceptor_state() - .put::(id.unwrap_or_default()); + .store_put::(id.unwrap_or_default()); Ok(()) } @@ -60,7 +81,7 @@ impl Interceptor for InvocationIdInterceptor { ) -> Result<(), BoxError> { let headers = ctx.request_mut().headers_mut(); let id = cfg - .get::() + .load::() .ok_or("Expected an InvocationId in the ConfigBag but none was present")?; headers.append(AMZ_SDK_INVOCATION_ID, id.0.clone()); Ok(()) @@ -90,6 +111,10 @@ impl Default for InvocationId { } } +impl Storable for InvocationId { + type Storer = StoreReplace; +} + #[cfg(feature = "test-util")] mod test_util { use super::*; @@ -188,7 +213,7 @@ mod tests { .modify_before_transmit(&mut ctx, &mut cfg) .unwrap(); - let expected = cfg.get::().expect("invocation ID was set"); + let expected = cfg.load::().expect("invocation ID was set"); let header = expect_header(&ctx, "amz-sdk-invocation-id"); assert_eq!(expected.0, header, "the invocation ID in the config bag must match the invocation ID in the request header"); // UUID should include 32 chars and 4 dashes diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index 974ca051aa..d2ea28cc35 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -43,7 +43,7 @@ impl RequestInfoInterceptor { cfg: &ConfigBag, ) -> Option<(Cow<'static, str>, Cow<'static, str>)> { let request_attempts = cfg - .get::() + .load::() .map(|r_a| r_a.attempts()) .unwrap_or(0); let request_attempts = request_attempts.to_string(); @@ -54,7 +54,7 @@ impl RequestInfoInterceptor { &self, cfg: &ConfigBag, ) -> Option<(Cow<'static, str>, Cow<'static, str>)> { - if let Some(retry_config) = cfg.get::() { + if let Some(retry_config) = cfg.load::() { let max_attempts = retry_config.max_attempts().to_string(); Some((Cow::Borrowed("max"), Cow::Owned(max_attempts))) } else { @@ -63,9 +63,9 @@ impl RequestInfoInterceptor { } fn build_ttl_pair(&self, cfg: &ConfigBag) -> Option<(Cow<'static, str>, Cow<'static, str>)> { - let timeout_config = cfg.get::()?; + let timeout_config = cfg.load::()?; let socket_read = timeout_config.read_timeout()?; - let estimated_skew: Duration = cfg.get::().cloned()?.into(); + let estimated_skew: Duration = cfg.load::().cloned()?.into(); let current_time = SystemTime::now(); let ttl = current_time.checked_add(socket_read + estimated_skew)?; let mut timestamp = DateTime::from(ttl); @@ -191,8 +191,8 @@ mod tests { context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let mut layer = Layer::new("test"); - layer.put(RetryConfig::standard()); - layer.put( + layer.store_put(RetryConfig::standard()); + layer.store_put( TimeoutConfig::builder() .read_timeout(Duration::from_secs(30)) .build(), diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 566dd37a07..3527fbe582 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -77,19 +77,19 @@ impl Interceptor for UserAgentInterceptor { cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let api_metadata = cfg - .get::() + .load::() .ok_or(UserAgentInterceptorError::MissingApiMetadata)?; // Allow for overriding the user agent by an earlier interceptor (so, for example, // tests can use `AwsUserAgent::for_tests()`) by attempting to grab one out of the // config bag before creating one. let ua: Cow<'_, AwsUserAgent> = cfg - .get::() + .load::() .map(Cow::Borrowed) .unwrap_or_else(|| { let mut ua = AwsUserAgent::new_from_environment(Env::real(), api_metadata.clone()); - let maybe_app_name = cfg.get::(); + let maybe_app_name = cfg.load::(); if let Some(app_name) = maybe_app_name { ua.set_app_name(app_name.clone()); } @@ -139,8 +139,8 @@ mod tests { let mut context = context(); let mut layer = Layer::new("test"); - layer.put(AwsUserAgent::for_tests()); - layer.put(ApiMetadata::new("unused", "unused")); + layer.store_put(AwsUserAgent::for_tests()); + layer.store_put(ApiMetadata::new("unused", "unused")); let mut cfg = ConfigBag::of_layers(vec![layer]); let interceptor = UserAgentInterceptor::new(); @@ -165,7 +165,7 @@ mod tests { let api_metadata = ApiMetadata::new("some-service", "some-version"); let mut layer = Layer::new("test"); - layer.put(api_metadata.clone()); + layer.store_put(api_metadata.clone()); let mut config = ConfigBag::of_layers(vec![layer]); let interceptor = UserAgentInterceptor::new(); @@ -195,8 +195,8 @@ mod tests { let api_metadata = ApiMetadata::new("some-service", "some-version"); let mut layer = Layer::new("test"); - layer.put(api_metadata); - layer.put(AppName::new("my_awesome_app").unwrap()); + layer.store_put(api_metadata); + layer.store_put(AppName::new("my_awesome_app").unwrap()); let mut config = ConfigBag::of_layers(vec![layer]); let interceptor = UserAgentInterceptor::new(); diff --git a/aws/rust-runtime/aws-types/src/lib.rs b/aws/rust-runtime/aws-types/src/lib.rs index de5bfa269e..ed4e635037 100644 --- a/aws/rust-runtime/aws-types/src/lib.rs +++ b/aws/rust-runtime/aws-types/src/lib.rs @@ -25,6 +25,7 @@ pub mod sdk_config; pub use aws_smithy_client::http_connector; pub use sdk_config::SdkConfig; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::borrow::Cow; /// The name of the service used to sign this request @@ -56,3 +57,7 @@ impl From<&'static str> for SigningService { Self::from_static(service) } } + +impl Storable for SigningService { + type Storer = StoreReplace; +} diff --git a/aws/rust-runtime/aws-types/src/region.rs b/aws/rust-runtime/aws-types/src/region.rs index be09aac6da..a58f809a70 100644 --- a/aws/rust-runtime/aws-types/src/region.rs +++ b/aws/rust-runtime/aws-types/src/region.rs @@ -82,3 +82,7 @@ impl SigningRegion { SigningRegion(Cow::Borrowed(region)) } } + +impl Storable for SigningRegion { + type Storer = StoreReplace; +} diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index 0cc280449b..1e5d379618 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -363,7 +363,7 @@ class AwsPresignedFluentBuilderMethod( fn config(&self) -> Option<#{FrozenLayer}> { use #{ConfigBagAccessors}; let mut cfg = #{Layer}::new("presigning_serializer"); - cfg.set_request_serializer(#{AlternateSerializer}); + cfg.set_request_serializer(#{SharedRequestSerializer}::new(#{AlternateSerializer})); Some(cfg.freeze()) } } @@ -374,6 +374,8 @@ class AwsPresignedFluentBuilderMethod( "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "Layer" to smithyTypes.resolve("config_bag::Layer"), "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), + "SharedRequestSerializer" to RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) + .resolve("client::orchestrator::SharedRequestSerializer"), ) } }, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 42084d0600..d1cd293dca 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -91,6 +91,7 @@ private class AuthOperationCustomization(private val codegenContext: ClientCodeg val runtimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) arrayOf( + "DynAuthOptionResolver" to runtimeApi.resolve("client::auth::DynAuthOptionResolver"), "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "HttpSignatureType" to awsRuntime.resolve("auth::sigv4::HttpSignatureType"), "SIGV4_SCHEME_ID" to awsRuntime.resolve("auth::sigv4::SCHEME_ID"), @@ -122,15 +123,15 @@ private class AuthOperationCustomization(private val codegenContext: ClientCodeg signing_options.signing_optional = $signingOptional; signing_options.payload_override = #{payload_override}; - ${section.newLayerName}.put(#{SigV4OperationSigningConfig} { + ${section.newLayerName}.store_put(#{SigV4OperationSigningConfig} { region: None, service: None, signing_options, }); // TODO(enableNewSmithyRuntimeLaunch): Make auth options additive in the config bag so that multiple codegen decorators can register them - let auth_option_resolver = #{StaticAuthOptionResolver}::new( + let auth_option_resolver = #{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new( vec![#{SIGV4_SCHEME_ID}] - ); + )); ${section.newLayerName}.set_auth_option_resolver(auth_option_resolver); """, *codegenScope, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt index dd1584ffc5..0549ecf525 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt @@ -113,8 +113,8 @@ class SigV4SigningConfig( if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - layer.put(#{SigningService}::from_static(${sigV4Trait.name.dq()})); - layer.load::<#{Region}>().cloned().map(|r| layer.put(#{SigningRegion}::from(r))); + layer.store_put(#{SigningService}::from_static(${sigV4Trait.name.dq()})); + layer.load::<#{Region}>().cloned().map(|r| layer.store_put(#{SigningRegion}::from(r))); """, *codegenScope, ) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index ebeee1c85a..19395ebea6 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -207,7 +207,7 @@ class UserAgentDecorator : ClientCodegenDecorator { is ServiceConfig.BuilderBuild -> writable { if (runtimeMode.defaultToOrchestrator) { - rust("layer.put(#T.clone());", ClientRustModule.Meta.toType().resolve("API_METADATA")) + rust("layer.store_put(#T.clone());", ClientRustModule.Meta.toType().resolve("API_METADATA")) } else { rust("app_name: self.app_name,") } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index bc3d2de4f1..bbdead7100 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -43,6 +43,7 @@ fun codegenScope(runtimeConfig: RuntimeConfig): Array> { return arrayOf( "ApiKeyAuthScheme" to authHttp.resolve("ApiKeyAuthScheme"), "ApiKeyLocation" to authHttp.resolve("ApiKeyLocation"), + "DynAuthOptionResolver" to smithyRuntimeApi.resolve("client::auth::DynAuthOptionResolver"), "StaticAuthOptionResolver" to smithyRuntimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "BasicAuthScheme" to authHttp.resolve("BasicAuthScheme"), "BearerAuthScheme" to authHttp.resolve("BearerAuthScheme"), @@ -207,8 +208,8 @@ private class HttpAuthOperationCustomization(codegenContext: ClientCodegenContex when (section) { is OperationSection.AdditionalRuntimePluginConfig -> { withBlockTemplate( - "let auth_option_resolver = #{StaticAuthOptionResolver}::new(vec![", - "]);", + "let auth_option_resolver = #{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new(vec![", + "]));", *codegenScope, ) { val authTrait: AuthTrait? = section.operationShape.getTrait() ?: serviceShape.getTrait() diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index c6ba33baa1..a32e8eab3c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -40,6 +40,7 @@ private class HttpConnectorConfigCustomization( *preludeScope, "Connection" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::Connection"), "ConnectorSettings" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::ConnectorSettings"), + "DynConnection" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::DynConnection"), "DynConnectorAdapter" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::connections::adapter::DynConnectorAdapter"), "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), "SharedAsyncSleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep::SharedAsyncSleep"), @@ -199,10 +200,10 @@ private class HttpConnectorConfigCustomization( if let Some(connection) = layer.load::<#{HttpConnector}>() .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) { - let connection: #{Box} = #{Box}::new(#{DynConnectorAdapter}::new( + let connection: #{DynConnection} = #{DynConnection}::new(#{DynConnectorAdapter}::new( // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation connection - )) as _; + )); layer.set_connection(connection); } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 314dd8d668..04be1fca91 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -9,7 +9,6 @@ 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.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.RuntimeConfig @@ -26,6 +25,7 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( *preludeScope, + "DynRetryStrategy" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::retries::DynRetryStrategy"), "RetryConfig" to retryConfig.resolve("RetryConfig"), "SharedAsyncSleep" to sleepModule.resolve("SharedAsyncSleep"), "Sleep" to sleepModule.resolve("Sleep"), @@ -318,7 +318,7 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf rustTemplate( """ let retry_config = layer.load::<#{RetryConfig}>().cloned().unwrap_or_else(#{RetryConfig}::disabled); - layer.set_retry_strategy(#{StandardRetryStrategy}::new(&retry_config)); + layer.set_retry_strategy(#{DynRetryStrategy}::new(#{StandardRetryStrategy}::new(&retry_config))); """, *codegenScope, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index 9df495f67b..9cfc92e92d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -35,6 +35,7 @@ internal class EndpointConfigCustomization( val codegenScope = arrayOf( *preludeScope, "DefaultEndpointResolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::endpoints::DefaultEndpointResolver"), + "DynEndpointResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::DynEndpointResolver"), "SharedEndpointResolver" to types.sharedEndpointResolver, "SmithyResolver" to types.resolveEndpoint, "Params" to typesGenerator.paramsStruct(), @@ -165,11 +166,15 @@ internal class EndpointConfigCustomization( if (defaultResolver != null) { if (runtimeMode.defaultToOrchestrator) { rustTemplate( + // TODO(enableNewSmithyRuntimeCleanup): Simplify the endpoint resolvers """ - let endpoint_resolver = #{DefaultEndpointResolver}::<#{Params}>::new( - layer.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(|| - #{SharedEndpointResolver}::new(#{DefaultResolver}::new()) - )); + let endpoint_resolver = #{DynEndpointResolver}::new( + #{DefaultEndpointResolver}::<#{Params}>::new( + layer.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(|| + #{SharedEndpointResolver}::new(#{DefaultResolver}::new()) + ) + ) + ); layer.set_endpoint_resolver(endpoint_resolver); """, *codegenScope, @@ -209,10 +214,13 @@ internal class EndpointConfigCustomization( if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - let endpoint_resolver = #{DefaultEndpointResolver}::<#{Params}>::new( - layer.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(|| - #{SharedEndpointResolver}::new(#{FailingResolver}) - ).clone()); + let endpoint_resolver = #{DynEndpointResolver}::new( + #{DefaultEndpointResolver}::<#{Params}>::new( + layer.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(|| + #{SharedEndpointResolver}::new(#{FailingResolver}) + ).clone() + ) + ); layer.set_endpoint_resolver(endpoint_resolver); """, *codegenScope, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 9d282e4b27..12a6168a9b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -173,7 +173,7 @@ class EndpointParamsInterceptorGenerator( codegenContext.smithyRuntimeMode, ) } - rust("cfg.interceptor_state().put(endpoint_prefix);") + rust("cfg.interceptor_state().store_put(endpoint_prefix);") } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index e47e279421..57d7b8d1a0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -27,13 +27,15 @@ class OperationRuntimePluginGenerator( arrayOf( "AuthOptionResolverParams" to runtimeApi.resolve("client::auth::AuthOptionResolverParams"), "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), - "Layer" to smithyTypes.resolve("config_bag::Layer"), - "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), + "DynResponseDeserializer" to runtimeApi.resolve("client::orchestrator::DynResponseDeserializer"), + "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), + "Layer" to smithyTypes.resolve("config_bag::Layer"), "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), + "SharedRequestSerializer" to runtimeApi.resolve("client::orchestrator::SharedRequestSerializer"), "StaticAuthOptionResolverParams" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolverParams"), ) } @@ -50,8 +52,8 @@ class OperationRuntimePluginGenerator( fn config(&self) -> #{Option}<#{FrozenLayer}> { let mut cfg = #{Layer}::new(${operationShape.id.name.dq()}); use #{ConfigBagAccessors} as _; - cfg.set_request_serializer(${operationStructName}RequestSerializer); - cfg.set_response_deserializer(${operationStructName}ResponseDeserializer); + cfg.set_request_serializer(#{SharedRequestSerializer}::new(${operationStructName}RequestSerializer)); + cfg.set_response_deserializer(#{DynResponseDeserializer}::new(${operationStructName}ResponseDeserializer)); ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StaticAuthOptionResolverParams}::new())); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index dcc3373f26..f650a514f6 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -46,7 +46,7 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { data class AdditionalConfig(val newLayerName: String) : ServiceRuntimePluginSection("AdditionalConfig") { /** Adds a value to the config bag */ fun putConfigValue(writer: RustWriter, value: Writable) { - writer.rust("$newLayerName.put(#T);", value) + writer.rust("$newLayerName.store_put(#T);", value) } } @@ -85,6 +85,7 @@ class ServiceRuntimePluginGenerator( "AnonymousIdentityResolver" to runtimeApi.resolve("client::identity::AnonymousIdentityResolver"), "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), + "DynAuthOptionResolver" to runtimeApi.resolve("client::auth::DynAuthOptionResolver"), "Layer" to smithyTypes.resolve("config_bag::Layer"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), @@ -136,7 +137,9 @@ class ServiceRuntimePluginGenerator( cfg.set_http_auth_schemes(http_auth_schemes); // Set an empty auth option resolver to be overridden by operations that need auth. - cfg.set_auth_option_resolver(#{StaticAuthOptionResolver}::new(#{Vec}::new())); + cfg.set_auth_option_resolver( + #{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new(#{Vec}::new())) + ); #{additional_config} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt index d89536960b..665196d8e1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt @@ -72,7 +72,7 @@ class RequestSerializerGenerator( ##[allow(unused_mut, clippy::let_and_return, clippy::needless_borrow, clippy::useless_conversion)] fn serialize_input(&self, input: #{Input}, _cfg: &mut #{ConfigBag}) -> Result<#{HttpRequest}, #{BoxError}> { let input = #{TypedBox}::<#{ConcreteInput}>::assume_from(input).expect("correct type").unwrap(); - let _header_serialization_settings = _cfg.get::<#{HeaderSerializationSettings}>().cloned().unwrap_or_default(); + let _header_serialization_settings = _cfg.load::<#{HeaderSerializationSettings}>().cloned().unwrap_or_default(); let mut request_builder = { #{create_http_request} }; diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt index 49e38991d8..d8c8bf9dc2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/HttpBoundProtocolGenerator.kt @@ -87,7 +87,7 @@ class ClientHttpBoundProtocolPayloadGenerator( if (propertyBagAvailable) { rust("properties.acquire_mut().insert(signer_sender);") } else { - rust("_cfg.interceptor_state().put(signer_sender);") + rust("_cfg.interceptor_state().store_put(signer_sender);") } }, ) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index ee097cdc45..9187f35e6e 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -124,6 +124,7 @@ class InlineDependency( "serialization_settings", CargoDependency.Http, CargoDependency.smithyHttp(runtimeConfig), + CargoDependency.smithyTypes(runtimeConfig), ) fun constrained(): InlineDependency = diff --git a/rust-runtime/aws-smithy-eventstream/src/frame.rs b/rust-runtime/aws-smithy-eventstream/src/frame.rs index 7d2d65ce64..5983d09c80 100644 --- a/rust-runtime/aws-smithy-eventstream/src/frame.rs +++ b/rust-runtime/aws-smithy-eventstream/src/frame.rs @@ -55,6 +55,10 @@ impl DeferredSignerSender { } } +impl Storable for DeferredSignerSender { + type Storer = StoreReplace; +} + /// Deferred event stream signer to allow a signer to be wired up later. /// /// HTTP request signing takes place after serialization, and the event stream @@ -389,6 +393,7 @@ mod value { } } +use aws_smithy_types::config_bag::{Storable, StoreReplace}; pub use value::HeaderValue; /// Event Stream header. diff --git a/rust-runtime/aws-smithy-http/src/endpoint.rs b/rust-runtime/aws-smithy-http/src/endpoint.rs index 47c1e1af00..3a4939434e 100644 --- a/rust-runtime/aws-smithy-http/src/endpoint.rs +++ b/rust-runtime/aws-smithy-http/src/endpoint.rs @@ -124,6 +124,10 @@ impl EndpointPrefix { } } +impl Storable for EndpointPrefix { + type Storer = StoreReplace; +} + /// Apply `endpoint` to `uri` /// /// This method mutates `uri` by setting the `endpoint` on it diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 16470a9490..9a866e635a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -6,7 +6,7 @@ use crate::box_error::BoxError; use crate::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use crate::client::orchestrator::HttpRequest; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_types::Document; use std::borrow::Cow; @@ -55,6 +55,10 @@ impl AuthOptionResolverParams { } } +impl Storable for AuthOptionResolverParams { + type Storer = StoreReplace; +} + pub trait AuthOptionResolver: Send + Sync + fmt::Debug { fn resolve_auth_options( &self, @@ -62,12 +66,25 @@ pub trait AuthOptionResolver: Send + Sync + fmt::Debug { ) -> Result, BoxError>; } -impl AuthOptionResolver for Box { +#[derive(Debug)] +pub struct DynAuthOptionResolver(Box); + +impl DynAuthOptionResolver { + pub fn new(auth_option_resolver: impl AuthOptionResolver + 'static) -> Self { + Self(Box::new(auth_option_resolver)) + } +} + +impl Storable for DynAuthOptionResolver { + type Storer = StoreReplace; +} + +impl AuthOptionResolver for DynAuthOptionResolver { fn resolve_auth_options( &self, params: &AuthOptionResolverParams, ) -> Result, BoxError> { - (**self).resolve_auth_options(params) + (*self.0).resolve_auth_options(params) } } @@ -94,6 +111,10 @@ impl HttpAuthSchemes { } } +impl Storable for HttpAuthSchemes { + type Storer = StoreReplace; +} + pub trait HttpAuthScheme: Send + Sync + fmt::Debug { fn scheme_id(&self) -> AuthSchemeId; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index d0e678563b..25443d86d2 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -11,9 +11,10 @@ use crate::client::interceptors::context::{ BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, InterceptorContext, }; -use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; use aws_smithy_types::error::display::DisplayErrorContext; use context::{Error, Input, Output}; +use std::fmt; use std::fmt::{Debug, Formatter}; use std::marker::PhantomData; use std::ops::Deref; @@ -597,7 +598,7 @@ impl SharedInterceptor { Self { interceptor: Arc::new(interceptor), check_enabled: Arc::new(|conf: &ConfigBag| { - conf.get::>().is_none() + conf.load::>().is_none() }), } } @@ -722,6 +723,13 @@ pub struct DisableInterceptor { cause: &'static str, } +impl Storable for DisableInterceptor +where + T: fmt::Debug + Send + Sync + 'static, +{ + type Storer = StoreReplace; +} + /// Disable an interceptor with a given cause pub fn disable_interceptor(cause: &'static str) -> DisableInterceptor { DisableInterceptor { @@ -961,7 +969,7 @@ mod tests { .read_before_transmit(&mut InterceptorContext::new(Input::new(5)), &mut cfg) .expect_err("interceptor returns error"); cfg.interceptor_state() - .put(disable_interceptor::("test")); + .store_put(disable_interceptor::("test")); assert_eq!( interceptors .interceptors() diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 0567eca853..4e5eabd746 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -4,16 +4,18 @@ */ use crate::box_error::BoxError; -use crate::client::auth::{AuthOptionResolver, AuthOptionResolverParams, HttpAuthSchemes}; +use crate::client::auth::{ + AuthOptionResolver, AuthOptionResolverParams, DynAuthOptionResolver, HttpAuthSchemes, +}; use crate::client::identity::IdentityResolvers; use crate::client::interceptors::context::{Error, Input, Output}; -use crate::client::retries::RetryClassifiers; use crate::client::retries::RetryStrategy; +use crate::client::retries::{DynRetryStrategy, RetryClassifiers}; use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_http::body::SdkBody; -use aws_smithy_types::config_bag::{ConfigBag, Layer}; +use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer, Storable, StoreReplace}; use aws_smithy_types::endpoint::Endpoint; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use bytes::Bytes; @@ -37,6 +39,25 @@ pub trait RequestSerializer: Send + Sync + fmt::Debug { fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result; } +#[derive(Clone, Debug)] +pub struct SharedRequestSerializer(Arc); + +impl SharedRequestSerializer { + pub fn new(serializer: impl RequestSerializer + 'static) -> Self { + Self(Arc::new(serializer)) + } +} + +impl RequestSerializer for SharedRequestSerializer { + fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result { + self.0.serialize_input(input, cfg) + } +} + +impl Storable for SharedRequestSerializer { + type Storer = StoreReplace; +} + pub trait ResponseDeserializer: Send + Sync + fmt::Debug { fn deserialize_streaming( &self, @@ -52,16 +73,58 @@ pub trait ResponseDeserializer: Send + Sync + fmt::Debug { ) -> Result>; } +#[derive(Debug)] +pub struct DynResponseDeserializer(Box); + +impl DynResponseDeserializer { + pub fn new(serializer: impl ResponseDeserializer + 'static) -> Self { + Self(Box::new(serializer)) + } +} + +impl ResponseDeserializer for DynResponseDeserializer { + fn deserialize_nonstreaming( + &self, + response: &HttpResponse, + ) -> Result> { + self.0.deserialize_nonstreaming(response) + } + + fn deserialize_streaming( + &self, + response: &mut HttpResponse, + ) -> Option>> { + self.0.deserialize_streaming(response) + } +} + +impl Storable for DynResponseDeserializer { + type Storer = StoreReplace; +} + pub trait Connection: Send + Sync + fmt::Debug { fn call(&self, request: HttpRequest) -> BoxFuture; } -impl Connection for Box { +#[derive(Debug)] +pub struct DynConnection(Box); + +impl DynConnection { + pub fn new(connection: impl Connection + 'static) -> Self { + Self(Box::new(connection)) + } +} + +impl Connection for DynConnection { fn call(&self, request: HttpRequest) -> BoxFuture { - (**self).call(request) + (*self.0).call(request) } } +impl Storable for DynConnection { + type Storer = StoreReplace; +} + #[derive(Debug)] pub struct EndpointResolverParams(TypeErasedBox); @@ -75,10 +138,33 @@ impl EndpointResolverParams { } } +impl Storable for EndpointResolverParams { + type Storer = StoreReplace; +} + pub trait EndpointResolver: Send + Sync + fmt::Debug { fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Result; } +#[derive(Debug)] +pub struct DynEndpointResolver(Box); + +impl DynEndpointResolver { + pub fn new(endpoint_resolver: impl EndpointResolver + 'static) -> Self { + Self(Box::new(endpoint_resolver)) + } +} + +impl EndpointResolver for DynEndpointResolver { + fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Result { + self.0.resolve_endpoint(params) + } +} + +impl Storable for DynEndpointResolver { + type Storer = StoreReplace; +} + /// Informs the orchestrator on whether or not the request body needs to be loaded into memory before transmit. /// /// This enum gets placed into the `ConfigBag` to change the orchestrator behavior. @@ -99,39 +185,68 @@ pub enum LoadedRequestBody { Loaded(Bytes), } -pub trait Settable { - fn layer(&mut self) -> &mut Layer; - fn put(&mut self, value: T) { - self.layer().put(value); - } +impl Storable for LoadedRequestBody { + type Storer = StoreReplace; } -pub trait Gettable { - fn config_bag(&self) -> &ConfigBag; - fn get(&self) -> Option<&T> { - self.config_bag().get::() +// Place traits in a private module so that they can be used in the public API without being a part of the public API. +mod internal { + use aws_smithy_types::config_bag::{ + ConfigBag, FrozenLayer, Layer, Storable, Store, StoreReplace, + }; + use std::fmt::Debug; + + pub trait Settable { + fn unset(&mut self); + + fn store_put(&mut self, value: T) + where + T: Storable>; } -} -impl Settable for Layer { - fn layer(&mut self) -> &mut Layer { - self + impl Settable for Layer { + fn unset(&mut self) { + Layer::unset::(self); + } + + fn store_put(&mut self, value: T) + where + T: Storable>, + { + Layer::store_put(self, value); + } } -} -impl Gettable for ConfigBag { - fn config_bag(&self) -> &ConfigBag { - self + pub trait Gettable { + fn load(&self) -> ::ReturnedType<'_>; + } + + impl Gettable for ConfigBag { + fn load(&self) -> ::ReturnedType<'_> { + ConfigBag::load::(self) + } + } + + impl Gettable for Layer { + fn load(&self) -> ::ReturnedType<'_> { + Layer::load::(self) + } + } + + impl Gettable for FrozenLayer { + fn load(&self) -> ::ReturnedType<'_> { + Layer::load::(self) + } } } +use internal::{Gettable, Settable}; pub trait ConfigBagAccessors { fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams where Self: Gettable, { - self.config_bag() - .get::() + self.load::() .expect("auth option resolver params must be set") } fn set_auth_option_resolver_params( @@ -140,32 +255,29 @@ pub trait ConfigBagAccessors { ) where Self: Settable, { - self.put::(auth_option_resolver_params); + self.store_put::(auth_option_resolver_params); } fn auth_option_resolver(&self) -> &dyn AuthOptionResolver where Self: Gettable, { - &**self - .config_bag() - .get::>() + self.load::() .expect("an auth option resolver must be set") } - fn set_auth_option_resolver(&mut self, auth_option_resolver: impl AuthOptionResolver + 'static) + fn set_auth_option_resolver(&mut self, auth_option_resolver: DynAuthOptionResolver) where Self: Settable, { - self.put::>(Box::new(auth_option_resolver)); + self.store_put::(auth_option_resolver); } fn endpoint_resolver_params(&self) -> &EndpointResolverParams where Self: Gettable, { - self.config_bag() - .get::() + self.load::() .expect("endpoint resolver params must be set") } @@ -173,32 +285,29 @@ pub trait ConfigBagAccessors { where Self: Settable, { - self.put::(endpoint_resolver_params); + self.store_put::(endpoint_resolver_params); } fn endpoint_resolver(&self) -> &dyn EndpointResolver where Self: Gettable, { - &**self - .config_bag() - .get::>() + self.load::() .expect("an endpoint resolver must be set") } - fn set_endpoint_resolver(&mut self, endpoint_resolver: impl EndpointResolver + 'static) + fn set_endpoint_resolver(&mut self, endpoint_resolver: DynEndpointResolver) where Self: Settable, { - self.put::>(Box::new(endpoint_resolver)); + self.store_put::(endpoint_resolver); } fn identity_resolvers(&self) -> &IdentityResolvers where Self: Gettable, { - self.config_bag() - .get::() + self.load::() .expect("identity resolvers must be configured") } @@ -206,127 +315,120 @@ pub trait ConfigBagAccessors { where Self: Settable, { - self.put::(identity_resolvers); + self.store_put::(identity_resolvers); } fn connection(&self) -> &dyn Connection where Self: Gettable, { - &**self - .config_bag() - .get::>() - .expect("missing connector") + self.load::().expect("missing connector") } - fn set_connection(&mut self, connection: impl Connection + 'static) + fn set_connection(&mut self, connection: DynConnection) where Self: Settable, { - self.put::>(Box::new(connection)); + self.store_put::(connection); } fn http_auth_schemes(&self) -> &HttpAuthSchemes where Self: Gettable, { - self.config_bag() - .get::() + self.load::() .expect("auth schemes must be set") } fn set_http_auth_schemes(&mut self, http_auth_schemes: HttpAuthSchemes) where Self: Settable, { - self.put::(http_auth_schemes); + self.store_put::(http_auth_schemes); } - fn request_serializer(&self) -> Arc + fn request_serializer(&self) -> SharedRequestSerializer where Self: Gettable, { - self.get::>() + self.load::() .expect("missing request serializer") .clone() } - fn set_request_serializer(&mut self, request_serializer: impl RequestSerializer + 'static) + fn set_request_serializer(&mut self, request_serializer: SharedRequestSerializer) where Self: Settable, { - self.put::>(Arc::new(request_serializer)); + self.store_put::(request_serializer); } fn response_deserializer(&self) -> &dyn ResponseDeserializer where Self: Gettable, { - &**self - .get::>() + self.load::() .expect("missing response deserializer") } - fn set_response_deserializer( - &mut self, - response_deserializer: impl ResponseDeserializer + 'static, - ) where + fn set_response_deserializer(&mut self, response_deserializer: DynResponseDeserializer) + where Self: Settable, { - self.put::>(Box::new(response_deserializer)); + self.store_put::(response_deserializer); } fn retry_classifiers(&self) -> &RetryClassifiers where Self: Gettable, { - self.get::() + self.load::() .expect("retry classifiers must be set") } fn set_retry_classifiers(&mut self, retry_classifiers: RetryClassifiers) where Self: Settable, { - self.put::(retry_classifiers); + self.store_put::(retry_classifiers); } fn retry_strategy(&self) -> Option<&dyn RetryStrategy> where Self: Gettable, { - self.get::>().map(|rs| &**rs) + self.load::().map(|rs| rs as _) } - fn set_retry_strategy(&mut self, retry_strategy: impl RetryStrategy + 'static) + fn set_retry_strategy(&mut self, retry_strategy: DynRetryStrategy) where Self: Settable, { - self.put::>(Box::new(retry_strategy)); + self.store_put::(retry_strategy); } fn request_time(&self) -> Option where Self: Gettable, { - self.get::().cloned() + self.load::().cloned() } fn set_request_time(&mut self, time_source: impl TimeSource + 'static) where Self: Settable, { - self.put::(SharedTimeSource::new(time_source)); + self.store_put::(SharedTimeSource::new(time_source)); } fn sleep_impl(&self) -> Option where Self: Gettable, { - self.get::().cloned() + self.load::().cloned() } fn set_sleep_impl(&mut self, async_sleep: Option) where Self: Settable, { if let Some(sleep_impl) = async_sleep { - self.put::(sleep_impl); + self.store_put::(sleep_impl); } else { - self.layer().unset::(); + self.unset::(); } } @@ -334,17 +436,18 @@ pub trait ConfigBagAccessors { where Self: Gettable, { - self.get::().unwrap_or(&NOT_NEEDED) + self.load::().unwrap_or(&NOT_NEEDED) } fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody) where Self: Settable, { - self.put::(loaded_request_body); + self.store_put::(loaded_request_body); } } const NOT_NEEDED: LoadedRequestBody = LoadedRequestBody::NotNeeded; impl ConfigBagAccessors for ConfigBag {} +impl ConfigBagAccessors for FrozenLayer {} impl ConfigBagAccessors for Layer {} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs b/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs index 3cfe306f10..1eb115d480 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_types::config_bag::{Storable, StoreReplace}; + #[derive(Debug, Clone, Copy)] pub struct RequestAttempts { attempts: usize, @@ -19,6 +21,10 @@ impl RequestAttempts { } } +impl Storable for RequestAttempts { + type Storer = StoreReplace; +} + impl From for RequestAttempts { fn from(attempts: usize) -> Self { Self { attempts } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index 93f075a6fc..1e1ea497e3 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -4,7 +4,7 @@ */ use crate::client::interceptors::context::InterceptorContext; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use std::fmt::Debug; use std::time::Duration; use tracing::trace; @@ -39,6 +39,33 @@ pub trait RetryStrategy: Send + Sync + Debug { ) -> Result; } +#[derive(Debug)] +pub struct DynRetryStrategy(Box); + +impl DynRetryStrategy { + pub fn new(retry_strategy: impl RetryStrategy + 'static) -> Self { + Self(Box::new(retry_strategy)) + } +} + +impl RetryStrategy for DynRetryStrategy { + fn should_attempt_initial_request(&self, cfg: &ConfigBag) -> Result { + self.0.should_attempt_initial_request(cfg) + } + + fn should_attempt_retry( + &self, + context: &InterceptorContext, + cfg: &ConfigBag, + ) -> Result { + self.0.should_attempt_retry(context, cfg) + } +} + +impl Storable for DynRetryStrategy { + type Storer = StoreReplace; +} + #[non_exhaustive] #[derive(Clone, Eq, PartialEq, Debug)] pub enum RetryReason { @@ -80,6 +107,10 @@ impl RetryClassifiers { // pub fn map_classifiers(mut self, fun: Fn() -> RetryClassifiers) } +impl Storable for RetryClassifiers { + type Storer = StoreReplace; +} + impl ClassifyRetry for RetryClassifiers { fn classify_retry(&self, ctx: &InterceptorContext) -> Option { // return the first non-None result diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 8f2dffbf80..46f617c565 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -20,7 +20,7 @@ use aws_smithy_runtime_api::client::interceptors::context::{ }; use aws_smithy_runtime_api::client::interceptors::Interceptors; use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, HttpResponse, LoadedRequestBody, OrchestratorError, + ConfigBagAccessors, HttpResponse, LoadedRequestBody, OrchestratorError, RequestSerializer, }; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::ShouldAttempt; @@ -198,7 +198,8 @@ async fn try_op( break; } // Track which attempt we're currently on. - cfg.interceptor_state().put::(i.into()); + cfg.interceptor_state() + .store_put::(i.into()); let attempt_timeout_config = cfg.maybe_timeout_config(TimeoutKind::OperationAttempt); let maybe_timeout = async { try_attempt(ctx, cfg, interceptors, stop_point).await; @@ -346,6 +347,10 @@ mod tests { use aws_smithy_runtime_api::client::interceptors::{ Interceptor, InterceptorRegistrar, SharedInterceptor, }; + use aws_smithy_runtime_api::client::orchestrator::{ + DynConnection, DynEndpointResolver, DynResponseDeserializer, SharedRequestSerializer, + }; + use aws_smithy_runtime_api::client::retries::DynRetryStrategy; use aws_smithy_runtime_api::client::runtime_plugin::{RuntimePlugin, RuntimePlugins}; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; @@ -377,12 +382,16 @@ mod tests { impl RuntimePlugin for TestOperationRuntimePlugin { fn config(&self) -> Option { let mut cfg = Layer::new("test operation"); - cfg.set_request_serializer(new_request_serializer()); - cfg.set_response_deserializer(new_response_deserializer()); - cfg.set_retry_strategy(NeverRetryStrategy::new()); - cfg.set_endpoint_resolver(StaticUriEndpointResolver::http_localhost(8080)); + cfg.set_request_serializer(SharedRequestSerializer::new(new_request_serializer())); + cfg.set_response_deserializer( + DynResponseDeserializer::new(new_response_deserializer()), + ); + cfg.set_retry_strategy(DynRetryStrategy::new(NeverRetryStrategy::new())); + cfg.set_endpoint_resolver(DynEndpointResolver::new( + StaticUriEndpointResolver::http_localhost(8080), + )); cfg.set_endpoint_resolver_params(StaticUriEndpointResolverParams::new().into()); - cfg.set_connection(OkConnector::new()); + cfg.set_connection(DynConnection::new(OkConnector::new())); Some(cfg.freeze()) } @@ -1030,12 +1039,6 @@ mod tests { interceptor: TestInterceptor, } impl RuntimePlugin for TestInterceptorRuntimePlugin { - fn config(&self) -> Option { - let mut layer = Layer::new("test"); - layer.put(self.interceptor.clone()); - Some(layer.freeze()) - } - fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { interceptors.register(SharedInterceptor::new(self.interceptor.clone())); } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index c6bacc1c42..50c5fd4a2f 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -80,7 +80,7 @@ pub(super) async fn orchestrate_auth( if let Some(identity_resolver) = auth_scheme.identity_resolver(identity_resolvers) { let request_signer = auth_scheme.request_signer(); let endpoint = cfg - .get::() + .load::() .expect("endpoint added to config bag by endpoint orchestrator"); let auth_scheme_endpoint_config = extract_endpoint_auth_scheme_config(endpoint, scheme_id)?; @@ -136,7 +136,8 @@ mod tests { use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::auth::option_resolver::StaticAuthOptionResolver; use aws_smithy_runtime_api::client::auth::{ - AuthOptionResolverParams, AuthSchemeId, HttpAuthScheme, HttpAuthSchemes, HttpRequestSigner, + AuthOptionResolverParams, AuthSchemeId, DynAuthOptionResolver, HttpAuthScheme, + HttpAuthSchemes, HttpRequestSigner, }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; @@ -204,7 +205,9 @@ mod tests { let mut layer = Layer::new("test"); layer.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); - layer.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![TEST_SCHEME_ID])); + layer.set_auth_option_resolver(DynAuthOptionResolver::new(StaticAuthOptionResolver::new( + vec![TEST_SCHEME_ID], + ))); layer.set_identity_resolvers( IdentityResolvers::builder() .identity_resolver(TEST_SCHEME_ID, TestIdentityResolver) @@ -215,7 +218,7 @@ mod tests { .auth_scheme(TEST_SCHEME_ID, TestAuthScheme { signer: TestSigner }) .build(), ); - layer.put(Endpoint::builder().url("dontcare").build()); + layer.store_put(Endpoint::builder().url("dontcare").build()); let mut cfg = ConfigBag::base(); cfg.push_layer(layer); @@ -248,17 +251,16 @@ mod tests { let mut layer = Layer::new("test"); layer.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); - layer.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![ - HTTP_BASIC_AUTH_SCHEME_ID, - HTTP_BEARER_AUTH_SCHEME_ID, - ])); + layer.set_auth_option_resolver(DynAuthOptionResolver::new(StaticAuthOptionResolver::new( + vec![HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID], + ))); layer.set_http_auth_schemes( HttpAuthSchemes::builder() .auth_scheme(HTTP_BASIC_AUTH_SCHEME_ID, BasicAuthScheme::new()) .auth_scheme(HTTP_BEARER_AUTH_SCHEME_ID, BearerAuthScheme::new()) .build(), ); - layer.put(Endpoint::builder().url("dontcare").build()); + layer.store_put(Endpoint::builder().url("dontcare").build()); // First, test the presence of a basic auth login and absence of a bearer token layer.set_identity_resolvers( diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 7c3720d399..104599b287 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -100,7 +100,7 @@ pub(super) fn orchestrate_endpoint( cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let params = cfg.endpoint_resolver_params(); - let endpoint_prefix = cfg.get::(); + let endpoint_prefix = cfg.load::(); let request = ctx.request_mut().expect("set during serialization"); let endpoint_resolver = cfg.endpoint_resolver(); @@ -108,7 +108,7 @@ pub(super) fn orchestrate_endpoint( apply_endpoint(request, &endpoint, endpoint_prefix)?; // Make the endpoint config available to interceptors - cfg.interceptor_state().put(endpoint); + cfg.interceptor_state().store_put(endpoint); Ok(()) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs index 0b928e7348..7a41de659a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs @@ -6,7 +6,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeDeserializationInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::date_time::Format; use aws_smithy_types::DateTime; use std::time::{Duration, SystemTime}; @@ -27,6 +27,10 @@ impl ServiceClockSkew { } } +impl Storable for ServiceClockSkew { + type Storer = StoreReplace; +} + impl From for Duration { fn from(skew: ServiceClockSkew) -> Duration { skew.inner @@ -78,7 +82,7 @@ impl Interceptor for ServiceClockSkewInterceptor { } }; let skew = ServiceClockSkew::new(calculate_skew(time_sent, time_received)); - cfg.interceptor_state().put(skew); + cfg.interceptor_state().store_put(skew); Ok(()) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs index 0976f14512..81437cead3 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -53,8 +53,8 @@ impl RetryStrategy for FixedDelayRetryStrategy { } // Check if we're out of attempts - let request_attempts: &RequestAttempts = cfg - .get() + let request_attempts = cfg + .load::() .expect("at least one request attempt is made before any retry is attempted"); if request_attempts.attempts() >= self.max_attempts as usize { tracing::trace!( @@ -66,7 +66,7 @@ impl RetryStrategy for FixedDelayRetryStrategy { } let retry_classifiers = cfg - .get::() + .load::() .expect("a retry classifier is set"); let retry_reason = retry_classifiers.classify_retry(ctx); diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index 7dcaf7d79c..4f520b555e 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -119,7 +119,7 @@ impl RetryStrategy for StandardRetryStrategy { let output_or_error = ctx.output_or_error().expect( "This must never be called without reaching the point where the result exists.", ); - let token_bucket = cfg.get::(); + let token_bucket = cfg.load::(); if output_or_error.is_ok() { tracing::debug!("request succeeded, no retry necessary"); if let Some(tb) = token_bucket { @@ -138,7 +138,7 @@ impl RetryStrategy for StandardRetryStrategy { // Check if we're out of attempts let request_attempts = cfg - .get::() + .load::() .expect("at least one request attempt is made before any retry is attempted") .attempts(); if request_attempts >= self.max_attempts { @@ -247,7 +247,7 @@ mod tests { layer.set_retry_classifiers( RetryClassifiers::new().with_classifier(AlwaysRetry(error_kind)), ); - layer.put(RequestAttempts::new(current_request_attempts)); + layer.store_put(RequestAttempts::new(current_request_attempts)); let cfg = ConfigBag::of_layers(vec![layer]); (ctx, cfg) @@ -374,16 +374,16 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5); - cfg.interceptor_state().put(TokenBucket::default()); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().store_put(TokenBucket::default()); + let token_bucket = cfg.load::().unwrap().clone(); - cfg.interceptor_state().put(RequestAttempts::new(1)); + cfg.interceptor_state().store_put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 495); - cfg.interceptor_state().put(RequestAttempts::new(2)); + cfg.interceptor_state().store_put(RequestAttempts::new(2)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(2)); @@ -391,7 +391,7 @@ mod tests { ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); - cfg.interceptor_state().put(RequestAttempts::new(3)); + cfg.interceptor_state().store_put(RequestAttempts::new(3)); let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); assert_eq!(token_bucket.available_permits(), 495); @@ -404,22 +404,22 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(3); - cfg.interceptor_state().put(TokenBucket::default()); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().store_put(TokenBucket::default()); + let token_bucket = cfg.load::().unwrap().clone(); - cfg.interceptor_state().put(RequestAttempts::new(1)); + cfg.interceptor_state().store_put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 495); - cfg.interceptor_state().put(RequestAttempts::new(2)); + cfg.interceptor_state().store_put(RequestAttempts::new(2)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(2)); assert_eq!(token_bucket.available_permits(), 490); - cfg.interceptor_state().put(RequestAttempts::new(3)); + cfg.interceptor_state().store_put(RequestAttempts::new(3)); let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); assert_eq!(token_bucket.available_permits(), 490); @@ -432,16 +432,16 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5); - cfg.interceptor_state().put(TokenBucket::new(5)); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().store_put(TokenBucket::new(5)); + let token_bucket = cfg.load::().unwrap().clone(); - cfg.interceptor_state().put(RequestAttempts::new(1)); + cfg.interceptor_state().store_put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 0); - cfg.interceptor_state().put(RequestAttempts::new(2)); + cfg.interceptor_state().store_put(RequestAttempts::new(2)); let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); assert_eq!(token_bucket.available_permits(), 0); @@ -457,16 +457,16 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5); - cfg.interceptor_state().put(TokenBucket::new(100)); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().store_put(TokenBucket::new(100)); + let token_bucket = cfg.load::().unwrap().clone(); - cfg.interceptor_state().put(RequestAttempts::new(1)); + cfg.interceptor_state().store_put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 90); - cfg.interceptor_state().put(RequestAttempts::new(2)); + cfg.interceptor_state().store_put(RequestAttempts::new(2)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); @@ -474,7 +474,7 @@ mod tests { ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); - cfg.interceptor_state().put(RequestAttempts::new(3)); + cfg.interceptor_state().store_put(RequestAttempts::new(3)); let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); @@ -489,8 +489,9 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(usize::MAX); - cfg.interceptor_state().put(TokenBucket::new(PERMIT_COUNT)); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state() + .store_put(TokenBucket::new(PERMIT_COUNT)); + let token_bucket = cfg.load::().unwrap().clone(); let mut attempt = 1; @@ -501,7 +502,8 @@ mod tests { panic!("This test should have completed by now (drain)"); } - cfg.interceptor_state().put(RequestAttempts::new(attempt)); + cfg.interceptor_state() + .store_put(RequestAttempts::new(attempt)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); assert!(matches!(should_retry, ShouldAttempt::YesAfterDelay(_))); attempt += 1; @@ -519,7 +521,8 @@ mod tests { panic!("This test should have completed by now (fillup)"); } - cfg.interceptor_state().put(RequestAttempts::new(attempt)); + cfg.interceptor_state() + .store_put(RequestAttempts::new(attempt)); let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); attempt += 1; @@ -536,34 +539,34 @@ mod tests { let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5); - cfg.interceptor_state().put(TokenBucket::default()); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().store_put(TokenBucket::default()); + let token_bucket = cfg.load::().unwrap().clone(); - cfg.interceptor_state().put(RequestAttempts::new(1)); + cfg.interceptor_state().store_put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 495); - cfg.interceptor_state().put(RequestAttempts::new(2)); + cfg.interceptor_state().store_put(RequestAttempts::new(2)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(2)); assert_eq!(token_bucket.available_permits(), 490); - cfg.interceptor_state().put(RequestAttempts::new(3)); + cfg.interceptor_state().store_put(RequestAttempts::new(3)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(4)); assert_eq!(token_bucket.available_permits(), 485); - cfg.interceptor_state().put(RequestAttempts::new(4)); + cfg.interceptor_state().store_put(RequestAttempts::new(4)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(8)); assert_eq!(token_bucket.available_permits(), 480); - cfg.interceptor_state().put(RequestAttempts::new(5)); + cfg.interceptor_state().store_put(RequestAttempts::new(5)); let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); assert_eq!(token_bucket.available_permits(), 480); @@ -578,34 +581,34 @@ mod tests { .with_max_attempts(5) .with_initial_backoff(Duration::from_secs(1)) .with_max_backoff(Duration::from_secs(3)); - cfg.interceptor_state().put(TokenBucket::default()); - let token_bucket = cfg.get::().unwrap().clone(); + cfg.interceptor_state().store_put(TokenBucket::default()); + let token_bucket = cfg.load::().unwrap().clone(); - cfg.interceptor_state().put(RequestAttempts::new(1)); + cfg.interceptor_state().store_put(RequestAttempts::new(1)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 495); - cfg.interceptor_state().put(RequestAttempts::new(2)); + cfg.interceptor_state().store_put(RequestAttempts::new(2)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(2)); assert_eq!(token_bucket.available_permits(), 490); - cfg.interceptor_state().put(RequestAttempts::new(3)); + cfg.interceptor_state().store_put(RequestAttempts::new(3)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(3)); assert_eq!(token_bucket.available_permits(), 485); - cfg.interceptor_state().put(RequestAttempts::new(4)); + cfg.interceptor_state().store_put(RequestAttempts::new(4)); let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(3)); assert_eq!(token_bucket.available_permits(), 480); - cfg.interceptor_state().put(RequestAttempts::new(5)); + cfg.interceptor_state().store_put(RequestAttempts::new(5)); let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); assert_eq!(token_bucket.available_permits(), 480); diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs index 7e34d0e7f7..6825660399 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs @@ -11,7 +11,8 @@ use aws_smithy_runtime_api::client::auth::option_resolver::{ StaticAuthOptionResolver, StaticAuthOptionResolverParams, }; use aws_smithy_runtime_api::client::auth::{ - AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpAuthSchemes, HttpRequestSigner, + AuthSchemeEndpointConfig, AuthSchemeId, DynAuthOptionResolver, HttpAuthScheme, HttpAuthSchemes, + HttpRequestSigner, }; use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpRequest}; @@ -43,9 +44,9 @@ impl AnonymousAuthRuntimePlugin { pub fn new() -> Self { let mut cfg = Layer::new("AnonymousAuth"); cfg.set_auth_option_resolver_params(StaticAuthOptionResolverParams::new().into()); - cfg.set_auth_option_resolver(StaticAuthOptionResolver::new(vec![ - ANONYMOUS_AUTH_SCHEME_ID, - ])); + cfg.set_auth_option_resolver(DynAuthOptionResolver::new(StaticAuthOptionResolver::new( + vec![ANONYMOUS_AUTH_SCHEME_ID], + ))); cfg.set_identity_resolvers( IdentityResolvers::builder() .identity_resolver(ANONYMOUS_AUTH_SCHEME_ID, AnonymousIdentityResolver::new()) diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs index e88ea351dc..0bccae98ef 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs @@ -5,7 +5,8 @@ use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, HttpResponse, OrchestratorError, ResponseDeserializer, + ConfigBagAccessors, DynResponseDeserializer, HttpResponse, OrchestratorError, + ResponseDeserializer, }; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{FrozenLayer, Layer}; @@ -45,10 +46,9 @@ impl ResponseDeserializer for CannedResponseDeserializer { impl RuntimePlugin for CannedResponseDeserializer { fn config(&self) -> Option { let mut cfg = Layer::new("CannedResponse"); - cfg.set_response_deserializer(Self { + cfg.set_response_deserializer(DynResponseDeserializer::new(Self { inner: Mutex::new(self.take()), - }); - + })); Some(cfg.freeze()) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs index 87bb2011e4..8ec112f72f 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -6,7 +6,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::Input; use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, HttpRequest, RequestSerializer, + ConfigBagAccessors, HttpRequest, RequestSerializer, SharedRequestSerializer, }; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; @@ -52,9 +52,9 @@ impl RequestSerializer for CannedRequestSerializer { impl RuntimePlugin for CannedRequestSerializer { fn config(&self) -> Option { let mut cfg = Layer::new("CannedRequest"); - cfg.set_request_serializer(Self { + cfg.set_request_serializer(SharedRequestSerializer::new(Self { inner: Mutex::new(self.take()), - }); + })); Some(cfg.freeze()) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs index 875707a4d1..c666389a81 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs @@ -114,7 +114,7 @@ pub(super) trait ProvideMaybeTimeoutConfig { impl ProvideMaybeTimeoutConfig for ConfigBag { fn maybe_timeout_config(&self, timeout_kind: TimeoutKind) -> MaybeTimeoutConfig { - if let Some(timeout_config) = self.get::() { + if let Some(timeout_config) = self.load::() { let sleep_impl = self.sleep_impl(); let timeout = match (sleep_impl.as_ref(), timeout_kind) { (None, _) => None, @@ -199,7 +199,7 @@ mod tests { let mut cfg = ConfigBag::base(); let mut timeout_config = Layer::new("timeout"); - timeout_config.put(TimeoutConfig::builder().build()); + timeout_config.store_put(TimeoutConfig::builder().build()); timeout_config.set_sleep_impl(Some(sleep_impl)); cfg.push_layer(timeout_config); @@ -225,7 +225,7 @@ mod tests { let mut cfg = ConfigBag::base(); let mut timeout_config = Layer::new("timeout"); - timeout_config.put( + timeout_config.store_put( TimeoutConfig::builder() .operation_timeout(Duration::from_millis(250)) .build(), diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index 29400c2faa..8a993e3114 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -25,29 +25,6 @@ use std::sync::Arc; pub use storable::{AppendItemIter, Storable, Store, StoreAppend, StoreReplace}; -/// Layered Configuration Structure -/// -/// [`ConfigBag`] is the "unlocked" form of the bag. Only the top layer of the bag may be unlocked. -#[must_use] -pub struct ConfigBag { - interceptor_state: Layer, - tail: Vec, -} - -impl Debug for ConfigBag { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - struct Layers<'a>(&'a ConfigBag); - impl Debug for Layers<'_> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_list().entries(self.0.layers()).finish() - } - } - f.debug_struct("ConfigBag") - .field("layers", &Layers(self)) - .finish() - } -} - /// [`FrozenLayer`] is the "locked" form of [`Layer`]. /// /// [`ConfigBag`] contains a ordered collection of [`FrozenLayer`] @@ -282,16 +259,6 @@ impl Layer { self } - /// Insert `value` into the bag - /// - /// NOTE: This method exists for legacy reasons to allow storing values that are not `Storeable` - /// - /// The implementation assumes that the type is [`StoreReplace`]. - pub fn put(&mut self, value: T) -> &mut Self { - self.put_directly::>(Value::Set(value)); - self - } - /// Stores `item` of type `T` into the config bag, overriding a previous value of the same type pub fn store_put(&mut self, item: T) -> &mut Self where @@ -398,6 +365,29 @@ impl FrozenLayer { } } +/// Layered Configuration Structure +/// +/// [`ConfigBag`] is the "unlocked" form of the bag. Only the top layer of the bag may be unlocked. +#[must_use] +pub struct ConfigBag { + interceptor_state: Layer, + tail: Vec, +} + +impl Debug for ConfigBag { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + struct Layers<'a>(&'a ConfigBag); + impl Debug for Layers<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.0.layers()).finish() + } + } + f.debug_struct("ConfigBag") + .field("layers", &Layers(self)) + .finish() + } +} + impl ConfigBag { /// Create a new config bag "base". /// @@ -441,12 +431,6 @@ impl ConfigBag { self.sourced_get::() } - /// Retrieve the value of type `T` from the bag if exists - pub fn get(&self) -> Option<&T> { - let out = self.sourced_get::>(); - out - } - /// Return a mutable reference to `T` if it is stored in the top layer of the bag pub fn get_mut(&mut self) -> Option<&mut T> where @@ -631,40 +615,52 @@ mod test { fn layered_property_bag() { #[derive(Debug)] struct Prop1; + impl Storable for Prop1 { + type Storer = StoreReplace; + } #[derive(Debug)] struct Prop2; + impl Storable for Prop2 { + type Storer = StoreReplace; + } let layer_a = |bag: &mut Layer| { - bag.put(Prop1); + bag.store_put(Prop1); }; let layer_b = |bag: &mut Layer| { - bag.put(Prop2); + bag.store_put(Prop2); }; #[derive(Debug)] struct Prop3; + impl Storable for Prop3 { + type Storer = StoreReplace; + } let mut base_bag = ConfigBag::base() .with_fn("a", layer_a) .with_fn("b", layer_b); - base_bag.interceptor_state().put(Prop3); - assert!(base_bag.get::().is_some()); + base_bag.interceptor_state().store_put(Prop3); + assert!(base_bag.load::().is_some()); #[derive(Debug)] struct Prop4; + impl Storable for Prop4 { + type Storer = StoreReplace; + } let layer_c = |bag: &mut Layer| { - bag.put(Prop4); + bag.store_put(Prop4); bag.unset::(); }; let final_bag = base_bag.with_fn("c", layer_c); - assert!(final_bag.get::().is_some()); - assert!(final_bag.get::().is_some()); - assert!(final_bag.get::().is_some()); + assert!(final_bag.load::().is_some()); + assert!(final_bag.load::().is_some()); + assert!(final_bag.load::().is_some()); // we unset prop3 - assert!(final_bag.get::().is_none()); + assert!(final_bag.load::().is_none()); println!("{:#?}", final_bag); } @@ -673,22 +669,33 @@ mod test { let bag = ConfigBag::base(); #[derive(Debug)] struct Region(&'static str); + impl Storable for Region { + type Storer = StoreReplace; + } let bag = bag.with_fn("service config", |layer: &mut Layer| { - layer.put(Region("asdf")); + layer.store_put(Region("asdf")); }); - assert_eq!(bag.get::().unwrap().0, "asdf"); + assert_eq!(bag.load::().unwrap().0, "asdf"); #[derive(Debug)] struct SigningName(&'static str); + impl Storable for SigningName { + type Storer = StoreReplace; + } let operation_config = bag.with_fn("operation", |layer: &mut Layer| { - layer.put(SigningName("s3")); + layer.store_put(SigningName("s3")); }); - assert_eq!(operation_config.get::().unwrap().0, "s3"); + assert_eq!(operation_config.load::().unwrap().0, "s3"); + #[derive(Debug)] + struct Prop; + impl Storable for Prop { + type Storer = StoreReplace; + } let mut open_bag = operation_config.with_fn("my_custom_info", |_bag: &mut Layer| {}); - open_bag.interceptor_state().put("foo"); + open_bag.interceptor_state().store_put(Prop); assert_eq!(open_bag.layers().count(), 4); } @@ -780,7 +787,7 @@ mod test { assert_eq!(bag_1.load::(), Some(&Foo(1))); assert_eq!(bag_2.load::(), Some(&Foo(0))); - bag_1.interceptor_state().put(Foo(3)); + bag_1.interceptor_state().store_put(Foo(3)); assert_eq!(bag_1.load::(), Some(&Foo(3))); } @@ -796,7 +803,7 @@ mod test { assert_eq!(bag.get_mut::(), None); assert_eq!(bag.get_mut_or_default::(), &Foo(0)); bag.get_mut_or_default::().0 += 1; - assert_eq!(bag.get::(), Some(&Foo(1))); + assert_eq!(bag.load::(), Some(&Foo(1))); let old_ref = bag.load::().unwrap(); assert_eq!(old_ref, &Foo(1)); diff --git a/rust-runtime/aws-smithy-types/src/endpoint.rs b/rust-runtime/aws-smithy-types/src/endpoint.rs index 8d27b2d55c..5ebf693e5c 100644 --- a/rust-runtime/aws-smithy-types/src/endpoint.rs +++ b/rust-runtime/aws-smithy-types/src/endpoint.rs @@ -4,6 +4,7 @@ */ //! Smithy Endpoint Types +use crate::config_bag::{Storable, StoreReplace}; use crate::Document; use std::borrow::Cow; use std::collections::HashMap; @@ -53,6 +54,10 @@ impl Endpoint { } } +impl Storable for Endpoint { + type Storer = StoreReplace; +} + #[derive(Debug, Clone)] /// Builder for [`Endpoint`] pub struct Builder { diff --git a/rust-runtime/inlineable/src/serialization_settings.rs b/rust-runtime/inlineable/src/serialization_settings.rs index 0bb85b98c9..a00e38e6b4 100644 --- a/rust-runtime/inlineable/src/serialization_settings.rs +++ b/rust-runtime/inlineable/src/serialization_settings.rs @@ -6,6 +6,7 @@ #![allow(dead_code)] use aws_smithy_http::header::set_request_header_if_absent; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use http::header::{HeaderName, CONTENT_LENGTH, CONTENT_TYPE}; /// Configuration for how default protocol headers are serialized @@ -57,6 +58,10 @@ impl HeaderSerializationSettings { } } +impl Storable for HeaderSerializationSettings { + type Storer = StoreReplace; +} + #[cfg(test)] mod tests { use super::*; From 57459f042b871df27e19b1255db223474809ea44 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 26 Jun 2023 16:11:05 -0700 Subject: [PATCH 181/253] Fix optional auth in the orchestrator (#2808) This PR: - Renames "anonymous auth" to "no auth" - Removes fallback to other auth schemes when an identity fails to resolve (this was not complying to the reference architecture) - Adds the ability to opt out of credentials in `ConfigLoader`, and removes defaulting of the shared credentials cache if no credentials provider is given - Makes `ConfigBagAccessors` work on `FrozenLayer` and `CloneableLayer` - Fixes STS and aws-config tests ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../src/default_provider/credentials.rs | 13 +- aws/rust-runtime/aws-config/src/lib.rs | 90 +++- aws/rust-runtime/aws-config/src/sts.rs | 4 +- .../src/glacier_interceptors.rs | 3 +- .../src/presigning_interceptors.rs | 2 +- .../aws-runtime/src/auth/sigv4.rs | 13 +- .../AwsCustomizableOperationDecorator.kt | 3 +- .../smithy/rustsdk/AwsPresigningDecorator.kt | 6 +- .../amazon/smithy/rustsdk/CredentialCaches.kt | 18 +- .../smithy/rustsdk/CredentialProviders.kt | 37 +- .../smithy/rustsdk/SigV4AuthDecorator.kt | 39 +- .../customize/DisabledAuthDecorator.kt | 15 +- .../customize/ServiceSpecificDecorator.kt | 14 +- .../rustsdk/customize/ec2/Ec2Decorator.kt | 3 +- .../customize/route53/Route53Decorator.kt | 3 +- .../rustsdk/customize/s3/S3Decorator.kt | 3 +- .../customize/s3control/S3ControlDecorator.kt | 3 +- .../rustsdk/customize/sso/SSODecorator.kt | 3 +- .../rustsdk/customize/sts/STSDecorator.kt | 3 +- .../rustsdk/AwsPresigningDecoratorTest.kt | 11 +- .../integration-tests/sts/tests/signing-it.rs | 19 + .../client/smithy/ClientCodegenVisitor.kt | 2 +- .../client/smithy/RustClientCodegenPlugin.kt | 2 + .../customizations/HttpAuthDecorator.kt | 199 ++++---- .../HttpConnectorConfigDecorator.kt | 3 +- .../IdentityConfigCustomization.kt | 32 ++ .../smithy/customizations/NoAuthDecorator.kt | 74 +++ .../customize/ClientCodegenDecorator.kt | 29 +- .../customize/RequiredCustomizations.kt | 13 +- .../EndpointParamsInterceptorGenerator.kt | 2 +- .../generators/OperationCustomization.kt | 4 +- .../smithy/generators/OperationGenerator.kt | 4 + .../OperationRuntimePluginGenerator.kt | 67 ++- .../smithy/generators/PaginatorGenerator.kt | 2 +- .../ServiceRuntimePluginGenerator.kt | 120 +++-- .../config/ServiceConfigGenerator.kt | 2 +- .../customizations/HttpAuthDecoratorTest.kt | 69 +++ .../generators/PaginatorGeneratorTest.kt | 2 + .../client/FluentClientGeneratorTest.kt | 1 + .../rust/codegen/core/smithy/RuntimeType.kt | 2 + .../smithy/customize/CoreCodegenDecorator.kt | 14 +- .../smithy/PythonServerCodegenVisitor.kt | 2 +- .../server/smithy/ServerCodegenVisitor.kt | 2 +- .../AdditionalErrorsDecorator.kt | 5 +- .../customize/ServerCodegenDecorator.kt | 5 +- .../AdditionalErrorsDecoratorTest.kt | 6 +- .../smithy/TsServerCodegenVisitor.kt | 2 +- .../aws-smithy-runtime-api/src/client.rs | 3 + .../aws-smithy-runtime-api/src/client/auth.rs | 128 ++--- .../src/client/config_bag_accessors.rs | 442 ++++++++++++++++++ .../src/client/identity.rs | 108 +++-- .../src/client/orchestrator.rs | 273 +---------- rust-runtime/aws-smithy-runtime/Cargo.toml | 2 +- rust-runtime/aws-smithy-runtime/src/client.rs | 3 - .../aws-smithy-runtime/src/client/auth.rs | 3 + .../src/client/auth/http.rs | 28 +- .../src/client/auth/no_auth.rs | 96 ++++ .../aws-smithy-runtime/src/client/identity.rs | 4 +- .../identity/{anonymous.rs => no_auth.rs} | 12 +- .../src/client/orchestrator.rs | 7 +- .../src/client/orchestrator/auth.rs | 86 ++-- .../src/client/orchestrator/endpoints.rs | 3 +- .../src/client/retries/client_rate_limiter.rs | 4 +- .../src/client/retries/strategy/standard.rs | 8 +- .../src/client/runtime_plugin.rs | 7 - .../client/runtime_plugin/anonymous_auth.rs | 111 ----- .../src/client/test_util/deserializer.rs | 4 +- .../src/client/test_util/interceptors.rs | 2 +- .../src/client/test_util/serializer.rs | 6 +- .../aws-smithy-runtime/src/client/timeout.rs | 3 +- .../check-aws-sdk-orchestrator-impl | 4 +- 71 files changed, 1425 insertions(+), 892 deletions(-) create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs rename rust-runtime/aws-smithy-runtime/src/client/identity/{anonymous.rs => no_auth.rs} (67%) delete mode 100644 rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs delete mode 100644 rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs diff --git a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs index db749d28e9..115cafcab1 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs @@ -250,11 +250,14 @@ mod test { .await .unwrap() .with_provider_config($provider_config_builder) - .$func(|conf| async { - crate::default_provider::credentials::Builder::default() - .configure(conf) - .build() - .await + .$func(|conf| { + let conf = conf.clone(); + async move { + crate::default_provider::credentials::Builder::default() + .configure(conf) + .build() + .await + } }) .await } diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 5871762204..f0e4e3bdca 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -171,6 +171,17 @@ mod loader { use crate::profile::profile_file::ProfileFiles; use crate::provider_config::ProviderConfig; + #[derive(Default, Debug)] + enum CredentialsProviderOption { + /// No provider was set by the user. We can set up the default credentials provider chain. + #[default] + NotSet, + /// The credentials provider was explicitly unset. Do not set up a default chain. + ExplicitlyUnset, + /// Use the given credentials provider. + Set(SharedCredentialsProvider), + } + /// Load a cross-service [`SdkConfig`](aws_types::SdkConfig) from the environment /// /// This builder supports overriding individual components of the generated config. Overriding a component @@ -181,7 +192,7 @@ mod loader { pub struct ConfigLoader { app_name: Option, credentials_cache: Option, - credentials_provider: Option, + credentials_provider: CredentialsProviderOption, endpoint_url: Option, region: Option>, retry_config: Option, @@ -348,7 +359,33 @@ mod loader { mut self, credentials_provider: impl ProvideCredentials + 'static, ) -> Self { - self.credentials_provider = Some(SharedCredentialsProvider::new(credentials_provider)); + self.credentials_provider = CredentialsProviderOption::Set( + SharedCredentialsProvider::new(credentials_provider), + ); + self + } + + // TODO(enableNewSmithyRuntimeLaunch): Remove the doc hidden from this function + #[doc(hidden)] + /// Don't use credentials to sign requests. + /// + /// Turning off signing with credentials is necessary in some cases, such as using + /// anonymous auth for S3, calling operations in STS that don't require a signature, + /// or using token-based auth. + /// + /// # Examples + /// + /// Turn off credentials in order to call a service without signing: + /// ```no_run + /// # async fn create_config() { + /// let config = aws_config::from_env() + /// .no_credentials() + /// .load() + /// .await; + /// # } + /// ``` + pub fn no_credentials(mut self) -> Self { + self.credentials_provider = CredentialsProviderOption::ExplicitlyUnset; self } @@ -570,13 +607,28 @@ mod loader { .http_connector .unwrap_or_else(|| HttpConnector::ConnectorFn(Arc::new(default_connector))); - let credentials_cache = self.credentials_cache.unwrap_or_else(|| { - let mut builder = CredentialsCache::lazy_builder().time_source( - aws_credential_types::time_source::TimeSource::shared(conf.time_source()), - ); - builder.set_sleep(conf.sleep()); - builder.into_credentials_cache() - }); + let credentials_provider = match self.credentials_provider { + CredentialsProviderOption::Set(provider) => Some(provider), + CredentialsProviderOption::NotSet => { + let mut builder = + credentials::DefaultCredentialsChain::builder().configure(conf.clone()); + builder.set_region(region.clone()); + Some(SharedCredentialsProvider::new(builder.build().await)) + } + CredentialsProviderOption::ExplicitlyUnset => None, + }; + + let credentials_cache = if credentials_provider.is_some() { + Some(self.credentials_cache.unwrap_or_else(|| { + let mut builder = CredentialsCache::lazy_builder().time_source( + aws_credential_types::time_source::TimeSource::shared(conf.time_source()), + ); + builder.set_sleep(conf.sleep()); + builder.into_credentials_cache() + })) + } else { + None + }; let use_fips = if let Some(use_fips) = self.use_fips { Some(use_fips) @@ -590,26 +642,18 @@ mod loader { use_dual_stack_provider(&conf).await }; - let credentials_provider = if let Some(provider) = self.credentials_provider { - provider - } else { - let mut builder = credentials::DefaultCredentialsChain::builder().configure(conf); - builder.set_region(region.clone()); - SharedCredentialsProvider::new(builder.build().await) - }; - let ts = self.time_source.unwrap_or_default(); let mut builder = SdkConfig::builder() .region(region) .retry_config(retry_config) .timeout_config(timeout_config) - .credentials_cache(credentials_cache) - .credentials_provider(credentials_provider) .time_source(ts) .http_connector(http_connector); builder.set_app_name(app_name); + builder.set_credentials_cache(credentials_cache); + builder.set_credentials_provider(credentials_provider); builder.set_sleep_impl(sleep_impl); builder.set_endpoint_url(self.endpoint_url); builder.set_use_fips(use_fips); @@ -719,5 +763,13 @@ mod loader { let conf = base_conf().app_name(app_name.clone()).load().await; assert_eq!(Some(&app_name), conf.app_name()); } + + #[cfg(aws_sdk_orchestrator_mode)] + #[tokio::test] + async fn disable_default_credentials() { + let config = from_env().no_credentials().load().await; + assert!(config.credentials_cache().is_none()); + assert!(config.credentials_provider().is_none()); + } } } diff --git a/aws/rust-runtime/aws-config/src/sts.rs b/aws/rust-runtime/aws-config/src/sts.rs index edaf1bfc15..aba7a24bfc 100644 --- a/aws/rust-runtime/aws-config/src/sts.rs +++ b/aws/rust-runtime/aws-config/src/sts.rs @@ -12,7 +12,6 @@ pub use assume_role::{AssumeRoleProvider, AssumeRoleProviderBuilder}; mod assume_role; use crate::connector::expect_connector; -use aws_credential_types::cache::CredentialsCache; use aws_sdk_sts::config::Builder as StsConfigBuilder; use aws_smithy_types::retry::RetryConfig; @@ -22,8 +21,7 @@ impl crate::provider_config::ProviderConfig { .http_connector(expect_connector(self.connector(&Default::default()))) .retry_config(RetryConfig::standard()) .region(self.region()) - .time_source(self.time_source()) - .credentials_cache(CredentialsCache::no_caching()); + .time_source(self.time_source()); builder.set_sleep_impl(self.sleep()); builder } diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs index e165dad882..18bf422f49 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -11,11 +11,12 @@ use aws_sigv4::http_request::SignableBody; use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream; use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{ BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, }; use aws_smithy_runtime_api::client::interceptors::Interceptor; -use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, LoadedRequestBody}; +use aws_smithy_runtime_api::client::orchestrator::LoadedRequestBody; use aws_smithy_types::config_bag::ConfigBag; use bytes::Bytes; use http::header::{HeaderName, HeaderValue}; diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs index 2a193c08c0..ddc969ac23 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs @@ -15,13 +15,13 @@ use aws_sigv4::http_request::SignableBody; use aws_smithy_async::time::{SharedTimeSource, StaticTimeSource}; use aws_smithy_runtime::client::retries::strategy::NeverRetryStrategy; use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{ BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, }; use aws_smithy_runtime_api::client::interceptors::{ disable_interceptor, Interceptor, InterceptorRegistrar, SharedInterceptor, }; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::retries::DynRetryStrategy; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs index ef127eb0d9..366e3ce0db 100644 --- a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -12,8 +12,11 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::{ AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, }; -use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; -use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpRequest}; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use aws_smithy_runtime_api::client::identity::{ + Identity, IdentityResolvers, SharedIdentityResolver, +}; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::Document; use aws_types::region::{Region, SigningRegion}; @@ -94,10 +97,10 @@ impl HttpAuthScheme for SigV4HttpAuthScheme { SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index fa98df834a..29718fa3cd 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -22,8 +22,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : .resolve("user_agent::AwsUserAgent"), "BeforeTransmitInterceptorContextMut" to RuntimeType.beforeTransmitInterceptorContextMut(runtimeConfig), "ConfigBag" to RuntimeType.configBag(runtimeConfig), - "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::orchestrator::ConfigBagAccessors"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "http" to CargoDependency.Http.toType(), "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), "SharedTimeSource" to CargoDependency.smithyAsync(runtimeConfig).withFeature("test-util").toType() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index 1e5d379618..8fe4d90024 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -17,6 +17,7 @@ import software.amazon.smithy.model.traits.HttpQueryTrait import software.amazon.smithy.model.traits.HttpTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection @@ -113,7 +114,7 @@ class AwsPresigningDecorator internal constructor( /** * Adds presignable trait to known presignable operations and creates synthetic presignable shapes for codegen */ - override fun transformModel(service: ServiceShape, model: Model): Model { + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model { val modelWithSynthetics = addSyntheticOperations(model) val presignableTransforms = mutableListOf() val intermediate = ModelTransformer.create().mapShapes(modelWithSynthetics) { shape -> @@ -369,8 +370,7 @@ class AwsPresignedFluentBuilderMethod( } """, "AlternateSerializer" to alternateSerializer(operationShape), - "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) - .resolve("client::orchestrator::ConfigBagAccessors"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "Layer" to smithyTypes.resolve("config_bag::Layer"), "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt index fcf2cd82cb..053e7ec7e6 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt @@ -79,8 +79,8 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom rustTemplate( """ /// Returns the credentials cache. - pub fn credentials_cache(&self) -> #{SharedCredentialsCache} { - self.inner.load::<#{SharedCredentialsCache}>().expect("credentials cache should be set").clone() + pub fn credentials_cache(&self) -> #{Option}<#{SharedCredentialsCache}> { + self.inner.load::<#{SharedCredentialsCache}>().cloned() } """, *codegenScope, @@ -145,9 +145,8 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - layer.store_put( - layer.load::<#{CredentialsCache}>() - .cloned() + if let Some(credentials_provider) = layer.load::<#{SharedCredentialsProvider}>().cloned() { + let cache_config = layer.load::<#{CredentialsCache}>().cloned() .unwrap_or_else({ let sleep = layer.load::<#{SharedAsyncSleep}>().cloned(); || match sleep { @@ -158,11 +157,10 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom } None => #{CredentialsCache}::lazy(), } - }) - .create_cache(layer.load::<#{SharedCredentialsProvider}>().cloned().unwrap_or_else(|| { - #{SharedCredentialsProvider}::new(#{DefaultProvider}) - })) - ); + }); + let shared_credentials_cache = cache_config.create_cache(credentials_provider); + layer.store_put(shared_credentials_cache); + } """, *codegenScope, ) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index fd0595d74d..4ca6c24534 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.config.Confi import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig 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.rustBlockTemplate 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 @@ -138,24 +139,24 @@ class CredentialsIdentityResolverRegistration( override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { is ServiceRuntimePluginSection.AdditionalConfig -> { - rustTemplate( - """ - cfg.set_identity_resolvers( - #{IdentityResolvers}::builder() - .identity_resolver( - #{SIGV4_SCHEME_ID}, - #{CredentialsIdentityResolver}::new(self.handle.conf.credentials_cache()) - ) - .build() - ); - """, - "SIGV4_SCHEME_ID" to AwsRuntimeType.awsRuntime(runtimeConfig) - .resolve("auth::sigv4::SCHEME_ID"), - "CredentialsIdentityResolver" to AwsRuntimeType.awsRuntime(runtimeConfig) - .resolve("identity::credentials::CredentialsIdentityResolver"), - "IdentityResolvers" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::identity::IdentityResolvers"), - ) + rustBlockTemplate("if let Some(credentials_cache) = self.handle.conf.credentials_cache()") { + section.registerIdentityResolver(this, runtimeConfig) { + rustTemplate( + """ + #{SIGV4_SCHEME_ID}, + #{SharedIdentityResolver}::new( + #{CredentialsIdentityResolver}::new(credentials_cache), + ), + """, + "SIGV4_SCHEME_ID" to AwsRuntimeType.awsRuntime(runtimeConfig) + .resolve("auth::sigv4::SCHEME_ID"), + "CredentialsIdentityResolver" to AwsRuntimeType.awsRuntime(runtimeConfig) + .resolve("identity::credentials::CredentialsIdentityResolver"), + "SharedIdentityResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::identity::SharedIdentityResolver"), + ) + } + } } else -> {} } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index d1cd293dca..7d469b4d98 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -9,8 +9,8 @@ import software.amazon.smithy.aws.traits.auth.SigV4Trait import software.amazon.smithy.aws.traits.auth.UnsignedPayloadTrait import software.amazon.smithy.model.knowledge.ServiceIndex import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection @@ -30,6 +30,20 @@ class SigV4AuthDecorator : ClientCodegenDecorator { override val name: String get() = "SigV4AuthDecorator" override val order: Byte = 0 + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthOptions: List, + ): List = baseAuthOptions.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + AuthOption.StaticAuthOption(SigV4Trait.ID) { + rustTemplate( + "#{scheme_id},", + "scheme_id" to AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig) + .resolve("auth::sigv4::SCHEME_ID"), + ) + } + } + override fun serviceRuntimePluginCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -58,26 +72,21 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext: "SigV4HttpAuthScheme" to awsRuntime.resolve("auth::sigv4::SigV4HttpAuthScheme"), "SigningRegion" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::SigningRegion"), "SigningService" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("SigningService"), + "SharedHttpAuthScheme" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::auth::SharedHttpAuthScheme"), ) } override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { - is ServiceRuntimePluginSection.HttpAuthScheme -> { - rustTemplate( - """ - .auth_scheme(#{SIGV4_SCHEME_ID}, #{SigV4HttpAuthScheme}::new()) - """, - *codegenScope, - ) - } - is ServiceRuntimePluginSection.AdditionalConfig -> { val serviceHasEventStream = codegenContext.serviceShape.hasEventStreamOperations(codegenContext.model) if (serviceHasEventStream) { // enable the aws-runtime `sign-eventstream` feature addDependency(AwsCargoDependency.awsRuntime(runtimeConfig).withFeature("event-stream").toType().toSymbol()) } + section.registerHttpAuthScheme(this, runtimeConfig) { + rustTemplate("#{SharedHttpAuthScheme}::new(#{SigV4HttpAuthScheme}::new())", *codegenScope) + } } else -> {} @@ -88,11 +97,8 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext: private class AuthOperationCustomization(private val codegenContext: ClientCodegenContext) : OperationCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope by lazy { - val runtimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) arrayOf( - "DynAuthOptionResolver" to runtimeApi.resolve("client::auth::DynAuthOptionResolver"), - "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "HttpSignatureType" to awsRuntime.resolve("auth::sigv4::HttpSignatureType"), "SIGV4_SCHEME_ID" to awsRuntime.resolve("auth::sigv4::SCHEME_ID"), "SigV4OperationSigningConfig" to awsRuntime.resolve("auth::sigv4::SigV4OperationSigningConfig"), @@ -113,14 +119,12 @@ private class AuthOperationCustomization(private val codegenContext: ClientCodeg val doubleUriEncode = unsignedPayload || !disableDoubleEncode(codegenContext.serviceShape) val contentSha256Header = needsAmzSha256(codegenContext.serviceShape) val normalizeUrlPath = !disableUriPathNormalization(codegenContext.serviceShape) - val signingOptional = section.operationShape.hasTrait() rustTemplate( """ let mut signing_options = #{SigningOptions}::default(); signing_options.double_uri_encode = $doubleUriEncode; signing_options.content_sha256_header = $contentSha256Header; signing_options.normalize_uri_path = $normalizeUrlPath; - signing_options.signing_optional = $signingOptional; signing_options.payload_override = #{payload_override}; ${section.newLayerName}.store_put(#{SigV4OperationSigningConfig} { @@ -128,11 +132,6 @@ private class AuthOperationCustomization(private val codegenContext: ClientCodeg service: None, signing_options, }); - // TODO(enableNewSmithyRuntimeLaunch): Make auth options additive in the config bag so that multiple codegen decorators can register them - let auth_option_resolver = #{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new( - vec![#{SIGV4_SCHEME_ID}] - )); - ${section.newLayerName}.set_auth_option_resolver(auth_option_resolver); """, *codegenScope, "payload_override" to writable { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt index dfbd2ca597..6cc7345dfa 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt @@ -9,12 +9,14 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.traits.AuthTrait +import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.util.shapeId // / STS (and possibly other services) need to have auth manually set to [] -class DisabledAuthDecorator : ClientCodegenDecorator { +class DisabledAuthDecorator() : ClientCodegenDecorator { override val name: String = "OptionalAuth" override val order: Byte = 0 @@ -30,14 +32,21 @@ class DisabledAuthDecorator : ClientCodegenDecorator { private fun applies(service: ServiceShape) = optionalAuth.containsKey(service.id) - override fun transformModel(service: ServiceShape, model: Model): Model { + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model { if (!applies(service)) { return model } val optionalOperations = optionalAuth[service.id]!! return ModelTransformer.create().mapShapes(model) { if (optionalOperations.contains(it.id) && it is OperationShape) { - it.toBuilder().addTrait(AuthTrait(setOf())).build() + if (settings.codegenConfig.enableNewSmithyRuntime.defaultToOrchestrator) { + it.toBuilder().addTrait(OptionalAuthTrait()).build() + } else { + // In middleware, having an empty @auth trait completely disabled + // auth for an operation since not having credentials isn't an option + // in that implementation. + it.toBuilder().addTrait(AuthTrait(setOf())).build() + } } else { it } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index 1a1c1d5788..3f8a8f6d61 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -11,6 +11,8 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientProtocolMap import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization @@ -58,6 +60,14 @@ class ServiceSpecificDecorator( // This kind of decorator gets explicitly added to the root sdk-codegen decorator override fun classpathDiscoverable(): Boolean = false + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthOptions: List, + ): List = baseAuthOptions.maybeApply(codegenContext.serviceShape) { + delegateTo.authOptions(codegenContext, operationShape, baseAuthOptions) + } + override fun builderCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -129,9 +139,9 @@ class ServiceSpecificDecorator( delegateTo.structureCustomizations(codegenContext, baseCustomizations) } - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = model.maybeApply(service) { - delegateTo.transformModel(service, model) + delegateTo.transformModel(service, model, settings) } override fun serviceRuntimePluginCustomizations( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt index e788920e1d..693b2b572d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ec2/Ec2Decorator.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rustsdk.customize.ec2 import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.ServiceShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator class Ec2Decorator : ClientCodegenDecorator { @@ -15,6 +16,6 @@ class Ec2Decorator : ClientCodegenDecorator { // EC2 incorrectly models primitive shapes as unboxed when they actually // need to be boxed for the API to work properly - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = EC2MakePrimitivesOptional.processModel(model) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt index befc4021b0..9e0888fa39 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/route53/Route53Decorator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.HttpLabelTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection @@ -33,7 +34,7 @@ class Route53Decorator : ClientCodegenDecorator { private val logger: Logger = Logger.getLogger(javaClass.name) private val resourceShapes = setOf(ShapeId.from("com.amazonaws.route53#ResourceId"), ShapeId.from("com.amazonaws.route53#ChangeId")) - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isResourceId(shape)) { logger.info("Adding TrimResourceId trait to $shape") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt index d49c2732c7..72f128fb70 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3/S3Decorator.kt @@ -18,6 +18,7 @@ import software.amazon.smithy.rulesengine.traits.EndpointTestCase import software.amazon.smithy.rulesengine.traits.EndpointTestOperationInput import software.amazon.smithy.rulesengine.traits.EndpointTestsTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName @@ -59,7 +60,7 @@ class S3Decorator : ClientCodegenDecorator { }, ) - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isInInvalidXmlRootAllowList(shape)) { logger.info("Adding AllowInvalidXmlRoot trait to $it") diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt index 3534258b18..ce96a33b82 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/s3control/S3ControlDecorator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.Node import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName @@ -23,7 +24,7 @@ class S3ControlDecorator : ClientCodegenDecorator { override val name: String = "S3Control" override val order: Byte = 0 - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = stripEndpointTrait("AccountId")(model) override fun endpointCustomizations(codegenContext: ClientCodegenContext): List { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt index f4e14cbe4b..8ead07d3bc 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sso/SSODecorator.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.traits.SensitiveTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.util.letIf import java.util.logging.Logger @@ -23,7 +24,7 @@ class SSODecorator : ClientCodegenDecorator { private fun isAwsCredentials(shape: Shape): Boolean = shape.id == ShapeId.from("com.amazonaws.sso#RoleCredentials") - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isAwsCredentials(shape)) { (shape as StructureShape).toBuilder().addTrait(SensitiveTrait()).build() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt index 9c83a266b5..ed23c99d8f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/sts/STSDecorator.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.RetryableTrait import software.amazon.smithy.model.traits.SensitiveTrait import software.amazon.smithy.model.transform.ModelTransformer +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.letIf @@ -29,7 +30,7 @@ class STSDecorator : ClientCodegenDecorator { private fun isAwsCredentials(shape: Shape): Boolean = shape.id == ShapeId.from("com.amazonaws.sts#Credentials") - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ClientRustSettings): Model = ModelTransformer.create().mapShapes(model) { shape -> shape.letIf(isIdpCommunicationError(shape)) { logger.info("Adding @retryable trait to $shape and setting its error type to 'server'") diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt index e79617a716..0435e74259 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecoratorTest.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.HttpTrait +import software.amazon.smithy.rust.codegen.client.testutil.testClientRustSettings import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.expectTrait import software.amazon.smithy.rust.codegen.core.util.orNull @@ -27,9 +28,10 @@ class AwsPresigningDecoratorTest { } private fun testTransform(namespace: String, name: String, presignable: Boolean) { + val settings = testClientRustSettings() val decorator = AwsPresigningDecorator() val model = testOperation(namespace, name) - val transformed = decorator.transformModel(serviceShape(model), model) + val transformed = decorator.transformModel(serviceShape(model), model, settings) hasPresignableTrait(transformed, namespace, name) shouldBe presignable } @@ -65,6 +67,7 @@ class AwsPresigningDecoratorTest { class OverrideHttpMethodTransformTest { @Test fun `it should override the HTTP method for the listed operations`() { + val settings = testClientRustSettings() val model = """ namespace test use aws.protocols#restJson1 @@ -105,7 +108,7 @@ class OverrideHttpMethodTransformTest { ShapeId.from("test#One") to presignableOp, ShapeId.from("test#Two") to presignableOp, ), - ).transformModel(serviceShape, model) + ).transformModel(serviceShape, model, settings) val synthNamespace = "test.synthetic.aws.presigned" transformed.expectShape(ShapeId.from("$synthNamespace#One")).expectTrait().method shouldBe "GET" @@ -115,8 +118,10 @@ class OverrideHttpMethodTransformTest { } class MoveDocumentMembersToQueryParamsTransformTest { + @Test fun `it should move document members to query parameters for the listed operations`() { + val settings = testClientRustSettings() val model = """ namespace test use aws.protocols#restJson1 @@ -164,7 +169,7 @@ class MoveDocumentMembersToQueryParamsTransformTest { ) val transformed = AwsPresigningDecorator( mapOf(ShapeId.from("test#One") to presignableOp), - ).transformModel(serviceShape, model) + ).transformModel(serviceShape, model, settings) val index = HttpBindingIndex(transformed) index.getRequestBindings(ShapeId.from("test.synthetic.aws.presigned#One")).map { (key, value) -> diff --git a/aws/sdk/integration-tests/sts/tests/signing-it.rs b/aws/sdk/integration-tests/sts/tests/signing-it.rs index f2c80bd9f7..01727a53c9 100644 --- a/aws/sdk/integration-tests/sts/tests/signing-it.rs +++ b/aws/sdk/integration-tests/sts/tests/signing-it.rs @@ -24,6 +24,8 @@ async fn assume_role_signed() { ); } +// TODO(enableNewSmithyRuntimeCleanup): Delete the middleware version of this test +#[cfg(not(aws_sdk_orchestrator_mode))] #[tokio::test] async fn web_identity_unsigned() { let creds = Credentials::for_tests(); @@ -42,6 +44,23 @@ async fn web_identity_unsigned() { ); } +#[cfg(aws_sdk_orchestrator_mode)] +#[tokio::test] +async fn web_identity_unsigned() { + let (server, request) = capture_request(None); + let conf = aws_sdk_sts::Config::builder() + .region(Region::new("us-east-1")) + .http_connector(server) + .build(); + let client = aws_sdk_sts::Client::from_conf(conf); + let _ = client.assume_role_with_web_identity().send().await; + // web identity should be unsigned + assert_eq!( + request.expect_request().headers().get("AUTHORIZATION"), + None + ); +} + #[tokio::test] async fn assume_role_saml_unsigned() { let (server, request) = capture_request(None); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index 7583bc3426..9ddbbd4dc8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -88,7 +88,7 @@ class ClientCodegenVisitor( codegenDecorator.protocols(untransformedService.id, ClientProtocolLoader.DefaultProtocols), ).protocolFor(context.model, untransformedService) protocolGeneratorFactory = generator - model = codegenDecorator.transformModel(untransformedService, baseModel) + model = codegenDecorator.transformModel(untransformedService, baseModel, settings) // the model transformer _might_ change the service shape val service = settings.getService(model) symbolProvider = RustClientCodegenPlugin.baseSymbolProvider(settings, model, service, rustSymbolProviderConfig, codegenDecorator) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt index 08ba90d140..7b05e664e8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/RustClientCodegenPlugin.kt @@ -13,6 +13,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.ApiKeyAu import software.amazon.smithy.rust.codegen.client.smithy.customizations.ClientCustomizations import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpAuthDecorator import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpConnectorConfigDecorator +import software.amazon.smithy.rust.codegen.client.smithy.customizations.NoAuthDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.CombinedClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.RequiredCustomizations @@ -61,6 +62,7 @@ class RustClientCodegenPlugin : ClientDecoratableBuildPlugin() { FluentClientDecorator(), EndpointsDecorator(), EndpointParamsDecorator(), + NoAuthDecorator(), ApiKeyAuthDecorator(), HttpAuthDecorator(), HttpConnectorConfigDecorator(), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index bbdead7100..4a8f6057f5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -7,25 +7,23 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.model.knowledge.ServiceIndex import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.traits.AuthTrait +import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.traits.HttpApiKeyAuthTrait import software.amazon.smithy.model.traits.HttpBasicAuthTrait import software.amazon.smithy.model.traits.HttpBearerAuthTrait import software.amazon.smithy.model.traits.HttpDigestAuthTrait 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.customize.AuthOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption.StaticAuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency 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.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType @@ -41,10 +39,10 @@ fun codegenScope(runtimeConfig: RuntimeConfig): Array> { val authHttp = smithyRuntime.resolve("client::auth::http") val authHttpApi = smithyRuntimeApi.resolve("client::auth::http") return arrayOf( + "AuthSchemeId" to smithyRuntimeApi.resolve("client::auth::AuthSchemeId"), "ApiKeyAuthScheme" to authHttp.resolve("ApiKeyAuthScheme"), "ApiKeyLocation" to authHttp.resolve("ApiKeyLocation"), - "DynAuthOptionResolver" to smithyRuntimeApi.resolve("client::auth::DynAuthOptionResolver"), - "StaticAuthOptionResolver" to smithyRuntimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "BasicAuthScheme" to authHttp.resolve("BasicAuthScheme"), "BearerAuthScheme" to authHttp.resolve("BearerAuthScheme"), "DigestAuthScheme" to authHttp.resolve("DigestAuthScheme"), @@ -53,9 +51,10 @@ fun codegenScope(runtimeConfig: RuntimeConfig): Array> { "HTTP_BEARER_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_BEARER_AUTH_SCHEME_ID"), "HTTP_DIGEST_AUTH_SCHEME_ID" to authHttpApi.resolve("HTTP_DIGEST_AUTH_SCHEME_ID"), "IdentityResolver" to smithyRuntimeApi.resolve("client::identity::IdentityResolver"), - "IdentityResolvers" to smithyRuntimeApi.resolve("client::identity::IdentityResolvers"), "Login" to smithyRuntimeApi.resolve("client::identity::http::Login"), "PropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::PropertyBag"), + "SharedHttpAuthScheme" to smithyRuntimeApi.resolve("client::auth::SharedHttpAuthScheme"), + "SharedIdentityResolver" to smithyRuntimeApi.resolve("client::identity::SharedIdentityResolver"), "Token" to smithyRuntimeApi.resolve("client::identity::http::Token"), ) } @@ -88,6 +87,37 @@ class HttpAuthDecorator : ClientCodegenDecorator { override val name: String get() = "HttpAuthDecorator" override val order: Byte = 0 + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthOptions: List, + ): List { + val serviceIndex = ServiceIndex.of(codegenContext.model) + val authSchemes = serviceIndex.getEffectiveAuthSchemes(codegenContext.serviceShape, operationShape) + val codegenScope = codegenScope(codegenContext.runtimeConfig) + val options = ArrayList() + for (authScheme in authSchemes.keys) { + fun addOption(schemeShapeId: ShapeId, name: String) { + options.add( + StaticAuthOption( + schemeShapeId, + writable { + rustTemplate("$name,", *codegenScope) + }, + ), + ) + } + when (authScheme) { + HttpApiKeyAuthTrait.ID -> addOption(authScheme, "#{HTTP_API_KEY_AUTH_SCHEME_ID}") + HttpBasicAuthTrait.ID -> addOption(authScheme, "#{HTTP_BASIC_AUTH_SCHEME_ID}") + HttpBearerAuthTrait.ID -> addOption(authScheme, "#{HTTP_BEARER_AUTH_SCHEME_ID}") + HttpDigestAuthTrait.ID -> addOption(authScheme, "#{HTTP_DIGEST_AUTH_SCHEME_ID}") + else -> {} + } + } + return baseAuthOptions + options + } + override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -108,17 +138,6 @@ class HttpAuthDecorator : ClientCodegenDecorator { } } - override fun operationCustomizations( - codegenContext: ClientCodegenContext, - operation: OperationShape, - baseCustomizations: List, - ): List = - HttpAuthSchemes.from(codegenContext).let { authSchemes -> - baseCustomizations.letIf(authSchemes.anyEnabled()) { - it + HttpAuthOperationCustomization(codegenContext) - } - } - override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { val authSchemes = HttpAuthSchemes.from(codegenContext) if (authSchemes.anyEnabled()) { @@ -136,7 +155,7 @@ class HttpAuthDecorator : ClientCodegenDecorator { } private class HttpAuthServiceRuntimePluginCustomization( - codegenContext: ClientCodegenContext, + private val codegenContext: ClientCodegenContext, private val authSchemes: HttpAuthSchemes, ) : ServiceRuntimePluginCustomization() { private val serviceShape = codegenContext.serviceShape @@ -144,7 +163,18 @@ private class HttpAuthServiceRuntimePluginCustomization( override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { - is ServiceRuntimePluginSection.HttpAuthScheme -> { + is ServiceRuntimePluginSection.AdditionalConfig -> { + fun registerAuthScheme(scheme: Writable) { + section.registerHttpAuthScheme(this, codegenContext.runtimeConfig) { + rustTemplate("#{SharedHttpAuthScheme}::new(#{Scheme})", *codegenScope, "Scheme" to scheme) + } + } + fun registerNamedAuthScheme(name: String) { + registerAuthScheme { + rustTemplate("#{$name}::new()", *codegenScope) + } + } + if (authSchemes.apiKey) { val trait = serviceShape.getTrait()!! val location = when (trait.`in`!!) { @@ -158,76 +188,30 @@ private class HttpAuthServiceRuntimePluginCustomization( HttpApiKeyAuthTrait.Location.QUERY -> "Query" } - rustTemplate( - """ - .auth_scheme( - #{HTTP_API_KEY_AUTH_SCHEME_ID}, + registerAuthScheme { + rustTemplate( + """ #{ApiKeyAuthScheme}::new( ${trait.scheme.orElse("").dq()}, #{ApiKeyLocation}::$location, ${trait.name.dq()}, ) + """, + *codegenScope, ) - """, - *codegenScope, - ) + } } if (authSchemes.basic) { - rustTemplate(".auth_scheme(#{HTTP_BASIC_AUTH_SCHEME_ID}, #{BasicAuthScheme}::new())", *codegenScope) + registerNamedAuthScheme("BasicAuthScheme") } if (authSchemes.bearer) { - rustTemplate( - ".auth_scheme(#{HTTP_BEARER_AUTH_SCHEME_ID}, #{BearerAuthScheme}::new())", - *codegenScope, - ) + registerNamedAuthScheme("BearerAuthScheme") } if (authSchemes.digest) { - rustTemplate( - ".auth_scheme(#{HTTP_DIGEST_AUTH_SCHEME_ID}, #{DigestAuthScheme}::new())", - *codegenScope, - ) + registerNamedAuthScheme("DigestAuthScheme") } } - is ServiceRuntimePluginSection.AdditionalConfig -> { - if (authSchemes.anyEnabled()) { - rust("cfg.set_identity_resolvers(self.handle.conf.identity_resolvers().clone());") - } - } - - else -> emptySection - } - } -} - -private class HttpAuthOperationCustomization(codegenContext: ClientCodegenContext) : OperationCustomization() { - private val serviceShape = codegenContext.serviceShape - private val codegenScope = codegenScope(codegenContext.runtimeConfig) - - override fun section(section: OperationSection): Writable = writable { - when (section) { - is OperationSection.AdditionalRuntimePluginConfig -> { - withBlockTemplate( - "let auth_option_resolver = #{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new(vec![", - "]));", - *codegenScope, - ) { - val authTrait: AuthTrait? = section.operationShape.getTrait() ?: serviceShape.getTrait() - for (authScheme in authTrait?.valueSet ?: emptySet()) { - when (authScheme) { - HttpApiKeyAuthTrait.ID -> rustTemplate("#{HTTP_API_KEY_AUTH_SCHEME_ID},", *codegenScope) - HttpBasicAuthTrait.ID -> rustTemplate("#{HTTP_BASIC_AUTH_SCHEME_ID},", *codegenScope) - HttpBearerAuthTrait.ID -> rustTemplate("#{HTTP_BEARER_AUTH_SCHEME_ID},", *codegenScope) - HttpDigestAuthTrait.ID -> rustTemplate("#{HTTP_DIGEST_AUTH_SCHEME_ID},", *codegenScope) - else -> {} - } - } - } - - // TODO(enableNewSmithyRuntimeLaunch): Make auth options additive in the config bag so that multiple codegen decorators can register them - rustTemplate("${section.newLayerName}.set_auth_option_resolver(auth_option_resolver);", *codegenScope) - } - else -> emptySection } } @@ -238,14 +222,9 @@ private class HttpAuthConfigCustomization( private val authSchemes: HttpAuthSchemes, ) : ConfigCustomization() { private val codegenScope = codegenScope(codegenContext.runtimeConfig) - private val runtimeMode = codegenContext.smithyRuntimeMode override fun section(section: ServiceConfig): Writable = writable { when (section) { - is ServiceConfig.BuilderStruct -> { - rustTemplate("identity_resolvers: #{IdentityResolvers},", *codegenScope) - } - is ServiceConfig.BuilderImpl -> { if (authSchemes.apiKey) { rustTemplate( @@ -257,9 +236,11 @@ private class HttpAuthConfigCustomization( /// Sets an API key resolver will be used for authentication. pub fn api_key_resolver(mut self, api_key_resolver: impl #{IdentityResolver} + 'static) -> Self { - self.identity_resolvers = self.identity_resolvers.to_builder() - .identity_resolver(#{HTTP_API_KEY_AUTH_SCHEME_ID}, api_key_resolver) - .build(); + #{ConfigBagAccessors}::push_identity_resolver( + &mut self.inner, + #{HTTP_API_KEY_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(api_key_resolver) + ); self } """, @@ -276,9 +257,11 @@ private class HttpAuthConfigCustomization( /// Sets a bearer token provider that will be used for HTTP bearer auth. pub fn bearer_token_resolver(mut self, bearer_token_resolver: impl #{IdentityResolver} + 'static) -> Self { - self.identity_resolvers = self.identity_resolvers.to_builder() - .identity_resolver(#{HTTP_BEARER_AUTH_SCHEME_ID}, bearer_token_resolver) - .build(); + #{ConfigBagAccessors}::push_identity_resolver( + &mut self.inner, + #{HTTP_BEARER_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(bearer_token_resolver) + ); self } """, @@ -295,9 +278,11 @@ private class HttpAuthConfigCustomization( /// Sets a login resolver that will be used for HTTP basic auth. pub fn basic_auth_login_resolver(mut self, basic_auth_resolver: impl #{IdentityResolver} + 'static) -> Self { - self.identity_resolvers = self.identity_resolvers.to_builder() - .identity_resolver(#{HTTP_BASIC_AUTH_SCHEME_ID}, basic_auth_resolver) - .build(); + #{ConfigBagAccessors}::push_identity_resolver( + &mut self.inner, + #{HTTP_BASIC_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(basic_auth_resolver) + ); self } """, @@ -314,9 +299,11 @@ private class HttpAuthConfigCustomization( /// Sets a login resolver that will be used for HTTP digest auth. pub fn digest_auth_login_resolver(mut self, digest_auth_resolver: impl #{IdentityResolver} + 'static) -> Self { - self.identity_resolvers = self.identity_resolvers.to_builder() - .identity_resolver(#{HTTP_DIGEST_AUTH_SCHEME_ID}, digest_auth_resolver) - .build(); + #{ConfigBagAccessors}::push_identity_resolver( + &mut self.inner, + #{HTTP_DIGEST_AUTH_SCHEME_ID}, + #{SharedIdentityResolver}::new(digest_auth_resolver) + ); self } """, @@ -325,32 +312,6 @@ private class HttpAuthConfigCustomization( } } - is ServiceConfig.BuilderBuild -> { - if (runtimeMode.defaultToMiddleware) { - rust("identity_resolvers: self.identity_resolvers,") - } - } - - is ServiceConfig.ConfigStruct -> { - rustTemplate("identity_resolvers: #{IdentityResolvers},", *codegenScope) - } - - is ServiceConfig.ConfigImpl -> { - rustTemplate( - """ - /// Returns the identity resolvers. - pub fn identity_resolvers(&self) -> &#{IdentityResolvers} { - &self.identity_resolvers - } - """, - *codegenScope, - ) - } - - is ServiceConfig.BuilderBuildExtras -> { - rust("identity_resolvers: self.identity_resolvers,") - } - else -> {} } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index a32e8eab3c..e4923c0d78 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -204,11 +204,12 @@ private class HttpConnectorConfigCustomization( // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation connection )); - layer.set_connection(connection); + #{ConfigBagAccessors}::set_connection(&mut layer, connection); } """, *codegenScope, + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "default_connector" to RuntimeType.smithyClient(runtimeConfig).resolve("conns::default_connector"), ) } else { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt new file mode 100644 index 0000000000..d08afe493b --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +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.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 + +class IdentityConfigCustomization(private val codegenContext: ClientCodegenContext) : ConfigCustomization() { + override fun section(section: ServiceConfig): Writable = writable { + if (section is ServiceConfig.ConfigImpl) { + rustTemplate( + """ + /// Returns the identity resolvers. + pub fn identity_resolvers(&self) -> #{IdentityResolvers} { + #{ConfigBagAccessors}::identity_resolvers(&self.inner) + } + """, + "IdentityResolvers" to RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) + .resolve("client::identity::IdentityResolvers"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig), + ) + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt new file mode 100644 index 0000000000..6667601e42 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt @@ -0,0 +1,74 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.traits.OptionalAuthTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +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.util.hasTrait +import software.amazon.smithy.rust.codegen.core.util.letIf + +val noAuthSchemeShapeId: ShapeId = ShapeId.from("aws.smithy.rs#NoAuth") + +private fun noAuthModule(codegenContext: ClientCodegenContext): RuntimeType = + CargoDependency.smithyRuntime(codegenContext.runtimeConfig) + .withFeature("no-auth") + .toType() + .resolve("client::auth::no_auth") + +class NoAuthDecorator : ClientCodegenDecorator { + override val name: String = "NoAuthDecorator" + override val order: Byte = 0 + + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthOptions: List, + ): List = baseAuthOptions.letIf(operationShape.hasTrait()) { + it + AuthOption.StaticAuthOption(noAuthSchemeShapeId) { + rustTemplate( + "#{NO_AUTH_SCHEME_ID},", + "NO_AUTH_SCHEME_ID" to noAuthModule(codegenContext).resolve("NO_AUTH_SCHEME_ID"), + ) + } + } + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = baseCustomizations + AnonymousAuthCustomization(codegenContext, operation) +} + +class AnonymousAuthCustomization( + private val codegenContext: ClientCodegenContext, + private val operationShape: OperationShape, +) : OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + if ( + codegenContext.smithyRuntimeMode.generateOrchestrator && + section is OperationSection.AdditionalRuntimePlugins && + operationShape.hasTrait() + ) { + section.addOperationRuntimePlugin(this) { + rustTemplate( + "#{NoAuthRuntimePlugin}::new()", + "NoAuthRuntimePlugin" to noAuthModule(codegenContext).resolve("NoAuthRuntimePlugin"), + ) + } + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt index 4089c0a648..3dfd77976f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt @@ -9,6 +9,7 @@ import software.amazon.smithy.build.PluginContext import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator @@ -25,6 +26,16 @@ import java.util.logging.Logger typealias ClientProtocolMap = ProtocolMap +sealed interface AuthOption { + /** Auth scheme for the `StaticAuthOptionResolver` */ + data class StaticAuthOption( + val schemeShapeId: ShapeId, + val constructor: Writable, + ) : AuthOption + + class CustomResolver(/* unimplemented */) : AuthOption +} + /** * [ClientCodegenDecorator] allows downstream users to customize code generation. * @@ -32,7 +43,13 @@ typealias ClientProtocolMap = ProtocolMap { +interface ClientCodegenDecorator : CoreCodegenDecorator { + fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthOptions: List, + ): List = baseAuthOptions + fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -84,12 +101,20 @@ interface ClientCodegenDecorator : CoreCodegenDecorator { * This makes the actual concrete codegen simpler by not needing to deal with multiple separate decorators. */ open class CombinedClientCodegenDecorator(decorators: List) : - CombinedCoreCodegenDecorator(decorators), ClientCodegenDecorator { + CombinedCoreCodegenDecorator(decorators), ClientCodegenDecorator { override val name: String get() = "CombinedClientCodegenDecorator" override val order: Byte get() = 0 + override fun authOptions( + codegenContext: ClientCodegenContext, + operationShape: OperationShape, + baseAuthOptions: List, + ): List = combineCustomizations(baseAuthOptions) { decorator, authOptions -> + decorator.authOptions(codegenContext, operationShape, authOptions) + } + override fun configCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index fcd61da066..fd1aacc689 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.Endpoint import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpChecksumRequiredGenerator import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpVersionListCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.IdempotencyTokenGenerator +import software.amazon.smithy.rust.codegen.client.smithy.customizations.IdentityConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.InterceptorConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization @@ -55,11 +56,15 @@ class RequiredCustomizations : ClientCodegenDecorator { baseCustomizations: List, ): List = if (codegenContext.smithyRuntimeMode.generateOrchestrator) { - baseCustomizations + ResiliencyConfigCustomization(codegenContext) + InterceptorConfigCustomization( - codegenContext, - ) + TimeSourceCustomization(codegenContext) + baseCustomizations + + ResiliencyConfigCustomization(codegenContext) + + InterceptorConfigCustomization(codegenContext) + + TimeSourceCustomization(codegenContext) + + IdentityConfigCustomization(codegenContext) } else { - baseCustomizations + ResiliencyConfigCustomization(codegenContext) + TimeSourceCustomization(codegenContext) + baseCustomizations + + ResiliencyConfigCustomization(codegenContext) + + TimeSourceCustomization(codegenContext) } override fun libRsCustomizations( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 12a6168a9b..c8efe85015 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -48,7 +48,7 @@ class EndpointParamsInterceptorGenerator( "BoxError" to RuntimeType.boxError(rc), "ConfigBag" to RuntimeType.configBag(rc), "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(rc) - .resolve("client::orchestrator::ConfigBagAccessors"), + .resolve("client::config_bag_accessors::ConfigBagAccessors"), "ContextAttachedError" to interceptors.resolve("error::ContextAttachedError"), "EndpointResolverParams" to orchestrator.resolve("EndpointResolverParams"), "HttpRequest" to orchestrator.resolve("HttpRequest"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt index 833c1f6985..87fead7142 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt @@ -155,11 +155,11 @@ sealed class OperationSection(name: String) : Section(name) { val operationShape: OperationShape, ) : OperationSection("AdditionalRuntimePlugins") { fun addServiceRuntimePlugin(writer: RustWriter, plugin: Writable) { - writer.rustTemplate(".with_service_runtime_plugin(#{plugin})", "plugin" to plugin) + writer.rustTemplate(".with_service_plugin(#{plugin})", "plugin" to plugin) } fun addOperationRuntimePlugin(writer: RustWriter, plugin: Writable) { - writer.rustTemplate(".with_operation_runtime_plugin(#{plugin})", "plugin" to plugin) + writer.rustTemplate(".with_operation_plugin(#{plugin})", "plugin" to plugin) } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 163d8e8bcd..6371c6a5c2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.shapes.OperationShape 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.customize.AuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsInterceptorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator @@ -105,6 +106,7 @@ open class OperationGenerator( renderOperationStruct( operationWriter, operationShape, + codegenDecorator.authOptions(codegenContext, operationShape, emptyList()), operationCustomizations, ) } @@ -112,6 +114,7 @@ open class OperationGenerator( private fun renderOperationStruct( operationWriter: RustWriter, operationShape: OperationShape, + authOptions: List, operationCustomizations: List, ) { val operationName = symbolProvider.toSymbol(operationShape).name @@ -219,6 +222,7 @@ open class OperationGenerator( operationWriter, operationShape, operationName, + authOptions, operationCustomizations, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index 57d7b8d1a0..c14e0ebdbc 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -5,21 +5,28 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators +import software.amazon.smithy.model.knowledge.ServiceIndex import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customizations.noAuthSchemeShapeId +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +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.withBlockTemplate 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.hasTrait /** * Generates operation-level runtime plugins */ class OperationRuntimePluginGenerator( - codegenContext: ClientCodegenContext, + private val codegenContext: ClientCodegenContext, ) { private val codegenScope = codegenContext.runtimeConfig.let { rc -> val runtimeApi = RuntimeType.smithyRuntimeApi(rc) @@ -28,7 +35,8 @@ class OperationRuntimePluginGenerator( "AuthOptionResolverParams" to runtimeApi.resolve("client::auth::AuthOptionResolverParams"), "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), - "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig), + "DynAuthOptionResolver" to runtimeApi.resolve("client::auth::DynAuthOptionResolver"), "DynResponseDeserializer" to runtimeApi.resolve("client::orchestrator::DynResponseDeserializer"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), @@ -36,6 +44,7 @@ class OperationRuntimePluginGenerator( "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), "SharedRequestSerializer" to runtimeApi.resolve("client::orchestrator::SharedRequestSerializer"), + "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "StaticAuthOptionResolverParams" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolverParams"), ) } @@ -44,6 +53,7 @@ class OperationRuntimePluginGenerator( writer: RustWriter, operationShape: OperationShape, operationStructName: String, + authOptions: List, customizations: List, ) { writer.rustTemplate( @@ -55,15 +65,17 @@ class OperationRuntimePluginGenerator( cfg.set_request_serializer(#{SharedRequestSerializer}::new(${operationStructName}RequestSerializer)); cfg.set_response_deserializer(#{DynResponseDeserializer}::new(${operationStructName}ResponseDeserializer)); - ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} - cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StaticAuthOptionResolverParams}::new())); - // Retry classifiers are operation-specific because they need to downcast operation-specific error types. let retry_classifiers = #{RetryClassifiers}::new() #{retry_classifier_customizations}; cfg.set_retry_classifiers(retry_classifiers); + ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} + cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StaticAuthOptionResolverParams}::new())); + + #{auth_options} #{additional_config} + Some(cfg.freeze()) } @@ -76,6 +88,7 @@ class OperationRuntimePluginGenerator( """, *codegenScope, *preludeScope, + "auth_options" to generateAuthOptions(operationShape, authOptions), "additional_config" to writable { writeCustomizations( customizations, @@ -106,4 +119,48 @@ class OperationRuntimePluginGenerator( }, ) } + + private fun generateAuthOptions( + operationShape: OperationShape, + authOptions: List, + ): Writable = writable { + if (authOptions.any { it is AuthOption.CustomResolver }) { + throw IllegalStateException("AuthOption.CustomResolver is unimplemented") + } else { + val authOptionsMap = authOptions.associate { + val option = it as AuthOption.StaticAuthOption + option.schemeShapeId to option + } + withBlockTemplate( + "cfg.set_auth_option_resolver(#{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new(vec![", + "])));", + *codegenScope, + ) { + val authSchemes = ServiceIndex.of(codegenContext.model) + .getEffectiveAuthSchemes(codegenContext.serviceShape, operationShape) + var atLeastOneScheme = false + for (schemeShapeId in authSchemes.keys) { + val authOption = authOptionsMap[schemeShapeId] + ?: throw IllegalStateException("no auth scheme implementation available for $schemeShapeId") + authOption.constructor(this) + atLeastOneScheme = true + } + if (operationShape.hasTrait()) { + val authOption = authOptionsMap[noAuthSchemeShapeId] + ?: throw IllegalStateException("missing 'no auth' implementation") + authOption.constructor(this) + atLeastOneScheme = true + } + if (!atLeastOneScheme) { + throw IllegalStateException( + "this client won't have any auth schemes " + + "(not even optional/no-auth auth), which means the generated client " + + "won't work at all for the ${operationShape.id} operation. See " + + "https://smithy.io/2.0/spec/authentication-traits.html for documentation " + + "on Smithy authentication traits.", + ) + } + } + } + } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt index d83e53f157..fc72dbb564 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt @@ -165,7 +165,7 @@ class PaginatorGenerator private constructor( // Move individual fields out of self for the borrow checker let builder = self.builder; let handle = self.handle; - #{runtime_plugin_init}; + #{runtime_plugin_init} #{fn_stream}::FnStream::new(move |tx| #{Box}::pin(async move { // Build the input for the first time. If required fields are missing, this is where we'll produce an early error. let mut input = match builder.build().map_err(#{SdkError}::construction_failure) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index f650a514f6..c8f0457b65 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -6,9 +6,9 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointTypesGenerator import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.isNotEmpty 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 @@ -21,25 +21,6 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizat import software.amazon.smithy.rust.codegen.core.util.dq sealed class ServiceRuntimePluginSection(name: String) : Section(name) { - /** - * Hook for adding HTTP auth schemes. - * - * Should emit code that looks like the following: - * ``` - * .auth_scheme("name", path::to::MyAuthScheme::new()) - * ``` - */ - data class HttpAuthScheme(val configBagName: String) : ServiceRuntimePluginSection("HttpAuthScheme") - - /** - * Hook for adding retry classifiers to an operation's `RetryClassifiers` bundle. - * - * Should emit code that looks like the following: - ``` - .with_classifier(AwsErrorCodeClassifier::new()) - */ - data class RetryClassifier(val configBagName: String) : ServiceRuntimePluginSection("RetryClassifier") - /** * Hook for adding additional things to config inside service runtime plugins. */ @@ -48,6 +29,32 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { fun putConfigValue(writer: RustWriter, value: Writable) { writer.rust("$newLayerName.store_put(#T);", value) } + + fun registerHttpAuthScheme(writer: RustWriter, runtimeConfig: RuntimeConfig, authScheme: Writable) { + writer.rustTemplate( + """ + #{ConfigBagAccessors}::push_http_auth_scheme( + &mut $newLayerName, + #{auth_scheme} + ); + """, + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), + "auth_scheme" to authScheme, + ) + } + + fun registerIdentityResolver(writer: RustWriter, runtimeConfig: RuntimeConfig, identityResolver: Writable) { + writer.rustTemplate( + """ + #{ConfigBagAccessors}::push_identity_resolver( + &mut $newLayerName, + #{identity_resolver} + ); + """, + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), + "identity_resolver" to identityResolver, + ) + } } data class RegisterInterceptor(val interceptorRegistrarName: String) : ServiceRuntimePluginSection("RegisterInterceptor") { @@ -72,44 +79,29 @@ typealias ServiceRuntimePluginCustomization = NamedCustomization - val http = RuntimeType.smithyHttp(rc) - val client = RuntimeType.smithyClient(rc) - val runtime = RuntimeType.smithyRuntime(rc) val runtimeApi = RuntimeType.smithyRuntimeApi(rc) val smithyTypes = RuntimeType.smithyTypes(rc) arrayOf( *preludeScope, "Arc" to RuntimeType.Arc, - "AnonymousIdentityResolver" to runtimeApi.resolve("client::identity::AnonymousIdentityResolver"), "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), - "DynAuthOptionResolver" to runtimeApi.resolve("client::auth::DynAuthOptionResolver"), "Layer" to smithyTypes.resolve("config_bag::Layer"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), - "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), - "Connection" to runtimeApi.resolve("client::orchestrator::Connection"), - "ConnectorSettings" to RuntimeType.smithyClient(rc).resolve("http_connector::ConnectorSettings"), - "DynConnectorAdapter" to runtime.resolve("client::connections::adapter::DynConnectorAdapter"), - "HttpAuthSchemes" to runtimeApi.resolve("client::auth::HttpAuthSchemes"), - "HttpConnector" to client.resolve("http_connector::HttpConnector"), - "IdentityResolvers" to runtimeApi.resolve("client::identity::IdentityResolvers"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(rc), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), - "StandardRetryStrategy" to runtime.resolve("client::retries::strategy::StandardRetryStrategy"), - "NeverRetryStrategy" to runtime.resolve("client::retries::strategy::NeverRetryStrategy"), - "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), - "Params" to endpointTypesGenerator.paramsStruct(), - "ResolveEndpoint" to http.resolve("endpoint::ResolveEndpoint"), "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), - "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), - "require_connector" to client.resolve("conns::require_connector"), - "TimeoutConfig" to smithyTypes.resolve("timeout::TimeoutConfig"), - "RetryConfig" to smithyTypes.resolve("retry::RetryConfig"), ) } - fun render(writer: RustWriter, customizations: List) { + fun render( + writer: RustWriter, + customizations: List, + ) { + val additionalConfig = writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg")) + } writer.rustTemplate( """ // TODO(enableNewSmithyRuntimeLaunch) Remove `allow(dead_code)` as well as a field `handle` when @@ -128,22 +120,7 @@ class ServiceRuntimePluginGenerator( impl #{RuntimePlugin} for ServiceRuntimePlugin { fn config(&self) -> #{Option}<#{FrozenLayer}> { - use #{ConfigBagAccessors}; - let mut cfg = #{Layer}::new(${codegenContext.serviceShape.id.name.dq()}); - - let http_auth_schemes = #{HttpAuthSchemes}::builder() - #{http_auth_scheme_customizations} - .build(); - cfg.set_http_auth_schemes(http_auth_schemes); - - // Set an empty auth option resolver to be overridden by operations that need auth. - cfg.set_auth_option_resolver( - #{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new(#{Vec}::new())) - ); - - #{additional_config} - - Some(cfg.freeze()) + #{config} } fn interceptors(&self, interceptors: &mut #{InterceptorRegistrar}) { @@ -153,11 +130,26 @@ class ServiceRuntimePluginGenerator( } """, *codegenScope, - "http_auth_scheme_customizations" to writable { - writeCustomizations(customizations, ServiceRuntimePluginSection.HttpAuthScheme("cfg")) - }, - "additional_config" to writable { - writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg")) + "config" to writable { + if (additionalConfig.isNotEmpty()) { + rustTemplate( + """ + let mut cfg = #{Layer}::new(${codegenContext.serviceShape.id.name.dq()}); + + // TODO(enableNewSmithyRuntimeLaunch): Make it possible to set retry classifiers at the service level. + // Retry classifiers can also be set at the operation level and those should be added to the + // list of classifiers defined here, rather than replacing them. + + #{additional_config} + + Some(cfg.freeze()) + """, + *codegenScope, + "additional_config" to additionalConfig, + ) + } else { + rust("None") + } }, "additional_interceptors" to writable { writeCustomizations(customizations, ServiceRuntimePluginSection.RegisterInterceptor("_interceptors")) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 1278541ad0..69634adba4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -319,7 +319,7 @@ class ServiceConfigGenerator( "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), - "ConfigBagAccessors" to runtimeApi.resolve("client::orchestrator::ConfigBagAccessors"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "Layer" to smithyTypes.resolve("config_bag::Layer"), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt index d0f6d756b2..92f653bf1b 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt @@ -285,6 +285,48 @@ class HttpAuthDecoratorTest { } } } + + @Test + fun optionalAuth() { + clientIntegrationTest( + TestModels.optionalAuth, + TestCodegenSettings.orchestratorModeTestParams, + ) { codegenContext, rustCrate -> + rustCrate.integrationTest("optional_auth") { + val moduleName = codegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn optional_auth() { + let connector = #{TestConnection}::new(vec![( + http::Request::builder() + .uri("http://localhost:1234/SomeOperation") + .body(#{SdkBody}::empty()) + .unwrap(), + http::Response::builder().status(200).body("").unwrap(), + )]); + + let config = $moduleName::Config::builder() + .endpoint_resolver("http://localhost:1234") + .http_connector(connector.clone()) + .build(); + let smithy_client = aws_smithy_client::Builder::new() + .connector(connector.clone()) + .middleware_fn(|r| r) + .build_dyn(); + let client = $moduleName::Client::with_config(smithy_client, config); + let _ = client.some_operation() + .send_orchestrator() + .await + .expect("success"); + connector.assert_requests_match(&[]); + } + """, + *codegenScope(codegenContext.runtimeConfig), + ) + } + } + } } private object TestModels { @@ -420,4 +462,31 @@ private object TestModels { output: SomeOutput } """.asSmithyModel() + + val optionalAuth = """ + namespace test + + use aws.api#service + use aws.protocols#restJson1 + + @service(sdkId: "Test Api Key Auth") + @restJson1 + @httpBearerAuth + @auth([httpBearerAuth]) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + @optionalAuth + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt index 574bc2e8d3..97f114fda0 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt @@ -26,6 +26,7 @@ internal class PaginatorGeneratorTest { } @readonly + @optionalAuth @paginated(inputToken: "nextToken", outputToken: "inner.token", pageSize: "maxResults", items: "inner.items") operation PaginatedList { @@ -34,6 +35,7 @@ internal class PaginatorGeneratorTest { } @readonly + @optionalAuth @paginated(inputToken: "nextToken", outputToken: "inner.token", pageSize: "maxResults", items: "inner.mapItems") operation PaginatedMap { diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt index b1a48abc63..de6b6f175e 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt @@ -29,6 +29,7 @@ class FluentClientGeneratorTest { version: "1" } + @optionalAuth operation SayHello { input: TestInput } structure TestInput { foo: String, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 4b77c0f589..89854d42ba 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -336,6 +336,8 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun configBag(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag") + fun configBagAccessors(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::config_bag_accessors::ConfigBagAccessors") fun boxError(runtimeConfig: RuntimeConfig): RuntimeType = smithyRuntimeApi(runtimeConfig).resolve("box_error::BoxError") fun interceptor(runtimeConfig: RuntimeConfig): RuntimeType = diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt index 1df3fb7dd3..c91d705ac3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/customize/CoreCodegenDecorator.kt @@ -23,7 +23,7 @@ import java.util.logging.Logger /** * Represents the bare minimum for codegen plugin customization. */ -interface CoreCodegenDecorator { +interface CoreCodegenDecorator { /** * The name of this decorator, used for logging and debug information */ @@ -43,7 +43,7 @@ interface CoreCodegenDecorator { /** * Hook to transform the Smithy model before codegen takes place. */ - fun transformModel(service: ServiceShape, model: Model): Model = model + fun transformModel(service: ServiceShape, model: Model, settings: CodegenSettings): Model = model /** * Hook to add additional modules to the generated crate. @@ -114,9 +114,9 @@ interface CoreCodegenDecorator { /** * Implementations for combining decorators for the core customizations. */ -abstract class CombinedCoreCodegenDecorator>( +abstract class CombinedCoreCodegenDecorator>( decorators: List, -) : CoreCodegenDecorator { +) : CoreCodegenDecorator { private val orderedDecorators = decorators.sortedBy { it.order } final override fun crateManifestCustomizations(codegenContext: CodegenContext): ManifestCustomizations = @@ -128,9 +128,9 @@ abstract class CombinedCoreCodegenDecorator - decorator.transformModel(otherModel.expectShape(service.id, ServiceShape::class.java), otherModel) + decorator.transformModel(otherModel.expectShape(service.id, ServiceShape::class.java), otherModel, settings) } final override fun moduleDocumentationCustomization( @@ -209,7 +209,7 @@ abstract class CombinedCoreCodegenDecorator> decoratorsFromClasspath( + protected fun > decoratorsFromClasspath( context: PluginContext, decoratorClass: Class, logger: Logger, diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt index 9dd3d0d658..3bb954bfc2 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/PythonServerCodegenVisitor.kt @@ -81,7 +81,7 @@ class PythonServerCodegenVisitor( .protocolFor(context.model, service) protocolGeneratorFactory = generator - model = codegenDecorator.transformModel(service, baseModel) + model = codegenDecorator.transformModel(service, baseModel, settings) // `publicConstrainedTypes` must always be `false` for the Python server, since Python generates its own // wrapper newtypes. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index fdf686e38e..0c6d27f564 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -129,7 +129,7 @@ open class ServerCodegenVisitor( .protocolFor(context.model, service) this.protocolGeneratorFactory = protocolGeneratorFactory - model = codegenDecorator.transformModel(service, baseModel) + model = codegenDecorator.transformModel(service, baseModel, settings) val serverSymbolProviders = ServerSymbolProviders.from( settings, diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt index 032de89304..8842686d11 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecorator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.model.traits.ErrorTrait import software.amazon.smithy.model.traits.RequiredTrait import software.amazon.smithy.model.transform.ModelTransformer import software.amazon.smithy.rust.codegen.core.smithy.transformers.allErrors +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings import software.amazon.smithy.rust.codegen.server.smithy.customize.ServerCodegenDecorator /** @@ -34,7 +35,7 @@ class AddInternalServerErrorToInfallibleOperationsDecorator : ServerCodegenDecor override val name: String = "AddInternalServerErrorToInfallibleOperations" override val order: Byte = 0 - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ServerRustSettings): Model = addErrorShapeToModelOperations(service, model) { shape -> shape.allErrors(model).isEmpty() } } @@ -60,7 +61,7 @@ class AddInternalServerErrorToAllOperationsDecorator : ServerCodegenDecorator { override val name: String = "AddInternalServerErrorToAllOperations" override val order: Byte = 0 - override fun transformModel(service: ServiceShape, model: Model): Model = + override fun transformModel(service: ServiceShape, model: Model, settings: ServerRustSettings): Model = addErrorShapeToModelOperations(service, model) { true } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt index 8e771cb122..2522024efc 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customize/ServerCodegenDecorator.kt @@ -11,6 +11,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.CombinedCoreCod import software.amazon.smithy.rust.codegen.core.smithy.customize.CoreCodegenDecorator import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap import software.amazon.smithy.rust.codegen.server.smithy.ServerCodegenContext +import software.amazon.smithy.rust.codegen.server.smithy.ServerRustSettings import software.amazon.smithy.rust.codegen.server.smithy.ValidationResult import software.amazon.smithy.rust.codegen.server.smithy.generators.ValidationExceptionConversionGenerator import software.amazon.smithy.rust.codegen.server.smithy.generators.protocol.ServerProtocolGenerator @@ -21,7 +22,7 @@ typealias ServerProtocolMap = ProtocolMap { +interface ServerCodegenDecorator : CoreCodegenDecorator { fun protocols(serviceId: ShapeId, currentProtocols: ServerProtocolMap): ServerProtocolMap = currentProtocols fun validationExceptionConversion(codegenContext: ServerCodegenContext): ValidationExceptionConversionGenerator? = null @@ -38,7 +39,7 @@ interface ServerCodegenDecorator : CoreCodegenDecorator { * This makes the actual concrete codegen simpler by not needing to deal with multiple separate decorators. */ class CombinedServerCodegenDecorator(private val decorators: List) : - CombinedCoreCodegenDecorator(decorators), + CombinedCoreCodegenDecorator(decorators), ServerCodegenDecorator { private val orderedDecorators = decorators.sortedBy { it.order } diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt index 406a1dbfea..d7783c7ac1 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/customizations/AdditionalErrorsDecoratorTest.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.rust.codegen.core.smithy.transformers.OperationNormalizer import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.util.lookup +import software.amazon.smithy.rust.codegen.server.smithy.testutil.serverTestRustSettings class AdditionalErrorsDecoratorTest { private val baseModel = """ @@ -35,12 +36,13 @@ class AdditionalErrorsDecoratorTest { """.asSmithyModel() private val model = OperationNormalizer.transform(baseModel) private val service = ServiceShape.builder().id("smithy.test#Test").build() + private val settings = serverTestRustSettings() @Test fun `add InternalServerError to infallible operations only`() { model.lookup("test#Infallible").errors.isEmpty() shouldBe true model.lookup("test#Fallible").errors.size shouldBe 1 - val transformedModel = AddInternalServerErrorToInfallibleOperationsDecorator().transformModel(service, model) + val transformedModel = AddInternalServerErrorToInfallibleOperationsDecorator().transformModel(service, model, settings) transformedModel.lookup("test#Infallible").errors.size shouldBe 1 transformedModel.lookup("test#Fallible").errors.size shouldBe 1 } @@ -49,7 +51,7 @@ class AdditionalErrorsDecoratorTest { fun `add InternalServerError to all model operations`() { model.lookup("test#Infallible").errors.isEmpty() shouldBe true model.lookup("test#Fallible").errors.size shouldBe 1 - val transformedModel = AddInternalServerErrorToAllOperationsDecorator().transformModel(service, model) + val transformedModel = AddInternalServerErrorToAllOperationsDecorator().transformModel(service, model, settings) transformedModel.lookup("test#Infallible").errors.size shouldBe 1 transformedModel.lookup("test#Fallible").errors.size shouldBe 2 } diff --git a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt index a5593d1847..b1513b1be3 100644 --- a/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt +++ b/codegen-server/typescript/src/main/kotlin/software/amazon/smithy/rust/codegen/server/typescript/smithy/TsServerCodegenVisitor.kt @@ -70,7 +70,7 @@ class TsServerCodegenVisitor( .protocolFor(context.model, service) protocolGeneratorFactory = generator - model = codegenDecorator.transformModel(service, baseModel) + model = codegenDecorator.transformModel(service, baseModel, settings) // `publicConstrainedTypes` must always be `false` for the Typescript server, since Typescript generates its own // wrapper newtypes. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client.rs b/rust-runtime/aws-smithy-runtime-api/src/client.rs index 5627d8ffc7..fec60cf892 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client.rs @@ -3,6 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +/// Client orchestrator configuration accessors for the [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag). +pub mod config_bag_accessors; + /// Smithy identity used by auth and signing. pub mod identity; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 9a866e635a..517e5a47e0 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -4,9 +4,9 @@ */ use crate::box_error::BoxError; -use crate::client::identity::{Identity, IdentityResolver, IdentityResolvers}; +use crate::client::identity::{Identity, IdentityResolvers, SharedIdentityResolver}; use crate::client::orchestrator::HttpRequest; -use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_types::Document; use std::borrow::Cow; @@ -19,7 +19,7 @@ pub mod http; pub mod option_resolver; /// New type around an auth scheme ID. -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct AuthSchemeId { scheme_id: &'static str, } @@ -88,42 +88,47 @@ impl AuthOptionResolver for DynAuthOptionResolver { } } -#[derive(Debug)] -struct HttpAuthSchemesInner { - schemes: Vec<(AuthSchemeId, Box)>, +pub trait HttpAuthScheme: Send + Sync + fmt::Debug { + fn scheme_id(&self) -> AuthSchemeId; + + fn identity_resolver( + &self, + identity_resolvers: &IdentityResolvers, + ) -> Option; + + fn request_signer(&self) -> &dyn HttpRequestSigner; } + +/// Container for a shared HTTP auth scheme implementation. #[derive(Clone, Debug)] -pub struct HttpAuthSchemes { - inner: Arc, +pub struct SharedHttpAuthScheme(Arc); + +impl SharedHttpAuthScheme { + /// Creates a new [`SharedHttpAuthScheme`] from the given auth scheme. + pub fn new(auth_scheme: impl HttpAuthScheme + 'static) -> Self { + Self(Arc::new(auth_scheme)) + } } -impl HttpAuthSchemes { - pub fn builder() -> builders::HttpAuthSchemesBuilder { - Default::default() +impl HttpAuthScheme for SharedHttpAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + self.0.scheme_id() } - pub fn scheme(&self, scheme_id: AuthSchemeId) -> Option<&dyn HttpAuthScheme> { - self.inner - .schemes - .iter() - .find(|scheme| scheme.0 == scheme_id) - .map(|scheme| &*scheme.1) + fn identity_resolver( + &self, + identity_resolvers: &IdentityResolvers, + ) -> Option { + self.0.identity_resolver(identity_resolvers) } -} -impl Storable for HttpAuthSchemes { - type Storer = StoreReplace; + fn request_signer(&self) -> &dyn HttpRequestSigner { + self.0.request_signer() + } } -pub trait HttpAuthScheme: Send + Sync + fmt::Debug { - fn scheme_id(&self) -> AuthSchemeId; - - fn identity_resolver<'a>( - &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver>; - - fn request_signer(&self) -> &dyn HttpRequestSigner; +impl Storable for SharedHttpAuthScheme { + type Storer = StoreAppend; } pub trait HttpRequestSigner: Send + Sync + fmt::Debug { @@ -162,34 +167,51 @@ impl<'a> AuthSchemeEndpointConfig<'a> { } } -pub mod builders { +#[cfg(test)] +mod tests { use super::*; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; + + #[test] + fn test_shared_http_auth_scheme_configuration() { + #[derive(Debug)] + struct TestHttpAuthScheme(&'static str); + impl HttpAuthScheme for TestHttpAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + AuthSchemeId::new(self.0) + } - #[derive(Debug, Default)] - pub struct HttpAuthSchemesBuilder { - schemes: Vec<(AuthSchemeId, Box)>, - } - - impl HttpAuthSchemesBuilder { - pub fn new() -> Self { - Default::default() - } - - pub fn auth_scheme( - mut self, - scheme_id: AuthSchemeId, - auth_scheme: impl HttpAuthScheme + 'static, - ) -> Self { - self.schemes.push((scheme_id, Box::new(auth_scheme) as _)); - self - } + fn identity_resolver(&self, _: &IdentityResolvers) -> Option { + unreachable!("this shouldn't get called in this test") + } - pub fn build(self) -> HttpAuthSchemes { - HttpAuthSchemes { - inner: Arc::new(HttpAuthSchemesInner { - schemes: self.schemes, - }), + fn request_signer(&self) -> &dyn HttpRequestSigner { + unreachable!("this shouldn't get called in this test") } } + + let mut config_bag = ConfigBag::base(); + + let mut layer = Layer::new("first"); + layer.store_append(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_1"))); + config_bag.push_layer(layer); + + let mut layer = Layer::new("second"); + layer.store_append(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_2"))); + layer.store_append(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_3"))); + config_bag.push_layer(layer); + + let auth_schemes = config_bag.load::(); + let encountered_scheme_ids: Vec = + auth_schemes.map(|s| s.scheme_id()).collect(); + + assert_eq!( + vec![ + AuthSchemeId::new("scheme_3"), + AuthSchemeId::new("scheme_2"), + AuthSchemeId::new("scheme_1") + ], + encountered_scheme_ids + ); } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs new file mode 100644 index 0000000000..3b9262f889 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs @@ -0,0 +1,442 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::client::auth::{ + AuthOptionResolver, AuthOptionResolverParams, AuthSchemeId, DynAuthOptionResolver, + SharedHttpAuthScheme, +}; +use crate::client::identity::{ + ConfiguredIdentityResolver, IdentityResolvers, SharedIdentityResolver, +}; +use crate::client::orchestrator::{ + Connection, DynConnection, DynEndpointResolver, DynResponseDeserializer, EndpointResolver, + EndpointResolverParams, LoadedRequestBody, ResponseDeserializer, SharedRequestSerializer, + NOT_NEEDED, +}; +use crate::client::retries::{DynRetryStrategy, RetryClassifiers, RetryStrategy}; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; +use aws_smithy_async::time::{SharedTimeSource, TimeSource}; +use aws_smithy_types::config_bag::{AppendItemIter, CloneableLayer, ConfigBag, FrozenLayer, Layer}; +use std::fmt::Debug; + +// Place traits in a private module so that they can be used in the public API without being a part of the public API. +mod internal { + use aws_smithy_types::config_bag::{ + CloneableLayer, ConfigBag, FrozenLayer, Layer, Storable, Store, StoreAppend, StoreReplace, + }; + use std::fmt::Debug; + + pub trait Settable { + fn unset(&mut self); + + fn store_put(&mut self, value: T) + where + T: Storable>; + + fn store_append(&mut self, item: T) + where + T: Storable>; + } + + impl Settable for Layer { + fn unset(&mut self) { + Layer::unset::(self); + } + + fn store_put(&mut self, value: T) + where + T: Storable>, + { + Layer::store_put(self, value); + } + + fn store_append(&mut self, item: T) + where + T: Storable>, + { + Layer::store_append(self, item); + } + } + + pub trait CloneableSettable { + fn store_put(&mut self, value: T) + where + T: Storable> + Clone; + + fn store_append(&mut self, item: T) + where + T: Storable> + Clone; + } + + impl CloneableSettable for S + where + S: Settable, + { + fn store_put(&mut self, value: T) + where + T: Storable> + Clone, + { + Settable::store_put(self, value); + } + + fn store_append(&mut self, item: T) + where + T: Storable> + Clone, + { + Settable::store_append(self, item); + } + } + + impl CloneableSettable for CloneableLayer { + fn store_put(&mut self, value: T) + where + T: Storable> + Clone, + { + CloneableLayer::store_put(self, value); + } + + fn store_append(&mut self, item: T) + where + T: Storable> + Clone, + { + CloneableLayer::store_append(self, item); + } + } + + pub trait Gettable { + fn load(&self) -> ::ReturnedType<'_>; + } + + impl Gettable for ConfigBag { + fn load(&self) -> ::ReturnedType<'_> { + ConfigBag::load::(self) + } + } + + impl Gettable for CloneableLayer { + fn load(&self) -> ::ReturnedType<'_> { + Layer::load::(self) + } + } + + impl Gettable for Layer { + fn load(&self) -> ::ReturnedType<'_> { + Layer::load::(self) + } + } + + impl Gettable for FrozenLayer { + fn load(&self) -> ::ReturnedType<'_> { + Layer::load::(self) + } + } +} +use internal::{CloneableSettable, Gettable, Settable}; + +pub trait ConfigBagAccessors { + fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams + where + Self: Gettable, + { + self.load::() + .expect("auth option resolver params must be set") + } + fn set_auth_option_resolver_params( + &mut self, + auth_option_resolver_params: AuthOptionResolverParams, + ) where + Self: Settable, + { + self.store_put::(auth_option_resolver_params); + } + + fn auth_option_resolver(&self) -> &dyn AuthOptionResolver + where + Self: Gettable, + { + self.load::() + .expect("an auth option resolver must be set") + } + + fn set_auth_option_resolver(&mut self, auth_option_resolver: DynAuthOptionResolver) + where + Self: Settable, + { + self.store_put::(auth_option_resolver); + } + + fn endpoint_resolver_params(&self) -> &EndpointResolverParams + where + Self: Gettable, + { + self.load::() + .expect("endpoint resolver params must be set") + } + + fn set_endpoint_resolver_params(&mut self, endpoint_resolver_params: EndpointResolverParams) + where + Self: Settable, + { + self.store_put::(endpoint_resolver_params); + } + + fn endpoint_resolver(&self) -> &dyn EndpointResolver + where + Self: Gettable, + { + self.load::() + .expect("an endpoint resolver must be set") + } + + fn set_endpoint_resolver(&mut self, endpoint_resolver: DynEndpointResolver) + where + Self: Settable, + { + self.store_put::(endpoint_resolver); + } + + /// Returns the configured identity resolvers. + fn identity_resolvers(&self) -> IdentityResolvers + where + Self: Gettable, + { + IdentityResolvers::new(self.load::()) + } + + /// Adds an identity resolver to the config. + fn push_identity_resolver( + &mut self, + auth_scheme_id: AuthSchemeId, + identity_resolver: SharedIdentityResolver, + ) where + Self: CloneableSettable, + { + self.store_append::(ConfiguredIdentityResolver::new( + auth_scheme_id, + identity_resolver, + )); + } + + fn connection(&self) -> &dyn Connection + where + Self: Gettable, + { + self.load::().expect("missing connector") + } + + fn set_connection(&mut self, connection: DynConnection) + where + Self: Settable, + { + self.store_put::(connection); + } + + /// Returns the configured HTTP auth schemes. + fn http_auth_schemes(&self) -> HttpAuthSchemes<'_> + where + Self: Gettable, + { + HttpAuthSchemes::new(self.load::()) + } + + /// Adds a HTTP auth scheme to the config. + fn push_http_auth_scheme(&mut self, auth_scheme: SharedHttpAuthScheme) + where + Self: Settable, + { + self.store_append::(auth_scheme); + } + + fn request_serializer(&self) -> SharedRequestSerializer + where + Self: Gettable, + { + self.load::() + .expect("missing request serializer") + .clone() + } + fn set_request_serializer(&mut self, request_serializer: SharedRequestSerializer) + where + Self: Settable, + { + self.store_put::(request_serializer); + } + + fn response_deserializer(&self) -> &dyn ResponseDeserializer + where + Self: Gettable, + { + self.load::() + .expect("missing response deserializer") + } + fn set_response_deserializer(&mut self, response_deserializer: DynResponseDeserializer) + where + Self: Settable, + { + self.store_put::(response_deserializer); + } + + fn retry_classifiers(&self) -> &RetryClassifiers + where + Self: Gettable, + { + self.load::() + .expect("retry classifiers must be set") + } + fn set_retry_classifiers(&mut self, retry_classifiers: RetryClassifiers) + where + Self: Settable, + { + self.store_put::(retry_classifiers); + } + + fn retry_strategy(&self) -> Option<&dyn RetryStrategy> + where + Self: Gettable, + { + self.load::().map(|rs| rs as _) + } + fn set_retry_strategy(&mut self, retry_strategy: DynRetryStrategy) + where + Self: Settable, + { + self.store_put::(retry_strategy); + } + + fn request_time(&self) -> Option + where + Self: Gettable, + { + self.load::().cloned() + } + fn set_request_time(&mut self, time_source: impl TimeSource + 'static) + where + Self: Settable, + { + self.store_put::(SharedTimeSource::new(time_source)); + } + + fn sleep_impl(&self) -> Option + where + Self: Gettable, + { + self.load::().cloned() + } + fn set_sleep_impl(&mut self, async_sleep: Option) + where + Self: Settable, + { + if let Some(sleep_impl) = async_sleep { + self.store_put::(sleep_impl); + } else { + self.unset::(); + } + } + + fn loaded_request_body(&self) -> &LoadedRequestBody + where + Self: Gettable, + { + self.load::().unwrap_or(&NOT_NEEDED) + } + fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody) + where + Self: Settable, + { + self.store_put::(loaded_request_body); + } +} + +impl ConfigBagAccessors for ConfigBag {} +impl ConfigBagAccessors for FrozenLayer {} +impl ConfigBagAccessors for CloneableLayer {} +impl ConfigBagAccessors for Layer {} + +/// Accessor for HTTP auth schemes. +#[derive(Debug)] +pub struct HttpAuthSchemes<'a> { + inner: AppendItemIter<'a, SharedHttpAuthScheme>, +} + +impl<'a> HttpAuthSchemes<'a> { + pub(crate) fn new(inner: AppendItemIter<'a, SharedHttpAuthScheme>) -> Self { + Self { inner } + } + + /// Returns the HTTP auth scheme with the given ID, if there is one. + pub fn scheme(mut self, scheme_id: AuthSchemeId) -> Option { + use crate::client::auth::HttpAuthScheme; + self.inner + .find(|&scheme| scheme.scheme_id() == scheme_id) + .cloned() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::client::auth::{HttpAuthScheme, HttpRequestSigner}; + use crate::client::config_bag_accessors::ConfigBagAccessors; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; + + #[test] + fn test_shared_http_auth_scheme_configuration() { + #[derive(Debug)] + struct TestHttpAuthScheme(&'static str); + impl HttpAuthScheme for TestHttpAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + AuthSchemeId::new(self.0) + } + + fn identity_resolver(&self, _: &IdentityResolvers) -> Option { + unreachable!("this shouldn't get called in this test") + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + unreachable!("this shouldn't get called in this test") + } + } + + let mut config_bag = ConfigBag::base(); + + let mut layer = Layer::new("first"); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_1"))); + config_bag.push_layer(layer); + + let mut layer = Layer::new("second"); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_2"))); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_3"))); + config_bag.push_layer(layer); + + assert!(config_bag + .http_auth_schemes() + .scheme(AuthSchemeId::new("does-not-exist")) + .is_none()); + assert_eq!( + AuthSchemeId::new("scheme_1"), + config_bag + .http_auth_schemes() + .scheme(AuthSchemeId::new("scheme_1")) + .unwrap() + .scheme_id() + ); + assert_eq!( + AuthSchemeId::new("scheme_2"), + config_bag + .http_auth_schemes() + .scheme(AuthSchemeId::new("scheme_2")) + .unwrap() + .scheme_id() + ); + assert_eq!( + AuthSchemeId::new("scheme_3"), + config_bag + .http_auth_schemes() + .scheme(AuthSchemeId::new("scheme_3")) + .unwrap() + .scheme_id() + ); + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index ad8188342d..2a1b6c8d23 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -5,7 +5,7 @@ use crate::client::auth::AuthSchemeId; use crate::client::orchestrator::Future; -use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; use std::any::Any; use std::fmt::Debug; use std::sync::Arc; @@ -14,13 +14,65 @@ use std::time::SystemTime; #[cfg(feature = "http-auth")] pub mod http; +/// Resolves an identity for a request. pub trait IdentityResolver: Send + Sync + Debug { fn resolve_identity(&self, config_bag: &ConfigBag) -> Future; } +/// Container for a shared identity resolver. +#[derive(Clone, Debug)] +pub struct SharedIdentityResolver(Arc); + +impl SharedIdentityResolver { + /// Creates a new [`SharedIdentityResolver`] from the given resolver. + pub fn new(resolver: impl IdentityResolver + 'static) -> Self { + Self(Arc::new(resolver)) + } +} + +impl IdentityResolver for SharedIdentityResolver { + fn resolve_identity(&self, config_bag: &ConfigBag) -> Future { + self.0.resolve_identity(config_bag) + } +} + +/// An identity resolver paired with an auth scheme ID that it resolves for. +#[derive(Clone, Debug)] +pub(crate) struct ConfiguredIdentityResolver { + auth_scheme: AuthSchemeId, + identity_resolver: SharedIdentityResolver, +} + +impl ConfiguredIdentityResolver { + /// Creates a new [`ConfiguredIdentityResolver`] from the given auth scheme and identity resolver. + pub(crate) fn new( + auth_scheme: AuthSchemeId, + identity_resolver: SharedIdentityResolver, + ) -> Self { + Self { + auth_scheme, + identity_resolver, + } + } + + /// Returns the auth scheme ID. + pub(crate) fn scheme_id(&self) -> AuthSchemeId { + self.auth_scheme + } + + /// Returns the identity resolver. + pub(crate) fn identity_resolver(&self) -> SharedIdentityResolver { + self.identity_resolver.clone() + } +} + +impl Storable for ConfiguredIdentityResolver { + type Storer = StoreAppend; +} + #[derive(Clone, Debug, Default)] pub struct IdentityResolvers { - identity_resolvers: Vec<(AuthSchemeId, Arc)>, + identity_resolvers: Vec, } impl Storable for IdentityResolvers { @@ -28,21 +80,19 @@ impl Storable for IdentityResolvers { } impl IdentityResolvers { - pub fn builder() -> builders::IdentityResolversBuilder { - builders::IdentityResolversBuilder::new() + pub(crate) fn new<'a>(resolvers: impl Iterator) -> Self { + let identity_resolvers: Vec<_> = resolvers.cloned().collect(); + if identity_resolvers.is_empty() { + tracing::warn!("no identity resolvers available for this request"); + } + Self { identity_resolvers } } - pub fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option<&dyn IdentityResolver> { + pub fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option { self.identity_resolvers .iter() - .find(|resolver| resolver.0 == scheme_id) - .map(|resolver| &*resolver.1) - } - - pub fn to_builder(self) -> builders::IdentityResolversBuilder { - builders::IdentityResolversBuilder { - identity_resolvers: self.identity_resolvers, - } + .find(|pair| pair.scheme_id() == scheme_id) + .map(|pair| pair.identity_resolver()) } } @@ -69,38 +119,6 @@ impl Identity { } } -pub mod builders { - use super::*; - use crate::client::auth::AuthSchemeId; - - #[derive(Debug, Default)] - pub struct IdentityResolversBuilder { - pub(super) identity_resolvers: Vec<(AuthSchemeId, Arc)>, - } - - impl IdentityResolversBuilder { - pub fn new() -> Self { - Default::default() - } - - pub fn identity_resolver( - mut self, - scheme_id: AuthSchemeId, - resolver: impl IdentityResolver + 'static, - ) -> Self { - self.identity_resolvers - .push((scheme_id, Arc::new(resolver) as _)); - self - } - - pub fn build(self) -> IdentityResolvers { - IdentityResolvers { - identity_resolvers: self.identity_resolvers, - } - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 4e5eabd746..d77647e20b 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -4,18 +4,10 @@ */ use crate::box_error::BoxError; -use crate::client::auth::{ - AuthOptionResolver, AuthOptionResolverParams, DynAuthOptionResolver, HttpAuthSchemes, -}; -use crate::client::identity::IdentityResolvers; use crate::client::interceptors::context::{Error, Input, Output}; -use crate::client::retries::RetryStrategy; -use crate::client::retries::{DynRetryStrategy, RetryClassifiers}; use aws_smithy_async::future::now_or_later::NowOrLater; -use aws_smithy_async::rt::sleep::SharedAsyncSleep; -use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use aws_smithy_http::body::SdkBody; -use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer, Storable, StoreReplace}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::endpoint::Endpoint; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use bytes::Bytes; @@ -189,265 +181,4 @@ impl Storable for LoadedRequestBody { type Storer = StoreReplace; } -// Place traits in a private module so that they can be used in the public API without being a part of the public API. -mod internal { - use aws_smithy_types::config_bag::{ - ConfigBag, FrozenLayer, Layer, Storable, Store, StoreReplace, - }; - use std::fmt::Debug; - - pub trait Settable { - fn unset(&mut self); - - fn store_put(&mut self, value: T) - where - T: Storable>; - } - - impl Settable for Layer { - fn unset(&mut self) { - Layer::unset::(self); - } - - fn store_put(&mut self, value: T) - where - T: Storable>, - { - Layer::store_put(self, value); - } - } - - pub trait Gettable { - fn load(&self) -> ::ReturnedType<'_>; - } - - impl Gettable for ConfigBag { - fn load(&self) -> ::ReturnedType<'_> { - ConfigBag::load::(self) - } - } - - impl Gettable for Layer { - fn load(&self) -> ::ReturnedType<'_> { - Layer::load::(self) - } - } - - impl Gettable for FrozenLayer { - fn load(&self) -> ::ReturnedType<'_> { - Layer::load::(self) - } - } -} -use internal::{Gettable, Settable}; - -pub trait ConfigBagAccessors { - fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams - where - Self: Gettable, - { - self.load::() - .expect("auth option resolver params must be set") - } - fn set_auth_option_resolver_params( - &mut self, - auth_option_resolver_params: AuthOptionResolverParams, - ) where - Self: Settable, - { - self.store_put::(auth_option_resolver_params); - } - - fn auth_option_resolver(&self) -> &dyn AuthOptionResolver - where - Self: Gettable, - { - self.load::() - .expect("an auth option resolver must be set") - } - - fn set_auth_option_resolver(&mut self, auth_option_resolver: DynAuthOptionResolver) - where - Self: Settable, - { - self.store_put::(auth_option_resolver); - } - - fn endpoint_resolver_params(&self) -> &EndpointResolverParams - where - Self: Gettable, - { - self.load::() - .expect("endpoint resolver params must be set") - } - - fn set_endpoint_resolver_params(&mut self, endpoint_resolver_params: EndpointResolverParams) - where - Self: Settable, - { - self.store_put::(endpoint_resolver_params); - } - - fn endpoint_resolver(&self) -> &dyn EndpointResolver - where - Self: Gettable, - { - self.load::() - .expect("an endpoint resolver must be set") - } - - fn set_endpoint_resolver(&mut self, endpoint_resolver: DynEndpointResolver) - where - Self: Settable, - { - self.store_put::(endpoint_resolver); - } - - fn identity_resolvers(&self) -> &IdentityResolvers - where - Self: Gettable, - { - self.load::() - .expect("identity resolvers must be configured") - } - - fn set_identity_resolvers(&mut self, identity_resolvers: IdentityResolvers) - where - Self: Settable, - { - self.store_put::(identity_resolvers); - } - - fn connection(&self) -> &dyn Connection - where - Self: Gettable, - { - self.load::().expect("missing connector") - } - - fn set_connection(&mut self, connection: DynConnection) - where - Self: Settable, - { - self.store_put::(connection); - } - - fn http_auth_schemes(&self) -> &HttpAuthSchemes - where - Self: Gettable, - { - self.load::() - .expect("auth schemes must be set") - } - fn set_http_auth_schemes(&mut self, http_auth_schemes: HttpAuthSchemes) - where - Self: Settable, - { - self.store_put::(http_auth_schemes); - } - - fn request_serializer(&self) -> SharedRequestSerializer - where - Self: Gettable, - { - self.load::() - .expect("missing request serializer") - .clone() - } - fn set_request_serializer(&mut self, request_serializer: SharedRequestSerializer) - where - Self: Settable, - { - self.store_put::(request_serializer); - } - - fn response_deserializer(&self) -> &dyn ResponseDeserializer - where - Self: Gettable, - { - self.load::() - .expect("missing response deserializer") - } - fn set_response_deserializer(&mut self, response_deserializer: DynResponseDeserializer) - where - Self: Settable, - { - self.store_put::(response_deserializer); - } - - fn retry_classifiers(&self) -> &RetryClassifiers - where - Self: Gettable, - { - self.load::() - .expect("retry classifiers must be set") - } - fn set_retry_classifiers(&mut self, retry_classifiers: RetryClassifiers) - where - Self: Settable, - { - self.store_put::(retry_classifiers); - } - - fn retry_strategy(&self) -> Option<&dyn RetryStrategy> - where - Self: Gettable, - { - self.load::().map(|rs| rs as _) - } - fn set_retry_strategy(&mut self, retry_strategy: DynRetryStrategy) - where - Self: Settable, - { - self.store_put::(retry_strategy); - } - - fn request_time(&self) -> Option - where - Self: Gettable, - { - self.load::().cloned() - } - fn set_request_time(&mut self, time_source: impl TimeSource + 'static) - where - Self: Settable, - { - self.store_put::(SharedTimeSource::new(time_source)); - } - - fn sleep_impl(&self) -> Option - where - Self: Gettable, - { - self.load::().cloned() - } - fn set_sleep_impl(&mut self, async_sleep: Option) - where - Self: Settable, - { - if let Some(sleep_impl) = async_sleep { - self.store_put::(sleep_impl); - } else { - self.unset::(); - } - } - - fn loaded_request_body(&self) -> &LoadedRequestBody - where - Self: Gettable, - { - self.load::().unwrap_or(&NOT_NEEDED) - } - fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody) - where - Self: Settable, - { - self.store_put::(loaded_request_body); - } -} - -const NOT_NEEDED: LoadedRequestBody = LoadedRequestBody::NotNeeded; - -impl ConfigBagAccessors for ConfigBag {} -impl ConfigBagAccessors for FrozenLayer {} -impl ConfigBagAccessors for Layer {} +pub(crate) const NOT_NEEDED: LoadedRequestBody = LoadedRequestBody::NotNeeded; diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 8c0847ccb1..9cc6bd9a89 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/awslabs/smithy-rs" [features] http-auth = ["aws-smithy-runtime-api/http-auth"] -anonymous-auth = [] +no-auth = [] test-util = ["dep:aws-smithy-protocol-test"] [dependencies] diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index 0c8a1705f7..6bf5397489 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -25,9 +25,6 @@ pub mod test_util; mod timeout; -/// Runtime plugins for Smithy clients. -pub mod runtime_plugin; - /// Smithy identity used by auth and signing. pub mod identity; diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/auth.rs index d06c1e4e86..a119daa924 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth.rs @@ -3,5 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +#[cfg(feature = "no-auth")] +pub mod no_auth; + #[cfg(feature = "http-auth")] pub mod http; diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs index efd397576d..5b62491bc9 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs @@ -13,7 +13,9 @@ use aws_smithy_runtime_api::client::auth::{ AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, }; use aws_smithy_runtime_api::client::identity::http::{Login, Token}; -use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; +use aws_smithy_runtime_api::client::identity::{ + Identity, IdentityResolvers, SharedIdentityResolver, +}; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_types::base64::encode; use aws_smithy_types::config_bag::ConfigBag; @@ -55,10 +57,10 @@ impl HttpAuthScheme for ApiKeyAuthScheme { HTTP_API_KEY_AUTH_SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -125,10 +127,10 @@ impl HttpAuthScheme for BasicAuthScheme { HTTP_BASIC_AUTH_SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -183,10 +185,10 @@ impl HttpAuthScheme for BearerAuthScheme { HTTP_BEARER_AUTH_SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -239,10 +241,10 @@ impl HttpAuthScheme for DigestAuthScheme { HTTP_DIGEST_AUTH_SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs new file mode 100644 index 0000000000..6fa815046d --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs @@ -0,0 +1,96 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! The [`NoAuthRuntimePlugin`] and supporting code. + +use crate::client::identity::no_auth::NoAuthIdentityResolver; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::auth::{ + AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, SharedHttpAuthScheme, +}; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use aws_smithy_runtime_api::client::identity::{ + Identity, IdentityResolvers, SharedIdentityResolver, +}; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; + +pub const NO_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("no_auth"); + +/// A [`RuntimePlugin`] that registers a "no auth" identity resolver and auth scheme. +/// +/// This plugin can be used to disable authentication in certain cases, such as when there is +/// a Smithy `@optionalAuth` trait. +#[non_exhaustive] +#[derive(Debug)] +pub struct NoAuthRuntimePlugin(FrozenLayer); + +impl Default for NoAuthRuntimePlugin { + fn default() -> Self { + Self::new() + } +} + +impl NoAuthRuntimePlugin { + pub fn new() -> Self { + let mut cfg = Layer::new("NoAuth"); + cfg.push_identity_resolver( + NO_AUTH_SCHEME_ID, + SharedIdentityResolver::new(NoAuthIdentityResolver::new()), + ); + cfg.push_http_auth_scheme(SharedHttpAuthScheme::new(NoAuthScheme::new())); + Self(cfg.freeze()) + } +} + +impl RuntimePlugin for NoAuthRuntimePlugin { + fn config(&self) -> Option { + Some(self.0.clone()) + } +} + +#[derive(Debug, Default)] +pub struct NoAuthScheme { + signer: NoAuthSigner, +} + +impl NoAuthScheme { + pub fn new() -> Self { + Self::default() + } +} + +#[derive(Debug, Default)] +struct NoAuthSigner; + +impl HttpRequestSigner for NoAuthSigner { + fn sign_request( + &self, + _request: &mut HttpRequest, + _identity: &Identity, + _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _config_bag: &ConfigBag, + ) -> Result<(), BoxError> { + Ok(()) + } +} + +impl HttpAuthScheme for NoAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + NO_AUTH_SCHEME_ID + } + + fn identity_resolver( + &self, + identity_resolvers: &IdentityResolvers, + ) -> Option { + identity_resolvers.identity_resolver(NO_AUTH_SCHEME_ID) + } + + fn request_signer(&self) -> &dyn HttpRequestSigner { + &self.signer + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity.rs b/rust-runtime/aws-smithy-runtime/src/client/identity.rs index 181bc8575e..13547ed917 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/identity.rs @@ -3,5 +3,5 @@ * SPDX-License-Identifier: Apache-2.0 */ -#[cfg(feature = "anonymous-auth")] -pub mod anonymous; +#[cfg(feature = "no-auth")] +pub mod no_auth; diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs b/rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs similarity index 67% rename from rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs rename to rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs index 020f12dcbd..8814336b3a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/identity/anonymous.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs @@ -8,25 +8,25 @@ use aws_smithy_runtime_api::client::orchestrator::Future; use aws_smithy_types::config_bag::ConfigBag; #[derive(Debug, Default)] -pub struct AnonymousIdentity; +pub struct NoAuthIdentity; -impl AnonymousIdentity { +impl NoAuthIdentity { pub fn new() -> Self { Self } } #[derive(Debug, Default)] -pub struct AnonymousIdentityResolver; +pub struct NoAuthIdentityResolver; -impl AnonymousIdentityResolver { +impl NoAuthIdentityResolver { pub fn new() -> Self { Self } } -impl IdentityResolver for AnonymousIdentityResolver { +impl IdentityResolver for NoAuthIdentityResolver { fn resolve_identity(&self, _: &ConfigBag) -> Future { - Future::ready(Ok(Identity::new(AnonymousIdentity::new(), None))) + Future::ready(Ok(Identity::new(NoAuthIdentity::new(), None))) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 46f617c565..c38b382045 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -15,12 +15,13 @@ use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream::ByteStream; use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{ Error, Input, InterceptorContext, Output, RewindResult, }; use aws_smithy_runtime_api::client::interceptors::Interceptors; use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, HttpResponse, LoadedRequestBody, OrchestratorError, RequestSerializer, + HttpResponse, LoadedRequestBody, OrchestratorError, RequestSerializer, }; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::ShouldAttempt; @@ -327,11 +328,11 @@ async fn finally_op( #[cfg(all(test, feature = "test-util", feature = "anonymous-auth"))] mod tests { use super::*; + use crate::client::auth::no_auth::NoAuthRuntimePlugin; use crate::client::orchestrator::endpoints::{ StaticUriEndpointResolver, StaticUriEndpointResolverParams, }; use crate::client::retries::strategy::NeverRetryStrategy; - use crate::client::runtime_plugin::anonymous_auth::AnonymousAuthRuntimePlugin; use crate::client::test_util::{ connector::OkConnector, deserializer::CannedResponseDeserializer, serializer::CannedRequestSerializer, @@ -449,7 +450,7 @@ mod tests { let runtime_plugins = RuntimePlugins::new() .with_client_plugin(FailingInterceptorsClientRuntimePlugin) .with_operation_plugin(TestOperationRuntimePlugin) - .with_operation_plugin(AnonymousAuthRuntimePlugin::new()) + .with_operation_plugin(NoAuthRuntimePlugin::new()) .with_operation_plugin(FailingInterceptorsOperationRuntimePlugin); let actual = invoke(input, &runtime_plugins) .await diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 50c5fd4a2f..92191782bc 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -4,9 +4,12 @@ */ use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::auth::{AuthSchemeEndpointConfig, AuthSchemeId}; +use aws_smithy_runtime_api::client::auth::{ + AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, +}; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use aws_smithy_runtime_api::client::identity::IdentityResolver; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::endpoint::Endpoint; use aws_smithy_types::Document; @@ -77,7 +80,7 @@ pub(super) async fn orchestrate_auth( for &scheme_id in auth_options.as_ref() { if let Some(auth_scheme) = cfg.http_auth_schemes().scheme(scheme_id) { - if let Some(identity_resolver) = auth_scheme.identity_resolver(identity_resolvers) { + if let Some(identity_resolver) = auth_scheme.identity_resolver(&identity_resolvers) { let request_signer = auth_scheme.request_signer(); let endpoint = cfg .load::() @@ -137,9 +140,12 @@ mod tests { use aws_smithy_runtime_api::client::auth::option_resolver::StaticAuthOptionResolver; use aws_smithy_runtime_api::client::auth::{ AuthOptionResolverParams, AuthSchemeId, DynAuthOptionResolver, HttpAuthScheme, - HttpAuthSchemes, HttpRequestSigner, + HttpRequestSigner, SharedHttpAuthScheme, + }; + use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; + use aws_smithy_runtime_api::client::identity::{ + Identity, IdentityResolver, IdentityResolvers, SharedIdentityResolver, }; - use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; use aws_smithy_types::config_bag::Layer; @@ -185,10 +191,10 @@ mod tests { TEST_SCHEME_ID } - fn identity_resolver<'a>( + fn identity_resolver( &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { + identity_resolvers: &IdentityResolvers, + ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -208,16 +214,13 @@ mod tests { layer.set_auth_option_resolver(DynAuthOptionResolver::new(StaticAuthOptionResolver::new( vec![TEST_SCHEME_ID], ))); - layer.set_identity_resolvers( - IdentityResolvers::builder() - .identity_resolver(TEST_SCHEME_ID, TestIdentityResolver) - .build(), - ); - layer.set_http_auth_schemes( - HttpAuthSchemes::builder() - .auth_scheme(TEST_SCHEME_ID, TestAuthScheme { signer: TestSigner }) - .build(), + layer.push_identity_resolver( + TEST_SCHEME_ID, + SharedIdentityResolver::new(TestIdentityResolver), ); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestAuthScheme { + signer: TestSigner, + })); layer.store_put(Endpoint::builder().url("dontcare").build()); let mut cfg = ConfigBag::base(); @@ -249,27 +252,28 @@ mod tests { let _ = ctx.take_input(); ctx.enter_before_transmit_phase(); - let mut layer = Layer::new("test"); - layer.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); - layer.set_auth_option_resolver(DynAuthOptionResolver::new(StaticAuthOptionResolver::new( - vec![HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID], - ))); - layer.set_http_auth_schemes( - HttpAuthSchemes::builder() - .auth_scheme(HTTP_BASIC_AUTH_SCHEME_ID, BasicAuthScheme::new()) - .auth_scheme(HTTP_BEARER_AUTH_SCHEME_ID, BearerAuthScheme::new()) - .build(), - ); - layer.store_put(Endpoint::builder().url("dontcare").build()); + fn config_with_identity( + scheme_id: AuthSchemeId, + identity: impl IdentityResolver + 'static, + ) -> ConfigBag { + let mut layer = Layer::new("test"); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(BasicAuthScheme::new())); + layer.push_http_auth_scheme(SharedHttpAuthScheme::new(BearerAuthScheme::new())); + layer.store_put(Endpoint::builder().url("dontcare").build()); + + layer.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); + layer.set_auth_option_resolver(DynAuthOptionResolver::new( + StaticAuthOptionResolver::new(vec![ + HTTP_BASIC_AUTH_SCHEME_ID, + HTTP_BEARER_AUTH_SCHEME_ID, + ]), + )); + layer.push_identity_resolver(scheme_id, SharedIdentityResolver::new(identity)); + ConfigBag::of_layers(vec![layer]) + } // First, test the presence of a basic auth login and absence of a bearer token - layer.set_identity_resolvers( - IdentityResolvers::builder() - .identity_resolver(HTTP_BASIC_AUTH_SCHEME_ID, Login::new("a", "b", None)) - .build(), - ); - let mut cfg = ConfigBag::of_layers(vec![layer]); - + let cfg = config_with_identity(HTTP_BASIC_AUTH_SCHEME_ID, Login::new("a", "b", None)); orchestrate_auth(&mut ctx, &cfg).await.expect("success"); assert_eq!( // "YTpi" == "a:b" in base64 @@ -281,16 +285,8 @@ mod tests { .unwrap() ); - let mut additional_resolver = Layer::new("extra"); - // Next, test the presence of a bearer token and absence of basic auth - additional_resolver.set_identity_resolvers( - IdentityResolvers::builder() - .identity_resolver(HTTP_BEARER_AUTH_SCHEME_ID, Token::new("t", None)) - .build(), - ); - cfg.push_layer(additional_resolver); - + let cfg = config_with_identity(HTTP_BEARER_AUTH_SCHEME_ID, Token::new("t", None)); let mut ctx = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); ctx.enter_serialization_phase(); ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 104599b287..582be343f6 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -9,9 +9,10 @@ use aws_smithy_http::endpoint::{ SharedEndpointResolver, }; use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, EndpointResolver, EndpointResolverParams, HttpRequest, + EndpointResolver, EndpointResolverParams, HttpRequest, }; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::endpoint::Endpoint; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs index 42a50017b1..6b41f0fddd 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs @@ -10,7 +10,7 @@ #![allow(dead_code)] use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::{builder, builder_methods, builder_struct}; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer, Storable, StoreReplace}; @@ -257,7 +257,7 @@ mod tests { use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; use aws_smithy_async::test_util::instant_time_and_sleep; use aws_smithy_async::time::SharedTimeSource; - use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; + use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_types::config_bag::ConfigBag; use std::time::{Duration, SystemTime}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index 4f520b555e..50a83cae9b 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -8,8 +8,8 @@ use crate::client::retries::strategy::standard::ReleaseResult::{ }; use crate::client::retries::token_bucket::TokenBucket; use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt, @@ -211,7 +211,11 @@ fn calculate_exponential_backoff(base: f64, initial_backoff: f64, retry_attempts #[cfg(test)] mod tests { use super::*; - use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, OrchestratorError}; + use super::{calculate_exponential_backoff, ShouldAttempt, StandardRetryStrategy}; + use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; + use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ AlwaysRetry, ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, }; diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs deleted file mode 100644 index 784de5cd71..0000000000 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin.rs +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -#[cfg(feature = "anonymous-auth")] -pub mod anonymous_auth; diff --git a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs deleted file mode 100644 index 6825660399..0000000000 --- a/rust-runtime/aws-smithy-runtime/src/client/runtime_plugin/anonymous_auth.rs +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -//! The [AnonymousAuthRuntimePlugin] and supporting code. - -use crate::client::identity::anonymous::AnonymousIdentityResolver; -use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::auth::option_resolver::{ - StaticAuthOptionResolver, StaticAuthOptionResolverParams, -}; -use aws_smithy_runtime_api::client::auth::{ - AuthSchemeEndpointConfig, AuthSchemeId, DynAuthOptionResolver, HttpAuthScheme, HttpAuthSchemes, - HttpRequestSigner, -}; -use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver, IdentityResolvers}; -use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpRequest}; -use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; - -const ANONYMOUS_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("anonymous"); - -/// A [RuntimePlugin] to provide anonymous authentication. This runtime plugin sets its own: -/// - [AuthOptionResolver](aws_smithy_runtime_api::client::auth::AuthOptionResolver) -/// - [AuthOptionResolverParams](aws_smithy_runtime_api::client::auth::AuthOptionResolverParams) -/// - [IdentityResolvers] -/// - [HttpAuthSchemes] -/// -/// **The above components will replace any existing ones!** As such, don't use this plugin unless: -/// - You only need to make anonymous requests, such as when interacting with [Open Data](https://aws.amazon.com/opendata/). -/// - You're writing orchestrator tests and don't care about authentication. -#[non_exhaustive] -#[derive(Debug)] -pub struct AnonymousAuthRuntimePlugin(FrozenLayer); - -impl Default for AnonymousAuthRuntimePlugin { - fn default() -> Self { - Self::new() - } -} - -impl AnonymousAuthRuntimePlugin { - pub fn new() -> Self { - let mut cfg = Layer::new("AnonymousAuth"); - cfg.set_auth_option_resolver_params(StaticAuthOptionResolverParams::new().into()); - cfg.set_auth_option_resolver(DynAuthOptionResolver::new(StaticAuthOptionResolver::new( - vec![ANONYMOUS_AUTH_SCHEME_ID], - ))); - cfg.set_identity_resolvers( - IdentityResolvers::builder() - .identity_resolver(ANONYMOUS_AUTH_SCHEME_ID, AnonymousIdentityResolver::new()) - .build(), - ); - cfg.set_http_auth_schemes( - HttpAuthSchemes::builder() - .auth_scheme(ANONYMOUS_AUTH_SCHEME_ID, AnonymousAuthScheme::new()) - .build(), - ); - Self(cfg.freeze()) - } -} - -impl RuntimePlugin for AnonymousAuthRuntimePlugin { - fn config(&self) -> Option { - Some(self.0.clone()) - } -} - -#[derive(Debug, Default)] -pub struct AnonymousAuthScheme { - signer: AnonymousSigner, -} - -impl AnonymousAuthScheme { - pub fn new() -> Self { - Self::default() - } -} - -#[derive(Debug, Default)] -struct AnonymousSigner; - -impl HttpRequestSigner for AnonymousSigner { - fn sign_request( - &self, - _request: &mut HttpRequest, - _identity: &Identity, - _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, - _config_bag: &ConfigBag, - ) -> Result<(), BoxError> { - Ok(()) - } -} - -impl HttpAuthScheme for AnonymousAuthScheme { - fn scheme_id(&self) -> AuthSchemeId { - ANONYMOUS_AUTH_SCHEME_ID - } - - fn identity_resolver<'a>( - &self, - identity_resolvers: &'a IdentityResolvers, - ) -> Option<&'a dyn IdentityResolver> { - identity_resolvers.identity_resolver(ANONYMOUS_AUTH_SCHEME_ID) - } - - fn request_signer(&self) -> &dyn HttpRequestSigner { - &self.signer - } -} diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs index 0bccae98ef..0a4b0ee860 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs @@ -3,10 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, DynResponseDeserializer, HttpResponse, OrchestratorError, - ResponseDeserializer, + DynResponseDeserializer, HttpResponse, OrchestratorError, ResponseDeserializer, }; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{FrozenLayer, Layer}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs index 8135151f1a..4c2a34af11 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs @@ -46,8 +46,8 @@ where mod tests { use super::*; use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; - use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; use aws_smithy_types::type_erasure::TypedBox; use std::time::{Duration, UNIX_EPOCH}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs index 8ec112f72f..4f5bff24bd 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -4,10 +4,10 @@ */ use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::Input; -use aws_smithy_runtime_api::client::orchestrator::{ - ConfigBagAccessors, HttpRequest, RequestSerializer, SharedRequestSerializer, -}; +use aws_smithy_runtime_api::client::orchestrator::SharedRequestSerializer; +use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, RequestSerializer}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; use std::sync::Mutex; diff --git a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs index c666389a81..cb8b0d0e90 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs @@ -6,7 +6,8 @@ use aws_smithy_async::future::timeout::Timeout; use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; use aws_smithy_client::SdkError; -use aws_smithy_runtime_api::client::orchestrator::{ConfigBagAccessors, HttpResponse}; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use aws_smithy_runtime_api::client::orchestrator::HttpResponse; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::timeout::TimeoutConfig; use pin_project_lite::pin_project; diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index 809c069909..413082fa7c 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -20,12 +20,11 @@ services_that_dont_compile=(\ # TODO(enableNewSmithyRuntimeLaunch): Move these into `services_that_pass_tests` as more progress is made services_that_compile=(\ - "aws-config"\ "s3"\ - "sts"\ ) services_that_pass_tests=(\ + "aws-config"\ "config"\ "dynamodb"\ "ec2"\ @@ -39,6 +38,7 @@ services_that_pass_tests=(\ "route53"\ "s3control"\ "sso"\ + "sts"\ "transcribestreaming"\ ) From 5eb885c26305b139e5c7a5f31aaf37f4bbdd97f5 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 28 Jun 2023 07:21:07 -0700 Subject: [PATCH 182/253] Update GitHub thumbprints for OIDC in CI (#2813) CI uses an `OpenIdConnectProvider` to grant access to certain operations in the CI AWS account to the GitHub Actions runners. This provider checks the certificate thumbprints to validate the authenticity of connect requests. GitHub recently [added a new thumbprint](https://github.blog/changelog/2023-06-27-github-actions-update-on-oidc-integration-with-aws/), which was causing CI and the PR bot to intermittently fail since the OIDC provider wasn't aware of it. This PR corrects the thumbprints to reestablish consistency in CI. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- tools/ci-cdk/lib/oidc-provider-stack.ts | 10 +- tools/ci-cdk/package-lock.json | 1901 ++++++++++------- tools/ci-cdk/test/oidc-provider-stack.test.ts | 4 +- 3 files changed, 1105 insertions(+), 810 deletions(-) diff --git a/tools/ci-cdk/lib/oidc-provider-stack.ts b/tools/ci-cdk/lib/oidc-provider-stack.ts index abcf6c5cea..45a0d40cee 100644 --- a/tools/ci-cdk/lib/oidc-provider-stack.ts +++ b/tools/ci-cdk/lib/oidc-provider-stack.ts @@ -17,7 +17,13 @@ import { Construct } from "constructs"; /// /// This was done with the initial Idp URL of: /// https://token.actions.githubusercontent.com/.well-known/openid-configuration -export const GITHUB_CERTIFICATE_THUMBPRINT = "6938FD4D98BAB03FAADB97B34396831E3780AEA1"; +/// +/// Note: as of June 27, 2023, there are now two possible thumbprints from GitHub: +/// https://github.blog/changelog/2023-06-27-github-actions-update-on-oidc-integration-with-aws/ +export const GITHUB_CERTIFICATE_THUMBPRINTS = [ + "6938FD4D98BAB03FAADB97B34396831E3780AEA1", + "1C58A3A8518E8759BF075B76B750D4F2DF264FCD", +]; // There can only be one OIDC provider for a given URL per AWS account, // so put these in their own stack to be shared with other stacks. @@ -32,7 +38,7 @@ export class OidcProviderStack extends Stack { this.githubActionsOidcProvider = new OpenIdConnectProvider(this, "oidc-provider", { url: "https://token.actions.githubusercontent.com", - thumbprints: [GITHUB_CERTIFICATE_THUMBPRINT], + thumbprints: GITHUB_CERTIFICATE_THUMBPRINTS, clientIds: ["sts.amazonaws.com"], }); } diff --git a/tools/ci-cdk/package-lock.json b/tools/ci-cdk/package-lock.json index f42ae7c338..b16f3ff4df 100644 --- a/tools/ci-cdk/package-lock.json +++ b/tools/ci-cdk/package-lock.json @@ -29,12 +29,12 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { @@ -42,64 +42,64 @@ } }, "node_modules/@aws-cdk/asset-awscli-v1": { - "version": "2.2.37", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.37.tgz", - "integrity": "sha512-1jLfQLpFEurbcBuwBLgw7u7OxRYQZTTl9L8/6Bx44fQksJch+9dyvbVnbhigAfc9K9z2e0QAsepZybE6lAGCDQ==", + "version": "2.2.199", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.199.tgz", + "integrity": "sha512-zNdD2OxALdsdQaRZBpTfMTuudxV+4jLMznJIvVj6O+OqCru4m5UtgVQmyApW1z2H9s4/06ovVt20aXe2G8Ta+w==", "dev": true }, "node_modules/@aws-cdk/asset-kubectl-v20": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz", - "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", + "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==", "dev": true }, "node_modules/@aws-cdk/asset-node-proxy-agent-v5": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.38.tgz", - "integrity": "sha512-BBwAjORhuUkTGO3CxGS5Evcp5n20h9v06Sftn2R1DuSm8zIoUlPsNlI1HUk8XqYuoEI4aD7IKRQBLglv09ciJQ==", + "version": "2.0.165", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.165.tgz", + "integrity": "sha512-bsyLQD/vqXQcc9RDmlM1XqiFNO/yewgVFXmkMcQkndJbmE/jgYkzewwYGrBlfL725hGLQipXq19+jwWwdsXQqg==", "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", - "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", + "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.7.tgz", - "integrity": "sha512-t1ZjCluspe5DW24bn2Rr1CDb2v9rn/hROtg9a2tmd0+QYf4bsloYfLQzjG4qHPNMhWtKdGC33R5AxGR2Af2cBw==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.7", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", + "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helpers": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", + "json5": "^2.2.2", "semver": "^6.3.0" }, "engines": { @@ -120,41 +120,28 @@ } }, "node_modules/@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", + "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", "dev": true, "dependencies": { - "@babel/types": "^7.20.7", + "@babel/types": "^7.22.5", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz", + "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", + "@babel/compat-data": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", "browserslist": "^4.21.3", "lru-cache": "^5.1.1", "semver": "^6.3.0" @@ -176,151 +163,151 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.7.tgz", - "integrity": "sha512-FNdu7r67fqMUSVuQpFQGE6BPdhJIhitoxhGzDbAXNcA07uoVG37fOiMk3OSV8rEICuyG6t8LGkd9EE64qIEoIA==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", + "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "dependencies": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", + "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", + "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -400,9 +387,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", + "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -559,12 +546,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -574,33 +561,33 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.20.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.8.tgz", - "integrity": "sha512-/RNkaYDeCy4MjyV70+QkSHhxbvj2JO/5Ft2Pa880qJOG8tWrqcT/wXUuCCv43yogfqPzHL77Xu101KQPf4clnQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", + "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -618,13 +605,13 @@ } }, "node_modules/@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" }, "engines": { @@ -659,15 +646,39 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz", - "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.5.2", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -682,10 +693,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/js": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", @@ -1064,13 +1084,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -1095,21 +1116,27 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1191,19 +1218,19 @@ "dev": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, "node_modules/@types/babel__core": { - "version": "7.1.20", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", - "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", "dev": true, "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" @@ -1229,18 +1256,18 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", "dev": true, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", "dev": true, "dependencies": { "@types/node": "*" @@ -1281,9 +1308,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "node_modules/@types/node": { @@ -1293,15 +1320,15 @@ "dev": true }, "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, "node_modules/@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "node_modules/@types/stack-utils": { @@ -1311,9 +1338,9 @@ "dev": true }, "node_modules/@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", + "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", "dev": true, "dependencies": { "@types/yargs-parser": "*" @@ -1326,18 +1353,19 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.0.tgz", - "integrity": "sha512-AHZtlXAMGkDmyLuLZsRpH3p4G/1iARIwc/T0vIem2YB+xW6pZaXYXzCBnZSF/5fdM97R9QqZWZ+h3iW10XgevQ==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", + "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/type-utils": "5.47.0", - "@typescript-eslint/utils": "5.47.0", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/type-utils": "5.60.1", + "@typescript-eslint/utils": "5.60.1", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" }, @@ -1359,14 +1387,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.0.tgz", - "integrity": "sha512-udPU4ckK+R1JWCGdQC4Qa27NtBg7w020ffHqGyAK8pAgOVuNw7YaKXGChk+udh+iiGIJf6/E/0xhVXyPAbsczw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", + "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/typescript-estree": "5.47.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/typescript-estree": "5.60.1", "debug": "^4.3.4" }, "engines": { @@ -1386,13 +1414,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz", - "integrity": "sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", + "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/visitor-keys": "5.47.0" + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1403,13 +1431,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.47.0.tgz", - "integrity": "sha512-1J+DFFrYoDUXQE1b7QjrNGARZE6uVhBqIvdaXTe5IN+NmEyD68qXR1qX1g2u4voA+nCaelQyG8w30SAOihhEYg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", + "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.47.0", - "@typescript-eslint/utils": "5.47.0", + "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/utils": "5.60.1", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -1430,9 +1458,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz", - "integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", + "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1443,13 +1471,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz", - "integrity": "sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", + "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/visitor-keys": "5.47.0", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1470,18 +1498,18 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.47.0.tgz", - "integrity": "sha512-U9xcc0N7xINrCdGVPwABjbAKqx4GK67xuMV87toI+HUqgXj26m6RBp9UshEXcTrgCkdGYFzgKLt8kxu49RilDw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", + "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", "dev": true, "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/typescript-estree": "5.47.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/typescript-estree": "5.60.1", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" }, "engines": { @@ -1496,12 +1524,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz", - "integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", + "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.47.0", + "@typescript-eslint/types": "5.60.1", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -1519,9 +1547,9 @@ "dev": true }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -1690,9 +1718,9 @@ "dev": true }, "node_modules/aws-cdk": { - "version": "2.56.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.56.0.tgz", - "integrity": "sha512-UjzCnuW5uw10MrzlOqp/cc8dGfTw5Og9YpMWBlCrYA+kVSSS2ikc6aWnk0IM07RQLr9RTvAzXkT2IfVivY3baQ==", + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.85.0.tgz", + "integrity": "sha512-duRE5rvP9Qu5iUNgA6+knHKsQ7xI6yKMUxyARTveYEzW/qDHD0RWKRu+pDbbwXLlzcr25oKGPjC3dM0ui2beKg==", "dev": true, "bin": { "cdk": "bin/cdk" @@ -1705,9 +1733,9 @@ } }, "node_modules/aws-cdk-lib": { - "version": "2.56.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.56.0.tgz", - "integrity": "sha512-WFcqPzqiVsRCDGWq+rW2RRKsqg6ijCsDGwPWZmRyTkWurHEFk6BUbU4peRaUw5xeSP/qH9WcL17Tt7CF5Z1UVg==", + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.85.0.tgz", + "integrity": "sha512-u+ypK8XEMRH3tGRMSmcbPYxLet7xBdGIztUkMcPtlNJGhS/vxqh12yYkem3g3zzmHwdX8OPLSnlZ2sIuiIqp/g==", "bundleDependencies": [ "@balena/dockerignore", "case", @@ -1717,21 +1745,23 @@ "minimatch", "punycode", "semver", + "table", "yaml" ], "dev": true, "dependencies": { - "@aws-cdk/asset-awscli-v1": "^2.2.30", + "@aws-cdk/asset-awscli-v1": "^2.2.177", "@aws-cdk/asset-kubectl-v20": "^2.1.1", - "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.38", + "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", - "fs-extra": "^9.1.0", - "ignore": "^5.2.1", + "fs-extra": "^11.1.1", + "ignore": "^5.2.4", "jsonschema": "^1.4.1", "minimatch": "^3.1.2", - "punycode": "^2.1.1", - "semver": "^7.3.8", + "punycode": "^2.3.0", + "semver": "^7.5.1", + "table": "^6.8.1", "yaml": "1.10.2" }, "engines": { @@ -1747,13 +1777,53 @@ "inBundle": true, "license": "Apache-2.0" }, - "node_modules/aws-cdk-lib/node_modules/at-least-node": { - "version": "1.0.0", + "node_modules/aws-cdk-lib/node_modules/ajv": { + "version": "8.12.0", "dev": true, "inBundle": true, - "license": "ISC", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/astral-regex": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/aws-cdk-lib/node_modules/balanced-match": { @@ -1781,35 +1851,64 @@ "node": ">= 0.8.0" } }, + "node_modules/aws-cdk-lib/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/aws-cdk-lib/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, "node_modules/aws-cdk-lib/node_modules/concat-map": { "version": "0.0.1", "dev": true, "inBundle": true, "license": "MIT" }, + "node_modules/aws-cdk-lib/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/aws-cdk-lib/node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, "node_modules/aws-cdk-lib/node_modules/fs-extra": { - "version": "9.1.0", + "version": "11.1.1", "dev": true, "inBundle": true, "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=14.14" } }, "node_modules/aws-cdk-lib/node_modules/graceful-fs": { - "version": "4.2.10", + "version": "4.2.11", "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/aws-cdk-lib/node_modules/ignore": { - "version": "5.2.1", + "version": "5.2.4", "dev": true, "inBundle": true, "license": "MIT", @@ -1817,6 +1916,21 @@ "node": ">= 4" } }, + "node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/json-schema-traverse": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, "node_modules/aws-cdk-lib/node_modules/jsonfile": { "version": "6.1.0", "dev": true, @@ -1838,6 +1952,12 @@ "node": "*" } }, + "node_modules/aws-cdk-lib/node_modules/lodash.truncate": { + "version": "4.4.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, "node_modules/aws-cdk-lib/node_modules/lru-cache": { "version": "6.0.0", "dev": true, @@ -1863,7 +1983,7 @@ } }, "node_modules/aws-cdk-lib/node_modules/punycode": { - "version": "2.1.1", + "version": "2.3.0", "dev": true, "inBundle": true, "license": "MIT", @@ -1871,8 +1991,17 @@ "node": ">=6" } }, + "node_modules/aws-cdk-lib/node_modules/require-from-string": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/aws-cdk-lib/node_modules/semver": { - "version": "7.3.8", + "version": "7.5.2", "dev": true, "inBundle": true, "license": "ISC", @@ -1886,6 +2015,65 @@ "node": ">=10" } }, + "node_modules/aws-cdk-lib/node_modules/slice-ansi": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/aws-cdk-lib/node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aws-cdk-lib/node_modules/table": { + "version": "6.8.1", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/aws-cdk-lib/node_modules/universalify": { "version": "2.0.0", "dev": true, @@ -1895,6 +2083,15 @@ "node": ">= 10.0.0" } }, + "node_modules/aws-cdk-lib/node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/aws-cdk-lib/node_modules/yallist": { "version": "4.0.0", "dev": true, @@ -2037,9 +2234,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "funding": [ { @@ -2049,13 +2246,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" }, "bin": { "browserslist": "cli.js" @@ -2110,9 +2311,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001441", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001441.tgz", - "integrity": "sha512-OyxRR4Vof59I3yGWXws6i908EtGbMzVUi3ganaZQHmydk1iwDhRnvaPG2WaR0KcqrDFKrxVZHULT396LEPhXfg==", + "version": "1.0.30001508", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz", + "integrity": "sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==", "dev": true, "funding": [ { @@ -2122,6 +2323,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -2151,18 +2356,24 @@ } }, "node_modules/ci-info": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", - "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "engines": { "node": ">=8" } }, "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, "node_modules/cliui": { @@ -2229,12 +2440,12 @@ "dev": true }, "node_modules/constructs": { - "version": "10.1.197", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.1.197.tgz", - "integrity": "sha512-YAuSMYDXPQvNqmCrnVBGHXGL1+8WICDDJLH91BUIWfL6PpMEm+LXBXchgGETyREf96nIIGuXC/0lkUURsLSrHA==", + "version": "10.2.61", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.2.61.tgz", + "integrity": "sha512-8l099E1XPLN6VgUitXP6wPHLl25vc8RgxePhbCY/iRF9RGErSy6JHnI3AJU8XJ3KMaUZqhq+q9q3o+KoKM1eVw==", "dev": true, "engines": { - "node": ">= 14.17.0" + "node": ">= 16.14.0" } }, "node_modules/convert-source-map": { @@ -2337,9 +2548,9 @@ "dev": true }, "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "engines": { "node": ">=0.10.0" @@ -2427,9 +2638,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "version": "1.4.442", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.442.tgz", + "integrity": "sha512-RkrZF//Ya+0aJq2NM3OdisNh5ZodZq1rdXOS96G8DdDgpDKqKE81yTbbQ3F/4CKm1JBPsGu1Lp/akkna2xO06Q==", "dev": true }, "node_modules/emittery": { @@ -2563,13 +2774,16 @@ } }, "node_modules/eslint": { - "version": "8.30.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz", - "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.4.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.43.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -2578,24 +2792,22 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -2603,7 +2815,6 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -2619,9 +2830,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -2643,46 +2854,22 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -2690,6 +2877,9 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/estraverse": { @@ -2702,14 +2892,14 @@ } }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "dependencies": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2732,9 +2922,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -2885,9 +3075,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -3073,9 +3263,9 @@ } }, "node_modules/globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -3108,9 +3298,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "node_modules/grapheme-splitter": { @@ -3119,6 +3309,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -3282,9 +3478,9 @@ "dev": true }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -4068,16 +4264,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4173,9 +4359,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", - "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -4397,9 +4583,9 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", "dev": true }, "node_modules/normalize-path": { @@ -4424,9 +4610,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", + "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", "dev": true }, "node_modules/once": { @@ -4606,9 +4792,9 @@ } }, "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { "node": ">= 6" @@ -4688,9 +4874,9 @@ } }, "node_modules/prettier": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", - "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, "bin": { "prettier": "bin-prettier.js" @@ -4748,9 +4934,9 @@ "dev": true }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "engines": { "node": ">=6" @@ -4788,18 +4974,6 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -4816,12 +4990,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -4863,9 +5037,9 @@ } }, "node_modules/resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", "dev": true, "engines": { "node": ">=10" @@ -4938,9 +5112,9 @@ } }, "node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5207,9 +5381,9 @@ "dev": true }, "node_modules/throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", "dev": true }, "node_modules/tmpl": { @@ -5240,9 +5414,9 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", - "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "dependencies": { "psl": "^1.1.33", @@ -5447,9 +5621,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "funding": [ { @@ -5459,6 +5633,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { @@ -5466,7 +5644,7 @@ "picocolors": "^1.0.0" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -5747,68 +5925,68 @@ }, "dependencies": { "@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", "dev": true, "requires": { - "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, "@aws-cdk/asset-awscli-v1": { - "version": "2.2.37", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.37.tgz", - "integrity": "sha512-1jLfQLpFEurbcBuwBLgw7u7OxRYQZTTl9L8/6Bx44fQksJch+9dyvbVnbhigAfc9K9z2e0QAsepZybE6lAGCDQ==", + "version": "2.2.199", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.199.tgz", + "integrity": "sha512-zNdD2OxALdsdQaRZBpTfMTuudxV+4jLMznJIvVj6O+OqCru4m5UtgVQmyApW1z2H9s4/06ovVt20aXe2G8Ta+w==", "dev": true }, "@aws-cdk/asset-kubectl-v20": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz", - "integrity": "sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.2.tgz", + "integrity": "sha512-3M2tELJOxQv0apCIiuKQ4pAbncz9GuLwnKFqxifWfe77wuMxyTRPmxssYHs42ePqzap1LT6GDcPygGs+hHstLg==", "dev": true }, "@aws-cdk/asset-node-proxy-agent-v5": { - "version": "2.0.38", - "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.38.tgz", - "integrity": "sha512-BBwAjORhuUkTGO3CxGS5Evcp5n20h9v06Sftn2R1DuSm8zIoUlPsNlI1HUk8XqYuoEI4aD7IKRQBLglv09ciJQ==", + "version": "2.0.165", + "resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.165.tgz", + "integrity": "sha512-bsyLQD/vqXQcc9RDmlM1XqiFNO/yewgVFXmkMcQkndJbmE/jgYkzewwYGrBlfL725hGLQipXq19+jwWwdsXQqg==", "dev": true }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.5" } }, "@babel/compat-data": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.5.tgz", - "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz", + "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==", "dev": true }, "@babel/core": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.7.tgz", - "integrity": "sha512-t1ZjCluspe5DW24bn2Rr1CDb2v9rn/hROtg9a2tmd0+QYf4bsloYfLQzjG4qHPNMhWtKdGC33R5AxGR2Af2cBw==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.7", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz", + "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helpers": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", + "json5": "^2.2.2", "semver": "^6.3.0" }, "dependencies": { @@ -5821,37 +5999,25 @@ } }, "@babel/generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz", - "integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz", + "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==", "dev": true, "requires": { - "@babel/types": "^7.20.7", + "@babel/types": "^7.22.5", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" - }, - "dependencies": { - "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - } } }, "@babel/helper-compilation-targets": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", - "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz", + "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==", "dev": true, "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-validator-option": "^7.18.6", + "@babel/compat-data": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", "browserslist": "^4.21.3", "lru-cache": "^5.1.1", "semver": "^6.3.0" @@ -5866,115 +6032,115 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "dev": true }, "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", "dev": true, "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-module-transforms": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.20.7.tgz", - "integrity": "sha512-FNdu7r67fqMUSVuQpFQGE6BPdhJIhitoxhGzDbAXNcA07uoVG37fOiMk3OSV8rEICuyG6t8LGkd9EE64qIEoIA==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.20.2", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz", + "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/helper-plugin-utils": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", - "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", "dev": true }, "@babel/helper-simple-access": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", - "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", "dev": true, "requires": { - "@babel/types": "^7.20.2" + "@babel/types": "^7.22.5" } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz", + "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", "dev": true }, "@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", "dev": true }, "@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz", + "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==", "dev": true, "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-validator-identifier": "^7.22.5", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, @@ -6038,9 +6204,9 @@ } }, "@babel/parser": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.7.tgz", - "integrity": "sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", + "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", "dev": true }, "@babel/plugin-syntax-async-generators": { @@ -6152,39 +6318,39 @@ } }, "@babel/plugin-syntax-typescript": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", - "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.19.0" + "@babel/helper-plugin-utils": "^7.22.5" } }, "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" } }, "@babel/traverse": { - "version": "7.20.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.8.tgz", - "integrity": "sha512-/RNkaYDeCy4MjyV70+QkSHhxbvj2JO/5Ft2Pa880qJOG8tWrqcT/wXUuCCv43yogfqPzHL77Xu101KQPf4clnQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz", + "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -6198,13 +6364,13 @@ } }, "@babel/types": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz", - "integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", + "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", "to-fast-properties": "^2.0.0" } }, @@ -6235,15 +6401,30 @@ } } }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, "@eslint/eslintrc": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz", - "integrity": "sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.4.0", + "espree": "^9.5.2", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -6252,10 +6433,16 @@ "strip-json-comments": "^3.1.1" } }, + "@eslint/js": { + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", + "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "dev": true + }, "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", @@ -6550,13 +6737,14 @@ } }, "@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "dev": true, "requires": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@jridgewell/resolve-uri": { @@ -6572,19 +6760,27 @@ "dev": true }, "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", "dev": true, "requires": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" + }, + "dependencies": { + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + } } }, "@nodelib/fs.scandir": { @@ -6656,19 +6852,19 @@ "dev": true }, "@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, "@types/babel__core": { - "version": "7.1.20", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.20.tgz", - "integrity": "sha512-PVb6Bg2QuscZ30FvOU7z4guG6c926D9YRvOxEaelzndpMsvP+YM74Q/dAFASpg2l6+XLalxSGxcq/lrgYWZtyQ==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", "dev": true, "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" @@ -6694,18 +6890,18 @@ } }, "@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", "dev": true, "requires": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "@types/graceful-fs": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", - "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", "dev": true, "requires": { "@types/node": "*" @@ -6746,9 +6942,9 @@ } }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "@types/node": { @@ -6758,15 +6954,15 @@ "dev": true }, "@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, "@types/semver": { - "version": "7.3.13", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", "dev": true }, "@types/stack-utils": { @@ -6776,9 +6972,9 @@ "dev": true }, "@types/yargs": { - "version": "16.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz", - "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==", + "version": "16.0.5", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", + "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", "dev": true, "requires": { "@types/yargs-parser": "*" @@ -6791,70 +6987,71 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.0.tgz", - "integrity": "sha512-AHZtlXAMGkDmyLuLZsRpH3p4G/1iARIwc/T0vIem2YB+xW6pZaXYXzCBnZSF/5fdM97R9QqZWZ+h3iW10XgevQ==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.60.1.tgz", + "integrity": "sha512-KSWsVvsJsLJv3c4e73y/Bzt7OpqMCADUO846bHcuWYSYM19bldbAeDv7dYyV0jwkbMfJ2XdlzwjhXtuD7OY6bw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/type-utils": "5.47.0", - "@typescript-eslint/utils": "5.47.0", + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/type-utils": "5.60.1", + "@typescript-eslint/utils": "5.60.1", "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", - "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" } }, "@typescript-eslint/parser": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.0.tgz", - "integrity": "sha512-udPU4ckK+R1JWCGdQC4Qa27NtBg7w020ffHqGyAK8pAgOVuNw7YaKXGChk+udh+iiGIJf6/E/0xhVXyPAbsczw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.1.tgz", + "integrity": "sha512-pHWlc3alg2oSMGwsU/Is8hbm3XFbcrb6P5wIxcQW9NsYBfnrubl/GhVVD/Jm/t8HXhA2WncoIRfBtnCgRGV96Q==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/typescript-estree": "5.47.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/typescript-estree": "5.60.1", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz", - "integrity": "sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.1.tgz", + "integrity": "sha512-Dn/LnN7fEoRD+KspEOV0xDMynEmR3iSHdgNsarlXNLGGtcUok8L4N71dxUgt3YvlO8si7E+BJ5Fe3wb5yUw7DQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/visitor-keys": "5.47.0" + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1" } }, "@typescript-eslint/type-utils": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.47.0.tgz", - "integrity": "sha512-1J+DFFrYoDUXQE1b7QjrNGARZE6uVhBqIvdaXTe5IN+NmEyD68qXR1qX1g2u4voA+nCaelQyG8w30SAOihhEYg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.60.1.tgz", + "integrity": "sha512-vN6UztYqIu05nu7JqwQGzQKUJctzs3/Hg7E2Yx8rz9J+4LgtIDFWjjl1gm3pycH0P3mHAcEUBd23LVgfrsTR8A==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.47.0", - "@typescript-eslint/utils": "5.47.0", + "@typescript-eslint/typescript-estree": "5.60.1", + "@typescript-eslint/utils": "5.60.1", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz", - "integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.1.tgz", + "integrity": "sha512-zDcDx5fccU8BA0IDZc71bAtYIcG9PowaOwaD8rjYbqwK7dpe/UMQl3inJ4UtUK42nOCT41jTSCwg76E62JpMcg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz", - "integrity": "sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.1.tgz", + "integrity": "sha512-hkX70J9+2M2ZT6fhti5Q2FoU9zb+GeZK2SLP1WZlvUDqdMbEKhexZODD1WodNRyO8eS+4nScvT0dts8IdaBzfw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/visitor-keys": "5.47.0", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/visitor-keys": "5.60.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -6863,28 +7060,28 @@ } }, "@typescript-eslint/utils": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.47.0.tgz", - "integrity": "sha512-U9xcc0N7xINrCdGVPwABjbAKqx4GK67xuMV87toI+HUqgXj26m6RBp9UshEXcTrgCkdGYFzgKLt8kxu49RilDw==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.60.1.tgz", + "integrity": "sha512-tiJ7FFdFQOWssFa3gqb94Ilexyw0JVxj6vBzaSpfN/8IhoKkDuSAenUKvsSHw2A/TMpJb26izIszTXaqygkvpQ==", "dev": true, "requires": { + "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.47.0", - "@typescript-eslint/types": "5.47.0", - "@typescript-eslint/typescript-estree": "5.47.0", + "@typescript-eslint/scope-manager": "5.60.1", + "@typescript-eslint/types": "5.60.1", + "@typescript-eslint/typescript-estree": "5.60.1", "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz", - "integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==", + "version": "5.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.1.tgz", + "integrity": "sha512-xEYIxKcultP6E/RMKqube11pGjXH1DCo60mQoWhVYyKfLkwbIVVjYxmOenNMxILx0TjCujPTjjnTIVzm09TXIw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.47.0", + "@typescript-eslint/types": "5.60.1", "eslint-visitor-keys": "^3.3.0" } }, @@ -6895,9 +7092,9 @@ "dev": true }, "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "dev": true }, "acorn-globals": { @@ -7019,31 +7216,32 @@ "dev": true }, "aws-cdk": { - "version": "2.56.0", - "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.56.0.tgz", - "integrity": "sha512-UjzCnuW5uw10MrzlOqp/cc8dGfTw5Og9YpMWBlCrYA+kVSSS2ikc6aWnk0IM07RQLr9RTvAzXkT2IfVivY3baQ==", + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.85.0.tgz", + "integrity": "sha512-duRE5rvP9Qu5iUNgA6+knHKsQ7xI6yKMUxyARTveYEzW/qDHD0RWKRu+pDbbwXLlzcr25oKGPjC3dM0ui2beKg==", "dev": true, "requires": { "fsevents": "2.3.2" } }, "aws-cdk-lib": { - "version": "2.56.0", - "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.56.0.tgz", - "integrity": "sha512-WFcqPzqiVsRCDGWq+rW2RRKsqg6ijCsDGwPWZmRyTkWurHEFk6BUbU4peRaUw5xeSP/qH9WcL17Tt7CF5Z1UVg==", + "version": "2.85.0", + "resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.85.0.tgz", + "integrity": "sha512-u+ypK8XEMRH3tGRMSmcbPYxLet7xBdGIztUkMcPtlNJGhS/vxqh12yYkem3g3zzmHwdX8OPLSnlZ2sIuiIqp/g==", "dev": true, "requires": { - "@aws-cdk/asset-awscli-v1": "^2.2.30", + "@aws-cdk/asset-awscli-v1": "^2.2.177", "@aws-cdk/asset-kubectl-v20": "^2.1.1", - "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.38", + "@aws-cdk/asset-node-proxy-agent-v5": "^2.0.148", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", - "fs-extra": "^9.1.0", - "ignore": "^5.2.1", + "fs-extra": "^11.1.1", + "ignore": "^5.2.4", "jsonschema": "^1.4.1", "minimatch": "^3.1.2", - "punycode": "^2.1.1", - "semver": "^7.3.8", + "punycode": "^2.3.0", + "semver": "^7.5.1", + "table": "^6.8.1", "yaml": "1.10.2" }, "dependencies": { @@ -7052,8 +7250,32 @@ "bundled": true, "dev": true }, - "at-least-node": { - "version": "1.0.0", + "ajv": { + "version": "8.12.0", + "bundled": true, + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", "bundled": true, "dev": true }, @@ -7076,29 +7298,61 @@ "bundled": true, "dev": true }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, "concat-map": { "version": "0.0.1", "bundled": true, "dev": true }, + "emoji-regex": { + "version": "8.0.0", + "bundled": true, + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true, + "dev": true + }, "fs-extra": { - "version": "9.1.0", + "version": "11.1.1", "bundled": true, "dev": true, "requires": { - "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "graceful-fs": { - "version": "4.2.10", + "version": "4.2.11", "bundled": true, "dev": true }, "ignore": { - "version": "5.2.1", + "version": "5.2.4", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", "bundled": true, "dev": true }, @@ -7116,6 +7370,11 @@ "bundled": true, "dev": true }, + "lodash.truncate": { + "version": "4.4.2", + "bundled": true, + "dev": true + }, "lru-cache": { "version": "6.0.0", "bundled": true, @@ -7133,23 +7392,76 @@ } }, "punycode": { - "version": "2.1.1", + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "require-from-string": { + "version": "2.0.2", "bundled": true, "dev": true }, "semver": { - "version": "7.3.8", + "version": "7.5.2", "bundled": true, "dev": true, "requires": { "lru-cache": "^6.0.0" } }, + "slice-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "table": { + "version": "6.8.1", + "bundled": true, + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + } + }, "universalify": { "version": "2.0.0", "bundled": true, "dev": true }, + "uri-js": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, "yallist": { "version": "4.0.0", "bundled": true, @@ -7265,15 +7577,15 @@ "dev": true }, "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" } }, "bs-logger": { @@ -7313,9 +7625,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001441", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001441.tgz", - "integrity": "sha512-OyxRR4Vof59I3yGWXws6i908EtGbMzVUi3ganaZQHmydk1iwDhRnvaPG2WaR0KcqrDFKrxVZHULT396LEPhXfg==", + "version": "1.0.30001508", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz", + "integrity": "sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==", "dev": true }, "chalk": { @@ -7335,15 +7647,15 @@ "dev": true }, "ci-info": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.0.tgz", - "integrity": "sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", "dev": true }, "cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, "cliui": { @@ -7400,9 +7712,9 @@ "dev": true }, "constructs": { - "version": "10.1.197", - "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.1.197.tgz", - "integrity": "sha512-YAuSMYDXPQvNqmCrnVBGHXGL1+8WICDDJLH91BUIWfL6PpMEm+LXBXchgGETyREf96nIIGuXC/0lkUURsLSrHA==", + "version": "10.2.61", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.2.61.tgz", + "integrity": "sha512-8l099E1XPLN6VgUitXP6wPHLl25vc8RgxePhbCY/iRF9RGErSy6JHnI3AJU8XJ3KMaUZqhq+q9q3o+KoKM1eVw==", "dev": true }, "convert-source-map": { @@ -7490,9 +7802,9 @@ "dev": true }, "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true }, "delayed-stream": { @@ -7555,9 +7867,9 @@ } }, "electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "version": "1.4.442", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.442.tgz", + "integrity": "sha512-RkrZF//Ya+0aJq2NM3OdisNh5ZodZq1rdXOS96G8DdDgpDKqKE81yTbbQ3F/4CKm1JBPsGu1Lp/akkna2xO06Q==", "dev": true }, "emittery": { @@ -7654,13 +7966,16 @@ } }, "eslint": { - "version": "8.30.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz", - "integrity": "sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ==", + "version": "8.43.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", + "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.4.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.43.0", + "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", @@ -7669,24 +7984,22 @@ "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -7694,16 +8007,15 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "dependencies": { "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -7719,9 +8031,9 @@ } }, "eslint-config-prettier": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", - "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", "dev": true, "requires": {} }, @@ -7735,38 +8047,21 @@ "estraverse": "^4.1.1" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", "dev": true }, "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", "dev": true, "requires": { "acorn": "^8.8.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" } }, "esprima": { @@ -7776,9 +8071,9 @@ "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -7899,9 +8194,9 @@ "dev": true }, "fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -8038,9 +8333,9 @@ } }, "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -8061,9 +8356,9 @@ } }, "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "grapheme-splitter": { @@ -8072,6 +8367,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -8193,9 +8494,9 @@ "dev": true }, "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, "requires": { "has": "^1.0.3" @@ -8802,12 +9103,6 @@ } } }, - "js-sdsl": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", - "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", - "dev": true - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -8883,9 +9178,9 @@ "dev": true }, "json5": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", - "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "kleur": { @@ -9061,9 +9356,9 @@ "dev": true }, "node-releases": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", + "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==", "dev": true }, "normalize-path": { @@ -9082,9 +9377,9 @@ } }, "nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", + "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", "dev": true }, "once": { @@ -9213,9 +9508,9 @@ "dev": true }, "pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true }, "pkg-dir": { @@ -9273,9 +9568,9 @@ "dev": true }, "prettier": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz", - "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true }, "pretty-format": { @@ -9314,9 +9609,9 @@ "dev": true }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true }, "querystringify": { @@ -9337,12 +9632,6 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -9356,12 +9645,12 @@ "dev": true }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -9390,9 +9679,9 @@ "dev": true }, "resolve.exports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", - "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", "dev": true }, "reusify": { @@ -9435,9 +9724,9 @@ } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -9639,9 +9928,9 @@ "dev": true }, "throat": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", - "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", + "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", "dev": true }, "tmpl": { @@ -9666,9 +9955,9 @@ } }, "tough-cookie": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", - "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "requires": { "psl": "^1.1.33", @@ -9789,9 +10078,9 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", "dev": true, "requires": { "escalade": "^3.1.1", diff --git a/tools/ci-cdk/test/oidc-provider-stack.test.ts b/tools/ci-cdk/test/oidc-provider-stack.test.ts index 24a31505ba..f776fd843e 100644 --- a/tools/ci-cdk/test/oidc-provider-stack.test.ts +++ b/tools/ci-cdk/test/oidc-provider-stack.test.ts @@ -5,7 +5,7 @@ import { App } from "aws-cdk-lib"; import { Template } from "aws-cdk-lib/assertions"; -import { GITHUB_CERTIFICATE_THUMBPRINT, OidcProviderStack } from "../lib/oidc-provider-stack"; +import { GITHUB_CERTIFICATE_THUMBPRINTS, OidcProviderStack } from "../lib/oidc-provider-stack"; test("it should have an OIDC provider", () => { const app = new App(); @@ -15,7 +15,7 @@ test("it should have an OIDC provider", () => { // Verify the OIDC provider template.hasResourceProperties("Custom::AWSCDKOpenIdConnectProvider", { ClientIDList: ["sts.amazonaws.com"], - ThumbprintList: [GITHUB_CERTIFICATE_THUMBPRINT], + ThumbprintList: GITHUB_CERTIFICATE_THUMBPRINTS, Url: "https://token.actions.githubusercontent.com", }); }); From c8ba2d5b801b0c8103ed934394793e3476cc5c74 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 28 Jun 2023 10:08:00 -0700 Subject: [PATCH 183/253] Fix auth failures in codegen-client-tests in orchestrator mode (#2812) ## Motivation and Context In orchestrator mode, most `codegen-client-test` tests were failing due to being unable to find a matching auth scheme, or due to some of the test models referencing the `@sigv4` trait. This PR fixes all of those failures, and adds the `smithy.runtime.mode` flag to `codegen-client-test` as well. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- buildSrc/src/main/kotlin/CodegenTestCommon.kt | 2 + codegen-client-test/build.gradle.kts | 136 ++++++++++-------- .../smithy/customizations/NoAuthDecorator.kt | 39 +---- .../generators/EndpointParamsGenerator.kt | 4 +- .../EndpointParamsInterceptorGenerator.kt | 6 +- .../smithy/generators/OperationGenerator.kt | 3 + .../OperationRuntimePluginGenerator.kt | 30 ++-- .../protocol/ProtocolTestGenerator.kt | 43 ++++-- .../protocol/RequestSerializerGenerator.kt | 6 +- .../protocol/ResponseDeserializerGenerator.kt | 18 +-- rust-runtime/aws-smithy-runtime/Cargo.toml | 1 - .../aws-smithy-runtime/src/client/auth.rs | 1 - .../aws-smithy-runtime/src/client/identity.rs | 1 - 13 files changed, 144 insertions(+), 146 deletions(-) diff --git a/buildSrc/src/main/kotlin/CodegenTestCommon.kt b/buildSrc/src/main/kotlin/CodegenTestCommon.kt index 4977f3adc4..cc996eae82 100644 --- a/buildSrc/src/main/kotlin/CodegenTestCommon.kt +++ b/buildSrc/src/main/kotlin/CodegenTestCommon.kt @@ -18,6 +18,7 @@ data class CodegenTest( val service: String, val module: String, val extraConfig: String? = null, + val extraCodegenConfig: String? = null, val imports: List = emptyList(), ) @@ -38,6 +39,7 @@ private fun generateSmithyBuild(projectDir: String, pluginName: String, tests: L "relativePath": "$projectDir/rust-runtime" }, "codegen": { + ${it.extraCodegenConfig ?: ""} }, "service": "${it.service}", "module": "${it.module}", diff --git a/codegen-client-test/build.gradle.kts b/codegen-client-test/build.gradle.kts index 3709d4e594..bb906e9968 100644 --- a/codegen-client-test/build.gradle.kts +++ b/codegen-client-test/build.gradle.kts @@ -15,6 +15,7 @@ plugins { val smithyVersion: String by project val defaultRustDocFlags: String by project val properties = PropertyRetriever(rootProject, project) +fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "middleware" val pluginName = "rust-client-codegen" val workingDirUnderBuildDir = "smithyprojections/codegen-client-test/" @@ -33,71 +34,82 @@ dependencies { implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") } -val allCodegenTests = "../codegen-core/common-test-models".let { commonModels -> - listOf( - CodegenTest("com.amazonaws.simple#SimpleService", "simple", imports = listOf("$commonModels/simple.smithy")), - CodegenTest("com.amazonaws.dynamodb#DynamoDB_20120810", "dynamo"), - CodegenTest("com.amazonaws.ebs#Ebs", "ebs", imports = listOf("$commonModels/ebs.json")), - CodegenTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), - CodegenTest("aws.protocoltests.json#JsonProtocol", "json_rpc11"), - CodegenTest("aws.protocoltests.restjson#RestJson", "rest_json"), - CodegenTest("aws.protocoltests.restjson#RestJsonExtras", "rest_json_extras", imports = listOf("$commonModels/rest-json-extras.smithy")), - CodegenTest("aws.protocoltests.misc#MiscService", "misc", imports = listOf("$commonModels/misc.smithy")), - CodegenTest( - "aws.protocoltests.restxml#RestXml", "rest_xml", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - - CodegenTest( - "aws.protocoltests.query#AwsQuery", "aws_query", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - CodegenTest( - "aws.protocoltests.ec2#AwsEc2", "ec2_query", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - CodegenTest( - "aws.protocoltests.restxml.xmlns#RestXmlWithNamespace", - "rest_xml_namespace", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - CodegenTest( - "aws.protocoltests.restxml#RestXmlExtras", - "rest_xml_extras", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - CodegenTest( - "aws.protocoltests.restxmlunwrapped#RestXmlExtrasUnwrappedErrors", - "rest_xml_extras_unwrapped", - extraConfig = """, "codegen": { "addMessageToErrors": false } """, - ), - CodegenTest( - "crate#Config", - "naming_test_ops", - """ - , "codegen": { "renameErrors": false } - """.trimIndent(), - imports = listOf("$commonModels/naming-obstacle-course-ops.smithy"), - ), - CodegenTest( - "casing#ACRONYMInside_Service", - "naming_test_casing", - imports = listOf("$commonModels/naming-obstacle-course-casing.smithy"), - ), - CodegenTest( - "naming_obs_structs#NamingObstacleCourseStructs", - "naming_test_structs", - """ - , "codegen": { "renameErrors": false } - """.trimIndent(), - imports = listOf("$commonModels/naming-obstacle-course-structs.smithy"), - ), - CodegenTest("aws.protocoltests.json#TestService", "endpoint-rules"), - CodegenTest("com.aws.example#PokemonService", "pokemon-service-client", imports = listOf("$commonModels/pokemon.smithy", "$commonModels/pokemon-common.smithy")), - CodegenTest("com.aws.example#PokemonService", "pokemon-service-awsjson-client", imports = listOf("$commonModels/pokemon-awsjson.smithy", "$commonModels/pokemon-common.smithy")), +data class ClientTest( + val serviceShapeName: String, + val moduleName: String, + val dependsOn: List = emptyList(), + val addMessageToErrors: Boolean = true, + val renameErrors: Boolean = true, +) { + fun toCodegenTest(): CodegenTest = CodegenTest( + serviceShapeName, + moduleName, + extraCodegenConfig = extraCodegenConfig(), + imports = imports(), ) + + private fun extraCodegenConfig(): String = StringBuilder().apply { + append("\"addMessageToErrors\": $addMessageToErrors,\n") + append("\"renameErrors\": $renameErrors\n,") + append("\"enableNewSmithyRuntime\": \"${getSmithyRuntimeMode()}\"") + }.toString() + + private fun imports(): List = dependsOn.map { "../codegen-core/common-test-models/$it" } } +val allCodegenTests = listOf( + ClientTest("com.amazonaws.simple#SimpleService", "simple", dependsOn = listOf("simple.smithy")), + ClientTest("com.amazonaws.dynamodb#DynamoDB_20120810", "dynamo"), + ClientTest("com.amazonaws.ebs#Ebs", "ebs", dependsOn = listOf("ebs.json")), + ClientTest("aws.protocoltests.json10#JsonRpc10", "json_rpc10"), + ClientTest("aws.protocoltests.json#JsonProtocol", "json_rpc11"), + ClientTest("aws.protocoltests.restjson#RestJson", "rest_json"), + ClientTest( + "aws.protocoltests.restjson#RestJsonExtras", + "rest_json_extras", + dependsOn = listOf("rest-json-extras.smithy"), + ), + ClientTest("aws.protocoltests.misc#MiscService", "misc", dependsOn = listOf("misc.smithy")), + ClientTest("aws.protocoltests.restxml#RestXml", "rest_xml", addMessageToErrors = false), + ClientTest("aws.protocoltests.query#AwsQuery", "aws_query", addMessageToErrors = false), + ClientTest("aws.protocoltests.ec2#AwsEc2", "ec2_query", addMessageToErrors = false), + ClientTest("aws.protocoltests.restxml.xmlns#RestXmlWithNamespace", "rest_xml_namespace", addMessageToErrors = false), + ClientTest("aws.protocoltests.restxml#RestXmlExtras", "rest_xml_extras", addMessageToErrors = false), + ClientTest( + "aws.protocoltests.restxmlunwrapped#RestXmlExtrasUnwrappedErrors", + "rest_xml_extras_unwrapped", + addMessageToErrors = false, + ), + ClientTest( + "crate#Config", + "naming_test_ops", + dependsOn = listOf("naming-obstacle-course-ops.smithy"), + renameErrors = false, + ), + ClientTest( + "casing#ACRONYMInside_Service", + "naming_test_casing", + dependsOn = listOf("naming-obstacle-course-casing.smithy"), + ), + ClientTest( + "naming_obs_structs#NamingObstacleCourseStructs", + "naming_test_structs", + dependsOn = listOf("naming-obstacle-course-structs.smithy"), + renameErrors = false, + ), + ClientTest("aws.protocoltests.json#TestService", "endpoint-rules"), + ClientTest( + "com.aws.example#PokemonService", + "pokemon-service-client", + dependsOn = listOf("pokemon.smithy", "pokemon-common.smithy"), + ), + ClientTest( + "com.aws.example#PokemonService", + "pokemon-service-awsjson-client", + dependsOn = listOf("pokemon-awsjson.smithy", "pokemon-common.smithy"), + ), +).map(ClientTest::toCodegenTest) + project.registerGenerateSmithyBuildTask(rootProject, pluginName, allCodegenTests) project.registerGenerateCargoWorkspaceTask(rootProject, pluginName, allCodegenTests, workingDirUnderBuildDir) project.registerGenerateCargoConfigTomlTask(buildDir.resolve(workingDirUnderBuildDir)) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt index 6667601e42..9cdcae78c5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt @@ -7,25 +7,17 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId -import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency -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.util.hasTrait -import software.amazon.smithy.rust.codegen.core.util.letIf val noAuthSchemeShapeId: ShapeId = ShapeId.from("aws.smithy.rs#NoAuth") private fun noAuthModule(codegenContext: ClientCodegenContext): RuntimeType = CargoDependency.smithyRuntime(codegenContext.runtimeConfig) - .withFeature("no-auth") .toType() .resolve("client::auth::no_auth") @@ -37,38 +29,11 @@ class NoAuthDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, operationShape: OperationShape, baseAuthOptions: List, - ): List = baseAuthOptions.letIf(operationShape.hasTrait()) { - it + AuthOption.StaticAuthOption(noAuthSchemeShapeId) { + ): List = baseAuthOptions + + AuthOption.StaticAuthOption(noAuthSchemeShapeId) { rustTemplate( "#{NO_AUTH_SCHEME_ID},", "NO_AUTH_SCHEME_ID" to noAuthModule(codegenContext).resolve("NO_AUTH_SCHEME_ID"), ) } - } - - override fun operationCustomizations( - codegenContext: ClientCodegenContext, - operation: OperationShape, - baseCustomizations: List, - ): List = baseCustomizations + AnonymousAuthCustomization(codegenContext, operation) -} - -class AnonymousAuthCustomization( - private val codegenContext: ClientCodegenContext, - private val operationShape: OperationShape, -) : OperationCustomization() { - override fun section(section: OperationSection): Writable = writable { - if ( - codegenContext.smithyRuntimeMode.generateOrchestrator && - section is OperationSection.AdditionalRuntimePlugins && - operationShape.hasTrait() - ) { - section.addOperationRuntimePlugin(this) { - rustTemplate( - "#{NoAuthRuntimePlugin}::new()", - "NoAuthRuntimePlugin" to noAuthModule(codegenContext).resolve("NoAuthRuntimePlugin"), - ) - } - } - } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt index ff19163afc..73752e12c9 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt @@ -28,6 +28,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.isOptional import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.smithy.mapRustType @@ -232,7 +233,8 @@ internal class EndpointParamsGenerator(private val parameters: Parameters) { rustWriter.rustBlock("impl ParamsBuilder") { docs("Consume this builder, creating [`Params`].") rustBlockTemplate( - "pub fn build(self) -> Result<#{Params}, #{ParamsError}>", + "pub fn build(self) -> #{Result}<#{Params}, #{ParamsError}>", + *preludeScope, "Params" to paramsStruct(), "ParamsError" to paramsError(), ) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index c8efe85015..b9dca27732 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -27,6 +27,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -45,6 +46,7 @@ class EndpointParamsInterceptorGenerator( val orchestrator = runtimeApi.resolve("client::orchestrator") val smithyTypes = CargoDependency.smithyTypes(rc).toType() arrayOf( + *preludeScope, "BoxError" to RuntimeType.boxError(rc), "ConfigBag" to RuntimeType.configBag(rc), "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(rc) @@ -78,7 +80,7 @@ class EndpointParamsInterceptorGenerator( &self, context: &#{BeforeSerializationInterceptorContextRef}<'_, #{Input}, #{Output}, #{Error}>, cfg: &mut #{ConfigBag}, - ) -> Result<(), #{BoxError}> { + ) -> #{Result}<(), #{BoxError}> { use #{ConfigBagAccessors}; let _input = context.input() .downcast_ref::<${operationInput.name}>() @@ -91,7 +93,7 @@ class EndpointParamsInterceptorGenerator( .build() .map_err(|err| #{ContextAttachedError}::new("endpoint params could not be built", err))?; cfg.interceptor_state().set_endpoint_resolver_params(#{EndpointResolverParams}::new(params)); - Ok(()) + #{Ok}(()) } } """, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 6371c6a5c2..0110a1620f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -59,6 +59,7 @@ open class OperationGenerator( let mut runtime_plugins = runtime_plugins .with_client_plugin(handle.conf.clone()) .with_client_plugin(crate::config::ServiceRuntimePlugin::new(handle)) + .with_client_plugin(#{NoAuthRuntimePlugin}::new()) .with_operation_plugin(operation); if let Some(config_override) = config_override { runtime_plugins = runtime_plugins.with_operation_plugin(config_override); @@ -71,6 +72,8 @@ open class OperationGenerator( "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::runtime_plugin::RuntimePlugins"), + "NoAuthRuntimePlugin" to RuntimeType.smithyRuntime(runtimeConfig) + .resolve("client::auth::no_auth::NoAuthRuntimePlugin"), ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index c14e0ebdbc..5edfdedb04 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -21,6 +21,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.pre import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.hasTrait +import java.util.logging.Logger /** * Generates operation-level runtime plugins @@ -28,6 +29,7 @@ import software.amazon.smithy.rust.codegen.core.util.hasTrait class OperationRuntimePluginGenerator( private val codegenContext: ClientCodegenContext, ) { + private val logger: Logger = Logger.getLogger(javaClass.name) private val codegenScope = codegenContext.runtimeConfig.let { rc -> val runtimeApi = RuntimeType.smithyRuntimeApi(rc) val smithyTypes = RuntimeType.smithyTypes(rc) @@ -136,29 +138,25 @@ class OperationRuntimePluginGenerator( "])));", *codegenScope, ) { + var noSupportedAuthSchemes = true val authSchemes = ServiceIndex.of(codegenContext.model) .getEffectiveAuthSchemes(codegenContext.serviceShape, operationShape) - var atLeastOneScheme = false for (schemeShapeId in authSchemes.keys) { val authOption = authOptionsMap[schemeShapeId] - ?: throw IllegalStateException("no auth scheme implementation available for $schemeShapeId") - authOption.constructor(this) - atLeastOneScheme = true + if (authOption != null) { + authOption.constructor(this) + noSupportedAuthSchemes = false + } else { + logger.warning( + "No auth scheme implementation available for $schemeShapeId. " + + "The generated client will not attempt to use this auth scheme.", + ) + } } - if (operationShape.hasTrait()) { + if (operationShape.hasTrait() || noSupportedAuthSchemes) { val authOption = authOptionsMap[noAuthSchemeShapeId] - ?: throw IllegalStateException("missing 'no auth' implementation") + ?: throw IllegalStateException("Missing 'no auth' implementation. This is a codegen bug.") authOption.constructor(this) - atLeastOneScheme = true - } - if (!atLeastOneScheme) { - throw IllegalStateException( - "this client won't have any auth schemes " + - "(not even optional/no-auth auth), which means the generated client " + - "won't work at all for the ${operationShape.id} operation. See " + - "https://smithy.io/2.0/spec/authentication-traits.html for documentation " + - "on Smithy authentication traits.", - ) } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index d9d978301c..22c64d36e0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -70,21 +70,34 @@ class DefaultProtocolTestGenerator( override val operationShape: OperationShape, private val renderClientCreation: RustWriter.(ClientCreationParams) -> Unit = { params -> - rustTemplate( - """ - let smithy_client = #{Builder}::new() - .connector(${params.connectorName}) - .middleware(#{MapRequestLayer}::for_mapper(#{SmithyEndpointStage}::new())) - .build(); - let ${params.clientName} = #{Client}::with_config(smithy_client, ${params.configBuilderName}.build()); - """, - "Client" to ClientRustModule.root.toType().resolve("Client"), - "Builder" to ClientRustModule.client.toType().resolve("Builder"), - "SmithyEndpointStage" to RuntimeType.smithyHttp(codegenContext.runtimeConfig) - .resolve("endpoint::middleware::SmithyEndpointStage"), - "MapRequestLayer" to RuntimeType.smithyHttpTower(codegenContext.runtimeConfig) - .resolve("map_request::MapRequestLayer"), - ) + if (params.codegenContext.smithyRuntimeMode.defaultToMiddleware) { + rustTemplate( + """ + let smithy_client = #{Builder}::new() + .connector(${params.connectorName}) + .middleware(#{MapRequestLayer}::for_mapper(#{SmithyEndpointStage}::new())) + .build(); + let ${params.clientName} = #{Client}::with_config(smithy_client, ${params.configBuilderName}.build()); + """, + "Client" to ClientRustModule.root.toType().resolve("Client"), + "Builder" to ClientRustModule.client.toType().resolve("Builder"), + "SmithyEndpointStage" to RuntimeType.smithyHttp(codegenContext.runtimeConfig) + .resolve("endpoint::middleware::SmithyEndpointStage"), + "MapRequestLayer" to RuntimeType.smithyHttpTower(codegenContext.runtimeConfig) + .resolve("map_request::MapRequestLayer"), + ) + } else { + rustTemplate( + """ + let ${params.clientName} = #{Client}::from_conf( + ${params.configBuilderName} + .http_connector(${params.connectorName}) + .build() + ); + """, + "Client" to ClientRustModule.root.toType().resolve("Client"), + ) + } }, ) : ProtocolTestGenerator { private val logger = Logger.getLogger(javaClass.name) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt index 665196d8e1..fb21d70cd2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt @@ -18,6 +18,7 @@ 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.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol @@ -39,6 +40,7 @@ class RequestSerializerGenerator( val orchestrator = runtimeApi.resolve("client::orchestrator") val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig) arrayOf( + *preludeScope, "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "config" to ClientRustModule.config, "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), @@ -70,7 +72,7 @@ class RequestSerializerGenerator( struct $serializerName; impl #{RequestSerializer} for $serializerName { ##[allow(unused_mut, clippy::let_and_return, clippy::needless_borrow, clippy::useless_conversion)] - fn serialize_input(&self, input: #{Input}, _cfg: &mut #{ConfigBag}) -> Result<#{HttpRequest}, #{BoxError}> { + fn serialize_input(&self, input: #{Input}, _cfg: &mut #{ConfigBag}) -> #{Result}<#{HttpRequest}, #{BoxError}> { let input = #{TypedBox}::<#{ConcreteInput}>::assume_from(input).expect("correct type").unwrap(); let _header_serialization_settings = _cfg.load::<#{HeaderSerializationSettings}>().cloned().unwrap_or_default(); let mut request_builder = { @@ -78,7 +80,7 @@ class RequestSerializerGenerator( }; let body = #{generate_body}; #{add_content_length} - Ok(request_builder.body(body).expect("valid request")) + #{Ok}(request_builder.body(body).expect("valid request")) } } """, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index 938c26c60b..e08ea15f13 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolFunctions @@ -36,6 +37,7 @@ class ResponseDeserializerGenerator( val orchestrator = CargoDependency.smithyRuntimeApi(runtimeConfig).toType().resolve("client::orchestrator") arrayOf( + *preludeScope, "Error" to interceptorContext.resolve("Error"), "HttpResponse" to orchestrator.resolve("HttpResponse"), "Instrument" to CargoDependency.Tracing.toType().resolve("Instrument"), @@ -92,14 +94,14 @@ class ResponseDeserializerGenerator( val successCode = httpBindingResolver.httpTrait(operationShape).code rustTemplate( """ - fn deserialize_streaming(&self, response: &mut #{HttpResponse}) -> Option<#{OutputOrError}> { + fn deserialize_streaming(&self, response: &mut #{HttpResponse}) -> #{Option}<#{OutputOrError}> { #{BeforeParseResponse} // If this is an error, defer to the non-streaming parser if !response.status().is_success() && response.status().as_u16() != $successCode { - return None; + return #{None}; } - Some(#{type_erase_result}(#{parse_streaming_response}(response))) + #{Some}(#{type_erase_result}(#{parse_streaming_response}(response))) } """, *codegenScope, @@ -135,7 +137,7 @@ class ResponseDeserializerGenerator( let (success, status) = (response.status().is_success(), response.status().as_u16()); let headers = response.headers(); let body = response.body().bytes().expect("body loaded"); - #{BeforeParseResponse} + #{BeforeParseResponse} let parse_result = if !success && status != $successCode { #{parse_error}(status, headers, body) } else { @@ -155,14 +157,14 @@ class ResponseDeserializerGenerator( private fun typeEraseResult(): RuntimeType = ProtocolFunctions.crossOperationFn("type_erase_result") { fnName -> rustTemplate( """ - pub(crate) fn $fnName(result: Result) -> Result<#{Output}, #{OrchestratorError}<#{Error}>> + pub(crate) fn $fnName(result: #{Result}) -> #{Result}<#{Output}, #{OrchestratorError}<#{Error}>> where - O: std::fmt::Debug + Send + Sync + 'static, - E: std::error::Error + std::fmt::Debug + Send + Sync + 'static, + O: ::std::fmt::Debug + #{Send} + #{Sync} + 'static, + E: ::std::error::Error + std::fmt::Debug + #{Send} + #{Sync} + 'static, { result.map(|output| #{TypedBox}::new(output).erase()) .map_err(|error| #{TypedBox}::new(error).erase_error()) - .map_err(Into::into) + .map_err(#{Into}::into) } """, *codegenScope, diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 9cc6bd9a89..281e4245f9 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -11,7 +11,6 @@ repository = "https://github.com/awslabs/smithy-rs" [features] http-auth = ["aws-smithy-runtime-api/http-auth"] -no-auth = [] test-util = ["dep:aws-smithy-protocol-test"] [dependencies] diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/auth.rs index a119daa924..a67537cb6f 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth.rs @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#[cfg(feature = "no-auth")] pub mod no_auth; #[cfg(feature = "http-auth")] diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity.rs b/rust-runtime/aws-smithy-runtime/src/client/identity.rs index 13547ed917..a8b8769057 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/identity.rs @@ -3,5 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 */ -#[cfg(feature = "no-auth")] pub mod no_auth; From 9c95803a73375566c6fa682bc88edaaf8c4f8d77 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Wed, 28 Jun 2023 12:38:00 -0500 Subject: [PATCH 184/253] add support for adaptive retries when orchestrator mode is enabled (#2800) ## Motivation and Context #2190 ## Description add support for adaptive retries ## Testing I wrote some tests and I'm open to suggestions for more. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti --- .../rustsdk/IntegrationTestDependencies.kt | 20 + aws/sdk/integration-tests/dynamodb/Cargo.toml | 10 +- .../retries-with-client-rate-limiting.rs | 173 +++++++++ .../ResiliencyConfigCustomization.kt | 116 +++++- .../customize/RequiredCustomizations.kt | 11 + .../ServiceRuntimePluginGenerator.kt | 13 + .../protocol/MakeOperationGenerator.kt | 7 + .../codegen/core/rustlang/CargoDependency.kt | 1 + .../testutil/PythonServerTestHelpers.kt | 2 +- .../aws-smithy-async/src/test_util.rs | 15 + .../aws-smithy-eventstream/src/frame.rs | 12 +- .../src/client/interceptors.rs | 11 +- .../src/client/request_attempts.rs | 18 +- rust-runtime/aws-smithy-runtime/Cargo.toml | 1 + .../src/client/connections/test_connection.rs | 8 +- .../src/client/orchestrator.rs | 27 +- .../aws-smithy-runtime/src/client/retries.rs | 34 +- .../src/client/retries/client_rate_limiter.rs | 365 +++++++++++------- .../client/retries/strategy/fixed_delay.rs | 2 +- .../src/client/retries/strategy/standard.rs | 185 ++++++--- .../src/client/retries/token_bucket.rs | 18 +- .../aws-smithy-runtime/src/client/timeout.rs | 11 +- rust-runtime/aws-smithy-runtime/src/lib.rs | 2 + .../src/static_partition_map.rs | 158 ++++++++ rust-runtime/aws-smithy-types/src/retry.rs | 48 ++- 25 files changed, 1017 insertions(+), 251 deletions(-) create mode 100644 aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/static_partition_map.rs diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt index 16b3823fca..8bdfe0cdc1 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.Approx import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.AsyncStd import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.AsyncStream import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.BytesUtils @@ -26,6 +27,8 @@ import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Compani import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.TracingAppender import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.TracingSubscriber import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.TracingTest +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.smithyRuntime +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.smithyRuntimeApi import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.writable @@ -73,6 +76,7 @@ class IntegrationTestDependencies( private val hasTests: Boolean, private val hasBenches: Boolean, ) : LibRsCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig override fun section(section: LibRsSection) = when (section) { is LibRsSection.Body -> testDependenciesOnly { if (hasTests) { @@ -80,14 +84,22 @@ class IntegrationTestDependencies( .copy(features = setOf("test-util"), scope = DependencyScope.Dev) val smithyAsync = CargoDependency.smithyAsync(codegenContext.runtimeConfig) .copy(features = setOf("test-util"), scope = DependencyScope.Dev) + val smithyTypes = CargoDependency.smithyTypes(codegenContext.runtimeConfig) + .copy(features = setOf("test-util"), scope = DependencyScope.Dev) addDependency(smithyClient) addDependency(smithyAsync) + addDependency(smithyTypes) addDependency(CargoDependency.smithyProtocolTestHelpers(codegenContext.runtimeConfig)) addDependency(SerdeJson) addDependency(Tokio) addDependency(FuturesUtil) addDependency(Tracing.toDevDependency()) addDependency(TracingSubscriber) + + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + addDependency(smithyRuntime(runtimeConfig).copy(features = setOf("test-util"), scope = DependencyScope.Dev)) + addDependency(smithyRuntimeApi(runtimeConfig).copy(features = setOf("test-util"), scope = DependencyScope.Dev)) + } } if (hasBenches) { addDependency(Criterion) @@ -103,6 +115,7 @@ class IntegrationTestDependencies( private fun serviceSpecificCustomizations(): List = when (moduleName) { "transcribestreaming" -> listOf(TranscribeTestDependencies()) "s3" -> listOf(S3TestDependencies(codegenContext)) + "dynamodb" -> listOf(DynamoDbTestDependencies()) else -> emptyList() } } @@ -116,6 +129,13 @@ class TranscribeTestDependencies : LibRsCustomization() { } } +class DynamoDbTestDependencies : LibRsCustomization() { + override fun section(section: LibRsSection): Writable = + writable { + addDependency(Approx) + } +} + class S3TestDependencies(private val codegenContext: ClientCodegenContext) : LibRsCustomization() { override fun section(section: LibRsSection): Writable = writable { diff --git a/aws/sdk/integration-tests/dynamodb/Cargo.toml b/aws/sdk/integration-tests/dynamodb/Cargo.toml index 7801137286..3cd3a98b49 100644 --- a/aws/sdk/integration-tests/dynamodb/Cargo.toml +++ b/aws/sdk/integration-tests/dynamodb/Cargo.toml @@ -11,14 +11,18 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +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-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async" } aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } -aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test" } -aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async" } +aws-smithy-runtime = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime", features = ["test-util"]} +aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["test-util"]} +aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types", features = ["test-util"]} aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } bytes = "1.0.0" criterion = { version = "0.4.0" } @@ -26,8 +30,8 @@ futures-util = { version = "0.3.16", default-features = false } http = "0.2.0" serde_json = "1.0.0" tokio = { version = "1.23.1", features = ["full", "test-util"] } -tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } tokio-stream = "0.1.5" +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } [[bench]] name = "deserialization_bench" diff --git a/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs b/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs new file mode 100644 index 0000000000..d3f05db865 --- /dev/null +++ b/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs @@ -0,0 +1,173 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(aws_sdk_orchestrator_mode)] +mod test { + use aws_sdk_dynamodb::config::{Credentials, Region, SharedAsyncSleep}; + use aws_sdk_dynamodb::{config::retry::RetryConfig, error::ProvideErrorMetadata}; + use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::test_util::instant_time_and_sleep; + use aws_smithy_async::time::SharedTimeSource; + use aws_smithy_async::time::SystemTimeSource; + use aws_smithy_client::test_connection::TestConnection; + use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime::client::retries::RetryPartition; + use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; + use aws_smithy_types::timeout::TimeoutConfigBuilder; + use std::time::{Duration, Instant, SystemTime}; + + fn req() -> HttpRequest { + http::Request::builder() + .body(SdkBody::from("request body")) + .unwrap() + } + + fn ok() -> HttpResponse { + http::Response::builder() + .status(200) + .header("server", "Server") + .header("content-type", "application/x-amz-json-1.0") + .header("content-length", "23") + .header("connection", "keep-alive") + .header("x-amz-crc32", "2335643545") + .body(SdkBody::from("{ \"TableNames\": [ \"Test\" ] }")) + .unwrap() + } + + fn err() -> HttpResponse { + http::Response::builder() + .status(500) + .body(SdkBody::from("{ \"message\": \"The request has failed because of an unknown error, exception or failure.\", \"code\": \"InternalServerError\" }")) + .unwrap() + } + + fn throttling_err() -> HttpResponse { + http::Response::builder() + .status(400) + .body(SdkBody::from("{ \"message\": \"The request was denied due to request throttling.\", \"code\": \"ThrottlingException\" }")) + .unwrap() + } + + #[tokio::test] + async fn test_adaptive_retries_with_no_throttling_errors() { + let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); + + let events = vec![ + // First operation + (req(), err()), + (req(), err()), + (req(), ok()), + // Second operation + (req(), err()), + (req(), ok()), + // Third operation will fail, only errors + (req(), err()), + (req(), err()), + (req(), err()), + (req(), err()), + ]; + + let conn = TestConnection::new(events); + let config = aws_sdk_dynamodb::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .retry_config( + RetryConfig::adaptive() + .with_max_attempts(4) + .with_use_static_exponential_base(true), + ) + .time_source(SharedTimeSource::new(time_source)) + .sleep_impl(SharedAsyncSleep::new(sleep_impl.clone())) + .retry_partition(RetryPartition::new( + "test_adaptive_retries_with_no_throttling_errors", + )) + .http_connector(conn.clone()) + .build(); + let expected_table_names = vec!["Test".to_owned()]; + + // We create a new client each time to ensure that the cross-client retry state is working. + let client = aws_sdk_dynamodb::Client::from_conf(config.clone()); + let res = client.list_tables().send().await.unwrap(); + assert_eq!(sleep_impl.total_duration(), Duration::from_secs(3)); + assert_eq!(res.table_names(), Some(expected_table_names.as_slice())); + // Three requests should have been made, two failing & one success + assert_eq!(conn.requests().len(), 3); + + let client = aws_sdk_dynamodb::Client::from_conf(config.clone()); + let res = client.list_tables().send().await.unwrap(); + assert_eq!(sleep_impl.total_duration(), Duration::from_secs(3 + 1)); + assert_eq!(res.table_names(), Some(expected_table_names.as_slice())); + // Two requests should have been made, one failing & one success (plus previous requests) + assert_eq!(conn.requests().len(), 5); + + let client = aws_sdk_dynamodb::Client::from_conf(config); + let err = client.list_tables().send().await.unwrap_err(); + assert_eq!(sleep_impl.total_duration(), Duration::from_secs(3 + 1 + 7),); + assert_eq!(err.code(), Some("InternalServerError")); + // four requests should have been made, all failing (plus previous requests) + assert_eq!(conn.requests().len(), 9); + } + + #[tokio::test] + async fn test_adaptive_retries_with_throttling_errors_times_out() { + tracing_subscriber::fmt::init(); + let events = vec![ + // First operation + (req(), err()), + (req(), ok()), + // Second operation + (req(), err()), + (req(), throttling_err()), + (req(), ok()), + ]; + + let conn = TestConnection::new(events); + let config = aws_sdk_dynamodb::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .retry_config( + RetryConfig::adaptive() + .with_max_attempts(4) + .with_initial_backoff(Duration::from_millis(50)) + .with_use_static_exponential_base(true), + ) + .timeout_config( + TimeoutConfigBuilder::new() + .operation_attempt_timeout(Duration::from_millis(100)) + .build(), + ) + .time_source(SharedTimeSource::new(SystemTimeSource::new())) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) + .http_connector(conn.clone()) + .retry_partition(RetryPartition::new( + "test_adaptive_retries_with_throttling_errors_times_out", + )) + .build(); + + let expected_table_names = vec!["Test".to_owned()]; + let start = Instant::now(); + + // We create a new client each time to ensure that the cross-client retry state is working. + let client = aws_sdk_dynamodb::Client::from_conf(config.clone()); + let res = client.list_tables().send().await.unwrap(); + assert_eq!(res.table_names(), Some(expected_table_names.as_slice())); + // Three requests should have been made, two failing & one success + assert_eq!(conn.requests().len(), 2); + + let client = aws_sdk_dynamodb::Client::from_conf(config); + let err = client.list_tables().send().await.unwrap_err(); + assert_eq!(err.to_string(), "request has timed out".to_owned()); + // two requests should have been made, both failing (plus previous requests) + assert_eq!(conn.requests().len(), 2 + 2); + + let since = start.elapsed(); + // At least 300 milliseconds must pass: + // - 50ms for the first retry on attempt 1 + // - 50ms for the second retry on attempt 3 + // - 100ms for the throttling delay triggered by attempt 4, which required a delay longer than the attempt timeout. + // - 100ms for the 5th attempt, which would have succeeded, but required a delay longer than the attempt timeout. + assert!(since.as_secs_f64() > 0.3); + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 04be1fca91..07ece3fb78 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -7,8 +7,12 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations 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.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection 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.core.rustlang.Attribute +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.RuntimeConfig @@ -16,12 +20,14 @@ 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.smithy.RustCrate -class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : ConfigCustomization() { +class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenContext) : ConfigCustomization() { private val runtimeConfig = codegenContext.runtimeConfig private val runtimeMode = codegenContext.smithyRuntimeMode private val retryConfig = RuntimeType.smithyTypes(runtimeConfig).resolve("retry") private val sleepModule = RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep") private val timeoutModule = RuntimeType.smithyTypes(runtimeConfig).resolve("timeout") + private val smithyRuntimeCrate = RuntimeType.smithyRuntime(runtimeConfig) + private val retries = smithyRuntimeCrate.resolve("client::retries") private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( *preludeScope, @@ -29,8 +35,17 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf "RetryConfig" to retryConfig.resolve("RetryConfig"), "SharedAsyncSleep" to sleepModule.resolve("SharedAsyncSleep"), "Sleep" to sleepModule.resolve("Sleep"), - "StandardRetryStrategy" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::retries::strategy::StandardRetryStrategy"), + "StandardRetryStrategy" to retries.resolve("strategy::StandardRetryStrategy"), + "SystemTime" to RuntimeType.std.resolve("time::SystemTime"), "TimeoutConfig" to timeoutModule.resolve("TimeoutConfig"), + "RetryMode" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::RetryMode"), + "TokenBucket" to retries.resolve("TokenBucket"), + "ClientRateLimiter" to retries.resolve("ClientRateLimiter"), + "SharedTimeSource" to RuntimeType.smithyAsync(runtimeConfig).resolve("time::SharedTimeSource"), + "ClientRateLimiterPartition" to retries.resolve("ClientRateLimiterPartition"), + "TokenBucketPartition" to retries.resolve("TokenBucketPartition"), + "RetryPartition" to retries.resolve("RetryPartition"), + "debug" to RuntimeType.Tracing.resolve("debug"), ) override fun section(section: ServiceConfig) = @@ -67,6 +82,15 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf pub fn timeout_config(&self) -> #{Option}<&#{TimeoutConfig}> { self.inner.load::<#{TimeoutConfig}>() } + + ##[doc(hidden)] + /// Returns a reference to the retry partition contained in this config, if any. + /// + /// WARNING: This method is unstable and may be removed at any time. Do not rely on this + /// method for anything! + pub fn retry_partition(&self) -> #{Option}<&#{RetryPartition}> { + self.inner.load::<#{RetryPartition}>() + } """, *codegenScope, ) @@ -311,13 +335,67 @@ class ResiliencyConfigCustomization(codegenContext: ClientCodegenContext) : Conf *codegenScope, ) } + + if (runtimeMode.defaultToOrchestrator) { + Attribute.DocHidden.render(this) + rustTemplate( + """ + /// Set the partition for retry-related state. When clients share a retry partition, they will + /// also share things like token buckets and client rate limiters. By default, all clients + /// for the same service will share a partition. + pub fn retry_partition(mut self, retry_partition: #{RetryPartition}) -> Self { + self.set_retry_partition(Some(retry_partition)); + self + } + """, + *codegenScope, + ) + + Attribute.DocHidden.render(this) + rustTemplate( + """ + /// Set the partition for retry-related state. When clients share a retry partition, they will + /// also share things like token buckets and client rate limiters. By default, all clients + /// for the same service will share a partition. + pub fn set_retry_partition(&mut self, retry_partition: #{Option}<#{RetryPartition}>) -> &mut Self { + retry_partition.map(|r| self.inner.store_put(r)); + self + } + """, + *codegenScope, + ) + } } ServiceConfig.BuilderBuild -> { if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ + let retry_partition = layer.load::<#{RetryPartition}>().cloned().unwrap_or_else(|| #{RetryPartition}::new("${codegenContext.serviceShape.id.name}")); let retry_config = layer.load::<#{RetryConfig}>().cloned().unwrap_or_else(#{RetryConfig}::disabled); + if retry_config.has_retry() { + #{debug}!("creating retry strategy with partition '{}'", retry_partition); + } + + if retry_config.mode() == #{RetryMode}::Adaptive { + if let Some(time_source) = layer.load::<#{SharedTimeSource}>().cloned() { + let seconds_since_unix_epoch = time_source + .now() + .duration_since(#{SystemTime}::UNIX_EPOCH) + .expect("the present takes place after the UNIX_EPOCH") + .as_secs_f64(); + let client_rate_limiter_partition = #{ClientRateLimiterPartition}::new(retry_partition.clone()); + let client_rate_limiter = CLIENT_RATE_LIMITER.get_or_init(client_rate_limiter_partition, || { + #{ClientRateLimiter}::new(seconds_since_unix_epoch) + }); + layer.store_put(client_rate_limiter); + } + } + + // The token bucket is used for both standard AND adaptive retries. + let token_bucket_partition = #{TokenBucketPartition}::new(retry_partition); + let token_bucket = TOKEN_BUCKET.get_or_init(token_bucket_partition, #{TokenBucket}::default); + layer.store_put(token_bucket); layer.set_retry_strategy(#{DynRetryStrategy}::new(#{StandardRetryStrategy}::new(&retry_config))); """, *codegenScope, @@ -355,6 +433,10 @@ class ResiliencyReExportCustomization(private val runtimeConfig: RuntimeConfig) "pub use #{types_retry}::{RetryConfig, RetryConfigBuilder, RetryMode};", "types_retry" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry"), ) + rustTemplate( + "pub use #{RetryPartition};", + "RetryPartition" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::retries::RetryPartition"), + ) } rustCrate.withModule(ClientRustModule.Config.timeout) { rustTemplate( @@ -364,3 +446,33 @@ class ResiliencyReExportCustomization(private val runtimeConfig: RuntimeConfig) } } } + +class ResiliencyServiceRuntimePluginCustomization(codegenContext: ClientCodegenContext) : ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + private val smithyRuntimeCrate = RuntimeType.smithyRuntime(runtimeConfig) + private val retries = smithyRuntimeCrate.resolve("client::retries") + private val codegenScope = arrayOf( + "TokenBucket" to retries.resolve("TokenBucket"), + "TokenBucketPartition" to retries.resolve("TokenBucketPartition"), + "ClientRateLimiter" to retries.resolve("ClientRateLimiter"), + "ClientRateLimiterPartition" to retries.resolve("ClientRateLimiterPartition"), + "StaticPartitionMap" to smithyRuntimeCrate.resolve("static_partition_map::StaticPartitionMap"), + ) + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + when (section) { + is ServiceRuntimePluginSection.DeclareSingletons -> { + // TODO(enableNewSmithyRuntimeCleanup) We can use the standard library's `OnceCell` once we upgrade the + // MSRV to 1.70 + rustTemplate( + """ + static TOKEN_BUCKET: #{StaticPartitionMap}<#{TokenBucketPartition}, #{TokenBucket}> = #{StaticPartitionMap}::new(); + static CLIENT_RATE_LIMITER: #{StaticPartitionMap}<#{ClientRateLimiterPartition}, #{ClientRateLimiter}> = #{StaticPartitionMap}::new(); + """, + *codegenScope, + ) + } + else -> emptySection + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index fd1aacc689..4b85a6bb53 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -16,9 +16,11 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.Identity import software.amazon.smithy.rust.codegen.client.smithy.customizations.InterceptorConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization +import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.TimeSourceCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.TimeSourceOperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.smithy.RustCrate @@ -95,4 +97,13 @@ class RequiredCustomizations : ClientCodegenDecorator { } } } + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + baseCustomizations + ResiliencyServiceRuntimePluginCustomization(codegenContext) + } else { + baseCustomizations + } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index c8f0457b65..bf47e9f41c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -21,6 +21,13 @@ import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizat import software.amazon.smithy.rust.codegen.core.util.dq sealed class ServiceRuntimePluginSection(name: String) : Section(name) { + /** + * Hook for declaring singletons that store cross-operation state. + * + * Examples include token buckets, ID generators, etc. + */ + class DeclareSingletons : ServiceRuntimePluginSection("DeclareSingletons") + /** * Hook for adding additional things to config inside service runtime plugins. */ @@ -128,6 +135,9 @@ class ServiceRuntimePluginGenerator( #{additional_interceptors} } } + + /// Cross-operation shared-state singletons + #{declare_singletons} """, *codegenScope, "config" to writable { @@ -154,6 +164,9 @@ class ServiceRuntimePluginGenerator( "additional_interceptors" to writable { writeCustomizations(customizations, ServiceRuntimePluginSection.RegisterInterceptor("_interceptors")) }, + "declare_singletons" to writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.DeclareSingletons()) + }, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt index c4af5f97fb..fdb92dd3f7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt @@ -67,6 +67,7 @@ open class MakeOperationGenerator( "OpBuildError" to runtimeConfig.operationBuildError(), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), "SharedPropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::SharedPropertyBag"), + "RetryMode" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::RetryMode"), ) fun generateMakeOperation( @@ -98,6 +99,12 @@ open class MakeOperationGenerator( "$fnType $functionName($self, _config: &#{config}::Config) -> $returnType", *codegenScope, ) { + rustTemplate( + """ + assert_ne!(_config.retry_config().map(|rc| rc.mode()), #{Option}::Some(#{RetryMode}::Adaptive), "Adaptive retry mode is unsupported, please use Standard mode or disable retries."); + """, + *codegenScope, + ) writeCustomizations(customizations, OperationSection.MutateInput(customizations, "self", "_config")) withBlock("let mut request = {", "};") { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index 9187f35e6e..d850b2cc26 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -233,6 +233,7 @@ data class CargoDependency( val Tracing: CargoDependency = CargoDependency("tracing", CratesIo("0.1")) // Test-only dependencies + val Approx: CargoDependency = CargoDependency("approx", CratesIo("0.5.1"), DependencyScope.Dev) val AsyncStd: CargoDependency = CargoDependency("async-std", CratesIo("1.12.0"), DependencyScope.Dev) val AsyncStream: CargoDependency = CargoDependency("async-stream", CratesIo("0.3.0"), DependencyScope.Dev) val Criterion: CargoDependency = CargoDependency("criterion", CratesIo("0.4.0"), DependencyScope.Dev) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt index 669279c8d5..c7e6023f3c 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/testutil/PythonServerTestHelpers.kt @@ -41,7 +41,7 @@ fun executePythonServerCodegenVisitor(pluginCtx: PluginContext) { fun cargoTest(workdir: Path) = // `--no-default-features` is required to disable `pyo3/extension-module` which causes linking errors // see `PyO3ExtensionModuleDecorator`'s comments fore more detail. - "cargo test --no-default-features".runCommand( + "cargo test --no-default-features --no-fail-fast".runCommand( workdir, mapOf( // Those are required to run tests on macOS, see: https://pyo3.rs/main/building_and_distribution#macos diff --git a/rust-runtime/aws-smithy-async/src/test_util.rs b/rust-runtime/aws-smithy-async/src/test_util.rs index 8c867869e8..fa1dfe300b 100644 --- a/rust-runtime/aws-smithy-async/src/test_util.rs +++ b/rust-runtime/aws-smithy-async/src/test_util.rs @@ -22,6 +22,21 @@ pub struct ManualTimeSource { log: Arc>>, } +#[cfg(feature = "test-util")] +impl ManualTimeSource { + /// Get the number of seconds since the UNIX Epoch as an f64. + /// + /// ## Panics + /// + /// This will panic if `self.now()` returns a time that's before the UNIX Epoch. + pub fn seconds_since_unix_epoch(&self) -> f64 { + self.now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs_f64() + } +} + impl TimeSource for ManualTimeSource { fn now(&self) -> SystemTime { self.start_time + self.log.lock().unwrap().iter().sum::() diff --git a/rust-runtime/aws-smithy-eventstream/src/frame.rs b/rust-runtime/aws-smithy-eventstream/src/frame.rs index 5983d09c80..202e410827 100644 --- a/rust-runtime/aws-smithy-eventstream/src/frame.rs +++ b/rust-runtime/aws-smithy-eventstream/src/frame.rs @@ -962,24 +962,20 @@ mod deferred_signer_tests { impl SignMessage for TestSigner { fn sign( &mut self, - message: crate::frame::Message, - ) -> Result { + message: Message, + ) -> Result { self.call_num += 1; Ok(message.add_header(Header::new("call_num", HeaderValue::Int32(self.call_num)))) } - fn sign_empty( - &mut self, - ) -> Option> { + fn sign_empty(&mut self) -> Option> { None } } let (mut signer, sender) = check_send_sync(DeferredSigner::new()); - sender - .send(Box::new(TestSigner::default())) - .expect("success"); + sender.send(Box::::default()).expect("success"); let message = signer.sign(Message::new(Bytes::new())).expect("success"); assert_eq!(1, message.headers()[0].value().as_int32().unwrap()); diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index 25443d86d2..dd918faba0 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -15,7 +15,6 @@ use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplac use aws_smithy_types::error::display::DisplayErrorContext; use context::{Error, Input, Output}; use std::fmt; -use std::fmt::{Debug, Formatter}; use std::marker::PhantomData; use std::ops::Deref; use std::sync::Arc; @@ -54,7 +53,7 @@ macro_rules! interceptor_trait_fn { /// of the SDK ’s request execution pipeline. Hooks are either "read" hooks, which make it possible /// to read in-flight request or response messages, or "read/write" hooks, which make it possible /// to modify in-flight request or output messages. -pub trait Interceptor: std::fmt::Debug { +pub trait Interceptor: fmt::Debug { interceptor_trait_fn!( read_before_execution, BeforeSerializationInterceptorContextRef, @@ -584,8 +583,8 @@ pub struct SharedInterceptor { check_enabled: Arc bool + Send + Sync>, } -impl Debug for SharedInterceptor { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for SharedInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SharedInterceptor") .field("interceptor", &self.interceptor) .finish() @@ -966,7 +965,7 @@ mod tests { 2 ); interceptors - .read_before_transmit(&mut InterceptorContext::new(Input::new(5)), &mut cfg) + .read_before_transmit(&InterceptorContext::new(Input::new(5)), &mut cfg) .expect_err("interceptor returns error"); cfg.interceptor_state() .store_put(disable_interceptor::("test")); @@ -979,7 +978,7 @@ mod tests { ); // shouldn't error because interceptors won't run interceptors - .read_before_transmit(&mut InterceptorContext::new(Input::new(5)), &mut cfg) + .read_before_transmit(&InterceptorContext::new(Input::new(5)), &mut cfg) .expect("interceptor is now disabled"); } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs b/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs index 1eb115d480..440019c660 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs @@ -7,26 +7,26 @@ use aws_smithy_types::config_bag::{Storable, StoreReplace}; #[derive(Debug, Clone, Copy)] pub struct RequestAttempts { - attempts: usize, + attempts: u32, } impl RequestAttempts { #[cfg(any(feature = "test-util", test))] - pub fn new(attempts: usize) -> Self { + pub fn new(attempts: u32) -> Self { Self { attempts } } - pub fn attempts(&self) -> usize { + pub fn attempts(&self) -> u32 { self.attempts } } -impl Storable for RequestAttempts { - type Storer = StoreReplace; -} - -impl From for RequestAttempts { - fn from(attempts: usize) -> Self { +impl From for RequestAttempts { + fn from(attempts: u32) -> Self { Self { attempts } } } + +impl Storable for RequestAttempts { + type Storer = StoreReplace; +} diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 281e4245f9..d63a748b5c 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -28,6 +28,7 @@ pin-utils = "0.1.0" tokio = { version = "1.25", features = [] } tracing = "0.1.37" fastrand = "1.4" +once_cell = "1.18.0" [dev-dependencies] approx = "0.5.1" diff --git a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs b/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs index 8c00beae6c..7db7ba541c 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs @@ -5,7 +5,7 @@ //! Module with client connectors useful for testing. -use aws_smithy_async::rt::sleep::AsyncSleep; +use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; @@ -191,16 +191,16 @@ impl ValidateRequest { pub struct TestConnection { data: Arc>, requests: Arc>>, - sleep_impl: Arc, + sleep_impl: SharedAsyncSleep, } impl TestConnection { - pub fn new(mut data: ConnectionEvents, sleep_impl: Arc) -> Self { + pub fn new(mut data: ConnectionEvents, sleep_impl: impl Into) -> Self { data.reverse(); TestConnection { data: Arc::new(Mutex::new(data)), requests: Default::default(), - sleep_impl, + sleep_impl: sleep_impl.into(), } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index c38b382045..bd9ae34d5f 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -28,7 +28,7 @@ use aws_smithy_runtime_api::client::retries::ShouldAttempt; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; use aws_smithy_types::config_bag::ConfigBag; use std::mem; -use tracing::{debug, debug_span, instrument, Instrument}; +use tracing::{debug, debug_span, instrument, trace, Instrument}; mod auth; /// Defines types that implement a trait for endpoint resolution @@ -174,7 +174,7 @@ async fn try_op( .unwrap_or(Ok(ShouldAttempt::Yes)); match should_attempt { // Yes, let's make a request - Ok(ShouldAttempt::Yes) => debug!("retry strategy has OK'd initial request"), + Ok(ShouldAttempt::Yes) => debug!("retry strategy has OKed initial request"), // No, this request shouldn't be sent Ok(ShouldAttempt::No) => { let err: BoxError = "the retry strategy indicates that an initial request shouldn't be made, but it didn't specify why".into(); @@ -182,15 +182,20 @@ async fn try_op( } // No, we shouldn't make a request because... Err(err) => halt!([ctx] => OrchestratorError::other(err)), - Ok(ShouldAttempt::YesAfterDelay(_)) => { - unreachable!("Delaying the initial request is currently unsupported. If this feature is important to you, please file an issue in GitHub.") + Ok(ShouldAttempt::YesAfterDelay(delay)) => { + let sleep_impl = halt_on_err!([ctx] => cfg.sleep_impl().ok_or(OrchestratorError::other( + "the retry strategy requested a delay before sending the initial request, but no 'async sleep' implementation was set" + ))); + debug!("retry strategy has OKed initial request after a {delay:?} delay"); + sleep_impl.sleep(delay).await; } } // Save a request checkpoint before we make the request. This will allow us to "rewind" // the request in the case of retry attempts. ctx.save_checkpoint(); - for i in 1usize.. { + let mut retry_delay = None; + for i in 1u32.. { debug!("beginning attempt #{i}"); // Break from the loop if we can't rewind the request's state. This will always succeed the // first time, but will fail on subsequent iterations if the request body wasn't retryable. @@ -203,6 +208,11 @@ async fn try_op( .store_put::(i.into()); let attempt_timeout_config = cfg.maybe_timeout_config(TimeoutKind::OperationAttempt); let maybe_timeout = async { + // We must await this here or else timeouts won't work as expected + if let Some(delay) = retry_delay.take() { + delay.await; + } + try_attempt(ctx, cfg, interceptors, stop_point).await; finally_attempt(ctx, cfg, interceptors).await; Result::<_, SdkError>::Ok(()) @@ -229,14 +239,14 @@ async fn try_op( ShouldAttempt::Yes => continue, // No, this request shouldn't be retried ShouldAttempt::No => { - debug!("this error is not retryable, exiting attempt loop"); + debug!("a retry is either unnecessary or not possible, exiting attempt loop"); break; } ShouldAttempt::YesAfterDelay(delay) => { let sleep_impl = halt_on_err!([ctx] => cfg.sleep_impl().ok_or(OrchestratorError::other( - "the retry strategy requested a delay before sending the next request, but no 'async sleep' implementation was set" + "the retry strategy requested a delay before sending the retry request, but no 'async sleep' implementation was set" ))); - sleep_impl.sleep(delay).await; + retry_delay = Some(sleep_impl.sleep(delay)); continue; } } @@ -278,6 +288,7 @@ async fn try_attempt( } }) }); + trace!(response = ?call_result, "received response from service"); ctx.set_response(call_result); ctx.enter_before_deserialization_phase(); diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries.rs b/rust-runtime/aws-smithy-runtime/src/client/retries.rs index 9c41e5366a..893c5f0163 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries.rs @@ -9,5 +9,35 @@ pub mod strategy; mod client_rate_limiter; mod token_bucket; -pub use client_rate_limiter::ClientRateLimiterRuntimePlugin; -pub use token_bucket::TokenBucketRuntimePlugin; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; +pub use client_rate_limiter::{ClientRateLimiter, ClientRateLimiterRuntimePlugin}; +use std::fmt; +pub use token_bucket::{TokenBucket, TokenBucketRuntimePlugin}; + +#[doc(hidden)] +pub use client_rate_limiter::ClientRateLimiterPartition; +#[doc(hidden)] +pub use token_bucket::TokenBucketPartition; + +#[doc(hidden)] +#[non_exhaustive] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct RetryPartition { + inner: &'static str, +} + +impl RetryPartition { + pub fn new(name: &'static str) -> Self { + Self { inner: name } + } +} + +impl fmt::Display for RetryPartition { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.inner) + } +} + +impl Storable for RetryPartition { + type Storer = StoreReplace; +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs index 6b41f0fddd..69f36272ab 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs @@ -6,43 +6,57 @@ //! A rate limiter for controlling the rate at which AWS requests are made. The rate changes based //! on the number of throttling errors encountered. -// TODO(enableNewSmithyRuntimeLaunch): Zelda will integrate this rate limiter into the retry policy in a separate PR. #![allow(dead_code)] -use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use crate::client::retries::RetryPartition; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_runtime_api::{builder, builder_methods, builder_struct}; -use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer, Storable, StoreReplace}; +use aws_smithy_types::config_bag::{FrozenLayer, Layer, Storable, StoreReplace}; use std::sync::{Arc, Mutex}; -use std::time::SystemTime; +use std::time::Duration; +use tracing::debug; /// A [RuntimePlugin] to provide a client rate limiter, usable by a retry strategy. #[non_exhaustive] #[derive(Debug)] pub struct ClientRateLimiterRuntimePlugin { - _rate_limiter: Arc>, + rate_limiter: ClientRateLimiter, } impl ClientRateLimiterRuntimePlugin { - pub fn new(cfg: &ConfigBag) -> Self { + pub fn new(seconds_since_unix_epoch: f64) -> Self { Self { - _rate_limiter: Arc::new(Mutex::new(ClientRateLimiter::new(cfg))), + rate_limiter: ClientRateLimiter::new(seconds_since_unix_epoch), } } } impl RuntimePlugin for ClientRateLimiterRuntimePlugin { fn config(&self) -> Option { - let cfg = Layer::new("client rate limiter"); - // TODO(enableNewSmithyRuntimeLaunch) Move the Arc/Mutex inside the rate limiter so that it - // be both storable and cloneable. - // cfg.store_put(self.rate_limiter.clone()); + let mut cfg = Layer::new("client rate limiter"); + cfg.store_put(self.rate_limiter.clone()); Some(cfg.freeze()) } } +#[doc(hidden)] +#[non_exhaustive] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct ClientRateLimiterPartition { + retry_partition: RetryPartition, +} + +impl ClientRateLimiterPartition { + pub fn new(retry_partition: RetryPartition) -> Self { + Self { retry_partition } + } +} + +const RETRY_COST: f64 = 5.0; +const RETRY_TIMEOUT_COST: f64 = RETRY_COST * 2.0; +const INITIAL_REQUEST_COST: f64 = 1.0; + const MIN_FILL_RATE: f64 = 0.5; const MIN_CAPACITY: f64 = 1.0; const SMOOTH: f64 = 0.8; @@ -52,32 +66,40 @@ const BETA: f64 = 0.7; const SCALE_CONSTANT: f64 = 0.4; #[derive(Clone, Debug)] -pub(crate) struct ClientRateLimiter { +pub struct ClientRateLimiter { + inner: Arc>, +} + +#[derive(Debug)] +pub(crate) struct Inner { /// The rate at which token are replenished. - token_refill_rate: f64, + fill_rate: f64, /// The maximum capacity allowed in the token bucket. - maximum_bucket_capacity: f64, + max_capacity: f64, /// The current capacity of the token bucket. - /// The minimum this can be is 1.0 - current_bucket_capacity: f64, + current_capacity: f64, /// The last time the token bucket was refilled. - time_of_last_refill: Option, + last_timestamp: Option, + /// Boolean indicating if the token bucket is enabled. + /// The token bucket is initially disabled. + /// When a throttling error is encountered it is enabled. + enabled: bool, /// The smoothed rate which tokens are being retrieved. - tokens_retrieved_per_second: f64, + measured_tx_rate: f64, /// The last half second time bucket used. - previous_time_bucket: f64, + last_tx_rate_bucket: f64, /// The number of requests seen within the current time bucket. request_count: u64, - /// Boolean indicating if the token bucket is enabled. - /// The token bucket is initially disabled. - /// When a throttling error is encountered it is enabled. - enable_throttling: bool, /// The maximum rate when the client was last throttled. - tokens_retrieved_per_second_at_time_of_last_throttle: f64, + last_max_rate: f64, /// The last time when the client was throttled. time_of_last_throttle: f64, - time_window: f64, - calculated_rate: f64, +} + +pub(crate) enum RequestReason { + Retry, + RetryTimeout, + InitialRequest, } impl Storable for ClientRateLimiter { @@ -85,10 +107,11 @@ impl Storable for ClientRateLimiter { } impl ClientRateLimiter { - pub(crate) fn new(cfg: &ConfigBag) -> Self { + pub fn new(seconds_since_unix_epoch: f64) -> Self { Self::builder() - .time_of_last_throttle(get_unix_timestamp(cfg)) - .previous_time_bucket(get_unix_timestamp(cfg).floor()) + .tokens_retrieved_per_second(MIN_FILL_RATE) + .time_of_last_throttle(seconds_since_unix_epoch) + .previous_time_bucket(seconds_since_unix_epoch.floor()) .build() } @@ -96,108 +119,139 @@ impl ClientRateLimiter { Builder::new() } - /// If this function returns `Ok(())`, you're OK to send a request. If it returns an error, - /// then you should not send a request; You've sent quite enough already. pub(crate) fn acquire_permission_to_send_a_request( - &mut self, + &self, seconds_since_unix_epoch: f64, - amount: f64, - ) -> Result<(), BoxError> { - if !self.enable_throttling { + kind: RequestReason, + ) -> Result<(), Duration> { + let mut it = self.inner.lock().unwrap(); + + if !it.enabled { // return early if we haven't encountered a throttling error yet return Ok(()); } + let amount = match kind { + RequestReason::Retry => RETRY_COST, + RequestReason::RetryTimeout => RETRY_TIMEOUT_COST, + RequestReason::InitialRequest => INITIAL_REQUEST_COST, + }; + + it.refill(seconds_since_unix_epoch); + + let res = if amount > it.current_capacity { + let sleep_time = (amount - it.current_capacity) / it.fill_rate; + debug!( + amount, + it.current_capacity, + it.fill_rate, + sleep_time, + "client rate limiter delayed a request" + ); - self.refill(seconds_since_unix_epoch); - - if self.current_bucket_capacity < amount { - Err(BoxError::from("the client rate limiter is out of tokens")) + Err(Duration::from_secs_f64(sleep_time)) } else { - self.current_bucket_capacity -= amount; Ok(()) - } + }; + + it.current_capacity -= amount; + res } pub(crate) fn update_rate_limiter( - &mut self, + &self, seconds_since_unix_epoch: f64, is_throttling_error: bool, ) { - self.update_tokens_retrieved_per_second(seconds_since_unix_epoch); + let mut it = self.inner.lock().unwrap(); + it.update_tokens_retrieved_per_second(seconds_since_unix_epoch); + let calculated_rate; if is_throttling_error { - let rate_to_use = if self.enable_throttling { - f64::min(self.tokens_retrieved_per_second, self.token_refill_rate) + let rate_to_use = if it.enabled { + f64::min(it.measured_tx_rate, it.fill_rate) } else { - self.tokens_retrieved_per_second + it.measured_tx_rate }; // The fill_rate is from the token bucket - self.tokens_retrieved_per_second_at_time_of_last_throttle = rate_to_use; - self.calculate_time_window(); - self.time_of_last_throttle = seconds_since_unix_epoch; - self.calculated_rate = cubic_throttle(rate_to_use); - self.enable_token_bucket(); + it.last_max_rate = rate_to_use; + it.calculate_time_window(); + it.time_of_last_throttle = seconds_since_unix_epoch; + calculated_rate = cubic_throttle(rate_to_use); + it.enable_token_bucket(); } else { - self.calculate_time_window(); - self.calculated_rate = self.cubic_success(seconds_since_unix_epoch); + it.calculate_time_window(); + calculated_rate = it.cubic_success(seconds_since_unix_epoch); } - let new_rate = f64::min(self.calculated_rate, 2.0 * self.tokens_retrieved_per_second); - self.update_bucket_refill_rate(seconds_since_unix_epoch, new_rate); + let new_rate = f64::min(calculated_rate, 2.0 * it.measured_tx_rate); + it.update_bucket_refill_rate(seconds_since_unix_epoch, new_rate); } +} +impl Inner { fn refill(&mut self, seconds_since_unix_epoch: f64) { - if let Some(last_timestamp) = self.time_of_last_refill { - let fill_amount = (seconds_since_unix_epoch - last_timestamp) * self.token_refill_rate; - self.current_bucket_capacity = f64::min( - self.maximum_bucket_capacity, - self.current_bucket_capacity + fill_amount, + if let Some(last_timestamp) = self.last_timestamp { + let fill_amount = (seconds_since_unix_epoch - last_timestamp) * self.fill_rate; + self.current_capacity = + f64::min(self.max_capacity, self.current_capacity + fill_amount); + debug!( + fill_amount, + self.current_capacity, self.max_capacity, "refilling client rate limiter tokens" ); } - self.time_of_last_refill = Some(seconds_since_unix_epoch); + self.last_timestamp = Some(seconds_since_unix_epoch); } fn update_bucket_refill_rate(&mut self, seconds_since_unix_epoch: f64, new_fill_rate: f64) { // Refill based on our current rate before we update to the new fill rate. self.refill(seconds_since_unix_epoch); - self.token_refill_rate = f64::max(new_fill_rate, MIN_FILL_RATE); - self.maximum_bucket_capacity = f64::max(new_fill_rate, MIN_CAPACITY); + self.fill_rate = f64::max(new_fill_rate, MIN_FILL_RATE); + self.max_capacity = f64::max(new_fill_rate, MIN_CAPACITY); + + debug!( + fill_rate = self.fill_rate, + max_capacity = self.max_capacity, + current_capacity = self.current_capacity, + measured_tx_rate = self.measured_tx_rate, + "client rate limiter state has been updated" + ); + // When we scale down we can't have a current capacity that exceeds our max_capacity. - self.current_bucket_capacity = - f64::min(self.current_bucket_capacity, self.maximum_bucket_capacity); + self.current_capacity = f64::min(self.current_capacity, self.max_capacity); } fn enable_token_bucket(&mut self) { - self.enable_throttling = true; + // If throttling wasn't already enabled, note that we're now enabling it. + if !self.enabled { + debug!("client rate limiting has been enabled"); + } + self.enabled = true; } fn update_tokens_retrieved_per_second(&mut self, seconds_since_unix_epoch: f64) { let next_time_bucket = (seconds_since_unix_epoch * 2.0).floor() / 2.0; self.request_count += 1; - if next_time_bucket > self.previous_time_bucket { + if next_time_bucket > self.last_tx_rate_bucket { let current_rate = - self.request_count as f64 / (next_time_bucket - self.previous_time_bucket); - self.tokens_retrieved_per_second = - current_rate * SMOOTH + self.tokens_retrieved_per_second * (1.0 - SMOOTH); + self.request_count as f64 / (next_time_bucket - self.last_tx_rate_bucket); + self.measured_tx_rate = current_rate * SMOOTH + self.measured_tx_rate * (1.0 - SMOOTH); self.request_count = 0; - self.previous_time_bucket = next_time_bucket; + self.last_tx_rate_bucket = next_time_bucket; } } - fn calculate_time_window(&mut self) { - // This is broken out into a separate calculation because it only - // gets updated when @tokens_retrieved_per_second_at_time_of_last_throttle() changes so it can be cached. - let base = (self.tokens_retrieved_per_second_at_time_of_last_throttle * (1.0 - BETA)) - / SCALE_CONSTANT; - self.time_window = base.powf(1.0 / 3.0); + fn calculate_time_window(&self) -> f64 { + let base = (self.last_max_rate * (1.0 - BETA)) / SCALE_CONSTANT; + base.powf(1.0 / 3.0) } fn cubic_success(&self, seconds_since_unix_epoch: f64) -> f64 { - let dt = seconds_since_unix_epoch - self.time_of_last_throttle - self.time_window; - (SCALE_CONSTANT * dt.powi(3)) + self.tokens_retrieved_per_second_at_time_of_last_throttle + let dt = + seconds_since_unix_epoch - self.time_of_last_throttle - self.calculate_time_window(); + (SCALE_CONSTANT * dt.powi(3)) + self.last_max_rate } } @@ -205,15 +259,6 @@ fn cubic_throttle(rate_to_use: f64) -> f64 { rate_to_use * BETA } -fn get_unix_timestamp(cfg: &ConfigBag) -> f64 { - let request_time = cfg.request_time().unwrap(); - request_time - .now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs_f64() -} - builder!( set_token_refill_rate, token_refill_rate, f64, "The rate at which token are replenished.", set_maximum_bucket_capacity, maximum_bucket_capacity, f64, "The maximum capacity allowed in the token bucket.", @@ -224,35 +269,34 @@ builder!( set_request_count, request_count, u64, "The number of requests seen within the current time bucket.", set_enable_throttling, enable_throttling, bool, "Boolean indicating if the token bucket is enabled. The token bucket is initially disabled. When a throttling error is encountered it is enabled.", set_tokens_retrieved_per_second_at_time_of_last_throttle, tokens_retrieved_per_second_at_time_of_last_throttle, f64, "The maximum rate when the client was last throttled.", - set_time_of_last_throttle, time_of_last_throttle, f64, "The last time when the client was throttled.", - set_time_window, time_window, f64, "The time window used to calculate the cubic success rate.", - set_calculated_rate, calculated_rate, f64, "The calculated rate used to update the sending rate." + set_time_of_last_throttle, time_of_last_throttle, f64, "The last time when the client was throttled." ); impl Builder { fn build(self) -> ClientRateLimiter { ClientRateLimiter { - token_refill_rate: self.token_refill_rate.unwrap_or_default(), - maximum_bucket_capacity: self.maximum_bucket_capacity.unwrap_or(f64::MAX), - current_bucket_capacity: self.current_bucket_capacity.unwrap_or_default(), - time_of_last_refill: self.time_of_last_refill, - enable_throttling: self.enable_throttling.unwrap_or_default(), - tokens_retrieved_per_second: self.tokens_retrieved_per_second.unwrap_or_default(), - previous_time_bucket: self.previous_time_bucket.unwrap_or_default(), - request_count: self.request_count.unwrap_or_default(), - tokens_retrieved_per_second_at_time_of_last_throttle: self - .tokens_retrieved_per_second_at_time_of_last_throttle - .unwrap_or_default(), - time_of_last_throttle: self.time_of_last_throttle.unwrap_or_default(), - time_window: self.time_window.unwrap_or_default(), - calculated_rate: self.calculated_rate.unwrap_or_default(), + inner: Arc::new(Mutex::new(Inner { + fill_rate: self.token_refill_rate.unwrap_or_default(), + max_capacity: self.maximum_bucket_capacity.unwrap_or(f64::MAX), + current_capacity: self.current_bucket_capacity.unwrap_or_default(), + last_timestamp: self.time_of_last_refill, + enabled: self.enable_throttling.unwrap_or_default(), + measured_tx_rate: self.tokens_retrieved_per_second.unwrap_or_default(), + last_tx_rate_bucket: self.previous_time_bucket.unwrap_or_default(), + request_count: self.request_count.unwrap_or_default(), + last_max_rate: self + .tokens_retrieved_per_second_at_time_of_last_throttle + .unwrap_or_default(), + time_of_last_throttle: self.time_of_last_throttle.unwrap_or_default(), + })), } } } #[cfg(test)] mod tests { - use super::{cubic_throttle, get_unix_timestamp, ClientRateLimiter}; + use super::{cubic_throttle, ClientRateLimiter}; + use crate::client::retries::client_rate_limiter::RequestReason; use approx::assert_relative_eq; use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; use aws_smithy_async::test_util::instant_time_and_sleep; @@ -261,28 +305,21 @@ mod tests { use aws_smithy_types::config_bag::ConfigBag; use std::time::{Duration, SystemTime}; - #[test] - fn it_sets_the_time_window_correctly() { - let mut rate_limiter = ClientRateLimiter::builder() - .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) - .build(); - - rate_limiter.calculate_time_window(); - assert_relative_eq!(rate_limiter.time_window, 1.9574338205844317); - } + const ONE_SECOND: Duration = Duration::from_secs(1); + const TWO_HUNDRED_MILLISECONDS: Duration = Duration::from_millis(200); #[test] fn should_match_beta_decrease() { let new_rate = cubic_throttle(10.0); assert_relative_eq!(new_rate, 7.0); - let mut rate_limiter = ClientRateLimiter::builder() + let rate_limiter = ClientRateLimiter::builder() .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) .time_of_last_throttle(1.0) .build(); - rate_limiter.calculate_time_window(); - let new_rate = rate_limiter.cubic_success(1.0); + rate_limiter.inner.lock().unwrap().calculate_time_window(); + let new_rate = rate_limiter.inner.lock().unwrap().cubic_success(1.0); assert_relative_eq!(new_rate, 7.0); } @@ -294,19 +331,18 @@ mod tests { .set_request_time(SharedTimeSource::new(time_source)); cfg.interceptor_state() .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl))); - let now = get_unix_timestamp(&cfg); - let mut rate_limiter = ClientRateLimiter::builder() - .previous_time_bucket((now).floor()) - .time_of_last_throttle(now) + let rate_limiter = ClientRateLimiter::builder() + .previous_time_bucket(0.0) + .time_of_last_throttle(0.0) .build(); assert!( - !rate_limiter.enable_throttling, + !rate_limiter.inner.lock().unwrap().enabled, "rate_limiter should be disabled by default" ); - rate_limiter.update_rate_limiter(now, true); + rate_limiter.update_rate_limiter(0.0, true); assert!( - rate_limiter.enable_throttling, + rate_limiter.inner.lock().unwrap().enabled, "rate_limiter should be enabled after throttling error" ); } @@ -320,9 +356,8 @@ mod tests { .set_request_time(SharedTimeSource::new(time_source)); cfg.interceptor_state() .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl.clone()))); - let now = get_unix_timestamp(&cfg); - let mut rate_limiter = ClientRateLimiter::builder() - .time_of_last_throttle(now) + let rate_limiter = ClientRateLimiter::builder() + .time_of_last_throttle(5.0) .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) .build(); @@ -366,8 +401,12 @@ mod tests { // was implemented. See for yourself: // https://github.com/aws/aws-sdk-go-v2/blob/844ff45cdc76182229ad098c95bf3f5ab8c20e9f/aws/retry/adaptive_ratelimit_test.go#L97 for attempt in attempts { - rate_limiter.calculate_time_window(); - let calculated_rate = rate_limiter.cubic_success(attempt.seconds_since_unix_epoch); + rate_limiter.inner.lock().unwrap().calculate_time_window(); + let calculated_rate = rate_limiter + .inner + .lock() + .unwrap() + .cubic_success(attempt.seconds_since_unix_epoch); assert_relative_eq!(attempt.expected_calculated_rate, calculated_rate); } @@ -382,10 +421,9 @@ mod tests { .set_request_time(SharedTimeSource::new(time_source)); cfg.interceptor_state() .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl.clone()))); - let now = get_unix_timestamp(&cfg); - let mut rate_limiter = ClientRateLimiter::builder() + let rate_limiter = ClientRateLimiter::builder() .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) - .time_of_last_throttle(now) + .time_of_last_throttle(5.0) .build(); struct Attempt { @@ -442,13 +480,14 @@ mod tests { // https://github.com/aws/aws-sdk-go-v2/blob/844ff45cdc76182229ad098c95bf3f5ab8c20e9f/aws/retry/adaptive_ratelimit_test.go#L97 let mut calculated_rate = 0.0; for attempt in attempts { - rate_limiter.calculate_time_window(); + let mut inner = rate_limiter.inner.lock().unwrap(); + inner.calculate_time_window(); if attempt.throttled { calculated_rate = cubic_throttle(calculated_rate); - rate_limiter.time_of_last_throttle = attempt.seconds_since_unix_epoch; - rate_limiter.tokens_retrieved_per_second_at_time_of_last_throttle = calculated_rate; + inner.time_of_last_throttle = attempt.seconds_since_unix_epoch; + inner.last_max_rate = calculated_rate; } else { - calculated_rate = rate_limiter.cubic_success(attempt.seconds_since_unix_epoch); + calculated_rate = inner.cubic_success(attempt.seconds_since_unix_epoch); }; assert_relative_eq!(attempt.expected_calculated_rate, calculated_rate); @@ -463,7 +502,7 @@ mod tests { .set_request_time(SharedTimeSource::new(time_source)); cfg.interceptor_state() .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl.clone()))); - let mut rate_limiter = ClientRateLimiter::builder().build(); + let rate_limiter = ClientRateLimiter::builder().build(); struct Attempt { throttled: bool, @@ -577,9 +616,8 @@ mod tests { }, ]; - let two_hundred_milliseconds = Duration::from_millis(200); for attempt in attempts { - sleep_impl.sleep(two_hundred_milliseconds).await; + sleep_impl.sleep(TWO_HUNDRED_MILLISECONDS).await; assert_eq!( attempt.seconds_since_unix_epoch, sleep_impl.total_duration().as_secs_f64() @@ -588,12 +626,53 @@ mod tests { rate_limiter.update_rate_limiter(attempt.seconds_since_unix_epoch, attempt.throttled); assert_relative_eq!( attempt.expected_tokens_retrieved_per_second, - rate_limiter.tokens_retrieved_per_second + rate_limiter.inner.lock().unwrap().measured_tx_rate ); assert_relative_eq!( attempt.expected_token_refill_rate, - rate_limiter.token_refill_rate + rate_limiter.inner.lock().unwrap().fill_rate ); } } + + // This test is only testing that we don't fail basic math and panic. It does include an + // element of randomness, but no duration between >= 0.0s and <= 1.0s will ever cause a panic. + // + // Because the cost of sending an individual request is 1.0, and because the minimum capacity is + // also 1.0, we will never encounter a situation where we run out of tokens. + #[tokio::test] + async fn test_when_throttling_is_enabled_requests_can_still_be_sent() { + let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); + let crl = ClientRateLimiter::builder() + .time_of_last_throttle(0.0) + .previous_time_bucket(0.0) + .build(); + + // Start by recording a throttling error + crl.update_rate_limiter(0.0, true); + + for _i in 0..100 { + // advance time by a random amount (up to 1s) each iteration + let duration = Duration::from_secs_f64(fastrand::f64()); + sleep_impl.sleep(duration).await; + if let Err(delay) = crl.acquire_permission_to_send_a_request( + time_source.seconds_since_unix_epoch(), + RequestReason::InitialRequest, + ) { + sleep_impl.sleep(delay).await; + } + + // Assume all further requests succeed on the first try + crl.update_rate_limiter(time_source.seconds_since_unix_epoch(), false); + } + + let inner = crl.inner.lock().unwrap(); + assert!(inner.enabled, "the rate limiter should still be enabled"); + // Assert that the rate limiter respects the passage of time. + assert_relative_eq!( + inner.last_timestamp.unwrap(), + sleep_impl.total_duration().as_secs_f64(), + max_relative = 0.0001 + ); + } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs index 81437cead3..088e7bfd8c 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -56,7 +56,7 @@ impl RetryStrategy for FixedDelayRetryStrategy { let request_attempts = cfg .load::() .expect("at least one request attempt is made before any retry is attempted"); - if request_attempts.attempts() >= self.max_attempts as usize { + if request_attempts.attempts() >= self.max_attempts { tracing::trace!( attempts = request_attempts.attempts(), max_attempts = self.max_attempts, diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index 50a83cae9b..307297e0c5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::client::retries::client_rate_limiter::{ClientRateLimiter, RequestReason}; use crate::client::retries::strategy::standard::ReleaseResult::{ APermitWasReleased, NoPermitWasReleased, }; @@ -15,20 +16,21 @@ use aws_smithy_runtime_api::client::retries::{ ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt, }; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; -use aws_smithy_types::retry::RetryConfig; +use aws_smithy_types::retry::{ErrorKind, RetryConfig}; use std::sync::Mutex; -use std::time::Duration; +use std::time::{Duration, SystemTime}; use tokio::sync::OwnedSemaphorePermit; +use tracing::debug; // The initial attempt, plus three retries. -const DEFAULT_MAX_ATTEMPTS: usize = 4; +const DEFAULT_MAX_ATTEMPTS: u32 = 4; #[derive(Debug)] pub struct StandardRetryStrategy { // Retry settings base: fn() -> f64, initial_backoff: Duration, - max_attempts: usize, + max_attempts: u32, max_backoff: Duration, retry_permit: Mutex>, } @@ -39,9 +41,16 @@ impl Storable for StandardRetryStrategy { impl StandardRetryStrategy { pub fn new(retry_config: &RetryConfig) -> Self { + let base = if retry_config.use_static_exponential_base() { + || 1.0 + } else { + fastrand::f64 + }; // TODO(enableNewSmithyRuntimeLaunch) add support for `retry_config.reconnect_mode()` here or in the orchestrator flow. Self::default() - .with_max_attempts(retry_config.max_attempts() as usize) + .with_base(base) + .with_max_backoff(retry_config.max_backoff()) + .with_max_attempts(retry_config.max_attempts()) .with_initial_backoff(retry_config.initial_backoff()) } @@ -50,7 +59,7 @@ impl StandardRetryStrategy { self } - pub fn with_max_attempts(mut self, max_attempts: usize) -> Self { + pub fn with_max_attempts(mut self, max_attempts: u32) -> Self { self.max_attempts = max_attempts; self } @@ -84,6 +93,61 @@ impl StandardRetryStrategy { p.forget() } } + + fn calculate_backoff( + &self, + cfg: &ConfigBag, + retry_reason: Option<&RetryReason>, + ) -> Result { + let request_attempts = cfg + .load::() + .expect("at least one request attempt is made before any retry is attempted") + .attempts(); + let token_bucket = cfg.load::(); + + match retry_reason { + Some(RetryReason::Explicit(backoff)) => Ok(*backoff), + Some(RetryReason::Error(kind)) => { + update_rate_limiter_if_exists(cfg, *kind == ErrorKind::ThrottlingError); + if let Some(delay) = check_rate_limiter_for_delay(cfg, *kind) { + let delay = delay.min(self.max_backoff); + debug!("rate limiter has requested a {delay:?} delay before retrying"); + Ok(delay) + } else { + if let Some(tb) = token_bucket { + match tb.acquire(kind) { + Some(permit) => self.set_retry_permit(permit), + None => { + debug!("attempt #{request_attempts} failed with {kind:?}; However, no retry permits are available, so no retry will be attempted."); + return Err(ShouldAttempt::No); + } + } + } + + let backoff = calculate_exponential_backoff( + // Generate a random base multiplier to create jitter + (self.base)(), + // Get the backoff time multiplier in seconds (with fractional seconds) + self.initial_backoff.as_secs_f64(), + // `self.local.attempts` tracks number of requests made including the initial request + // The initial attempt shouldn't count towards backoff calculations so we subtract it + request_attempts - 1, + ); + Ok(Duration::from_secs_f64(backoff).min(self.max_backoff)) + } + } + Some(_) => unreachable!("RetryReason is non-exhaustive"), + None => { + update_rate_limiter_if_exists(cfg, false); + debug!( + attempts = request_attempts, + max_attempts = self.max_attempts, + "encountered unretryable error" + ); + Err(ShouldAttempt::No) + } + } + } } enum ReleaseResult { @@ -105,8 +169,19 @@ impl Default for StandardRetryStrategy { } impl RetryStrategy for StandardRetryStrategy { - fn should_attempt_initial_request(&self, _cfg: &ConfigBag) -> Result { - // The standard token bucket is only ever considered for retry requests. + fn should_attempt_initial_request(&self, cfg: &ConfigBag) -> Result { + if let Some(crl) = cfg.load::() { + let seconds_since_unix_epoch = get_seconds_since_unix_epoch(cfg); + if let Err(delay) = crl.acquire_permission_to_send_a_request( + seconds_since_unix_epoch, + RequestReason::InitialRequest, + ) { + return Ok(ShouldAttempt::YesAfterDelay(delay)); + } + } else { + debug!("no client rate limiter configured, so no token is required for the initial request."); + } + Ok(ShouldAttempt::Yes) } @@ -121,7 +196,7 @@ impl RetryStrategy for StandardRetryStrategy { ); let token_bucket = cfg.load::(); if output_or_error.is_ok() { - tracing::debug!("request succeeded, no retry necessary"); + debug!("request succeeded, no retry necessary"); if let Some(tb) = token_bucket { // If this retry strategy is holding any permits, release them back to the bucket. if let NoPermitWasReleased = self.release_retry_permit() { @@ -132,6 +207,7 @@ impl RetryStrategy for StandardRetryStrategy { tb.regenerate_a_token(); } } + update_rate_limiter_if_exists(cfg, false); return Ok(ShouldAttempt::No); } @@ -142,7 +218,9 @@ impl RetryStrategy for StandardRetryStrategy { .expect("at least one request attempt is made before any retry is attempted") .attempts(); if request_attempts >= self.max_attempts { - tracing::trace!( + update_rate_limiter_if_exists(cfg, false); + + debug!( attempts = request_attempts, max_attempts = self.max_attempts, "not retrying because we are out of attempts" @@ -155,46 +233,12 @@ impl RetryStrategy for StandardRetryStrategy { let retry_reason = retry_classifiers.classify_retry(ctx); // Calculate the appropriate backoff time. - let backoff = match retry_reason { - Some(RetryReason::Explicit(dur)) => dur, - Some(RetryReason::Error(kind)) => { - // If a token bucket was set, and the RetryReason IS NOT explicit, attempt to acquire a retry permit. - if let Some(tb) = token_bucket { - match tb.acquire(&kind) { - Some(permit) => self.set_retry_permit(permit), - None => { - tracing::debug!( - "attempt #{request_attempts} failed with {kind:?}; \ - However, no retry permits are available, so no retry will be attempted.", - ); - return Ok(ShouldAttempt::No); - } - } - }; - - let backoff = calculate_exponential_backoff( - // Generate a random base multiplier to create jitter - (self.base)(), - // Get the backoff time multiplier in seconds (with fractional seconds) - self.initial_backoff.as_secs_f64(), - // `self.local.attempts` tracks number of requests made including the initial request - // The initial attempt shouldn't count towards backoff calculations so we subtract it - (request_attempts - 1) as u32, - ); - Duration::from_secs_f64(backoff).min(self.max_backoff) - } - Some(_) => unreachable!("RetryReason is non-exhaustive"), - None => { - tracing::debug!( - attempts = request_attempts, - max_attempts = self.max_attempts, - "encountered unretryable error" - ); - return Ok(ShouldAttempt::No); - } + let backoff = match self.calculate_backoff(cfg, retry_reason.as_ref()) { + Ok(value) => value, + // In some cases, backoff calculation will decide that we shouldn't retry at all. + Err(value) => return Ok(value), }; - - tracing::debug!( + debug!( "attempt #{request_attempts} failed with {:?}; retrying after {:?}", retry_reason.expect("the match statement above ensures this is not None"), backoff @@ -204,18 +248,47 @@ impl RetryStrategy for StandardRetryStrategy { } } +fn update_rate_limiter_if_exists(cfg: &ConfigBag, is_throttling_error: bool) { + if let Some(crl) = cfg.load::() { + let seconds_since_unix_epoch = get_seconds_since_unix_epoch(cfg); + crl.update_rate_limiter(seconds_since_unix_epoch, is_throttling_error); + } +} + +fn check_rate_limiter_for_delay(cfg: &ConfigBag, kind: ErrorKind) -> Option { + if let Some(crl) = cfg.load::() { + let retry_reason = if kind == ErrorKind::ThrottlingError { + RequestReason::RetryTimeout + } else { + RequestReason::Retry + }; + if let Err(delay) = crl + .acquire_permission_to_send_a_request(get_seconds_since_unix_epoch(cfg), retry_reason) + { + return Some(delay); + } + } + + None +} + fn calculate_exponential_backoff(base: f64, initial_backoff: f64, retry_attempts: u32) -> f64 { base * initial_backoff * 2_u32.pow(retry_attempts) as f64 } +fn get_seconds_since_unix_epoch(cfg: &ConfigBag) -> f64 { + let request_time = cfg.request_time().unwrap(); + request_time + .now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs_f64() +} + #[cfg(test)] mod tests { use super::*; - use super::{calculate_exponential_backoff, ShouldAttempt, StandardRetryStrategy}; - use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; - use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; - use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ AlwaysRetry, ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, }; @@ -243,7 +316,7 @@ mod tests { fn set_up_cfg_and_context( error_kind: ErrorKind, - current_request_attempts: usize, + current_request_attempts: u32, ) -> (InterceptorContext, ConfigBag) { let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); ctx.set_output_or_error(Err(OrchestratorError::other("doesn't matter"))); @@ -492,7 +565,7 @@ mod tests { let (mut cfg, mut ctx) = setup_test(vec![RetryReason::Error(ErrorKind::TransientError)]); let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) - .with_max_attempts(usize::MAX); + .with_max_attempts(u32::MAX); cfg.interceptor_state() .store_put(TokenBucket::new(PERMIT_COUNT)); let token_bucket = cfg.load::().unwrap().clone(); @@ -522,7 +595,7 @@ mod tests { // Replenish permits until we get back to `PERMIT_COUNT` while token_bucket.available_permits() < PERMIT_COUNT { if attempt > 23 { - panic!("This test should have completed by now (fillup)"); + panic!("This test should have completed by now (fill-up)"); } cfg.interceptor_state() diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs index df1510459a..a7c95d2a51 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::client::retries::RetryPartition; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{FrozenLayer, Layer, Storable, StoreReplace}; use aws_smithy_types::retry::ErrorKind; @@ -34,13 +35,26 @@ impl RuntimePlugin for TokenBucketRuntimePlugin { } } +#[doc(hidden)] +#[non_exhaustive] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct TokenBucketPartition { + retry_partition: RetryPartition, +} + +impl TokenBucketPartition { + pub fn new(retry_partition: RetryPartition) -> Self { + Self { retry_partition } + } +} + const DEFAULT_CAPACITY: usize = 500; const RETRY_COST: u32 = 5; const RETRY_TIMEOUT_COST: u32 = RETRY_COST * 2; const PERMIT_REGENERATION_AMOUNT: usize = 1; #[derive(Clone, Debug)] -pub(crate) struct TokenBucket { +pub struct TokenBucket { semaphore: Arc, max_permits: usize, timeout_retry_cost: u32, @@ -63,7 +77,7 @@ impl Default for TokenBucket { } impl TokenBucket { - pub(crate) fn new(initial_quota: usize) -> Self { + pub fn new(initial_quota: usize) -> Self { Self { semaphore: Arc::new(Semaphore::new(initial_quota)), max_permits: initial_quota, diff --git a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs index cb8b0d0e90..e1ba3aec63 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs @@ -180,11 +180,16 @@ where #[cfg(test)] mod tests { - use super::*; + use crate::client::timeout::{MaybeTimeout, TimeoutKind}; use aws_smithy_async::assert_elapsed; use aws_smithy_async::future::never::Never; - use aws_smithy_async::rt::sleep::TokioSleep; - use aws_smithy_types::config_bag::Layer; + use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, TokioSleep}; + use aws_smithy_http::result::SdkError; + use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; + use aws_smithy_runtime_api::client::orchestrator::HttpResponse; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; + use aws_smithy_types::timeout::TimeoutConfig; + use std::time::Duration; #[tokio::test] async fn test_no_timeout() { diff --git a/rust-runtime/aws-smithy-runtime/src/lib.rs b/rust-runtime/aws-smithy-runtime/src/lib.rs index 195fde2d06..2c2eb6a9f8 100644 --- a/rust-runtime/aws-smithy-runtime/src/lib.rs +++ b/rust-runtime/aws-smithy-runtime/src/lib.rs @@ -11,3 +11,5 @@ )] pub mod client; + +pub mod static_partition_map; diff --git a/rust-runtime/aws-smithy-runtime/src/static_partition_map.rs b/rust-runtime/aws-smithy-runtime/src/static_partition_map.rs new file mode 100644 index 0000000000..10b0070ccf --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/static_partition_map.rs @@ -0,0 +1,158 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use once_cell::sync::OnceCell; +use std::collections::HashMap; +use std::hash::Hash; +use std::sync::{Mutex, MutexGuard}; + +/// A data structure for persisting and sharing state between multiple clients. +/// +/// Some state should be shared between multiple clients. For example, when creating multiple clients +/// for the same service, it's desirable to share a client rate limiter. This way, when one client +/// receives a throttling response, the other clients will be aware of it as well. +/// +/// Whether clients share state is dependent on their partition key `K`. Going back to the client +/// rate limiter example, `K` would be a struct containing the name of the service as well as the +/// client's configured region, since receiving throttling responses in `us-east-1` shouldn't +/// throttle requests to the same service made in other regions. +/// +/// Values stored in a `StaticPartitionMap` will be cloned whenever they are requested. Values must +/// be initialized before they can be retrieved, and the `StaticPartitionMap::get_or_init` method is +/// how you can ensure this. +/// +/// # Example +/// +/// ``` +///use std::sync::{Arc, Mutex}; +/// use aws_smithy_runtime::static_partition_map::StaticPartitionMap; +/// +/// // The shared state must be `Clone` and will be internally mutable. Deriving `Default` isn't +/// // necessary, but allows us to use the `StaticPartitionMap::get_or_init_default` method. +/// #[derive(Clone, Default)] +/// pub struct SomeSharedState { +/// inner: Arc> +/// } +/// +/// #[derive(Default)] +/// struct Inner { +/// // Some shared state... +/// } +/// +/// // `Clone`, `Hash`, and `Eq` are all required trait impls for partition keys +/// #[derive(Clone, Hash, PartialEq, Eq)] +/// pub struct SharedStatePartition { +/// region: String, +/// service_name: String, +/// } +/// +/// impl SharedStatePartition { +/// pub fn new(region: impl Into, service_name: impl Into) -> Self { +/// Self { region: region.into(), service_name: service_name.into() } +/// } +/// } +/// +/// static SOME_SHARED_STATE: StaticPartitionMap = StaticPartitionMap::new(); +/// +/// struct Client { +/// shared_state: SomeSharedState, +/// } +/// +/// impl Client { +/// pub fn new() -> Self { +/// let key = SharedStatePartition::new("us-east-1", "example_service_20230628"); +/// Self { +/// // If the stored value implements `Default`, you can call the +/// // `StaticPartitionMap::get_or_init_default` convenience method. +/// shared_state: SOME_SHARED_STATE.get_or_init_default(key), +/// } +/// } +/// } +/// ``` +#[derive(Debug, Default)] +pub struct StaticPartitionMap { + inner: OnceCell>>, +} + +impl StaticPartitionMap { + pub const fn new() -> Self { + Self { + inner: OnceCell::new(), + } + } +} + +impl StaticPartitionMap +where + K: Eq + Hash, +{ + fn get_or_init_inner(&self) -> MutexGuard<'_, HashMap> { + self.inner + // At the very least, we'll always be storing the default state. + .get_or_init(|| Mutex::new(HashMap::with_capacity(1))) + .lock() + .unwrap() + } +} + +impl StaticPartitionMap +where + K: Eq + Hash, + V: Clone, +{ + #[must_use] + pub fn get(&self, partition_key: K) -> Option { + self.get_or_init_inner().get(&partition_key).cloned() + } + + #[must_use] + pub fn get_or_init(&self, partition_key: K, f: F) -> V + where + F: FnOnce() -> V, + { + let mut inner = self.get_or_init_inner(); + let v = inner.entry(partition_key).or_insert_with(f); + v.clone() + } +} + +impl StaticPartitionMap +where + K: Eq + Hash, + V: Clone + Default, +{ + #[must_use] + pub fn get_or_init_default(&self, partition_key: K) -> V { + self.get_or_init(partition_key, V::default) + } +} + +#[cfg(test)] +mod tests { + use super::StaticPartitionMap; + + #[test] + fn test_keyed_partition_returns_same_value_for_same_key() { + let kp = StaticPartitionMap::new(); + let _ = kp.get_or_init("A", || "A".to_owned()); + let actual = kp.get_or_init("A", || "B".to_owned()); + let expected = "A".to_owned(); + assert_eq!(expected, actual); + } + + #[test] + fn test_keyed_partition_returns_different_value_for_different_key() { + let kp = StaticPartitionMap::new(); + let _ = kp.get_or_init("A", || "A".to_owned()); + let actual = kp.get_or_init("B", || "B".to_owned()); + + let expected = "B".to_owned(); + assert_eq!(expected, actual); + + let actual = kp.get("A").unwrap(); + let expected = "A".to_owned(); + assert_eq!(expected, actual); + } +} diff --git a/rust-runtime/aws-smithy-types/src/retry.rs b/rust-runtime/aws-smithy-types/src/retry.rs index 1db024a411..a514417814 100644 --- a/rust-runtime/aws-smithy-types/src/retry.rs +++ b/rust-runtime/aws-smithy-types/src/retry.rs @@ -99,12 +99,12 @@ impl FromStr for RetryMode { fn from_str(string: &str) -> Result { let string = string.trim(); + // eq_ignore_ascii_case is OK here because the only strings we need to check for are ASCII if string.eq_ignore_ascii_case("standard") { Ok(RetryMode::Standard) - // TODO(https://github.com/awslabs/aws-sdk-rust/issues/247): adaptive retries - // } else if string.eq_ignore_ascii_case("adaptive") { - // Ok(RetryMode::Adaptive) + } else if string.eq_ignore_ascii_case("adaptive") { + Ok(RetryMode::Adaptive) } else { Err(RetryModeParseError::new(string)) } @@ -264,6 +264,7 @@ impl RetryConfigBuilder { .reconnect_mode .unwrap_or(ReconnectMode::ReconnectOnTransientError), max_backoff: self.max_backoff.unwrap_or_else(|| Duration::from_secs(20)), + use_static_exponential_base: false, } } } @@ -277,6 +278,7 @@ pub struct RetryConfig { initial_backoff: Duration, max_backoff: Duration, reconnect_mode: ReconnectMode, + use_static_exponential_base: bool, } impl Storable for RetryConfig { @@ -308,6 +310,19 @@ impl RetryConfig { initial_backoff: Duration::from_secs(1), reconnect_mode: ReconnectMode::ReconnectOnTransientError, max_backoff: Duration::from_secs(20), + use_static_exponential_base: false, + } + } + + /// Creates a default `RetryConfig` with `RetryMode::Adaptive` and max attempts of three. + pub fn adaptive() -> Self { + Self { + mode: RetryMode::Adaptive, + max_attempts: 3, + initial_backoff: Duration::from_secs(1), + reconnect_mode: ReconnectMode::ReconnectOnTransientError, + max_backoff: Duration::from_secs(20), + use_static_exponential_base: false, } } @@ -363,6 +378,20 @@ impl RetryConfig { self } + /// Hint to the retry strategy whether to use a static exponential base. + /// + /// When a retry strategy uses exponential backoff, it calculates a random base. This causes the + /// retry delay to be slightly random, and helps prevent "thundering herd" scenarios. However, + /// it's often useful during testing to know exactly how long the delay will be. + /// + /// Therefore, if you're writing a test and asserting an expected retry delay, + /// set this to `true`. + #[cfg(feature = "test-util")] + pub fn with_use_static_exponential_base(mut self, use_static_exponential_base: bool) -> Self { + self.use_static_exponential_base = use_static_exponential_base; + self + } + /// Returns the retry mode. pub fn mode(&self) -> RetryMode { self.mode @@ -383,10 +412,23 @@ impl RetryConfig { self.initial_backoff } + /// Returns the max backoff duration. + pub fn max_backoff(&self) -> Duration { + self.max_backoff + } + /// Returns true if retry is enabled with this config pub fn has_retry(&self) -> bool { self.max_attempts > 1 } + + /// Returns `true` if retry strategies should use a static exponential base instead of the + /// default random base. + /// + /// To set this value, the `test-util` feature must be enabled. + pub fn use_static_exponential_base(&self) -> bool { + self.use_static_exponential_base + } } #[cfg(test)] From 80de569d2babeabbec1e2a288c16b49663c08375 Mon Sep 17 00:00:00 2001 From: Sam Bartlett Date: Wed, 28 Jun 2023 15:00:26 -0700 Subject: [PATCH 185/253] Expand skipped headers for sigv4 canonical request signing to include x-amzn-trace-id and authorization headers. (#2815) ## Motivation and Context When customers add x-ray headers to requests, the SigV4 signer should exclude them, or the generated canonical signature will not match the remote service's, since many services being called are written with non-rust SDKs that automatically exclude these common headers. The Rust SDK should exclude a similar set of headers to the other popular AWS SDKs. While this is not uniform across the SDKs, a minimal set should be excluded and others should be considered to be excluded in future PRs. ## Description * Expands the set of headers excluded from canonical request calculation to include "x-amzn-trace-id" and "authorization" (since authorization will be added as a part of this process). ## Testing * Added headers to exclusion test & validated with `cargo test` * `./gradlew :aws:sdk:test` ## Checklist - [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._ --------- Co-authored-by: Sam Bartlett Co-authored-by: Zelda Hessler --- CHANGELOG.next.toml | 6 ++++ .../src/http_request/canonical_request.rs | 34 ++++++++++++++++++- .../aws-sigv4/src/http_request/settings.rs | 27 ++++++++++++--- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index c1389a7463..35349116c0 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -11,6 +11,12 @@ # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} # author = "rcoh" + [[aws-sdk-rust]] + message = "Automatically exclude X-Ray trace ID headers and authorization headers from SigV4 canonical request calculations." + references = ["smithy-rs#2815"] + meta = { "breaking" = false, "tada" = false, "bug" = true } + author = "relevantsam" + [[aws-sdk-rust]] message = "Add accessors to Builders" references = ["smithy-rs#2791"] diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs b/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs index f7c2692a37..221463ada2 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/canonical_request.rs @@ -774,14 +774,46 @@ mod tests { assert_eq!(creq.values.signed_headers().as_str(), "host;x-amz-date"); } - // It should exclude user-agent and x-amz-user-agent headers from presigning + // It should exclude authorization, user-agent, x-amzn-trace-id headers from presigning + #[test] + fn non_presigning_header_exclusion() { + let request = http::Request::builder() + .uri("https://some-endpoint.some-region.amazonaws.com") + .header("authorization", "test-authorization") + .header("content-type", "application/xml") + .header("content-length", "0") + .header("user-agent", "test-user-agent") + .header("x-amzn-trace-id", "test-trace-id") + .header("x-amz-user-agent", "test-user-agent") + .body("") + .unwrap(); + let request = SignableRequest::from(&request); + + let settings = SigningSettings { + signature_location: SignatureLocation::Headers, + ..Default::default() + }; + + let signing_params = signing_params(settings); + let canonical = CanonicalRequest::from(&request, &signing_params).unwrap(); + + let values = canonical.values.as_headers().unwrap(); + assert_eq!( + "content-length;content-type;host;x-amz-date;x-amz-user-agent", + values.signed_headers.as_str() + ); + } + + // It should exclude authorization, user-agent, x-amz-user-agent, x-amzn-trace-id headers from presigning #[test] fn presigning_header_exclusion() { let request = http::Request::builder() .uri("https://some-endpoint.some-region.amazonaws.com") + .header("authorization", "test-authorization") .header("content-type", "application/xml") .header("content-length", "0") .header("user-agent", "test-user-agent") + .header("x-amzn-trace-id", "test-trace-id") .header("x-amz-user-agent", "test-user-agent") .body("") .unwrap(); diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs b/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs index bac85281df..501cd5c775 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/settings.rs @@ -3,12 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use http::header::{HeaderName, USER_AGENT}; +use http::header::{HeaderName, AUTHORIZATION, USER_AGENT}; use std::time::Duration; /// HTTP signing parameters pub type SigningParams<'a> = crate::SigningParams<'a, SigningSettings>; +const HEADER_NAME_X_RAY_TRACE_ID: &str = "x-amzn-trace-id"; + /// HTTP-specific signing settings #[derive(Debug, PartialEq)] #[non_exhaustive] @@ -97,15 +99,30 @@ pub enum SessionTokenMode { impl Default for SigningSettings { fn default() -> Self { - // The user agent header should not be signed because it may be altered by proxies - const EXCLUDED_HEADERS: [HeaderName; 1] = [USER_AGENT]; - + // Headers that are potentially altered by proxies or as a part of standard service operations. + // Reference: + // Go SDK: + // Java SDK: + // JS SDK: + // There is no single source of truth for these available, so this uses the minimum common set of the excluded options. + // Instantiate this every time, because SigningSettings takes a Vec (which cannot be const); + let excluded_headers = Some( + [ + // This header is calculated as part of the signing process, so if it's present, discard it + AUTHORIZATION, + // Changes when sent by proxy + USER_AGENT, + // Changes based on the request from the client + HeaderName::from_static(HEADER_NAME_X_RAY_TRACE_ID), + ] + .to_vec(), + ); Self { percent_encoding_mode: PercentEncodingMode::Double, payload_checksum_kind: PayloadChecksumKind::NoHeader, signature_location: SignatureLocation::Headers, expires_in: None, - excluded_headers: Some(EXCLUDED_HEADERS.to_vec()), + excluded_headers, uri_path_normalization_mode: UriPathNormalizationMode::Enabled, session_token_mode: SessionTokenMode::Include, } From ee5aadc29f4cb6f3a925fe5a90251a194af485f3 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 28 Jun 2023 18:06:13 -0400 Subject: [PATCH 186/253] Make endpoint resolution async (#2816) ## Motivation and Context Make endpoint resolution async from the perspective of the orchestrator. - #2608 ## Description Change the endpoint trait in the orchestrator to be asynchronous ## Testing - compiled code ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../src/client/orchestrator.rs | 4 ++-- .../src/client/orchestrator.rs | 2 +- .../src/client/orchestrator/endpoints.rs | 20 +++++++++++-------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index d77647e20b..6bcb8f0431 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -135,7 +135,7 @@ impl Storable for EndpointResolverParams { } pub trait EndpointResolver: Send + Sync + fmt::Debug { - fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Result; + fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future; } #[derive(Debug)] @@ -148,7 +148,7 @@ impl DynEndpointResolver { } impl EndpointResolver for DynEndpointResolver { - fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Result { + fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future { self.0.resolve_endpoint(params) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index bd9ae34d5f..6c950b81d5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -261,7 +261,7 @@ async fn try_attempt( stop_point: StopPoint, ) { halt_on_err!([ctx] => interceptors.read_before_attempt(ctx, cfg)); - halt_on_err!([ctx] => orchestrate_endpoint(ctx, cfg).map_err(OrchestratorError::other)); + halt_on_err!([ctx] => orchestrate_endpoint(ctx, cfg).await.map_err(OrchestratorError::other)); halt_on_err!([ctx] => interceptors.modify_before_signing(ctx, cfg)); halt_on_err!([ctx] => interceptors.read_before_signing(ctx, cfg)); diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 582be343f6..12db9cb606 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -12,7 +12,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ - EndpointResolver, EndpointResolverParams, HttpRequest, + EndpointResolver, EndpointResolverParams, Future, HttpRequest, }; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::endpoint::Endpoint; @@ -40,8 +40,10 @@ impl StaticUriEndpointResolver { } impl EndpointResolver for StaticUriEndpointResolver { - fn resolve_endpoint(&self, _params: &EndpointResolverParams) -> Result { - Ok(Endpoint::builder().url(self.endpoint.to_string()).build()) + fn resolve_endpoint(&self, _params: &EndpointResolverParams) -> Future { + Future::ready(Ok(Endpoint::builder() + .url(self.endpoint.to_string()) + .build())) } } @@ -86,17 +88,19 @@ impl EndpointResolver for DefaultEndpointResolver where Params: Debug + Send + Sync + 'static, { - fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Result { - match params.get::() { - Some(params) => Ok(self.inner.resolve_endpoint(params)?), + fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future { + let ep = match params.get::() { + Some(params) => self.inner.resolve_endpoint(params).map_err(Box::new), None => Err(Box::new(ResolveEndpointError::message( "params of expected type was not present", ))), } + .map_err(|e| e as _); + Future::ready(ep) } } -pub(super) fn orchestrate_endpoint( +pub(super) async fn orchestrate_endpoint( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { @@ -105,7 +109,7 @@ pub(super) fn orchestrate_endpoint( let request = ctx.request_mut().expect("set during serialization"); let endpoint_resolver = cfg.endpoint_resolver(); - let endpoint = endpoint_resolver.resolve_endpoint(params)?; + let endpoint = endpoint_resolver.resolve_endpoint(params).await?; apply_endpoint(request, &endpoint, endpoint_prefix)?; // Make the endpoint config available to interceptors From 1de38029b7f846903da13a577239e78343173de1 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 28 Jun 2023 15:07:51 -0700 Subject: [PATCH 187/253] Fix `@httpChecksumRequired` and idempotency tokens in the orchestrator (#2817) This PR fixes the Smithy `@httpChecksumRequired` trait and idempotency token auto-fill in the orchestrator implementation. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../HttpChecksumRequiredGenerator.kt | 21 ++++++ .../IdempotencyTokenGenerator.kt | 39 ++++++++++- rust-runtime/inlineable/Cargo.toml | 30 +++++---- .../src/client_http_checksum_required.rs | 48 ++++++++++++++ .../src/client_idempotency_token.rs | 66 +++++++++++++++++++ .../inlineable/src/http_checksum_required.rs | 48 ++++++++++++++ rust-runtime/inlineable/src/lib.rs | 4 ++ .../check-client-codegen-integration-tests | 5 +- 8 files changed, 243 insertions(+), 18 deletions(-) create mode 100644 rust-runtime/inlineable/src/client_http_checksum_required.rs create mode 100644 rust-runtime/inlineable/src/client_idempotency_token.rs create mode 100644 rust-runtime/inlineable/src/http_checksum_required.rs diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt index 8e76266079..fc5c1d6298 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt @@ -8,10 +8,15 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.HttpChecksumRequiredTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule 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.toType import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType @@ -32,6 +37,22 @@ class HttpChecksumRequiredGenerator( throw CodegenException("HttpChecksum required cannot be applied to a streaming shape") } return when (section) { + is OperationSection.AdditionalRuntimePlugins -> writable { + section.addOperationRuntimePlugin(this) { + rustTemplate( + "#{HttpChecksumRequiredRuntimePlugin}", + "HttpChecksumRequiredRuntimePlugin" to + InlineDependency.forRustFile( + RustModule.pubCrate("client_http_checksum_required", parent = ClientRustModule.root), + "/inlineable/src/client_http_checksum_required.rs", + CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig), + CargoDependency.smithyTypes(codegenContext.runtimeConfig), + CargoDependency.Http, + CargoDependency.Md5, + ).toType().resolve("HttpChecksumRequiredRuntimePlugin"), + ) + } + } is OperationSection.MutateRequest -> writable { rustTemplate( """ diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt index 1c6ef7eb42..3ee06bcea5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdempotencyTokenGenerator.kt @@ -7,27 +7,60 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.IdempotencyTokenTrait +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency +import software.amazon.smithy.rust.codegen.core.rustlang.RustModule 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.toType import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.util.findMemberWithTrait import software.amazon.smithy.rust.codegen.core.util.inputShape -class IdempotencyTokenGenerator(codegenContext: CodegenContext, operationShape: OperationShape) : - OperationCustomization() { +class IdempotencyTokenGenerator( + codegenContext: CodegenContext, + operationShape: OperationShape, +) : OperationCustomization() { private val model = codegenContext.model + private val runtimeConfig = codegenContext.runtimeConfig private val symbolProvider = codegenContext.symbolProvider - private val idempotencyTokenMember = operationShape.inputShape(model).findMemberWithTrait(model) + private val inputShape = operationShape.inputShape(model) + private val idempotencyTokenMember = inputShape.findMemberWithTrait(model) + override fun section(section: OperationSection): Writable { if (idempotencyTokenMember == null) { return emptySection } val memberName = symbolProvider.toMemberName(idempotencyTokenMember) return when (section) { + is OperationSection.AdditionalRuntimePlugins -> writable { + section.addOperationRuntimePlugin(this) { + rustTemplate( + """ + #{IdempotencyTokenRuntimePlugin}::new(|token_provider, input| { + let input: &mut #{Input} = input.downcast_mut().expect("correct type"); + if input.$memberName.is_none() { + input.$memberName = #{Some}(token_provider.make_idempotency_token()); + } + }) + """, + *preludeScope, + "Input" to symbolProvider.toSymbol(inputShape), + "IdempotencyTokenRuntimePlugin" to + InlineDependency.forRustFile( + RustModule.pubCrate("client_idempotency_token", parent = ClientRustModule.root), + "/inlineable/src/client_idempotency_token.rs", + CargoDependency.smithyRuntimeApi(runtimeConfig), + CargoDependency.smithyTypes(runtimeConfig), + ).toType().resolve("IdempotencyTokenRuntimePlugin"), + ) + } + } is OperationSection.MutateInput -> writable { rustTemplate( """ diff --git a/rust-runtime/inlineable/Cargo.toml b/rust-runtime/inlineable/Cargo.toml index 5c89e925bf..5e7351291f 100644 --- a/rust-runtime/inlineable/Cargo.toml +++ b/rust-runtime/inlineable/Cargo.toml @@ -18,22 +18,24 @@ default = ["gated-tests"] [dependencies] -"bytes" = "1" -"http" = "0.2.1" -"aws-smithy-types" = { path = "../aws-smithy-types" } -"aws-smithy-json" = { path = "../aws-smithy-json" } -"aws-smithy-xml" = { path = "../aws-smithy-xml" } -"aws-smithy-http-server" = { path = "../aws-smithy-http-server" } -"fastrand" = "1" -"futures-util" = "0.3" -"pin-project-lite" = "0.2" -"tower" = { version = "0.4.11", default-features = false } -"async-trait" = "0.1" -percent-encoding = "2.2.0" +async-trait = "0.1" +aws-smithy-http = { path = "../aws-smithy-http" } +aws-smithy-http-server = { path = "../aws-smithy-http-server" } +aws-smithy-json = { path = "../aws-smithy-json" } +aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api" } +aws-smithy-types = { path = "../aws-smithy-types" } +aws-smithy-xml = { path = "../aws-smithy-xml" } +bytes = "1" +fastrand = "1" +futures-util = "0.3" +http = "0.2.1" +md-5 = "0.10.0" once_cell = "1.16.0" +percent-encoding = "2.2.0" +pin-project-lite = "0.2" regex = "1.5.5" -"url" = "2.2.2" -aws-smithy-http = { path = "../aws-smithy-http" } +tower = { version = "0.4.11", default-features = false } +url = "2.2.2" [dev-dependencies] proptest = "1" diff --git a/rust-runtime/inlineable/src/client_http_checksum_required.rs b/rust-runtime/inlineable/src/client_http_checksum_required.rs new file mode 100644 index 0000000000..636016a3fc --- /dev/null +++ b/rust-runtime/inlineable/src/client_http_checksum_required.rs @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::{ + Interceptor, InterceptorRegistrar, SharedInterceptor, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::base64; +use aws_smithy_types::config_bag::ConfigBag; +use http::header::HeaderName; + +#[derive(Debug)] +pub(crate) struct HttpChecksumRequiredRuntimePlugin; + +impl RuntimePlugin for HttpChecksumRequiredRuntimePlugin { + fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { + interceptors.register(SharedInterceptor::new(HttpChecksumRequiredInterceptor)); + } +} + +#[derive(Debug)] +struct HttpChecksumRequiredInterceptor; + +impl Interceptor for HttpChecksumRequiredInterceptor { + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let request = context.request_mut(); + let body_bytes = request + .body() + .bytes() + .expect("checksum can only be computed for non-streaming operations"); + let checksum = ::digest(body_bytes); + request.headers_mut().insert( + HeaderName::from_static("content-md5"), + base64::encode(&checksum[..]) + .parse() + .expect("checksum is a valid header value"), + ); + Ok(()) + } +} diff --git a/rust-runtime/inlineable/src/client_idempotency_token.rs b/rust-runtime/inlineable/src/client_idempotency_token.rs new file mode 100644 index 0000000000..cf4d2d7e53 --- /dev/null +++ b/rust-runtime/inlineable/src/client_idempotency_token.rs @@ -0,0 +1,66 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::idempotency_token::IdempotencyTokenProvider; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeSerializationInterceptorContextMut, Input, +}; +use aws_smithy_runtime_api::client::interceptors::{ + Interceptor, InterceptorRegistrar, SharedInterceptor, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::config_bag::ConfigBag; +use std::fmt; + +#[derive(Debug)] +pub(crate) struct IdempotencyTokenRuntimePlugin { + interceptor: SharedInterceptor, +} + +impl IdempotencyTokenRuntimePlugin { + pub(crate) fn new(set_token: S) -> Self + where + S: Fn(IdempotencyTokenProvider, &mut Input) + Send + Sync + 'static, + { + Self { + interceptor: SharedInterceptor::new(IdempotencyTokenInterceptor { set_token }), + } + } +} + +impl RuntimePlugin for IdempotencyTokenRuntimePlugin { + fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { + interceptors.register(self.interceptor.clone()); + } +} + +struct IdempotencyTokenInterceptor { + set_token: S, +} + +impl fmt::Debug for IdempotencyTokenInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("IdempotencyTokenInterceptor").finish() + } +} + +impl Interceptor for IdempotencyTokenInterceptor +where + S: Fn(IdempotencyTokenProvider, &mut Input) + Send + Sync, +{ + fn modify_before_serialization( + &self, + context: &mut BeforeSerializationInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let token_provider = cfg + .load::() + .expect("the idempotency provider must be set") + .clone(); + (self.set_token)(token_provider, context.input_mut()); + Ok(()) + } +} diff --git a/rust-runtime/inlineable/src/http_checksum_required.rs b/rust-runtime/inlineable/src/http_checksum_required.rs new file mode 100644 index 0000000000..636016a3fc --- /dev/null +++ b/rust-runtime/inlineable/src/http_checksum_required.rs @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; +use aws_smithy_runtime_api::client::interceptors::{ + Interceptor, InterceptorRegistrar, SharedInterceptor, +}; +use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_types::base64; +use aws_smithy_types::config_bag::ConfigBag; +use http::header::HeaderName; + +#[derive(Debug)] +pub(crate) struct HttpChecksumRequiredRuntimePlugin; + +impl RuntimePlugin for HttpChecksumRequiredRuntimePlugin { + fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { + interceptors.register(SharedInterceptor::new(HttpChecksumRequiredInterceptor)); + } +} + +#[derive(Debug)] +struct HttpChecksumRequiredInterceptor; + +impl Interceptor for HttpChecksumRequiredInterceptor { + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let request = context.request_mut(); + let body_bytes = request + .body() + .bytes() + .expect("checksum can only be computed for non-streaming operations"); + let checksum = ::digest(body_bytes); + request.headers_mut().insert( + HeaderName::from_static("content-md5"), + base64::encode(&checksum[..]) + .parse() + .expect("checksum is a valid header value"), + ); + Ok(()) + } +} diff --git a/rust-runtime/inlineable/src/lib.rs b/rust-runtime/inlineable/src/lib.rs index c6786d504e..b672eeef9d 100644 --- a/rust-runtime/inlineable/src/lib.rs +++ b/rust-runtime/inlineable/src/lib.rs @@ -6,6 +6,10 @@ #[allow(dead_code)] mod aws_query_compatible_errors; #[allow(unused)] +mod client_http_checksum_required; +#[allow(dead_code)] +mod client_idempotency_token; +#[allow(unused)] mod constrained; #[allow(dead_code)] mod ec2_query_errors; diff --git a/tools/ci-scripts/check-client-codegen-integration-tests b/tools/ci-scripts/check-client-codegen-integration-tests index 09ab8f735c..86013436c5 100755 --- a/tools/ci-scripts/check-client-codegen-integration-tests +++ b/tools/ci-scripts/check-client-codegen-integration-tests @@ -6,4 +6,7 @@ set -eux cd smithy-rs -./gradlew codegen-client-test:test + +# TODO(enableNewSmithyRuntimeCleanup): Only run the orchestrator version of this +./gradlew codegen-client-test:test -Psmithy.runtime.mode=middleware +./gradlew codegen-client-test:test -Psmithy.runtime.mode=orchestrator From ba726ef1eb157323fa06c5dc24ef9315323019c1 Mon Sep 17 00:00:00 2001 From: Burak Date: Thu, 29 Jun 2023 15:42:47 +0100 Subject: [PATCH 188/253] Python: Make sure to log errors from async handlers (#2820) ## Motivation and Context We were skipping logging of the errors occurred during execution of asynchronous Python handlers due to the question mark at the end: ``` result.await.map(|r| #{pyo3}::Python::with_gil(|py| r.extract::<$output>(py)))? // <- ``` Any exception thrown in asynchronous Python handlers will be logged properly with this change. ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --- .../smithy/generators/PythonServerOperationHandlerGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt index e8a704ecdd..d9ab006b0d 100644 --- a/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt +++ b/codegen-server/python/src/main/kotlin/software/amazon/smithy/rust/codegen/server/python/smithy/generators/PythonServerOperationHandlerGenerator.kt @@ -115,7 +115,7 @@ class PythonServerOperationHandlerGenerator( }; #{pyo3_asyncio}::tokio::into_future(coroutine) })?; - result.await.map(|r| #{pyo3}::Python::with_gil(|py| r.extract::<$output>(py)))? + result.await.and_then(|r| #{pyo3}::Python::with_gil(|py| r.extract::<$output>(py))) """, *codegenScope, ) From 6532a2beb687bb55a2bbe47f0e8e1072b678e724 Mon Sep 17 00:00:00 2001 From: Burak Date: Thu, 29 Jun 2023 16:19:37 +0100 Subject: [PATCH 189/253] Error out for non-escaped special chars in `@pattern`s (#2752) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation and Context Closes https://github.com/awslabs/smithy-rs/issues/2508 ## Testing Modify `codegen-core/common-test-models/pokemon.smithy`: ```diff diff --git a/codegen-core/common-test-models/pokemon.smithy b/codegen-core/common-test-models/pokemon.smithy index 014ee61c4..2dd37046e 100644 --- a/codegen-core/common-test-models/pokemon.smithy +++ b/codegen-core/common-test-models/pokemon.smithy @@ -59,9 +59,12 @@ structure GetStorageInput { passcode: String, } +@pattern("[.\\n\r]+") +string Species + /// A list of Pokémon species. list SpeciesCollection { - member: String + member: Species } /// Contents of the Pokémon storage. ``` Try to codegen example service and see the error: ```bash $ cd examples $ make codegen ... [ERROR] com.aws.example#Species: Non-escaped special characters used inside `@pattern`. You must escape them: `@pattern("[.\\n\\r]+")`. See https://github.com/awslabs/smithy-rs/issues/2508 for more details. | PatternTraitEscapedSpecialChars /smithy-rs/codegen-server-test/../codegen-core/common-test-models/pokemon.smithy:62:1 ``` (For some reason validation errors reported by plugins get formatted by [LineValidationEventFormatter](https://github.com/awslabs/smithy/blob/aca7df7daf31a0e71aebfeb2e72aee06ff707568/smithy-model/src/main/java/software/amazon/smithy/model/validation/LineValidationEventFormatter.java) but errors reported by smithy-cli formatted by [PrettyAnsiValidationFormatter](https://github.com/awslabs/smithy/blob/aca7df7daf31a0e71aebfeb2e72aee06ff707568/smithy-cli/src/main/java/software/amazon/smithy/cli/commands/PrettyAnsiValidationFormatter.java). Might be a bug in smithy-cli) Replace pattern with `@pattern("[.\\n\\r]+")` and observe that error is gone: ```bash $ make codegen ``` ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --- ...atternTraitEscapedSpecialCharsValidator.kt | 54 ++++++++ ...e.amazon.smithy.model.validation.Validator | 5 + ...rnTraitEscapedSpecialCharsValidatorTest.kt | 123 ++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidator.kt create mode 100644 codegen-server/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator create mode 100644 codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidatorTest.kt diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidator.kt new file mode 100644 index 0000000000..7b54ff48b0 --- /dev/null +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidator.kt @@ -0,0 +1,54 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import software.amazon.smithy.model.Model +import software.amazon.smithy.model.shapes.Shape +import software.amazon.smithy.model.traits.PatternTrait +import software.amazon.smithy.model.validation.AbstractValidator +import software.amazon.smithy.model.validation.ValidationEvent +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.expectTrait + +class PatternTraitEscapedSpecialCharsValidator : AbstractValidator() { + private val specialCharsWithEscapes = mapOf( + '\b' to "\\b", + '\u000C' to "\\f", + '\n' to "\\n", + '\r' to "\\r", + '\t' to "\\t", + ) + private val specialChars = specialCharsWithEscapes.keys + + override fun validate(model: Model): List { + val shapes = model.getStringShapesWithTrait(PatternTrait::class.java) + + model.getMemberShapesWithTrait(PatternTrait::class.java) + return shapes + .filter { shape -> checkMisuse(shape) } + .map { shape -> makeError(shape) } + .toList() + } + + private fun makeError(shape: Shape): ValidationEvent { + val pattern = shape.expectTrait() + val replacement = pattern.pattern.toString() + .map { specialCharsWithEscapes.getOrElse(it) { it.toString() } } + .joinToString("") + .dq() + val message = + """ + Non-escaped special characters used inside `@pattern`. + You must escape them: `@pattern($replacement)`. + See https://github.com/awslabs/smithy-rs/issues/2508 for more details. + """.trimIndent() + return error(shape, pattern, message) + } + + private fun checkMisuse(shape: Shape): Boolean { + val pattern = shape.expectTrait().pattern.pattern() + return pattern.any(specialChars::contains) + } +} diff --git a/codegen-server/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator b/codegen-server/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator new file mode 100644 index 0000000000..753c72d1ce --- /dev/null +++ b/codegen-server/src/main/resources/META-INF/services/software.amazon.smithy.model.validation.Validator @@ -0,0 +1,5 @@ +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# +software.amazon.smithy.rust.codegen.server.smithy.PatternTraitEscapedSpecialCharsValidator diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidatorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidatorTest.kt new file mode 100644 index 0000000000..042aba7159 --- /dev/null +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/PatternTraitEscapedSpecialCharsValidatorTest.kt @@ -0,0 +1,123 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.server.smithy + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.model.validation.Severity +import software.amazon.smithy.model.validation.ValidatedResultException +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel + +class PatternTraitEscapedSpecialCharsValidatorTest { + @Test + fun `should error out with a suggestion if non-escaped special chars used inside @pattern`() { + val exception = shouldThrow { + """ + namespace test + + @pattern("\t") + string MyString + """.asSmithyModel(smithyVersion = "2") + } + val events = exception.validationEvents.filter { it.severity == Severity.ERROR } + + events shouldHaveSize 1 + events[0].shapeId.get() shouldBe ShapeId.from("test#MyString") + events[0].message shouldBe """ + Non-escaped special characters used inside `@pattern`. + You must escape them: `@pattern("\\t")`. + See https://github.com/awslabs/smithy-rs/issues/2508 for more details. + """.trimIndent() + } + + @Test + fun `should suggest escaping spacial characters properly`() { + val exception = shouldThrow { + """ + namespace test + + @pattern("[.\n\\r]+") + string MyString + """.asSmithyModel(smithyVersion = "2") + } + val events = exception.validationEvents.filter { it.severity == Severity.ERROR } + + events shouldHaveSize 1 + events[0].shapeId.get() shouldBe ShapeId.from("test#MyString") + events[0].message shouldBe """ + Non-escaped special characters used inside `@pattern`. + You must escape them: `@pattern("[.\\n\\r]+")`. + See https://github.com/awslabs/smithy-rs/issues/2508 for more details. + """.trimIndent() + } + + @Test + fun `should report all non-escaped special characters`() { + val exception = shouldThrow { + """ + namespace test + + @pattern("\b") + string MyString + + @pattern("^\n$") + string MyString2 + + @pattern("^[\n]+$") + string MyString3 + + @pattern("^[\r\t]$") + string MyString4 + """.asSmithyModel(smithyVersion = "2") + } + val events = exception.validationEvents.filter { it.severity == Severity.ERROR } + events shouldHaveSize 4 + } + + @Test + fun `should report errors on string members`() { + val exception = shouldThrow { + """ + namespace test + + @pattern("\t") + string MyString + + structure MyStructure { + @pattern("\b") + field: String + } + """.asSmithyModel(smithyVersion = "2") + } + val events = exception.validationEvents.filter { it.severity == Severity.ERROR } + + events shouldHaveSize 2 + events[0].shapeId.get() shouldBe ShapeId.from("test#MyString") + events[1].shapeId.get() shouldBe ShapeId.from("test#MyStructure\$field") + } + + @Test + fun `shouldn't error out if special chars are properly escaped`() { + """ + namespace test + + @pattern("\\t") + string MyString + + @pattern("[.\\n\\r]+") + string MyString2 + + @pattern("\\b\\f\\n\\r\\t") + string MyString3 + + @pattern("\\w+") + string MyString4 + """.asSmithyModel(smithyVersion = "2") + } +} From 47b3d23ff3cabd67e797af616101f5a4ea6be5e8 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 29 Jun 2023 10:23:55 -0700 Subject: [PATCH 190/253] Create client runtime plugins once at client construction (#2819) This PR refactors the fluent client so that the client runtime plugins are created exactly once at client creation time, and reused thereafter. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../smithy/rustsdk/AwsPresigningDecorator.kt | 8 +-- .../smithy/rustsdk/CredentialProviders.kt | 2 +- .../smithy/generators/OperationGenerator.kt | 56 +++---------------- .../smithy/generators/PaginatorGenerator.kt | 10 ++-- .../ServiceRuntimePluginGenerator.kt | 25 ++++----- .../client/FluentClientGenerator.kt | 53 ++++++++++++------ .../rust/codegen/core/smithy/RuntimeType.kt | 2 + 7 files changed, 65 insertions(+), 91 deletions(-) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index 8fe4d90024..5ffd15a6a8 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -320,9 +320,8 @@ class AwsPresignedFluentBuilderMethod( """ #{alternate_presigning_serializer} - let runtime_plugins = #{Operation}::register_runtime_plugins( - #{RuntimePlugins}::new(), - self.handle.clone(), + let runtime_plugins = #{Operation}::operation_runtime_plugins( + self.handle.runtime_plugins.clone(), self.config_override ) .with_client_plugin(#{SigV4PresigningRuntimePlugin}::new(presigning_config, #{payload_override})) @@ -344,8 +343,7 @@ class AwsPresignedFluentBuilderMethod( *codegenScope, "Operation" to codegenContext.symbolProvider.toSymbol(section.operationShape), "OperationError" to section.operationErrorType, - "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::runtime_plugin::RuntimePlugins"), + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors") .resolve("SharedInterceptor"), "SigV4PresigningRuntimePlugin" to AwsRuntimeType.presigningInterceptor(runtimeConfig) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index 4ca6c24534..f73d1f8483 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -139,7 +139,7 @@ class CredentialsIdentityResolverRegistration( override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { is ServiceRuntimePluginSection.AdditionalConfig -> { - rustBlockTemplate("if let Some(credentials_cache) = self.handle.conf.credentials_cache()") { + rustBlockTemplate("if let Some(credentials_cache) = ${section.serviceConfigName}.credentials_cache()") { section.registerIdentityResolver(this, runtimeConfig) { rustTemplate( """ diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 0110a1620f..b59ff1c339 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.shapes.OperationShape 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.customize.AuthOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsInterceptorGenerator @@ -23,7 +22,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock 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.RuntimeConfig 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.smithy.customize.writeCustomizations @@ -45,39 +43,6 @@ open class OperationGenerator( // TODO(enableNewSmithyRuntimeCleanup): Remove the `traitGenerator` private val traitGenerator: HttpBoundProtocolTraitImplGenerator, ) { - companion object { - fun registerDefaultRuntimePluginsFn(runtimeConfig: RuntimeConfig): RuntimeType = - RuntimeType.forInlineFun("register_default_runtime_plugins", ClientRustModule.Operation) { - rustTemplate( - """ - pub(crate) fn register_default_runtime_plugins( - runtime_plugins: #{RuntimePlugins}, - operation: impl #{RuntimePlugin} + 'static, - handle: #{Arc}, - config_override: #{Option}, - ) -> #{RuntimePlugins} { - let mut runtime_plugins = runtime_plugins - .with_client_plugin(handle.conf.clone()) - .with_client_plugin(crate::config::ServiceRuntimePlugin::new(handle)) - .with_client_plugin(#{NoAuthRuntimePlugin}::new()) - .with_operation_plugin(operation); - if let Some(config_override) = config_override { - runtime_plugins = runtime_plugins.with_operation_plugin(config_override); - } - runtime_plugins - } - """, - *preludeScope, - "Arc" to RuntimeType.Arc, - "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), - "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::runtime_plugin::RuntimePlugins"), - "NoAuthRuntimePlugin" to RuntimeType.smithyRuntime(runtimeConfig) - .resolve("client::auth::no_auth::NoAuthRuntimePlugin"), - ) - } - } - private val model = codegenContext.model private val runtimeConfig = codegenContext.runtimeConfig private val symbolProvider = codegenContext.symbolProvider @@ -180,18 +145,16 @@ open class OperationGenerator( #{invoke_with_stop_point}(input, runtime_plugins, stop_point).await } - pub(crate) fn register_runtime_plugins( - runtime_plugins: #{RuntimePlugins}, - handle: #{Arc}, + pub(crate) fn operation_runtime_plugins( + client_runtime_plugins: #{RuntimePlugins}, config_override: #{Option}, ) -> #{RuntimePlugins} { - #{register_default_runtime_plugins}( - runtime_plugins, - Self::new(), - handle, - config_override - ) - #{additional_runtime_plugins} + let mut runtime_plugins = client_runtime_plugins.with_operation_plugin(Self::new()); + if let Some(config_override) = config_override { + runtime_plugins = runtime_plugins.with_operation_plugin(config_override); + } + runtime_plugins + #{additional_runtime_plugins} } """, *codegenScope, @@ -200,10 +163,9 @@ open class OperationGenerator( "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), "OrchestratorError" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::error::OrchestratorError"), "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), - "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugins"), + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), "StopPoint" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::StopPoint"), "invoke_with_stop_point" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke_with_stop_point"), - "register_default_runtime_plugins" to registerDefaultRuntimePluginsFn(runtimeConfig), "additional_runtime_plugins" to writable { writeCustomizations( operationCustomizations, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt index fc72dbb564..5e18a99114 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt @@ -240,15 +240,13 @@ class PaginatorGenerator private constructor( if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { rustTemplate( """ - let runtime_plugins = #{operation}::register_runtime_plugins( - #{RuntimePlugins}::new(), - handle, - None + let runtime_plugins = #{operation}::operation_runtime_plugins( + handle.runtime_plugins.clone(), + None, ); """, *codegenScope, - "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::runtime_plugin::RuntimePlugins"), + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), ) } }, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index bf47e9f41c..b8023cd6c4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -31,7 +31,7 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { /** * Hook for adding additional things to config inside service runtime plugins. */ - data class AdditionalConfig(val newLayerName: String) : ServiceRuntimePluginSection("AdditionalConfig") { + data class AdditionalConfig(val newLayerName: String, val serviceConfigName: String) : ServiceRuntimePluginSection("AdditionalConfig") { /** Adds a value to the config bag */ fun putConfigValue(writer: RustWriter, value: Writable) { writer.rust("$newLayerName.store_put(#T);", value) @@ -107,27 +107,28 @@ class ServiceRuntimePluginGenerator( customizations: List, ) { val additionalConfig = writable { - writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg")) + writeCustomizations(customizations, ServiceRuntimePluginSection.AdditionalConfig("cfg", "_service_config")) } writer.rustTemplate( """ - // TODO(enableNewSmithyRuntimeLaunch) Remove `allow(dead_code)` as well as a field `handle` when - // the field is no longer used. - ##[allow(dead_code)] ##[derive(Debug)] pub(crate) struct ServiceRuntimePlugin { - handle: #{Arc}, + config: #{Option}<#{FrozenLayer}>, } impl ServiceRuntimePlugin { - pub fn new(handle: #{Arc}) -> Self { - Self { handle } + pub fn new(_service_config: crate::config::Config) -> Self { + Self { + config: { + #{config} + }, + } } } impl #{RuntimePlugin} for ServiceRuntimePlugin { fn config(&self) -> #{Option}<#{FrozenLayer}> { - #{config} + self.config.clone() } fn interceptors(&self, interceptors: &mut #{InterceptorRegistrar}) { @@ -145,13 +146,7 @@ class ServiceRuntimePluginGenerator( rustTemplate( """ let mut cfg = #{Layer}::new(${codegenContext.serviceShape.id.name.dq()}); - - // TODO(enableNewSmithyRuntimeLaunch): Make it possible to set retry classifiers at the service level. - // Retry classifiers can also be set at the operation level and those should be added to the - // list of classifiers defined here, rather than replacing them. - #{additional_config} - Some(cfg.freeze()) """, *codegenScope, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 3f2f451f44..3292ae10bb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -41,6 +41,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTypeParameters import software.amazon.smithy.rust.codegen.core.rustlang.stripOuter import software.amazon.smithy.rust.codegen.core.rustlang.withBlockTemplate import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig 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.smithy.RustCrate @@ -125,6 +126,7 @@ class FluentClientGenerator( } }, "RetryConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::RetryConfig"), + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), "TimeoutConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout::TimeoutConfig"), // TODO(enableNewSmithyRuntimeCleanup): Delete the generics when cleaning up middleware "generics_decl" to generics.decl, @@ -182,18 +184,13 @@ class FluentClientGenerator( ##[derive(Debug)] pub(crate) struct Handle { pub(crate) conf: crate::Config, + pub(crate) runtime_plugins: #{RuntimePlugins}, } #{client_docs:W} - ##[derive(::std::fmt::Debug)] + ##[derive(#{Clone}, ::std::fmt::Debug)] pub struct Client { - handle: #{Arc} - } - - impl #{Clone} for Client { - fn clone(&self) -> Self { - Self { handle: self.handle.clone() } - } + handle: #{Arc}, } impl Client { @@ -215,7 +212,12 @@ class FluentClientGenerator( } Self { - handle: #{Arc}::new(Handle { conf }) + handle: #{Arc}::new( + Handle { + conf: conf.clone(), + runtime_plugins: #{base_client_runtime_plugins}(conf), + } + ) } } @@ -229,9 +231,7 @@ class FluentClientGenerator( // This is currently kept around so the tests still compile in both modes /// Creates a client with the given service configuration. pub fn with_config(_client: #{client}::Client, conf: crate::Config) -> Self { - Self { - handle: #{Arc}::new(Handle { conf }) - } + Self::from_conf(conf) } ##[doc(hidden)] @@ -244,6 +244,7 @@ class FluentClientGenerator( } """, *clientScope, + "base_client_runtime_plugins" to baseClientRuntimePluginsFn(runtimeConfig), ) } } @@ -505,8 +506,7 @@ class FluentClientGenerator( "Operation" to operationSymbol, "OperationError" to errorType, "OperationOutput" to outputType, - "RuntimePlugins" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::runtime_plugin::RuntimePlugins"), + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), "SendResult" to ClientRustModule.Client.customize.toType() .resolve("internal::SendResult"), "SdkError" to RuntimeType.sdkError(runtimeConfig), @@ -516,9 +516,8 @@ class FluentClientGenerator( ##[doc(hidden)] pub async fn send_orchestrator(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; - let runtime_plugins = #{Operation}::register_runtime_plugins( - #{RuntimePlugins}::new(), - self.handle, + let runtime_plugins = #{Operation}::operation_runtime_plugins( + self.handle.runtime_plugins.clone(), self.config_override ); #{Operation}::orchestrate(&runtime_plugins, input).await @@ -647,6 +646,26 @@ class FluentClientGenerator( } } +private fun baseClientRuntimePluginsFn(runtimeConfig: RuntimeConfig): RuntimeType = + RuntimeType.forInlineFun("base_client_runtime_plugins", ClientRustModule.config) { + rustTemplate( + """ + pub(crate) fn base_client_runtime_plugins( + config: crate::Config, + ) -> #{RuntimePlugins} { + #{RuntimePlugins}::new() + .with_client_plugin(config.clone()) + .with_client_plugin(crate::config::ServiceRuntimePlugin::new(config)) + .with_client_plugin(#{NoAuthRuntimePlugin}::new()) + } + """, + *preludeScope, + "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), + "NoAuthRuntimePlugin" to RuntimeType.smithyRuntime(runtimeConfig) + .resolve("client::auth::no_auth::NoAuthRuntimePlugin"), + ) + } + /** * For a given `operation` shape, return a list of strings where each string describes the name and input type of one of * the operation's corresponding fluent builder methods as well as that method's documentation from the smithy model diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index 89854d42ba..cbf2514157 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -338,6 +338,8 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag") fun configBagAccessors(runtimeConfig: RuntimeConfig): RuntimeType = smithyRuntimeApi(runtimeConfig).resolve("client::config_bag_accessors::ConfigBagAccessors") + fun runtimePlugins(runtimeConfig: RuntimeConfig): RuntimeType = + smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugins") fun boxError(runtimeConfig: RuntimeConfig): RuntimeType = smithyRuntimeApi(runtimeConfig).resolve("box_error::BoxError") fun interceptor(runtimeConfig: RuntimeConfig): RuntimeType = From ab2ea0d4c2841154c6ac57075ae239429abe989b Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Thu, 29 Jun 2023 15:21:02 -0500 Subject: [PATCH 191/253] Bifurcate the expected error message based on runtime mode (#2821) ## Motivation and Context Makes a failing test `multi_region_access_points` pass in the orchestrator ## Description The orchestrator has already an `AwsAuthStage` counterpart implemented (which is where the expected error for `multi_region_access_points` comes from in the middleware mode). This PR just updates the expected error message in the test for the orchestrator to make it pass. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ Co-authored-by: Yuki Saito --- .../integration-tests/s3/tests/endpoints.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/aws/sdk/integration-tests/s3/tests/endpoints.rs b/aws/sdk/integration-tests/s3/tests/endpoints.rs index e6428ccc2b..ec87eac2a3 100644 --- a/aws/sdk/integration-tests/s3/tests/endpoints.rs +++ b/aws/sdk/integration-tests/s3/tests/endpoints.rs @@ -62,6 +62,29 @@ async fn dual_stack() { ); } +#[cfg(aws_sdk_orchestrator_mode)] +#[tokio::test] +async fn multi_region_access_points() { + let (_captured_request, client) = test_client(|b| b); + let response = client + .get_object() + .bucket("arn:aws:s3::123456789012:accesspoint/mfzwi23gnjvgw.mrap") + .key("blah") + .send() + .await; + let error = response.expect_err("should fail—sigv4a is not supported"); + assert!( + dbg!(format!( + "{}", + aws_smithy_types::error::display::DisplayErrorContext(&error) + )) + .contains("selected auth scheme / endpoint config mismatch"), + "message should contain the correct error, found: {:?}", + error + ); +} + +#[cfg(not(aws_sdk_orchestrator_mode))] #[tokio::test] async fn multi_region_access_points() { let (_captured_request, client) = test_client(|b| b); From ac95a5d1f033725639acc70be96e65188320aa02 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 30 Jun 2023 10:46:11 -0700 Subject: [PATCH 192/253] Migrate to a new SDK example workspace structure (#2811) ## Motivation and Context When the WebAssembly SDK example was added some months ago, we changed the build process to make each SDK example its own Cargo workspace. This allowed the `.cargo/config.toml` that changed the compiler target to work correctly. However, this has led to other issues: dependency compilation is no longer shared between examples which greatly increases the time it takes for that CI step to run, and now it is even running out of disk space on the GitHub Actions runners. This PR adds support for a new workspace layout where the [`aws-doc-sdk-examples`](https://github.com/awsdocs/aws-doc-sdk-examples) repo gets to decide how the workspaces are logically grouped. If a `Cargo.toml` file exists at the example root, then the build system assumes that the _old_ "one example, one workspace" layout should be used. If there is no root `Cargo.toml`, then it assumes the new layout should be used. The `sdk-versioner` tool had to be adapted to support more complex relative path resolution to make this work, and the `publisher fix-manifests` subcommand had to be fixed to ignore workspace-only `Cargo.toml` files. The build system in this PR needs to work for both the old and new examples layout so that the `sdk-sync` process will succeed. #2810 has been filed to track removing the old example layout at a later date. [aws-doc-sdk-examples#4997](https://github.com/awsdocs/aws-doc-sdk-examples/pull/4997) changes the workspace structure of the actual examples to the new one. ## Testing - [x] Generated a full SDK with the old example layout, manually examined the output, and spot checked that some examples compile - [x] Generated a full SDK with the new example layout, manually examined the output, and spot checked that some examples compile - [x] Examples pass in CI with the old example layout - [x] Examples pass in CI with the new example layout ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/sdk/build.gradle.kts | 25 +- .../src/main/kotlin/aws/sdk/ServiceLoader.kt | 46 +++- tools/ci-build/publisher/src/package.rs | 65 +++-- .../src/subcommand/claim_crate_names.rs | 21 +- .../publisher/src/subcommand/fix_manifests.rs | 14 +- .../subcommand/generate_version_manifest.rs | 7 +- tools/ci-build/sdk-versioner/Cargo.lock | 7 + tools/ci-build/sdk-versioner/Cargo.toml | 1 + tools/ci-build/sdk-versioner/src/main.rs | 226 +++++++++++++++--- tools/ci-scripts/check-aws-sdk-examples | 2 +- tools/ci-scripts/generate-aws-sdk | 3 +- 11 files changed, 317 insertions(+), 100 deletions(-) diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index e90752cce3..d5225d66a9 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +import aws.sdk.AwsExamplesLayout import aws.sdk.AwsServices import aws.sdk.Membership import aws.sdk.discoverServices @@ -201,6 +202,7 @@ tasks.register("relocateExamples") { } into(outputDir) exclude("**/target") + exclude("**/rust-toolchain.toml") filter { line -> line.replace("build/aws-sdk/sdk/", "sdk/") } } } @@ -242,13 +244,22 @@ tasks.register("fixExampleManifests") { toolPath = sdkVersionerToolPath binaryName = "sdk-versioner" - arguments = listOf( - "use-path-and-version-dependencies", - "--isolate-crates", - "--sdk-path", "../../sdk", - "--versions-toml", outputDir.resolve("versions.toml").absolutePath, - outputDir.resolve("examples").absolutePath, - ) + arguments = when (AwsExamplesLayout.detect(project)) { + AwsExamplesLayout.Flat -> listOf( + "use-path-and-version-dependencies", + "--isolate-crates", + "--sdk-path", "../../sdk", + "--versions-toml", outputDir.resolve("versions.toml").absolutePath, + outputDir.resolve("examples").absolutePath, + ) + AwsExamplesLayout.Workspaces -> listOf( + "use-path-and-version-dependencies", + "--isolate-crates", + "--sdk-path", sdkOutputDir.absolutePath, + "--versions-toml", outputDir.resolve("versions.toml").absolutePath, + outputDir.resolve("examples").absolutePath, + ) + } outputs.dir(outputDir) dependsOn("relocateExamples", "generateVersionManifest") diff --git a/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt b/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt index c1f76be0c0..189bce6a67 100644 --- a/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt +++ b/buildSrc/src/main/kotlin/aws/sdk/ServiceLoader.kt @@ -19,6 +19,38 @@ data class RootTest( val manifestName: String, ) +// TODO(https://github.com/awslabs/smithy-rs/issues/2810): We can remove the `Flat` layout after the switch +// to `Workspaces` has been released. This can be checked by looking at the `examples/` directory in aws-sdk-rust's +// main branch. +// +// The `Flat` layout is retained for backwards compatibility so that the next release process can succeed. +enum class AwsExamplesLayout { + /** + * Directory layout for examples used prior to June 26, 2023, + * where each example was in the `rust_dev_preview/` root directory and + * was considered to be its own workspace. + * + * This layout had issues with CI in terms of time to compile and disk space required + * since the dependencies would get recompiled for every example. + */ + Flat, + + /** + * Current directory layout where there are a small number of workspaces + * rooted in `rust_dev_preview/`. + */ + Workspaces, + ; + + companion object { + fun detect(project: Project): AwsExamplesLayout = if (project.projectDir.resolve("examples/Cargo.toml").exists()) { + AwsExamplesLayout.Flat + } else { + AwsExamplesLayout.Workspaces + } + } +} + class AwsServices( private val project: Project, services: List, @@ -44,10 +76,16 @@ class AwsServices( } val examples: List by lazy { - project.projectDir.resolve("examples") - .listFiles { file -> !file.name.startsWith(".") }.orEmpty().toList() - .filter { file -> manifestCompatibleWithGeneratedServices(file) } - .map { "examples/${it.name}" } + val examplesRoot = project.projectDir.resolve("examples") + if (AwsExamplesLayout.detect(project) == AwsExamplesLayout.Flat) { + examplesRoot.listFiles { file -> !file.name.startsWith(".") }.orEmpty().toList() + .filter { file -> manifestCompatibleWithGeneratedServices(file) } + .map { "examples/${it.name}" } + } else { + examplesRoot.listFiles { file -> + !file.name.startsWith(".") && file.isDirectory() && file.resolve("Cargo.toml").exists() + }.orEmpty().toList().map { "examples/${it.name}" } + } } /** diff --git a/tools/ci-build/publisher/src/package.rs b/tools/ci-build/publisher/src/package.rs index 7156c9214b..c0cd981b0b 100644 --- a/tools/ci-build/publisher/src/package.rs +++ b/tools/ci-build/publisher/src/package.rs @@ -145,8 +145,7 @@ pub async fn discover_and_validate_package_batches( fs: Fs, path: impl AsRef, ) -> Result<(Vec, PackageStats)> { - let manifest_paths = discover_package_manifests(path.as_ref().into()).await?; - let packages = read_packages(fs, manifest_paths) + let packages = discover_packages(fs, path.as_ref().into()) .await? .into_iter() .filter(|package| package.publish == Publish::Allowed) @@ -176,7 +175,7 @@ pub enum Error { /// Discovers all Cargo.toml files under the given path recursively #[async_recursion::async_recursion] -pub async fn discover_package_manifests(path: PathBuf) -> Result> { +pub async fn discover_manifests(path: PathBuf) -> Result> { let mut manifests = Vec::new(); let mut read_dir = fs::read_dir(&path).await?; while let Some(entry) = read_dir.next_entry().await? { @@ -185,14 +184,19 @@ pub async fn discover_package_manifests(path: PathBuf) -> Result> { let manifest_path = package_path.join("Cargo.toml"); if manifest_path.exists() { manifests.push(manifest_path); - } else { - manifests.extend(discover_package_manifests(package_path).await?.into_iter()); } + manifests.extend(discover_manifests(package_path).await?.into_iter()); } } Ok(manifests) } +/// Discovers and parses all Cargo.toml files that are packages (as opposed to being exclusively workspaces) +pub async fn discover_packages(fs: Fs, path: PathBuf) -> Result> { + let manifest_paths = discover_manifests(path).await?; + read_packages(fs, manifest_paths).await +} + /// Parses a semver version number and adds additional error context when parsing fails. pub fn parse_version(manifest_path: &Path, version: &str) -> Result { Version::parse(version) @@ -219,26 +223,33 @@ fn read_dependencies(path: &Path, dependencies: &DepsSet) -> Result Result { +/// Returns `Ok(None)` when the Cargo.toml is a workspace rather than a package +fn read_package(path: &Path, manifest_bytes: &[u8]) -> Result> { let manifest = Manifest::from_slice(manifest_bytes) .with_context(|| format!("failed to load package manifest for {:?}", path))?; - let package = manifest - .package - .ok_or_else(|| Error::InvalidManifest(path.into())) - .context("crate manifest doesn't have a `[package]` section")?; - let name = package.name; - let version = parse_version(path, &package.version)?; - let handle = PackageHandle { name, version }; - let publish = match package.publish { - cargo_toml::Publish::Flag(true) => Publish::Allowed, - _ => Publish::NotAllowed, - }; - - let mut local_dependencies = BTreeSet::new(); - local_dependencies.extend(read_dependencies(path, &manifest.dependencies)?.into_iter()); - local_dependencies.extend(read_dependencies(path, &manifest.dev_dependencies)?.into_iter()); - local_dependencies.extend(read_dependencies(path, &manifest.build_dependencies)?.into_iter()); - Ok(Package::new(handle, path, local_dependencies, publish)) + if let Some(package) = manifest.package { + let name = package.name; + let version = parse_version(path, &package.version)?; + let handle = PackageHandle { name, version }; + let publish = match package.publish { + cargo_toml::Publish::Flag(true) => Publish::Allowed, + _ => Publish::NotAllowed, + }; + + let mut local_dependencies = BTreeSet::new(); + local_dependencies.extend(read_dependencies(path, &manifest.dependencies)?.into_iter()); + local_dependencies.extend(read_dependencies(path, &manifest.dev_dependencies)?.into_iter()); + local_dependencies + .extend(read_dependencies(path, &manifest.build_dependencies)?.into_iter()); + Ok(Some(Package::new( + handle, + path, + local_dependencies, + publish, + ))) + } else { + Ok(None) + } } /// Validates that all of the publishable crates use consistent version numbers @@ -275,7 +286,9 @@ pub async fn read_packages(fs: Fs, manifest_paths: Vec) -> Result = fs.read_file(path).await?; - result.push(read_package(path, &contents)?); + if let Some(package) = read_package(path, &contents)? { + result.push(package); + } } Ok(result) } @@ -350,7 +363,9 @@ mod tests { "#; let path: PathBuf = "test/Cargo.toml".into(); - let package = read_package(&path, manifest).expect("parse success"); + let package = read_package(&path, manifest) + .expect("parse success") + .expect("is a package"); assert_eq!("test", package.handle.name); assert_eq!(version("1.2.0-preview"), package.handle.version); diff --git a/tools/ci-build/publisher/src/subcommand/claim_crate_names.rs b/tools/ci-build/publisher/src/subcommand/claim_crate_names.rs index 72a3285ddb..ae7a41b304 100644 --- a/tools/ci-build/publisher/src/subcommand/claim_crate_names.rs +++ b/tools/ci-build/publisher/src/subcommand/claim_crate_names.rs @@ -3,12 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ use crate::fs::Fs; -use crate::package::{discover_package_manifests, Error, PackageHandle}; +use crate::package::{discover_packages, PackageHandle, Publish}; use crate::publish::{has_been_published_on_crates_io, publish}; use crate::subcommand::publish::correct_owner; use crate::{cargo, SDK_REPO_NAME}; -use anyhow::Context; -use cargo_toml::Manifest; use clap::Parser; use dialoguer::Confirm; use semver::Version; @@ -79,20 +77,11 @@ async fn discover_publishable_crate_names(repository_root: &Path) -> anyhow::Res fs: Fs, path: PathBuf, ) -> anyhow::Result> { - let manifest_paths = discover_package_manifests(path).await?; + let packages = discover_packages(fs, path).await?; let mut publishable_package_names = HashSet::new(); - for manifest_path in manifest_paths { - let contents: Vec = fs.read_file(&manifest_path).await?; - let manifest = Manifest::from_slice(&contents).with_context(|| { - format!("failed to load package manifest for {:?}", manifest_path) - })?; - let package = manifest - .package - .ok_or(Error::InvalidManifest(manifest_path)) - .context("crate manifest doesn't have a `[package]` section")?; - let name = package.name; - if let cargo_toml::Publish::Flag(true) = package.publish { - publishable_package_names.insert(name); + for package in packages { + if let Publish::Allowed = package.publish { + publishable_package_names.insert(package.handle.name); } } Ok(publishable_package_names) diff --git a/tools/ci-build/publisher/src/subcommand/fix_manifests.rs b/tools/ci-build/publisher/src/subcommand/fix_manifests.rs index c6c15afceb..910f5fd51c 100644 --- a/tools/ci-build/publisher/src/subcommand/fix_manifests.rs +++ b/tools/ci-build/publisher/src/subcommand/fix_manifests.rs @@ -10,7 +10,7 @@ //! version numbers in addition to the dependency path. use crate::fs::Fs; -use crate::package::{discover_package_manifests, parse_version}; +use crate::package::{discover_manifests, parse_version}; use crate::SDK_REPO_NAME; use anyhow::{bail, Context, Result}; use clap::Parser; @@ -20,6 +20,7 @@ use std::collections::BTreeMap; use std::ffi::OsStr; use std::path::{Path, PathBuf}; use toml::value::Table; +use toml::Value; use tracing::info; mod validate; @@ -55,7 +56,7 @@ pub async fn subcommand_fix_manifests( true => Mode::Check, false => Mode::Execute, }; - let manifest_paths = discover_package_manifests(location.into()).await?; + let manifest_paths = discover_manifests(location.into()).await?; let mut manifests = read_manifests(Fs::Real, manifest_paths).await?; let versions = package_versions(&manifests)?; @@ -91,6 +92,15 @@ fn package_versions(manifests: &[Manifest]) -> Result> Some(package) => package, None => continue, }; + // ignore non-publishable crates + if let Some(Value::Boolean(false)) = manifest + .metadata + .get("package") + .expect("checked above") + .get("publish") + { + continue; + } let name = package .get("name") .and_then(|name| name.as_str()) diff --git a/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs b/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs index ffa5eabca1..6b9ac35397 100644 --- a/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs +++ b/tools/ci-build/publisher/src/subcommand/generate_version_manifest.rs @@ -4,7 +4,7 @@ */ use crate::fs::Fs; -use crate::package::{discover_package_manifests, read_packages}; +use crate::package::discover_packages; use anyhow::{bail, Context, Result}; use clap::Parser; use semver::Version; @@ -70,10 +70,7 @@ pub async fn subcommand_generate_version_manifest( (None, Some(output_location)) => output_location, _ => bail!("Only one of `--location` or `--output-location` should be provided"), }; - let manifests = discover_package_manifests(input_location.into()) - .await - .context("discover package manifests")?; - let packages = read_packages(Fs::Real, manifests) + let packages = discover_packages(Fs::Real, input_location.into()) .await .context("read packages")?; diff --git a/tools/ci-build/sdk-versioner/Cargo.lock b/tools/ci-build/sdk-versioner/Cargo.lock index 4da329eedd..eafd9037ee 100644 --- a/tools/ci-build/sdk-versioner/Cargo.lock +++ b/tools/ci-build/sdk-versioner/Cargo.lock @@ -572,6 +572,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + [[package]] name = "percent-encoding" version = "2.2.0" @@ -748,6 +754,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", + "pathdiff", "pretty_assertions", "smithy-rs-tool-common", "tempfile", diff --git a/tools/ci-build/sdk-versioner/Cargo.toml b/tools/ci-build/sdk-versioner/Cargo.toml index 7a20bbdd14..ee732cc430 100644 --- a/tools/ci-build/sdk-versioner/Cargo.toml +++ b/tools/ci-build/sdk-versioner/Cargo.toml @@ -15,6 +15,7 @@ opt-level = 0 [dependencies] anyhow = "1.0" clap = { version = "~3.1.18", features = ["derive"] } +pathdiff = "0.2.1" smithy-rs-tool-common = { version = "0.1", path = "../smithy-rs-tool-common" } toml_edit = { version = "0.19.6" } diff --git a/tools/ci-build/sdk-versioner/src/main.rs b/tools/ci-build/sdk-versioner/src/main.rs index 77cf9ff606..63ffd6bd2f 100644 --- a/tools/ci-build/sdk-versioner/src/main.rs +++ b/tools/ci-build/sdk-versioner/src/main.rs @@ -29,6 +29,7 @@ enum Args { /// Path(s) to recursively update Cargo.toml files in #[clap()] crate_paths: Vec, + // TODO(https://github.com/awslabs/smithy-rs/issues/2810): `isolate_crates` can be removed once the `Flat` example directory structure is cleaned up /// Makes each individual crate its own workspace #[clap(long)] isolate_crates: bool, @@ -41,6 +42,7 @@ enum Args { /// Path(s) to recursively update Cargo.toml files in #[clap()] crate_paths: Vec, + // TODO(https://github.com/awslabs/smithy-rs/issues/2810): `isolate_crates` can be removed once the `Flat` example directory structure is cleaned up /// Makes each individual crate its own workspace #[clap(long)] isolate_crates: bool, @@ -56,6 +58,7 @@ enum Args { /// Path(s) to recursively update Cargo.toml files in #[clap()] crate_paths: Vec, + // TODO(https://github.com/awslabs/smithy-rs/issues/2810): `isolate_crates` can be removed once the `Flat` example directory structure is cleaned up /// Makes each individual crate its own workspace #[clap(long)] isolate_crates: bool, @@ -87,8 +90,26 @@ impl Args { } } -struct DependencyContext<'a> { - sdk_path: Option<&'a Path>, +// TODO(https://github.com/awslabs/smithy-rs/issues/2810): Remove `SdkPath` and just use a `PathBuf` with the new logic +// This is only around for backwards compatibility for the next release's sync process0 +enum SdkPath { + /// Don't even attempt to resolve the correct relative path to dependencies + UseDumbLogic(PathBuf), + /// Resolve the correct relative path to dependencies + UseNewLogic(PathBuf), +} +impl From<&PathBuf> for SdkPath { + fn from(value: &PathBuf) -> Self { + if !value.exists() { + SdkPath::UseDumbLogic(value.into()) + } else { + SdkPath::UseNewLogic(value.into()) + } + } +} + +struct DependencyContext { + sdk_path: Option, versions_manifest: Option, } @@ -96,7 +117,7 @@ fn main() -> Result<()> { let args = Args::parse().validate()?; let dependency_context = match &args { Args::UsePathDependencies { sdk_path, .. } => DependencyContext { - sdk_path: Some(sdk_path), + sdk_path: Some(sdk_path.into()), versions_manifest: None, }, Args::UseVersionDependencies { versions_toml, .. } => DependencyContext { @@ -108,7 +129,7 @@ fn main() -> Result<()> { versions_toml, .. } => DependencyContext { - sdk_path: Some(sdk_path), + sdk_path: Some(sdk_path.into()), versions_manifest: Some(VersionsManifest::from_file(versions_toml)?), }, }; @@ -133,6 +154,7 @@ fn update_manifest( isolate_crates: bool, ) -> anyhow::Result<()> { println!("Updating {:?}...", manifest_path); + let crate_path = manifest_path.parent().expect("manifest has a parent"); let mut metadata: Document = String::from_utf8( fs::read(manifest_path).with_context(|| format!("failed to read {manifest_path:?}"))?, @@ -150,9 +172,11 @@ fn update_manifest( manifest_path ); } - changed = - update_dependencies(dependencies.as_table_mut().unwrap(), dependency_context)? - || changed; + changed = update_dependencies( + dependencies.as_table_mut().unwrap(), + dependency_context, + crate_path, + )? || changed; } } if isolate_crates && !metadata.contains_key("workspace") { @@ -177,6 +201,7 @@ fn update_manifest( fn update_dependencies( dependencies: &mut Table, dependency_context: &DependencyContext, + crate_path: &Path, ) -> Result { let mut changed = false; for (key, value) in dependencies.iter_mut() { @@ -191,6 +216,7 @@ fn update_dependencies( key.get(), old_value, dependency_context, + crate_path, )?)); changed = true; } @@ -210,10 +236,14 @@ fn crate_path_name(name: &str) -> &str { } fn updated_dependency_value( - crate_name: &str, + dependency_name: &str, old_value: Table, dependency_context: &DependencyContext, + crate_path: &Path, ) -> Result { + let crate_path = crate_path + .canonicalize() + .context("failed to canonicalize crate path")?; let mut value = old_value; // Remove keys that will be replaced @@ -223,25 +253,45 @@ fn updated_dependency_value( value.remove("path"); // Set the `path` if one was given - if let Some(path) = &dependency_context.sdk_path { - let crate_path = path.join(crate_path_name(crate_name)); - value["path"] = toml_edit::value( - crate_path - .as_os_str() - .to_str() - .expect("valid utf-8 path") - .to_string(), - ); + match &dependency_context.sdk_path { + Some(SdkPath::UseDumbLogic(sdk_path)) => { + let crate_path = sdk_path.join(crate_path_name(dependency_name)); + value["path"] = toml_edit::value( + crate_path + .as_os_str() + .to_str() + .expect("valid utf-8 path") + .to_string(), + ); + } + Some(SdkPath::UseNewLogic(sdk_path)) => { + let dependency_path = sdk_path + .join(crate_path_name(dependency_name)) + .canonicalize() + .context("failed to canonicalize dependency path")?; + if let Some(relative_path) = pathdiff::diff_paths(&dependency_path, &crate_path) { + value["path"] = toml_edit::value( + relative_path + .as_os_str() + .to_str() + .expect("valid utf-8 path") + .to_string(), + ); + } else { + bail!("Failed to create relative path from {crate_path:?} to {dependency_path:?}"); + } + } + _ => {} } // Set the `version` if one was given if let Some(manifest) = &dependency_context.versions_manifest { - if let Some(crate_metadata) = manifest.crates.get(crate_name) { + if let Some(crate_metadata) = manifest.crates.get(dependency_name) { value["version"] = toml_edit::value(crate_metadata.version.clone()); } else { bail!( "Crate `{}` was missing from the `versions.toml`", - crate_name + dependency_name ); } } @@ -270,11 +320,12 @@ fn discover_manifests(manifests: &mut Vec, path: impl AsRef) -> a #[cfg(test)] mod tests { - use crate::{update_manifest, DependencyContext}; + use crate::{crate_path_name, update_manifest, DependencyContext, SdkPath}; use pretty_assertions::assert_eq; use smithy_rs_tool_common::package::PackageCategory; use smithy_rs_tool_common::versions_manifest::{CrateVersion, VersionsManifest}; use std::path::PathBuf; + use std::{fs, process}; fn versions_toml_for(crates: &[(&str, &str)]) -> VersionsManifest { VersionsManifest { @@ -321,12 +372,47 @@ features = ["foo", "baz"] "#; #[track_caller] - fn test_with_context(isolate_crates: bool, context: DependencyContext, expected: &[u8]) { - let manifest_file = tempfile::NamedTempFile::new().unwrap(); - let manifest_path = manifest_file.into_temp_path(); + fn test_with_context( + isolate_crates: bool, + crate_path_rel: &str, + sdk_crates: &[&'static str], + context: DependencyContext, + expected: &[u8], + ) { + let temp_dir = tempfile::tempdir().unwrap(); + let crate_path = temp_dir.path().join(crate_path_rel); + fs::create_dir_all(&crate_path).unwrap(); + + let manifest_path = crate_path.join("Cargo.toml"); std::fs::write(&manifest_path, TEST_MANIFEST).unwrap(); - update_manifest(&manifest_path, &context, isolate_crates).expect("success"); + if let Some(SdkPath::UseNewLogic(sdk_path)) = context.sdk_path.as_ref() { + for sdk_crate in sdk_crates { + let sdk_crate_path = temp_dir + .path() + .join(sdk_path) + .join(crate_path_name(sdk_crate)); + fs::create_dir_all(sdk_crate_path).unwrap(); + } + } + // Assist with debugging when the tests fail + if let Ok(output) = process::Command::new("find").arg(temp_dir.path()).output() { + println!( + "Test directory structure:\n{}", + String::from_utf8_lossy(&output.stdout) + ); + } + + let fixed_context = if let Some(SdkPath::UseNewLogic(sdk_path)) = context.sdk_path.as_ref() + { + DependencyContext { + sdk_path: Some(SdkPath::UseNewLogic(temp_dir.path().join(sdk_path))), + versions_manifest: context.versions_manifest, + } + } else { + context + }; + update_manifest(&manifest_path, &fixed_context, isolate_crates).expect("success"); let actual = String::from_utf8(std::fs::read(&manifest_path).expect("read tmp file")).unwrap(); @@ -338,6 +424,8 @@ features = ["foo", "baz"] fn update_dependencies_with_versions() { test_with_context( false, + "examples/foo", + &[], DependencyContext { sdk_path: None, versions_manifest: Some(versions_toml_for(&[ @@ -374,8 +462,54 @@ features = ["foo", "baz"] fn update_dependencies_with_paths() { test_with_context( false, + "path/to/test", + &[ + "aws-config", + "aws-sdk-s3", + "aws-smithy-types", + "aws-smithy-http", + ], + DependencyContext { + sdk_path: Some(SdkPath::UseNewLogic(PathBuf::from("sdk"))), + versions_manifest: None, + }, + br#" +[package] +name = "test" +version = "0.1.0" + +# Some comment that should be preserved +[dependencies] +aws-config = { path = "../../../sdk/aws-config" } +aws-sdk-s3 = { path = "../../../sdk/s3" } +aws-smithy-types = { path = "../../../sdk/aws-smithy-types" } +aws-smithy-http = { path = "../../../sdk/aws-smithy-http", features = ["test-util"] } +something-else = { version = "0.1", no-default-features = true } +tokio = { version = "1.18", features = ["net"] } + +[dev-dependencies.another-thing] +# some comment +version = "5.0" +# another comment +features = ["foo", "baz"] +"#, + ); + } + + // TODO(https://github.com/awslabs/smithy-rs/issues/2810): Remove this test + #[test] + fn update_dependencies_with_paths_dumb_logic() { + test_with_context( + false, + "path/to/test", + &[ + "aws-config", + "aws-sdk-s3", + "aws-smithy-types", + "aws-smithy-http", + ], DependencyContext { - sdk_path: Some(&PathBuf::from("/foo/asdf/")), + sdk_path: Some(SdkPath::UseDumbLogic(PathBuf::from("a/dumb/path/to"))), versions_manifest: None, }, br#" @@ -385,10 +519,10 @@ version = "0.1.0" # Some comment that should be preserved [dependencies] -aws-config = { path = "/foo/asdf/aws-config" } -aws-sdk-s3 = { path = "/foo/asdf/s3" } -aws-smithy-types = { path = "/foo/asdf/aws-smithy-types" } -aws-smithy-http = { path = "/foo/asdf/aws-smithy-http", features = ["test-util"] } +aws-config = { path = "a/dumb/path/to/aws-config" } +aws-sdk-s3 = { path = "a/dumb/path/to/s3" } +aws-smithy-types = { path = "a/dumb/path/to/aws-smithy-types" } +aws-smithy-http = { path = "a/dumb/path/to/aws-smithy-http", features = ["test-util"] } something-else = { version = "0.1", no-default-features = true } tokio = { version = "1.18", features = ["net"] } @@ -405,8 +539,15 @@ features = ["foo", "baz"] fn update_dependencies_with_versions_and_paths() { test_with_context( false, + "deep/path/to/test", + &[ + "aws-config", + "aws-sdk-s3", + "aws-smithy-types", + "aws-smithy-http", + ], DependencyContext { - sdk_path: Some(&PathBuf::from("/foo/asdf/")), + sdk_path: Some(SdkPath::UseNewLogic(PathBuf::from("sdk"))), versions_manifest: Some(versions_toml_for(&[ ("aws-config", "0.5.0"), ("aws-sdk-s3", "0.13.0"), @@ -421,10 +562,10 @@ version = "0.1.0" # Some comment that should be preserved [dependencies] -aws-config = { version = "0.5.0", path = "/foo/asdf/aws-config" } -aws-sdk-s3 = { version = "0.13.0", path = "/foo/asdf/s3" } -aws-smithy-types = { version = "0.10.0", path = "/foo/asdf/aws-smithy-types" } -aws-smithy-http = { version = "0.9.0", path = "/foo/asdf/aws-smithy-http", features = ["test-util"] } +aws-config = { version = "0.5.0", path = "../../../../sdk/aws-config" } +aws-sdk-s3 = { version = "0.13.0", path = "../../../../sdk/s3" } +aws-smithy-types = { version = "0.10.0", path = "../../../../sdk/aws-smithy-types" } +aws-smithy-http = { version = "0.9.0", path = "../../../../sdk/aws-smithy-http", features = ["test-util"] } something-else = { version = "0.1", no-default-features = true } tokio = { version = "1.18", features = ["net"] } @@ -441,8 +582,15 @@ features = ["foo", "baz"] fn update_dependencies_isolate_crates() { test_with_context( true, + "deep/path/to/test", + &[ + "aws-config", + "aws-sdk-s3", + "aws-smithy-types", + "aws-smithy-http", + ], DependencyContext { - sdk_path: Some(&PathBuf::from("/foo/asdf/")), + sdk_path: Some(SdkPath::UseNewLogic(PathBuf::from("sdk"))), versions_manifest: Some(versions_toml_for(&[ ("aws-config", "0.5.0"), ("aws-sdk-s3", "0.13.0"), @@ -459,10 +607,10 @@ version = "0.1.0" # Some comment that should be preserved [dependencies] -aws-config = { version = "0.5.0", path = "/foo/asdf/aws-config" } -aws-sdk-s3 = { version = "0.13.0", path = "/foo/asdf/s3" } -aws-smithy-types = { version = "0.10.0", path = "/foo/asdf/aws-smithy-types" } -aws-smithy-http = { version = "0.9.0", path = "/foo/asdf/aws-smithy-http", features = ["test-util"] } +aws-config = { version = "0.5.0", path = "../../../../sdk/aws-config" } +aws-sdk-s3 = { version = "0.13.0", path = "../../../../sdk/s3" } +aws-smithy-types = { version = "0.10.0", path = "../../../../sdk/aws-smithy-types" } +aws-smithy-http = { version = "0.9.0", path = "../../../../sdk/aws-smithy-http", features = ["test-util"] } something-else = { version = "0.1", no-default-features = true } tokio = { version = "1.18", features = ["net"] } diff --git a/tools/ci-scripts/check-aws-sdk-examples b/tools/ci-scripts/check-aws-sdk-examples index 46495665a0..db5880b499 100755 --- a/tools/ci-scripts/check-aws-sdk-examples +++ b/tools/ci-scripts/check-aws-sdk-examples @@ -13,6 +13,6 @@ cd aws-sdk/examples for example in *; do echo -e "${C_YELLOW}Checking examples/${example}...${C_RESET}" pushd "${example}" &>/dev/null - cargo check + cargo check && cargo clean popd &>/dev/null done diff --git a/tools/ci-scripts/generate-aws-sdk b/tools/ci-scripts/generate-aws-sdk index 818e536126..18b29529ad 100755 --- a/tools/ci-scripts/generate-aws-sdk +++ b/tools/ci-scripts/generate-aws-sdk @@ -25,7 +25,8 @@ echo -e "${C_YELLOW}Taking examples from 'awsdocs/aws-doc-sdk-examples'...${C_RE examples_revision=$(cd aws-doc-sdk-examples; git rev-parse HEAD) mv aws-doc-sdk-examples/rust_dev_preview smithy-rs/aws/sdk/examples rm -rf smithy-rs/aws/sdk/examples/.cargo -rm smithy-rs/aws/sdk/examples/Cargo.toml +# TODO(https://github.com/awslabs/smithy-rs/issues/2810): This Cargo.toml `rm` can be removed when the flat example structure is cleaned up +rm -f smithy-rs/aws/sdk/examples/Cargo.toml echo -e "${C_YELLOW}Creating empty model metadata file since we don't have model update information...${C_RESET}" MODEL_METADATA_PATH="$(pwd)/model-metadata.toml" From 71b401f5bf22904323d2ddeedc71d423c2d3d822 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 30 Jun 2023 10:48:41 -0700 Subject: [PATCH 193/253] Move endpoint module and add endpoint re-exports (#2822) All the types in the `crate::endpoint` module are related to endpoint configuration, so they should be moved into `crate::config::endpoint` to be consistent with the rest of the generated crate organization. This PR moves them for the orchestrator implementation, but retains them in `crate::endpoint` when generating for middleware. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../codegen/client/smithy/ClientRustModule.kt | 16 +++++++++++++++- .../endpoint/EndpointConfigCustomization.kt | 8 +++++--- .../smithy/endpoint/EndpointTypesGenerator.kt | 6 +++--- .../smithy/endpoint/EndpointsDecorator.kt | 6 +++--- .../generators/EndpointParamsGenerator.kt | 19 +++++++++++-------- .../generators/EndpointResolverGenerator.kt | 16 ++++++++++------ .../ClientRuntimeTypesReExportGenerator.kt | 10 ++++++++++ .../endpoint/EndpointParamsGeneratorTest.kt | 4 +++- .../endpoint/EndpointResolverGeneratorTest.kt | 13 ++++++++----- 9 files changed, 68 insertions(+), 30 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt index 5f46cdafb1..93a0c2de16 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt @@ -60,6 +60,9 @@ object ClientRustModule { /** crate::client */ val self = RustModule.public("config") + /** crate::config::endpoint */ + val endpoint = RustModule.public("endpoint", parent = self) + /** crate::config::retry */ val retry = RustModule.public("retry", parent = self) @@ -70,8 +73,18 @@ object ClientRustModule { val interceptors = RustModule.public("interceptors", parent = self) } - val Error = RustModule.public("error") + // TODO(enableNewSmithyRuntimeCleanup): Delete this root endpoint module + @Deprecated(message = "use the endpoint() method to get the endpoint module for now") val Endpoint = RustModule.public("endpoint") + + // TODO(enableNewSmithyRuntimeCleanup): Just use Config.endpoint directly and delete this function + fun endpoint(codegenContext: ClientCodegenContext): RustModule.LeafModule = if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + Endpoint + } else { + Config.endpoint + } + + val Error = RustModule.public("error") val Operation = RustModule.public("operation") val Meta = RustModule.public("meta") val Input = RustModule.public("input") @@ -99,6 +112,7 @@ class ClientModuleDocProvider( ClientRustModule.client -> clientModuleDoc() ClientRustModule.Client.customize -> customizeModuleDoc() ClientRustModule.config -> strDoc("Configuration for $serviceName.") + ClientRustModule.Config.endpoint -> strDoc("Types needed to configure endpoint resolution.") ClientRustModule.Config.retry -> strDoc("Retry configuration.") ClientRustModule.Config.timeout -> strDoc("Timeout configuration.") ClientRustModule.Config.interceptors -> strDoc("Types needed to implement [`Interceptor`](crate::config::Interceptor).") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index 9cfc92e92d..a6b07ecaaa 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -19,7 +19,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.pre * Customization which injects an Endpoints 2.0 Endpoint Resolver into the service config struct */ internal class EndpointConfigCustomization( - codegenContext: ClientCodegenContext, + private val codegenContext: ClientCodegenContext, private val typesGenerator: EndpointTypesGenerator, ) : ConfigCustomization() { @@ -86,6 +86,8 @@ internal class EndpointConfigCustomization( ServiceConfig.BuilderImpl -> { // if there are no rules, we don't generate a default resolver—we need to also suppress those docs. val defaultResolverDocs = if (typesGenerator.defaultResolver() != null) { + val endpointModule = ClientRustModule.endpoint(codegenContext).fullyQualifiedPath() + .replace("crate::", "$moduleUseName::") """ /// /// When unset, the client will used a generated endpoint resolver based on the endpoint resolution @@ -94,7 +96,7 @@ internal class EndpointConfigCustomization( /// ## Examples /// ```no_run /// use aws_smithy_http::endpoint; - /// use $moduleUseName::endpoint::{Params as EndpointParams, DefaultResolver}; + /// use $endpointModule::{Params as EndpointParams, DefaultResolver}; /// /// Endpoint resolver which adds a prefix to the generated endpoint /// ##[derive(Debug)] /// struct PrefixResolver { @@ -193,7 +195,7 @@ internal class EndpointConfigCustomization( } } else { val alwaysFailsResolver = - RuntimeType.forInlineFun("MissingResolver", ClientRustModule.Endpoint) { + RuntimeType.forInlineFun("MissingResolver", ClientRustModule.endpoint(codegenContext)) { rustTemplate( """ ##[derive(Debug)] diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt index 0906a4f6db..889b741ebb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointTypesGenerator.kt @@ -40,10 +40,10 @@ class EndpointTypesGenerator( } } - fun paramsStruct(): RuntimeType = EndpointParamsGenerator(params).paramsStruct() - fun paramsBuilder(): RuntimeType = EndpointParamsGenerator(params).paramsBuilder() + fun paramsStruct(): RuntimeType = EndpointParamsGenerator(codegenContext, params).paramsStruct() + fun paramsBuilder(): RuntimeType = EndpointParamsGenerator(codegenContext, params).paramsBuilder() fun defaultResolver(): RuntimeType? = - rules?.let { EndpointResolverGenerator(stdlib, runtimeConfig).defaultEndpointResolver(it) } + rules?.let { EndpointResolverGenerator(codegenContext, stdlib).defaultEndpointResolver(it) } fun testGenerator(): Writable = defaultResolver()?.let { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt index e9176ff767..351d205813 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecorator.kt @@ -18,7 +18,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.CustomRuntimeFunction import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsGenerator -import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointTests +import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.endpointTestsModule import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rulesgen.SmithyEndpointsStdLib import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection @@ -134,8 +134,8 @@ class EndpointsDecorator : ClientCodegenDecorator { override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { val generator = EndpointTypesGenerator.fromContext(codegenContext) - rustCrate.withModule(ClientRustModule.Endpoint) { - withInlineModule(EndpointTests, rustCrate.moduleDocProvider) { + rustCrate.withModule(ClientRustModule.endpoint(codegenContext)) { + withInlineModule(endpointTestsModule(codegenContext), rustCrate.moduleDocProvider) { generator.testGenerator()(this) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt index 73752e12c9..779a4eb8d2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsGenerator.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators import software.amazon.smithy.rulesengine.language.eval.Value import software.amazon.smithy.rulesengine.language.syntax.Identifier import software.amazon.smithy.rulesengine.language.syntax.parameters.Parameters +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.endpoint.memberName import software.amazon.smithy.rust.codegen.client.smithy.endpoint.rustName @@ -37,12 +38,12 @@ import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.orNull // internals contains the actual resolver function -val EndpointImpl = RustModule.private("internals", parent = ClientRustModule.Endpoint) +fun endpointImplModule(codegenContext: ClientCodegenContext) = RustModule.private("internals", parent = ClientRustModule.endpoint(codegenContext)) -val EndpointTests = RustModule.new( +fun endpointTestsModule(codegenContext: ClientCodegenContext) = RustModule.new( "test", visibility = Visibility.PRIVATE, - parent = ClientRustModule.Endpoint, + parent = ClientRustModule.endpoint(codegenContext), inline = true, documentationOverride = "", ).cfgTest() @@ -108,22 +109,24 @@ val EndpointStdLib = RustModule.private("endpoint_lib") * ``` */ -internal class EndpointParamsGenerator(private val parameters: Parameters) { - +internal class EndpointParamsGenerator( + private val codegenContext: ClientCodegenContext, + private val parameters: Parameters, +) { companion object { fun memberName(parameterName: String) = Identifier.of(parameterName).rustName() fun setterName(parameterName: String) = "set_${memberName(parameterName)}" } - fun paramsStruct(): RuntimeType = RuntimeType.forInlineFun("Params", ClientRustModule.Endpoint) { + fun paramsStruct(): RuntimeType = RuntimeType.forInlineFun("Params", ClientRustModule.endpoint(codegenContext)) { generateEndpointsStruct(this) } - internal fun paramsBuilder(): RuntimeType = RuntimeType.forInlineFun("ParamsBuilder", ClientRustModule.Endpoint) { + internal fun paramsBuilder(): RuntimeType = RuntimeType.forInlineFun("ParamsBuilder", ClientRustModule.endpoint(codegenContext)) { generateEndpointParamsBuilder(this) } - private fun paramsError(): RuntimeType = RuntimeType.forInlineFun("InvalidParams", ClientRustModule.Endpoint) { + private fun paramsError(): RuntimeType = RuntimeType.forInlineFun("InvalidParams", ClientRustModule.endpoint(codegenContext)) { rust( """ /// An error that occurred during endpoint resolution diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt index 7dbede9596..a823720571 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointResolverGenerator.kt @@ -14,6 +14,7 @@ import software.amazon.smithy.rulesengine.language.syntax.fn.IsSet import software.amazon.smithy.rulesengine.language.syntax.rule.Condition import software.amazon.smithy.rulesengine.language.syntax.rule.Rule import software.amazon.smithy.rulesengine.language.visit.RuleValueVisitor +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.endpoint.Context import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Types @@ -33,7 +34,6 @@ 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.toType import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.orNull @@ -119,7 +119,11 @@ class FunctionRegistry(private val functions: List) { * */ -internal class EndpointResolverGenerator(stdlib: List, runtimeConfig: RuntimeConfig) { +internal class EndpointResolverGenerator( + private val codegenContext: ClientCodegenContext, + stdlib: List, +) { + private val runtimeConfig = codegenContext.runtimeConfig private val registry: FunctionRegistry = FunctionRegistry(stdlib) private val types = Types(runtimeConfig) private val codegenScope = arrayOf( @@ -164,7 +168,7 @@ internal class EndpointResolverGenerator(stdlib: List, ru // Now that we rendered the rules once (and then threw it away) we can see what functions we actually used! val fnsUsed = registry.fnsUsed() - return RuntimeType.forInlineFun("DefaultResolver", ClientRustModule.Endpoint) { + return RuntimeType.forInlineFun("DefaultResolver", ClientRustModule.endpoint(codegenContext)) { rustTemplate( """ /// The default endpoint resolver @@ -190,7 +194,7 @@ internal class EndpointResolverGenerator(stdlib: List, ru """, "custom_fields" to fnsUsed.mapNotNull { it.structField() }.join(","), "custom_fields_init" to fnsUsed.mapNotNull { it.structFieldInit() }.join(","), - "Params" to EndpointParamsGenerator(endpointRuleSet.parameters).paramsStruct(), + "Params" to EndpointParamsGenerator(codegenContext, endpointRuleSet.parameters).paramsStruct(), "additional_args" to fnsUsed.mapNotNull { it.additionalArgsInvocation("self") }.join(","), "resolver_fn" to resolverFn(endpointRuleSet, fnsUsed), *codegenScope, @@ -202,7 +206,7 @@ internal class EndpointResolverGenerator(stdlib: List, ru endpointRuleSet: EndpointRuleSet, fnsUsed: List, ): RuntimeType { - return RuntimeType.forInlineFun("resolve_endpoint", EndpointImpl) { + return RuntimeType.forInlineFun("resolve_endpoint", endpointImplModule(codegenContext)) { Attribute(allow(allowLintsForResolver)).render(this) rustTemplate( """ @@ -212,7 +216,7 @@ internal class EndpointResolverGenerator(stdlib: List, ru """, *codegenScope, - "Params" to EndpointParamsGenerator(endpointRuleSet.parameters).paramsStruct(), + "Params" to EndpointParamsGenerator(codegenContext, endpointRuleSet.parameters).paramsStruct(), "additional_args" to fnsUsed.mapNotNull { it.additionalArgsSignature() }.join(","), "body" to resolverFnBody(endpointRuleSet), ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt index 71d8ab9e4c..408ff44390 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt @@ -33,6 +33,16 @@ class ClientRuntimeTypesReExportGenerator( "Interceptor" to RuntimeType.interceptor(rc), ) } + rustCrate.withModule(ClientRustModule.endpoint(codegenContext)) { + rustTemplate( + """ + pub use #{ResolveEndpoint}; + pub use #{SharedEndpointResolver}; + """, + "ResolveEndpoint" to RuntimeType.smithyHttp(rc).resolve("endpoint::ResolveEndpoint"), + "SharedEndpointResolver" to RuntimeType.smithyHttp(rc).resolve("endpoint::SharedEndpointResolver"), + ) + } rustCrate.withModule(ClientRustModule.Config.retry) { rustTemplate( """ diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsGeneratorTest.kt index 6397e117e1..34636f596c 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointParamsGeneratorTest.kt @@ -9,6 +9,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import software.amazon.smithy.rulesengine.testutil.TestDiscovery import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsGenerator +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.testutil.TestWorkspace import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest @@ -25,6 +26,7 @@ internal class EndpointParamsGeneratorTest { @MethodSource("testSuites") fun `generate endpoint params for provided test suites`(testSuite: TestDiscovery.RulesTestSuite) { val project = TestWorkspace.testProject() + val context = testClientCodegenContext() project.lib { unitTest("params_work") { rustTemplate( @@ -32,7 +34,7 @@ internal class EndpointParamsGeneratorTest { // this might fail if there are required fields let _ = #{Params}::builder().build(); """, - "Params" to EndpointParamsGenerator(testSuite.ruleSet().parameters).paramsStruct(), + "Params" to EndpointParamsGenerator(context, testSuite.ruleSet().parameters).paramsStruct(), ) } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointResolverGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointResolverGeneratorTest.kt index b842705f99..5a798cae4d 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointResolverGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointResolverGeneratorTest.kt @@ -53,15 +53,16 @@ class EndpointResolverGeneratorTest { // return } val project = TestWorkspace.testProject() + val context = testClientCodegenContext() suite.ruleSet().typecheck() project.lib { val ruleset = EndpointResolverGenerator( + context, SmithyEndpointsStdLib + awsStandardLib(TestRuntimeConfig, partitionsJson), - TestRuntimeConfig, ).defaultEndpointResolver(suite.ruleSet()) val testGenerator = EndpointTestGenerator( suite.testSuite().testCases, - paramsType = EndpointParamsGenerator(suite.ruleSet().parameters).paramsStruct(), + paramsType = EndpointParamsGenerator(context, suite.ruleSet().parameters).paramsStruct(), resolverType = ruleset, suite.ruleSet().parameters, codegenContext = testClientCodegenContext(model = Model.builder().build()), @@ -79,15 +80,16 @@ class EndpointResolverGeneratorTest { testSuites().filter { it.ruleSet().sourceLocation.filename.endsWith("/uri-encode.json") }.findFirst() .orElseThrow() val project = TestWorkspace.testProject() + val context = testClientCodegenContext() suite.ruleSet().typecheck() project.lib { val ruleset = EndpointResolverGenerator( + context, SmithyEndpointsStdLib + awsStandardLib(TestRuntimeConfig, partitionsJson), - TestRuntimeConfig, ).defaultEndpointResolver(suite.ruleSet()) val testGenerator = EndpointTestGenerator( suite.testSuite().testCases, - paramsType = EndpointParamsGenerator(suite.ruleSet().parameters).paramsStruct(), + paramsType = EndpointParamsGenerator(context, suite.ruleSet().parameters).paramsStruct(), resolverType = ruleset, suite.ruleSet().parameters, codegenContext = testClientCodegenContext(Model.builder().build()), @@ -115,7 +117,8 @@ class EndpointResolverGeneratorTest { val scope = Scope() scope.insert("Region", Type.string()) endpoint.typeCheck(scope) - val generator = EndpointResolverGenerator(listOf(), TestRuntimeConfig) + val context = testClientCodegenContext() + val generator = EndpointResolverGenerator(context, listOf()) TestWorkspace.testProject().unitTest { rustTemplate( """ From 386ded887d108940080b3d096ed29aa66b43f39a Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Fri, 30 Jun 2023 14:09:51 -0500 Subject: [PATCH 194/253] add connection poisoning support to the orchestrator (#2824) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR updates the orchestrator to support connection poisoning for the `hyper` connector. It also renames many usages of "connection" to "connector" in order to make talking about these concepts less confusing. In short: ``` /// A "connector" manages one or more "connections", handles connection timeouts, re-establishes /// connections, etc. /// /// "Connections" refers to the actual transport layer implementation of the connector. /// By default, the orchestrator uses a connector provided by `hyper`. ``` One possibly controversial decision I made is that `reconnect_mode` is now a standalone configurable field, rather that a sub-field of `RetryConfig`. I think we should get together as a team and discuss what kinds of things we want to allow users to do when configuring connections. My thoughts on this are that we should either: - **A.** Make connection-related settings their own types instead of including them within other config types - **B.** Make a single config struct for all connector-related stuff/use the preëxisting `ConnectorSettings` struct. Personally, I'm partial to A. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti --- .../rustsdk/RetryClassifierDecorator.kt | 11 +- .../timestream/TimestreamDecorator.kt | 1 - aws/sdk/integration-tests/s3/Cargo.toml | 8 +- .../integration-tests/s3/tests/reconnects.rs | 30 ++-- .../ConnectionPoisoningConfigCustomization.kt | 37 +++++ .../HttpConnectorConfigDecorator.kt | 12 +- .../ResiliencyConfigCustomization.kt | 58 ++++---- .../customize/RequiredCustomizations.kt | 7 +- .../ServiceRuntimePluginGenerator.kt | 3 +- .../ResiliencyConfigCustomizationTest.kt | 2 +- .../rust/codegen/core/rustlang/RustType.kt | 1 + .../aws-smithy-runtime-api/src/client.rs | 3 + .../src/client/config_bag_accessors.rs | 15 +- .../src/client/connectors.rs | 31 ++++ .../client/interceptors/context/wrappers.rs | 10 ++ .../src/client/orchestrator.rs | 24 ---- rust-runtime/aws-smithy-runtime/src/client.rs | 10 +- .../client/{connections.rs => connectors.rs} | 10 +- .../client/connectors/connection_poisoning.rs | 132 ++++++++++++++++++ .../test_util.rs} | 9 +- .../src/client/orchestrator.rs | 25 +++- .../src/client/test_util.rs | 1 - .../src/client/test_util/connector.rs | 27 ---- rust-runtime/aws-smithy-types/src/retry.rs | 4 + 24 files changed, 336 insertions(+), 135 deletions(-) create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ConnectionPoisoningConfigCustomization.kt create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs rename rust-runtime/aws-smithy-runtime/src/client/{connections.rs => connectors.rs} (77%) create mode 100644 rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs rename rust-runtime/aws-smithy-runtime/src/client/{connections/test_connection.rs => connectors/test_util.rs} (96%) delete mode 100644 rust-runtime/aws-smithy-runtime/src/client/test_util/connector.rs diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt index b9f901131d..16f0644496 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryClassifierDecorator.kt @@ -15,6 +15,7 @@ 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.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.util.letIf class RetryClassifierDecorator : ClientCodegenDecorator { override val name: String = "RetryPolicy" @@ -25,10 +26,12 @@ class RetryClassifierDecorator : ClientCodegenDecorator { operation: OperationShape, baseCustomizations: List, ): List = - baseCustomizations + RetryClassifierFeature(codegenContext.runtimeConfig) + OperationRetryClassifiersFeature( - codegenContext, - operation, - ) + (baseCustomizations + RetryClassifierFeature(codegenContext.runtimeConfig)).letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + OperationRetryClassifiersFeature( + codegenContext, + operation, + ) + } } class RetryClassifierFeature(private val runtimeConfig: RuntimeConfig) : OperationCustomization() { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt index c066562ac3..57893b1c0a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt @@ -52,7 +52,6 @@ class TimestreamDecorator : ClientCodegenDecorator { Visibility.PUBLIC, CargoDependency.Tokio.copy(scope = DependencyScope.Compile, features = setOf("sync")), ) - val runtimeMode = codegenContext.smithyRuntimeMode rustCrate.lib { // helper function to resolve an endpoint given a base client rustTemplate( diff --git a/aws/sdk/integration-tests/s3/Cargo.toml b/aws/sdk/integration-tests/s3/Cargo.toml index 554ff97101..1dfcc94257 100644 --- a/aws/sdk/integration-tests/s3/Cargo.toml +++ b/aws/sdk/integration-tests/s3/Cargo.toml @@ -12,8 +12,8 @@ publish = false [dev-dependencies] async-std = "1.12.0" -aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } 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-s3 = { path = "../../build/aws-sdk/sdk/s3" } aws-sdk-sts = { path = "../../build/aws-sdk/sdk/sts" } @@ -36,9 +36,9 @@ serde_json = "1" smol = "1.2" tempfile = "3" tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } -# If you're writing a test with this, take heed! `no-env-filter` means you'll be capturing -# logs from everything that speaks, so be specific with your asserts. -tracing-test = { version = "0.2.4", features = ["no-env-filter"] } tracing = "0.1.37" tracing-appender = "0.2.2" tracing-subscriber = { version = "0.3.15", features = ["env-filter", "json"] } +# If you're writing a test with this, take heed! `no-env-filter` means you'll be capturing +# logs from everything that speaks, so be specific with your asserts. +tracing-test = { version = "0.2.4", features = ["no-env-filter"] } diff --git a/aws/sdk/integration-tests/s3/tests/reconnects.rs b/aws/sdk/integration-tests/s3/tests/reconnects.rs index 91935319fb..cb29bb2a66 100644 --- a/aws/sdk/integration-tests/s3/tests/reconnects.rs +++ b/aws/sdk/integration-tests/s3/tests/reconnects.rs @@ -3,20 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_credential_types::provider::SharedCredentialsProvider; -use aws_credential_types::Credentials; -use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; +use aws_sdk_s3::config::retry::{ReconnectMode, RetryConfig}; +use aws_sdk_s3::config::{Credentials, Region, SharedAsyncSleep}; +use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_client::test_connection::wire_mock::{ check_matches, ReplayedEvent, WireLevelTestConnection, }; use aws_smithy_client::{ev, match_events}; -use aws_smithy_types::retry::{ReconnectMode, RetryConfig}; -use aws_types::region::Region; -use aws_types::SdkConfig; #[tokio::test] -/// test that disabling reconnects on retry config disables them for the client -async fn disable_reconnects() { +async fn test_disable_reconnect_on_503() { let mock = WireLevelTestConnection::spinup(vec![ ReplayedEvent::status(503), ReplayedEvent::status(503), @@ -24,9 +20,9 @@ async fn disable_reconnects() { ]) .await; - let sdk_config = SdkConfig::builder() + let config = aws_sdk_s3::Config::builder() .region(Region::from_static("us-east-2")) - .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) + .credentials_provider(Credentials::for_tests()) .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .endpoint_url(mock.endpoint_url()) .http_connector(mock.http_connector()) @@ -34,7 +30,7 @@ async fn disable_reconnects() { RetryConfig::standard().with_reconnect_mode(ReconnectMode::ReuseAllConnections), ) .build(); - let client = aws_sdk_s3::Client::new(&sdk_config); + let client = aws_sdk_s3::Client::from_conf(config); let resp = client .get_object() .bucket("bucket") @@ -56,7 +52,7 @@ async fn disable_reconnects() { } #[tokio::test] -async fn reconnect_on_503() { +async fn test_enabling_reconnect_on_503() { let mock = WireLevelTestConnection::spinup(vec![ ReplayedEvent::status(503), ReplayedEvent::status(503), @@ -64,15 +60,17 @@ async fn reconnect_on_503() { ]) .await; - let sdk_config = SdkConfig::builder() + let config = aws_sdk_s3::Config::builder() .region(Region::from_static("us-east-2")) - .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) + .credentials_provider(Credentials::for_tests()) .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) .endpoint_url(mock.endpoint_url()) .http_connector(mock.http_connector()) - .retry_config(RetryConfig::standard()) + .retry_config( + RetryConfig::standard().with_reconnect_mode(ReconnectMode::ReconnectOnTransientError), + ) .build(); - let client = aws_sdk_s3::Client::new(&sdk_config); + let client = aws_sdk_s3::Client::from_conf(config); let resp = client .get_object() .bucket("bucket") diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ConnectionPoisoningConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ConnectionPoisoningConfigCustomization.kt new file mode 100644 index 0000000000..eaed687aeb --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ConnectionPoisoningConfigCustomization.kt @@ -0,0 +1,37 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.rust +import software.amazon.smithy.rust.codegen.core.rustlang.writable +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.smithyRuntime + +class ConnectionPoisoningRuntimePluginCustomization( + codegenContext: ClientCodegenContext, +) : ServiceRuntimePluginCustomization() { + private val runtimeConfig = codegenContext.runtimeConfig + + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + when (section) { + is ServiceRuntimePluginSection.RegisterInterceptor -> { + // This interceptor assumes that a compatible Connector is set. Otherwise, connection poisoning + // won't work and an error message will be logged. + section.registerInterceptor(runtimeConfig, this) { + rust( + "#T::new()", + smithyRuntime(runtimeConfig).resolve("client::connectors::connection_poisoning::ConnectionPoisoningInterceptor"), + ) + } + } + + else -> emptySection + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index e4923c0d78..2c80bfd0cb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -40,8 +40,8 @@ private class HttpConnectorConfigCustomization( *preludeScope, "Connection" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::Connection"), "ConnectorSettings" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::ConnectorSettings"), - "DynConnection" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::DynConnection"), - "DynConnectorAdapter" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::connections::adapter::DynConnectorAdapter"), + "DynConnector" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::connectors::DynConnector"), + "DynConnectorAdapter" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::connectors::adapter::DynConnectorAdapter"), "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), "SharedAsyncSleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep::SharedAsyncSleep"), "TimeoutConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout::TimeoutConfig"), @@ -197,14 +197,14 @@ private class HttpConnectorConfigCustomization( let connector_settings = #{ConnectorSettings}::from_timeout_config(&timeout_config); - if let Some(connection) = layer.load::<#{HttpConnector}>() + if let Some(connector) = layer.load::<#{HttpConnector}>() .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) { - let connection: #{DynConnection} = #{DynConnection}::new(#{DynConnectorAdapter}::new( + let connector: #{DynConnector} = #{DynConnector}::new(#{DynConnectorAdapter}::new( // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation - connection + connector )); - #{ConfigBagAccessors}::set_connection(&mut layer, connection); + #{ConfigBagAccessors}::set_connector(&mut layer, connector); } """, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 07ece3fb78..1c68cb0e87 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -15,7 +15,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Attribute 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.RuntimeConfig 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.smithy.RustCrate @@ -26,8 +25,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon private val retryConfig = RuntimeType.smithyTypes(runtimeConfig).resolve("retry") private val sleepModule = RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep") private val timeoutModule = RuntimeType.smithyTypes(runtimeConfig).resolve("timeout") - private val smithyRuntimeCrate = RuntimeType.smithyRuntime(runtimeConfig) - private val retries = smithyRuntimeCrate.resolve("client::retries") + private val retries = RuntimeType.smithyRuntime(runtimeConfig).resolve("client::retries") private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( *preludeScope, @@ -367,7 +365,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon } } - ServiceConfig.BuilderBuild -> { + is ServiceConfig.BuilderBuild -> { if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ @@ -420,7 +418,10 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon } } -class ResiliencyReExportCustomization(private val runtimeConfig: RuntimeConfig) { +class ResiliencyReExportCustomization(codegenContext: ClientCodegenContext) { + private val runtimeConfig = codegenContext.runtimeConfig + private val runtimeMode = codegenContext.smithyRuntimeMode + fun extras(rustCrate: RustCrate) { rustCrate.withModule(ClientRustModule.config) { rustTemplate( @@ -430,13 +431,16 @@ class ResiliencyReExportCustomization(private val runtimeConfig: RuntimeConfig) } rustCrate.withModule(ClientRustModule.Config.retry) { rustTemplate( - "pub use #{types_retry}::{RetryConfig, RetryConfigBuilder, RetryMode};", + "pub use #{types_retry}::{RetryConfig, RetryConfigBuilder, RetryMode, ReconnectMode};", "types_retry" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry"), ) - rustTemplate( - "pub use #{RetryPartition};", - "RetryPartition" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::retries::RetryPartition"), - ) + + if (runtimeMode.generateOrchestrator) { + rustTemplate( + "pub use #{types_retry}::RetryPartition;", + "types_retry" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::retries"), + ) + } } rustCrate.withModule(ClientRustModule.Config.timeout) { rustTemplate( @@ -449,30 +453,34 @@ class ResiliencyReExportCustomization(private val runtimeConfig: RuntimeConfig) class ResiliencyServiceRuntimePluginCustomization(codegenContext: ClientCodegenContext) : ServiceRuntimePluginCustomization() { private val runtimeConfig = codegenContext.runtimeConfig - private val smithyRuntimeCrate = RuntimeType.smithyRuntime(runtimeConfig) - private val retries = smithyRuntimeCrate.resolve("client::retries") + private val runtimeMode = codegenContext.smithyRuntimeMode + private val smithyRuntime = RuntimeType.smithyRuntime(runtimeConfig) + private val retries = smithyRuntime.resolve("client::retries") private val codegenScope = arrayOf( "TokenBucket" to retries.resolve("TokenBucket"), "TokenBucketPartition" to retries.resolve("TokenBucketPartition"), "ClientRateLimiter" to retries.resolve("ClientRateLimiter"), "ClientRateLimiterPartition" to retries.resolve("ClientRateLimiterPartition"), - "StaticPartitionMap" to smithyRuntimeCrate.resolve("static_partition_map::StaticPartitionMap"), + "StaticPartitionMap" to smithyRuntime.resolve("static_partition_map::StaticPartitionMap"), ) override fun section(section: ServiceRuntimePluginSection): Writable = writable { - when (section) { - is ServiceRuntimePluginSection.DeclareSingletons -> { - // TODO(enableNewSmithyRuntimeCleanup) We can use the standard library's `OnceCell` once we upgrade the - // MSRV to 1.70 - rustTemplate( - """ - static TOKEN_BUCKET: #{StaticPartitionMap}<#{TokenBucketPartition}, #{TokenBucket}> = #{StaticPartitionMap}::new(); - static CLIENT_RATE_LIMITER: #{StaticPartitionMap}<#{ClientRateLimiterPartition}, #{ClientRateLimiter}> = #{StaticPartitionMap}::new(); - """, - *codegenScope, - ) + if (runtimeMode.generateOrchestrator) { + when (section) { + is ServiceRuntimePluginSection.DeclareSingletons -> { + // TODO(enableNewSmithyRuntimeCleanup) We can use the standard library's `OnceCell` once we upgrade the + // MSRV to 1.70 + rustTemplate( + """ + static TOKEN_BUCKET: #{StaticPartitionMap}<#{TokenBucketPartition}, #{TokenBucket}> = #{StaticPartitionMap}::new(); + static CLIENT_RATE_LIMITER: #{StaticPartitionMap}<#{ClientRateLimiterPartition}, #{ClientRateLimiter}> = #{StaticPartitionMap}::new(); + """, + *codegenScope, + ) + } + + else -> emptySection } - else -> emptySection } } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index 4b85a6bb53..e26e228c7e 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.customize import software.amazon.smithy.model.shapes.OperationShape 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.customizations.ConnectionPoisoningRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.EndpointPrefixGenerator import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpChecksumRequiredGenerator import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpVersionListCustomization @@ -82,7 +83,7 @@ class RequiredCustomizations : ClientCodegenDecorator { rustCrate.mergeFeature(TestUtilFeature) // Re-export resiliency types - ResiliencyReExportCustomization(codegenContext.runtimeConfig).extras(rustCrate) + ResiliencyReExportCustomization(codegenContext).extras(rustCrate) rustCrate.withModule(ClientRustModule.Primitives) { pubUseSmithyPrimitives(codegenContext, codegenContext.model)(this) @@ -102,7 +103,9 @@ class RequiredCustomizations : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = if (codegenContext.smithyRuntimeMode.generateOrchestrator) { - baseCustomizations + ResiliencyServiceRuntimePluginCustomization(codegenContext) + baseCustomizations + + ResiliencyServiceRuntimePluginCustomization(codegenContext) + + ConnectionPoisoningRuntimePluginCustomization(codegenContext) } else { baseCustomizations } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index b8023cd6c4..c77e95940a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -67,13 +67,12 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { data class RegisterInterceptor(val interceptorRegistrarName: String) : ServiceRuntimePluginSection("RegisterInterceptor") { /** Generates the code to register an interceptor */ fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { - val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) writer.rustTemplate( """ $interceptorRegistrarName.register(#{SharedInterceptor}::new(#{interceptor}) as _); """, "interceptor" to interceptor, - "SharedInterceptor" to smithyRuntimeApi.resolve("client::interceptors::SharedInterceptor"), + "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::SharedInterceptor"), ) } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt index 4f9c1e23e3..b84f204c24 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt @@ -40,7 +40,7 @@ internal class ResiliencyConfigCustomizationTest { val codegenContext = testClientCodegenContext(model, settings = project.clientRustSettings()) stubConfigProject(codegenContext, ResiliencyConfigCustomization(codegenContext), project) - ResiliencyReExportCustomization(codegenContext.runtimeConfig).extras(project) + ResiliencyReExportCustomization(codegenContext).extras(project) project.compileAndTest() } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index 6fc9ca7adf..3cb637b64b 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.core.rustlang import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.derive +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute.Companion.serde import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.dq diff --git a/rust-runtime/aws-smithy-runtime-api/src/client.rs b/rust-runtime/aws-smithy-runtime-api/src/client.rs index fec60cf892..cdcfb847a5 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client.rs @@ -30,3 +30,6 @@ pub mod auth; /// A type to track the number of requests sent by the orchestrator for a given operation. pub mod request_attempts; + +/// Smithy connectors and related code. +pub mod connectors; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs index 3b9262f889..54832acf80 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs @@ -7,13 +7,13 @@ use crate::client::auth::{ AuthOptionResolver, AuthOptionResolverParams, AuthSchemeId, DynAuthOptionResolver, SharedHttpAuthScheme, }; +use crate::client::connectors::{Connector, DynConnector}; use crate::client::identity::{ ConfiguredIdentityResolver, IdentityResolvers, SharedIdentityResolver, }; use crate::client::orchestrator::{ - Connection, DynConnection, DynEndpointResolver, DynResponseDeserializer, EndpointResolver, - EndpointResolverParams, LoadedRequestBody, ResponseDeserializer, SharedRequestSerializer, - NOT_NEEDED, + DynEndpointResolver, DynResponseDeserializer, EndpointResolver, EndpointResolverParams, + LoadedRequestBody, ResponseDeserializer, SharedRequestSerializer, NOT_NEEDED, }; use crate::client::retries::{DynRetryStrategy, RetryClassifiers, RetryStrategy}; use aws_smithy_async::rt::sleep::SharedAsyncSleep; @@ -133,6 +133,7 @@ mod internal { } } } + use internal::{CloneableSettable, Gettable, Settable}; pub trait ConfigBagAccessors { @@ -219,18 +220,18 @@ pub trait ConfigBagAccessors { )); } - fn connection(&self) -> &dyn Connection + fn connector(&self) -> &dyn Connector where Self: Gettable, { - self.load::().expect("missing connector") + self.load::().expect("missing connector") } - fn set_connection(&mut self, connection: DynConnection) + fn set_connector(&mut self, connection: DynConnector) where Self: Settable, { - self.store_put::(connection); + self.store_put::(connection); } /// Returns the configured HTTP auth schemes. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs new file mode 100644 index 0000000000..fd63eea38c --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse}; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; +use std::fmt; + +pub trait Connector: Send + Sync + fmt::Debug { + fn call(&self, request: HttpRequest) -> BoxFuture; +} + +#[derive(Debug)] +pub struct DynConnector(Box); + +impl DynConnector { + pub fn new(connection: impl Connector + 'static) -> Self { + Self(Box::new(connection)) + } +} + +impl Connector for DynConnector { + fn call(&self, request: HttpRequest) -> BoxFuture { + (*self.0).call(request) + } +} + +impl Storable for DynConnector { + type Storer = StoreReplace; +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs index 1094ce4ab2..7a79c13427 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs @@ -158,6 +158,16 @@ declare_wrapper!( (response: Response) ); +impl<'a, I, O, E: Debug> BeforeDeserializationInterceptorContextMut<'a, I, O, E> { + #[doc(hidden)] + /// Downgrade this helper struct, returning the underlying InterceptorContext. There's no good + /// reason to use this unless you're writing tests or you have to interact with an API that + /// doesn't support the helper structs. + pub fn into_inner(&mut self) -> &'_ mut InterceptorContext { + self.inner + } +} + declare_wrapper!( (AfterDeserializationInterceptorContextRef readonly) (input: I) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 6bcb8f0431..9384509900 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -12,7 +12,6 @@ use aws_smithy_types::endpoint::Endpoint; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use bytes::Bytes; use std::fmt; -use std::fmt::Debug; use std::future::Future as StdFuture; use std::pin::Pin; use std::sync::Arc; @@ -94,29 +93,6 @@ impl Storable for DynResponseDeserializer { type Storer = StoreReplace; } -pub trait Connection: Send + Sync + fmt::Debug { - fn call(&self, request: HttpRequest) -> BoxFuture; -} - -#[derive(Debug)] -pub struct DynConnection(Box); - -impl DynConnection { - pub fn new(connection: impl Connection + 'static) -> Self { - Self(Box::new(connection)) - } -} - -impl Connection for DynConnection { - fn call(&self, request: HttpRequest) -> BoxFuture { - (*self.0).call(request) - } -} - -impl Storable for DynConnection { - type Storer = StoreReplace; -} - #[derive(Debug)] pub struct EndpointResolverParams(TypeErasedBox); diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index 6bf5397489..cb0d8f1ae8 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -5,8 +5,14 @@ pub mod auth; -/// Smithy connector runtime plugins -pub mod connections; +/// Smithy code related to connectors and connections. +/// +/// A "connector" manages one or more "connections", handles connection timeouts, re-establishes +/// connections, etc. +/// +/// "Connections" refers to the actual transport layer implementation of the connector. +/// By default, the orchestrator uses a connector provided by `hyper`. +pub mod connectors; /// The client orchestrator implementation pub mod orchestrator; diff --git a/rust-runtime/aws-smithy-runtime/src/client/connections.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors.rs similarity index 77% rename from rust-runtime/aws-smithy-runtime/src/client/connections.rs rename to rust-runtime/aws-smithy-runtime/src/client/connectors.rs index e05eedf2e8..60e9a77096 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connections.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors.rs @@ -3,14 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ +pub mod connection_poisoning; #[cfg(feature = "test-util")] -pub mod test_connection; +pub mod test_util; pub mod adapter { use aws_smithy_client::erase::DynConnector; - use aws_smithy_runtime_api::client::orchestrator::{ - BoxFuture, Connection, HttpRequest, HttpResponse, - }; + use aws_smithy_runtime_api::client::connectors::Connector; + use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse}; use std::sync::{Arc, Mutex}; #[derive(Debug)] @@ -27,7 +27,7 @@ pub mod adapter { } } - impl Connection for DynConnectorAdapter { + impl Connector for DynConnectorAdapter { fn call(&self, request: HttpRequest) -> BoxFuture { let future = self.dyn_connector.lock().unwrap().call_lite(request); future diff --git a/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs new file mode 100644 index 0000000000..e2d08a60d5 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs @@ -0,0 +1,132 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_http::connection::{CaptureSmithyConnection, ConnectionMetadata}; +use aws_smithy_runtime_api::box_error::BoxError; +use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeDeserializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, +}; +use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::retry::{ErrorKind, ReconnectMode, RetryConfig}; +use std::fmt; +use tracing::{debug, error}; + +/// A interceptor for poisoning connections in response to certain events. +/// +/// This interceptor, when paired with a compatible connection, allows the connection to be +/// poisoned in reaction to certain events *(like receiving a transient error.)* This allows users +/// to avoid sending requests to a server that isn't responding. This can increase the load on a +/// server, because more connections will be made overall. +/// +/// **In order for this interceptor to work,** the configured connection must interact with the +/// "connection retriever" stored in an HTTP request's `extensions` map. For an example of this, +/// see [aws_smithy_client::hyper_ext::Adapter](https://github.com/awslabs/smithy-rs/blob/47b3d23ff3cabd67e797af616101f5a4ea6be5e8/rust-runtime/aws-smithy-client/src/hyper_ext.rs#L155). +/// When a connection is made available to the retriever, this interceptor will call a `.poison` +/// method on it, signalling that the connection should be dropped. It is up to the connection +/// implementer to handle this. +#[non_exhaustive] +#[derive(Debug, Default)] +pub struct ConnectionPoisoningInterceptor {} + +impl ConnectionPoisoningInterceptor { + pub fn new() -> Self { + Self::default() + } +} + +impl Interceptor for ConnectionPoisoningInterceptor { + fn modify_before_transmit( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let capture_smithy_connection = CaptureSmithyConnectionWrapper::new(); + context + .request_mut() + .extensions_mut() + .insert(capture_smithy_connection.clone_inner()); + cfg.interceptor_state().store_put(capture_smithy_connection); + + Ok(()) + } + + fn modify_before_deserialization( + &self, + context: &mut BeforeDeserializationInterceptorContextMut<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let reconnect_mode = cfg + .load::() + .map(RetryConfig::reconnect_mode) + .unwrap_or(ReconnectMode::ReconnectOnTransientError); + let captured_connection = cfg.load::().cloned(); + let retry_classifiers = cfg.retry_classifiers(); + + let error_is_transient = retry_classifiers + .classify_retry(context.into_inner()) + .map(|reason| reason == RetryReason::Error(ErrorKind::TransientError)) + .unwrap_or_default(); + let connection_poisoning_is_enabled = + reconnect_mode == ReconnectMode::ReconnectOnTransientError; + + if error_is_transient && connection_poisoning_is_enabled { + debug!("received a transient error, poisoning the connection..."); + + if let Some(captured_connection) = captured_connection.and_then(|conn| conn.get()) { + captured_connection.poison(); + debug!("the connection was poisoned") + } else { + error!( + "unable to poison the connection because no connection was found! The underlying HTTP connector never set a connection." + ); + } + } + + Ok(()) + } +} + +// TODO(enableNewSmithyRuntimeLaunch) We won't need this once we absorb aws_smithy_http into the +// new runtime crate. +#[derive(Clone, Default)] +pub struct CaptureSmithyConnectionWrapper { + inner: CaptureSmithyConnection, +} + +impl CaptureSmithyConnectionWrapper { + pub fn new() -> Self { + Self { + inner: CaptureSmithyConnection::new(), + } + } + + pub fn clone_inner(&self) -> CaptureSmithyConnection { + self.inner.clone() + } + + pub fn get(&self) -> Option { + self.inner.get() + } + + pub fn set_connection_retriever(&self, f: F) + where + F: Fn() -> Option + Send + Sync + 'static, + { + self.inner.set_connection_retriever(f) + } +} + +impl Storable for CaptureSmithyConnectionWrapper { + type Storer = StoreReplace; +} + +impl fmt::Debug for CaptureSmithyConnectionWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CaptureSmithyConnectionWrapper") + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs similarity index 96% rename from rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs rename to rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs index 7db7ba541c..5a9b3bfdfd 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connections/test_connection.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs @@ -9,9 +9,8 @@ use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; -use aws_smithy_runtime_api::client::orchestrator::{ - BoxFuture, Connection, HttpRequest, HttpResponse, -}; +use aws_smithy_runtime_api::client::connectors::Connector; +use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse}; use http::header::{HeaderName, CONTENT_TYPE}; use std::fmt::Debug; use std::ops::Deref; @@ -182,7 +181,7 @@ impl ValidateRequest { } } -/// TestConnection for use as a [`Connection`]. +/// TestConnection for use as a [`Connector`]. /// /// A basic test connection. It will: /// - Respond to requests with a preloaded series of responses @@ -223,7 +222,7 @@ impl TestConnection { } } -impl Connection for TestConnection { +impl Connector for TestConnection { fn call(&self, request: HttpRequest) -> BoxFuture { let (res, simulated_latency) = if let Some(event) = self.data.lock().unwrap().pop() { self.requests.lock().unwrap().push(ValidateRequest { diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 6c950b81d5..de6f9dceaa 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -281,7 +281,7 @@ async fn try_attempt( ctx.enter_transmit_phase(); let call_result = halt_on_err!([ctx] => { let request = ctx.take_request().expect("set during serialization"); - cfg.connection().call(request).await.map_err(|err| { + cfg.connector().call(request).await.map_err(|err| { match err.downcast() { Ok(connector_error) => OrchestratorError::connector(*connector_error), Err(box_err) => OrchestratorError::other(box_err) @@ -349,6 +349,7 @@ mod tests { serializer::CannedRequestSerializer, }; use ::http::{Request, Response, StatusCode}; + use aws_smithy_runtime_api::client::connectors::Connector; use aws_smithy_runtime_api::client::interceptors::context::{ AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, @@ -360,7 +361,7 @@ mod tests { Interceptor, InterceptorRegistrar, SharedInterceptor, }; use aws_smithy_runtime_api::client::orchestrator::{ - DynConnection, DynEndpointResolver, DynResponseDeserializer, SharedRequestSerializer, + DynConnector, DynEndpointResolver, DynResponseDeserializer, SharedRequestSerializer, }; use aws_smithy_runtime_api::client::retries::DynRetryStrategy; use aws_smithy_runtime_api::client::runtime_plugin::{RuntimePlugin, RuntimePlugins}; @@ -388,6 +389,24 @@ mod tests { ) } + #[derive(Debug, Default)] + pub struct OkConnector {} + + impl OkConnector { + pub fn new() -> Self { + Self::default() + } + } + + impl Connector for OkConnector { + fn call(&self, _request: HttpRequest) -> BoxFuture { + Box::pin(Future::ready(Ok(http::Response::builder() + .status(200) + .body(SdkBody::empty()) + .expect("OK response is valid")))) + } + } + #[derive(Debug)] struct TestOperationRuntimePlugin; @@ -403,7 +422,7 @@ mod tests { StaticUriEndpointResolver::http_localhost(8080), )); cfg.set_endpoint_resolver_params(StaticUriEndpointResolverParams::new().into()); - cfg.set_connection(DynConnection::new(OkConnector::new())); + cfg.set_connector(DynConnector::new(OkConnector::new())); Some(cfg.freeze()) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs index 101c971eff..de7aef4ac5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs @@ -3,7 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -pub mod connector; pub mod deserializer; pub mod interceptors; pub mod serializer; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/connector.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/connector.rs deleted file mode 100644 index 73507bf9fb..0000000000 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/connector.rs +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_smithy_http::body::SdkBody; -use aws_smithy_runtime_api::client::orchestrator::{ - BoxFuture, Connection, Future, HttpRequest, HttpResponse, -}; - -#[derive(Debug, Default)] -pub struct OkConnector {} - -impl OkConnector { - pub fn new() -> Self { - Self::default() - } -} - -impl Connection for OkConnector { - fn call(&self, _request: HttpRequest) -> BoxFuture { - Box::pin(Future::ready(Ok(http::Response::builder() - .status(200) - .body(SdkBody::empty()) - .expect("OK response is valid")))) - } -} diff --git a/rust-runtime/aws-smithy-types/src/retry.rs b/rust-runtime/aws-smithy-types/src/retry.rs index a514417814..a2866b84c1 100644 --- a/rust-runtime/aws-smithy-types/src/retry.rs +++ b/rust-runtime/aws-smithy-types/src/retry.rs @@ -301,6 +301,10 @@ pub enum ReconnectMode { ReuseAllConnections, } +impl Storable for ReconnectMode { + type Storer = StoreReplace; +} + impl RetryConfig { /// Creates a default `RetryConfig` with `RetryMode::Standard` and max attempts of three. pub fn standard() -> Self { From 847ed0b66afe6eca3c64a764a4e7c7c16b0851df Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 30 Jun 2023 14:21:15 -0700 Subject: [PATCH 195/253] Fix the S3 alternate runtime retries test in orchestrator mode (#2825) This PR fixes the S3 `alternative-async-runtime` retry tests. The retry backoff time was being included in the operation attempt timeout, which I think is undesirable as it makes retry config much harder to get right since the backoff times are not predictable (they have randomness incorporated into them). The overall operation timeout is the only one that needs to incorporate backoff times. In addition, this PR also: - Updates READMEs for the `aws-smithy-runtime-api` and `aws-smithy-runtime` crates - Adds top-level crate docs to describe features to `aws-smithy-runtime` - Copies `capture_test_logs` into `aws-smithy-runtime` so that it can be used (just about) everywhere instead of just in `aws-config` - Adds service/operation name to the tracing `invoke` span so it's possible to tell which operation the events are for - Makes the `Debug` impl for `Identity` useful - Adds a ton of trace/debug spans and events to the orchestrator - Fixes an issue in `aws-smithy-runtime` where a huge amount of the orchestrator tests weren't being compiled due to a removed feature flag ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-config/src/test_case.rs | 2 + .../retries-with-client-rate-limiting.rs | 53 +++---- aws/sdk/integration-tests/s3/Cargo.toml | 1 + .../s3/tests/alternative-async-runtime.rs | 17 ++- .../ResiliencyConfigCustomization.kt | 5 +- .../smithy/generators/OperationGenerator.kt | 10 +- .../OperationRuntimePluginGenerator.kt | 1 + .../protocol/MakeOperationGenerator.kt | 7 +- .../smithy/rust/codegen/core/util/Smithy.kt | 5 + rust-runtime/aws-smithy-runtime-api/README.md | 4 +- .../src/client/identity.rs | 22 ++- .../src/client/interceptors.rs | 11 ++ .../src/client/runtime_plugin.rs | 2 + rust-runtime/aws-smithy-runtime/Cargo.toml | 9 +- rust-runtime/aws-smithy-runtime/README.md | 4 +- .../src/client/orchestrator.rs | 138 ++++++++++++------ .../src/client/orchestrator/auth.rs | 14 +- .../src/client/orchestrator/endpoints.rs | 3 + .../src/client/retries/strategy/standard.rs | 2 +- rust-runtime/aws-smithy-runtime/src/lib.rs | 15 +- .../aws-smithy-runtime/src/test_util.rs | 7 + .../src/test_util/capture_test_logs.rs | 90 ++++++++++++ .../check-aws-sdk-orchestrator-impl | 14 -- 23 files changed, 318 insertions(+), 118 deletions(-) create mode 100644 rust-runtime/aws-smithy-runtime/src/test_util.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/test_util/capture_test_logs.rs diff --git a/aws/rust-runtime/aws-config/src/test_case.rs b/aws/rust-runtime/aws-config/src/test_case.rs index 8392a92a16..14859d57ef 100644 --- a/aws/rust-runtime/aws-config/src/test_case.rs +++ b/aws/rust-runtime/aws-config/src/test_case.rs @@ -136,6 +136,8 @@ pub(crate) struct Metadata { name: String, } +// TODO(enableNewSmithyRuntimeCleanup): Replace Tee, capture_test_logs, and Rx with +// the implementations added to aws_smithy_runtime::test_util::capture_test_logs struct Tee { buf: Arc>>, quiet: bool, diff --git a/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs b/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs index d3f05db865..21ad0470bc 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs @@ -7,16 +7,13 @@ mod test { use aws_sdk_dynamodb::config::{Credentials, Region, SharedAsyncSleep}; use aws_sdk_dynamodb::{config::retry::RetryConfig, error::ProvideErrorMetadata}; - use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_async::test_util::instant_time_and_sleep; use aws_smithy_async::time::SharedTimeSource; - use aws_smithy_async::time::SystemTimeSource; use aws_smithy_client::test_connection::TestConnection; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime::client::retries::RetryPartition; use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, HttpResponse}; - use aws_smithy_types::timeout::TimeoutConfigBuilder; - use std::time::{Duration, Instant, SystemTime}; + use std::time::{Duration, SystemTime}; fn req() -> HttpRequest { http::Request::builder() @@ -111,15 +108,16 @@ mod test { } #[tokio::test] - async fn test_adaptive_retries_with_throttling_errors_times_out() { - tracing_subscriber::fmt::init(); + async fn test_adaptive_retries_with_throttling_errors() { + let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); + let events = vec![ // First operation - (req(), err()), + (req(), throttling_err()), + (req(), throttling_err()), (req(), ok()), // Second operation (req(), err()), - (req(), throttling_err()), (req(), ok()), ]; @@ -130,44 +128,31 @@ mod test { .retry_config( RetryConfig::adaptive() .with_max_attempts(4) - .with_initial_backoff(Duration::from_millis(50)) .with_use_static_exponential_base(true), ) - .timeout_config( - TimeoutConfigBuilder::new() - .operation_attempt_timeout(Duration::from_millis(100)) - .build(), - ) - .time_source(SharedTimeSource::new(SystemTimeSource::new())) - .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) - .http_connector(conn.clone()) + .time_source(SharedTimeSource::new(time_source)) + .sleep_impl(SharedAsyncSleep::new(sleep_impl.clone())) .retry_partition(RetryPartition::new( - "test_adaptive_retries_with_throttling_errors_times_out", + "test_adaptive_retries_with_throttling_errors", )) + .http_connector(conn.clone()) .build(); - let expected_table_names = vec!["Test".to_owned()]; - let start = Instant::now(); // We create a new client each time to ensure that the cross-client retry state is working. let client = aws_sdk_dynamodb::Client::from_conf(config.clone()); let res = client.list_tables().send().await.unwrap(); + assert_eq!(sleep_impl.total_duration(), Duration::from_secs(40)); assert_eq!(res.table_names(), Some(expected_table_names.as_slice())); // Three requests should have been made, two failing & one success - assert_eq!(conn.requests().len(), 2); + assert_eq!(conn.requests().len(), 3); - let client = aws_sdk_dynamodb::Client::from_conf(config); - let err = client.list_tables().send().await.unwrap_err(); - assert_eq!(err.to_string(), "request has timed out".to_owned()); - // two requests should have been made, both failing (plus previous requests) - assert_eq!(conn.requests().len(), 2 + 2); - - let since = start.elapsed(); - // At least 300 milliseconds must pass: - // - 50ms for the first retry on attempt 1 - // - 50ms for the second retry on attempt 3 - // - 100ms for the throttling delay triggered by attempt 4, which required a delay longer than the attempt timeout. - // - 100ms for the 5th attempt, which would have succeeded, but required a delay longer than the attempt timeout. - assert!(since.as_secs_f64() > 0.3); + let client = aws_sdk_dynamodb::Client::from_conf(config.clone()); + let res = client.list_tables().send().await.unwrap(); + assert!(Duration::from_secs(48) < sleep_impl.total_duration()); + assert!(Duration::from_secs(49) > sleep_impl.total_duration()); + assert_eq!(res.table_names(), Some(expected_table_names.as_slice())); + // Two requests should have been made, one failing & one success (plus previous requests) + assert_eq!(conn.requests().len(), 5); } } diff --git a/aws/sdk/integration-tests/s3/Cargo.toml b/aws/sdk/integration-tests/s3/Cargo.toml index 1dfcc94257..8a27b1cd2c 100644 --- a/aws/sdk/integration-tests/s3/Cargo.toml +++ b/aws/sdk/integration-tests/s3/Cargo.toml @@ -21,6 +21,7 @@ aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } 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 = ["test-util"] } aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } bytes = "1" diff --git a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs index db36cfeb92..8d1a604d95 100644 --- a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs +++ b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs @@ -20,6 +20,9 @@ use aws_smithy_types::timeout::TimeoutConfig; use std::fmt::Debug; use std::time::{Duration, Instant}; +#[cfg(aws_sdk_orchestrator_mode)] +use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs; + #[derive(Debug)] struct SmolSleep; @@ -33,6 +36,9 @@ impl AsyncSleep for SmolSleep { #[test] fn test_smol_runtime_timeouts() { + #[cfg(aws_sdk_orchestrator_mode)] + let _guard = capture_test_logs(); + if let Err(err) = smol::block_on(async { timeout_test(SharedAsyncSleep::new(SmolSleep)).await }) { println!("{err}"); @@ -42,6 +48,9 @@ fn test_smol_runtime_timeouts() { #[test] fn test_smol_runtime_retry() { + #[cfg(aws_sdk_orchestrator_mode)] + let _guard = capture_test_logs(); + if let Err(err) = smol::block_on(async { retry_test(SharedAsyncSleep::new(SmolSleep)).await }) { println!("{err}"); panic!(); @@ -59,6 +68,9 @@ impl AsyncSleep for AsyncStdSleep { #[test] fn test_async_std_runtime_timeouts() { + #[cfg(aws_sdk_orchestrator_mode)] + let _guard = capture_test_logs(); + if let Err(err) = async_std::task::block_on(async { timeout_test(SharedAsyncSleep::new(AsyncStdSleep)).await }) { @@ -69,6 +81,9 @@ fn test_async_std_runtime_timeouts() { #[test] fn test_async_std_runtime_retry() { + #[cfg(aws_sdk_orchestrator_mode)] + let _guard = capture_test_logs(); + if let Err(err) = async_std::task::block_on(async { retry_test(SharedAsyncSleep::new(AsyncStdSleep)).await }) { @@ -137,7 +152,7 @@ async fn retry_test(sleep_impl: SharedAsyncSleep) -> Result<(), Box().cloned().unwrap_or_else(|| #{RetryPartition}::new("${codegenContext.serviceShape.id.name}")); + let retry_partition = layer.load::<#{RetryPartition}>().cloned().unwrap_or_else(|| #{RetryPartition}::new("${codegenContext.serviceShape.sdkId()}")); let retry_config = layer.load::<#{RetryConfig}>().cloned().unwrap_or_else(#{RetryConfig}::disabled); if retry_config.has_retry() { - #{debug}!("creating retry strategy with partition '{}'", retry_partition); + #{debug}!("using retry strategy with partition '{}'", retry_partition); } if retry_config.mode() == #{RetryMode}::Adaptive { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index b59ff1c339..5bf9c8615b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -27,8 +27,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType.Companion.pre import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol +import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.outputShape +import software.amazon.smithy.rust.codegen.core.util.sdkId open class OperationGenerator( private val codegenContext: ClientCodegenContext, @@ -142,7 +144,13 @@ open class OperationGenerator( stop_point: #{StopPoint}, ) -> #{Result}<#{InterceptorContext}, #{SdkError}<#{Error}, #{HttpResponse}>> { let input = #{TypedBox}::new(input).erase(); - #{invoke_with_stop_point}(input, runtime_plugins, stop_point).await + #{invoke_with_stop_point}( + ${codegenContext.serviceShape.sdkId().dq()}, + ${operationName.dq()}, + input, + runtime_plugins, + stop_point + ).await } pub(crate) fn operation_runtime_plugins( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index 5edfdedb04..febdb95825 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -64,6 +64,7 @@ class OperationRuntimePluginGenerator( fn config(&self) -> #{Option}<#{FrozenLayer}> { let mut cfg = #{Layer}::new(${operationShape.id.name.dq()}); use #{ConfigBagAccessors} as _; + cfg.set_request_serializer(#{SharedRequestSerializer}::new(${operationStructName}RequestSerializer)); cfg.set_response_deserializer(#{DynResponseDeserializer}::new(${operationStructName}ResponseDeserializer)); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt index fdb92dd3f7..d79817df40 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/MakeOperationGenerator.kt @@ -5,7 +5,6 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol -import software.amazon.smithy.aws.traits.ServiceTrait import software.amazon.smithy.model.shapes.BlobShape import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule @@ -31,9 +30,9 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.HttpLocation import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.findStreamingMember -import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.letIf +import software.amazon.smithy.rust.codegen.core.util.sdkId // TODO(enableNewSmithyRuntimeCleanup): Delete this class when cleaning up `enableNewSmithyRuntime` /** Generates the `make_operation` function on input structs */ @@ -53,9 +52,7 @@ open class MakeOperationGenerator( private val defaultClassifier = RuntimeType.smithyHttp(runtimeConfig) .resolve("retry::DefaultResponseRetryClassifier") - private val sdkId = - codegenContext.serviceShape.getTrait()?.sdkId?.lowercase()?.replace(" ", "") - ?: codegenContext.serviceShape.id.getName(codegenContext.serviceShape) + private val sdkId = codegenContext.serviceShape.sdkId() private val codegenScope = arrayOf( *preludeScope, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt index 299cdff24d..bde5c4b338 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Smithy.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rust.codegen.core.util +import software.amazon.smithy.aws.traits.ServiceTrait import software.amazon.smithy.codegen.core.CodegenException import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.BooleanShape @@ -146,3 +147,7 @@ fun String.shapeId() = ShapeId.from(this) /** Returns the service name, or a default value if the service doesn't have a title trait */ fun ServiceShape.serviceNameOrDefault(default: String) = getTrait()?.value ?: default + +/** Returns the SDK ID of the given service shape */ +fun ServiceShape.sdkId(): String = + getTrait()?.sdkId?.lowercase()?.replace(" ", "") ?: id.getName(this) diff --git a/rust-runtime/aws-smithy-runtime-api/README.md b/rust-runtime/aws-smithy-runtime-api/README.md index 0e33661021..7eb00c40dc 100644 --- a/rust-runtime/aws-smithy-runtime-api/README.md +++ b/rust-runtime/aws-smithy-runtime-api/README.md @@ -1,8 +1,8 @@ -# aws-smithy-retries +# aws-smithy-runtime-api **This crate is UNSTABLE! All internal and external interfaces are subject to change without notice.** -Smithy runtime types. +Lightweight crate with traits and types necessary to configure runtime logic in the `aws-smithy-runtime` crate. This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index 2a1b6c8d23..a8ab993960 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -7,6 +7,7 @@ use crate::client::auth::AuthSchemeId; use crate::client::orchestrator::Future; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; use std::any::Any; +use std::fmt; use std::fmt::Debug; use std::sync::Arc; use std::time::SystemTime; @@ -96,21 +97,27 @@ impl IdentityResolvers { } } -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct Identity { data: Arc, + #[allow(clippy::type_complexity)] + data_debug: Arc) -> &dyn Debug) + Send + Sync>, expiration: Option, } impl Identity { - pub fn new(data: impl Any + Send + Sync, expiration: Option) -> Self { + pub fn new(data: T, expiration: Option) -> Self + where + T: Any + Debug + Send + Sync, + { Self { data: Arc::new(data), + data_debug: Arc::new(|d| d.downcast_ref::().expect("type-checked") as _), expiration, } } - pub fn data(&self) -> Option<&T> { + pub fn data(&self) -> Option<&T> { self.data.downcast_ref() } @@ -119,6 +126,15 @@ impl Identity { } } +impl fmt::Debug for Identity { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Identity") + .field("data", (self.data_debug)(&self.data)) + .field("expiration", &self.expiration) + .finish() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index dd918faba0..5d93c64566 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -673,6 +673,11 @@ macro_rules! interceptor_impl_fn { ctx: &mut InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { + tracing::trace!(concat!( + "running `", + stringify!($interceptor), + "` interceptors" + )); let mut result: Result<(), BoxError> = Ok(()); let mut ctx = ctx.into(); for interceptor in self.interceptors() { @@ -774,6 +779,7 @@ impl Interceptors { ctx: &InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { + tracing::trace!("running `client_read_before_execution` interceptors"); let mut result: Result<(), BoxError> = Ok(()); let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); for interceptor in self.client_interceptors() { @@ -794,6 +800,7 @@ impl Interceptors { ctx: &InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { + tracing::trace!("running `operation_read_before_execution` interceptors"); let mut result: Result<(), BoxError> = Ok(()); let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); for interceptor in self.operation_interceptors() { @@ -829,6 +836,7 @@ impl Interceptors { ctx: &mut InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { + tracing::trace!("running `modify_before_attempt_completion` interceptors"); let mut result: Result<(), BoxError> = Ok(()); let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); for interceptor in self.interceptors() { @@ -850,6 +858,7 @@ impl Interceptors { ctx: &InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { + tracing::trace!("running `read_after_attempt` interceptors"); let mut result: Result<(), BoxError> = Ok(()); let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); for interceptor in self.interceptors() { @@ -870,6 +879,7 @@ impl Interceptors { ctx: &mut InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { + tracing::trace!("running `modify_before_completion` interceptors"); let mut result: Result<(), BoxError> = Ok(()); let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); for interceptor in self.interceptors() { @@ -890,6 +900,7 @@ impl Interceptors { ctx: &InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { + tracing::trace!("running `read_after_execution` interceptors"); let mut result: Result<(), BoxError> = Ok(()); let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); for interceptor in self.interceptors() { diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index 15a29c3ba5..d0a8e18b59 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -70,6 +70,7 @@ impl RuntimePlugins { cfg: &mut ConfigBag, interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { + tracing::trace!("applying client runtime plugins"); for plugin in self.client_plugins.iter() { if let Some(layer) = plugin.config() { cfg.push_shared_layer(layer); @@ -85,6 +86,7 @@ impl RuntimePlugins { cfg: &mut ConfigBag, interceptors: &mut InterceptorRegistrar, ) -> Result<(), BoxError> { + tracing::trace!("applying operation runtime plugins"); for plugin in self.operation_plugins.iter() { if let Some(layer) = plugin.config() { cfg.push_shared_layer(layer); diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index d63a748b5c..b481d7ff99 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/awslabs/smithy-rs" [features] http-auth = ["aws-smithy-runtime-api/http-auth"] -test-util = ["dep:aws-smithy-protocol-test"] +test-util = ["dep:aws-smithy-protocol-test", "dep:tracing-subscriber"] [dependencies] aws-smithy-async = { path = "../aws-smithy-async" } @@ -21,14 +21,15 @@ aws-smithy-protocol-test = { path = "../aws-smithy-protocol-test", optional = tr aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api" } aws-smithy-types = { path = "../aws-smithy-types" } bytes = "1" +fastrand = "1.4" http = "0.2.8" http-body = "0.4.5" +once_cell = "1.18.0" pin-project-lite = "0.2.7" pin-utils = "0.1.0" tokio = { version = "1.25", features = [] } tracing = "0.1.37" -fastrand = "1.4" -once_cell = "1.18.0" +tracing-subscriber = { version = "0.3.16", optional = true, features = ["fmt", "json"] } [dev-dependencies] approx = "0.5.1" @@ -36,7 +37,7 @@ aws-smithy-async = { path = "../aws-smithy-async", features = ["rt-tokio", "test aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api", features = ["test-util"] } aws-smithy-types = { path = "../aws-smithy-types", features = ["test-util"] } tokio = { version = "1.25", features = ["macros", "rt", "test-util"] } -tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } +tracing-subscriber = { version = "0.3.16", features = ["env-filter"] } tracing-test = "0.2.1" [package.metadata.docs.rs] diff --git a/rust-runtime/aws-smithy-runtime/README.md b/rust-runtime/aws-smithy-runtime/README.md index ba6dbc29ae..f283643f8c 100644 --- a/rust-runtime/aws-smithy-runtime/README.md +++ b/rust-runtime/aws-smithy-runtime/README.md @@ -1,8 +1,8 @@ -# aws-smithy-orchestrator +# aws-smithy-runtime **This crate is UNSTABLE! All internal and external interfaces are subject to change without notice.** -Code which enables users to access a smithy service. Handles the configuration and construction of requests, as well as responses. +Runtime support logic and types for smithy-rs generated code. This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index de6f9dceaa..552c78b04b 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -56,19 +56,27 @@ macro_rules! halt_on_err { macro_rules! continue_on_err { ([$ctx:ident] => $expr:expr) => { if let Err(err) = $expr { - debug!("encountered orchestrator error; continuing"); + debug!(err = ?err, "encountered orchestrator error; continuing"); $ctx.fail(err.into()); } }; } pub async fn invoke( + service_name: &str, + operation_name: &str, input: Input, runtime_plugins: &RuntimePlugins, ) -> Result> { - invoke_with_stop_point(input, runtime_plugins, StopPoint::None) - .await? - .finalize() + invoke_with_stop_point( + service_name, + operation_name, + input, + runtime_plugins, + StopPoint::None, + ) + .await? + .finalize() } /// Allows for returning early at different points during orchestration. @@ -82,32 +90,38 @@ pub enum StopPoint { BeforeTransmit, } -#[tracing::instrument(skip_all, name = "invoke")] pub async fn invoke_with_stop_point( + service_name: &str, + operation_name: &str, input: Input, runtime_plugins: &RuntimePlugins, stop_point: StopPoint, ) -> Result> { - let mut cfg = ConfigBag::base(); - let cfg = &mut cfg; + async move { + let mut cfg = ConfigBag::base(); + let cfg = &mut cfg; - let mut interceptors = Interceptors::new(); - let mut ctx = InterceptorContext::new(input); + let mut interceptors = Interceptors::new(); + let mut ctx = InterceptorContext::new(input); - if let Err(err) = apply_configuration(&mut ctx, cfg, &mut interceptors, runtime_plugins) { - return Err(SdkError::construction_failure(err)); - } - let operation_timeout_config = cfg.maybe_timeout_config(TimeoutKind::Operation); - async { - // If running the pre-execution interceptors failed, then we skip running the op and run the - // final interceptors instead. - if !ctx.is_failed() { - try_op(&mut ctx, cfg, &interceptors, stop_point).await; + if let Err(err) = apply_configuration(&mut ctx, cfg, &mut interceptors, runtime_plugins) { + return Err(SdkError::construction_failure(err)); + } + let operation_timeout_config = cfg.maybe_timeout_config(TimeoutKind::Operation); + trace!(operation_timeout_config = ?operation_timeout_config); + async { + // If running the pre-execution interceptors failed, then we skip running the op and run the + // final interceptors instead. + if !ctx.is_failed() { + try_op(&mut ctx, cfg, &interceptors, stop_point).await; + } + finally_op(&mut ctx, cfg, &interceptors).await; + Ok(ctx) } - finally_op(&mut ctx, cfg, &interceptors).await; - Ok(ctx) + .maybe_timeout_with_config(operation_timeout_config) + .await } - .maybe_timeout_with_config(operation_timeout_config) + .instrument(debug_span!("invoke", service = %service_name, operation = %operation_name)) .await } @@ -123,6 +137,7 @@ fn apply_configuration( ) -> Result<(), BoxError> { runtime_plugins.apply_client_configuration(cfg, interceptors.client_interceptors_mut())?; continue_on_err!([ctx] => interceptors.client_read_before_execution(ctx, cfg)); + runtime_plugins .apply_operation_configuration(cfg, interceptors.operation_interceptors_mut())?; continue_on_err!([ctx] => interceptors.operation_read_before_execution(ctx, cfg)); @@ -144,6 +159,7 @@ async fn try_op( // Serialization ctx.enter_serialization_phase(); { + let _span = debug_span!("serialization").entered(); let request_serializer = cfg.request_serializer(); let input = ctx.take_input().expect("input set at this point"); let request = halt_on_err!([ctx] => request_serializer.serialize_input(input, cfg).map_err(OrchestratorError::other)); @@ -152,6 +168,7 @@ async fn try_op( // Load the request body into memory if configured to do so if let LoadedRequestBody::Requested = cfg.loaded_request_body() { + debug!("loading request body into memory"); let mut body = SdkBody::taken(); mem::swap(&mut body, ctx.request_mut().expect("set above").body_mut()); let loaded_body = halt_on_err!([ctx] => ByteStream::new(body).collect().await).into_bytes(); @@ -196,9 +213,9 @@ async fn try_op( ctx.save_checkpoint(); let mut retry_delay = None; for i in 1u32.. { - debug!("beginning attempt #{i}"); // Break from the loop if we can't rewind the request's state. This will always succeed the // first time, but will fail on subsequent iterations if the request body wasn't retryable. + trace!("checking if context can be rewound for attempt #{i}"); if let RewindResult::Impossible = ctx.rewind(cfg) { debug!("request cannot be retried since the request body cannot be cloned"); break; @@ -206,13 +223,15 @@ async fn try_op( // Track which attempt we're currently on. cfg.interceptor_state() .store_put::(i.into()); + // Backoff time should not be included in the attempt timeout + if let Some((delay, sleep)) = retry_delay.take() { + debug!("delaying for {delay:?}"); + sleep.await; + } let attempt_timeout_config = cfg.maybe_timeout_config(TimeoutKind::OperationAttempt); + trace!(attempt_timeout_config = ?attempt_timeout_config); let maybe_timeout = async { - // We must await this here or else timeouts won't work as expected - if let Some(delay) = retry_delay.take() { - delay.await; - } - + debug!("beginning attempt #{i}"); try_attempt(ctx, cfg, interceptors, stop_point).await; finally_attempt(ctx, cfg, interceptors).await; Result::<_, SdkError>::Ok(()) @@ -246,7 +265,7 @@ async fn try_op( let sleep_impl = halt_on_err!([ctx] => cfg.sleep_impl().ok_or(OrchestratorError::other( "the retry strategy requested a delay before sending the retry request, but no 'async sleep' implementation was set" ))); - retry_delay = Some(sleep_impl.sleep(delay)); + retry_delay = Some((delay, sleep_impl.sleep(delay))); continue; } } @@ -261,7 +280,9 @@ async fn try_attempt( stop_point: StopPoint, ) { halt_on_err!([ctx] => interceptors.read_before_attempt(ctx, cfg)); + halt_on_err!([ctx] => orchestrate_endpoint(ctx, cfg).await.map_err(OrchestratorError::other)); + halt_on_err!([ctx] => interceptors.modify_before_signing(ctx, cfg)); halt_on_err!([ctx] => interceptors.read_before_signing(ctx, cfg)); @@ -273,14 +294,16 @@ async fn try_attempt( // Return early if a stop point is set for before transmit if let StopPoint::BeforeTransmit = stop_point { + debug!("ending orchestration early because the stop point is `BeforeTransmit`"); return; } // The connection consumes the request but we need to keep a copy of it // within the interceptor context, so we clone it here. ctx.enter_transmit_phase(); - let call_result = halt_on_err!([ctx] => { + let response = halt_on_err!([ctx] => { let request = ctx.take_request().expect("set during serialization"); + trace!(request = ?request, "transmitting request"); cfg.connector().call(request).await.map_err(|err| { match err.downcast() { Ok(connector_error) => OrchestratorError::connector(*connector_error), @@ -288,8 +311,8 @@ async fn try_attempt( } }) }); - trace!(response = ?call_result, "received response from service"); - ctx.set_response(call_result); + trace!(response = ?response, "received response from service"); + ctx.set_response(response); ctx.enter_before_deserialization_phase(); halt_on_err!([ctx] => interceptors.read_after_transmit(ctx, cfg)); @@ -300,16 +323,25 @@ async fn try_attempt( let output_or_error = async { let response = ctx.response_mut().expect("set during transmit"); let response_deserializer = cfg.response_deserializer(); - match response_deserializer.deserialize_streaming(response) { + let maybe_deserialized = { + let _span = debug_span!("deserialize_streaming").entered(); + response_deserializer.deserialize_streaming(response) + }; + match maybe_deserialized { Some(output_or_error) => output_or_error, None => read_body(response) .instrument(debug_span!("read_body")) .await .map_err(OrchestratorError::response) - .and_then(|_| response_deserializer.deserialize_nonstreaming(response)), + .and_then(|_| { + let _span = debug_span!("deserialize_nonstreaming").entered(); + response_deserializer.deserialize_nonstreaming(response) + }), } } + .instrument(debug_span!("deserialization")) .await; + trace!(output_or_error = ?output_or_error); ctx.set_output_or_error(output_or_error); ctx.enter_after_deserialization_phase(); @@ -336,20 +368,21 @@ async fn finally_op( continue_on_err!([ctx] => interceptors.read_after_execution(ctx, cfg)); } -#[cfg(all(test, feature = "test-util", feature = "anonymous-auth"))] +#[cfg(all(test, feature = "test-util"))] mod tests { use super::*; - use crate::client::auth::no_auth::NoAuthRuntimePlugin; + use crate::client::auth::no_auth::{NoAuthRuntimePlugin, NO_AUTH_SCHEME_ID}; use crate::client::orchestrator::endpoints::{ StaticUriEndpointResolver, StaticUriEndpointResolverParams, }; use crate::client::retries::strategy::NeverRetryStrategy; use crate::client::test_util::{ - connector::OkConnector, deserializer::CannedResponseDeserializer, - serializer::CannedRequestSerializer, + deserializer::CannedResponseDeserializer, serializer::CannedRequestSerializer, }; use ::http::{Request, Response, StatusCode}; - use aws_smithy_runtime_api::client::connectors::Connector; + use aws_smithy_runtime_api::client::auth::option_resolver::StaticAuthOptionResolver; + use aws_smithy_runtime_api::client::auth::{AuthOptionResolverParams, DynAuthOptionResolver}; + use aws_smithy_runtime_api::client::connectors::{Connector, DynConnector}; use aws_smithy_runtime_api::client::interceptors::context::{ AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, @@ -361,7 +394,8 @@ mod tests { Interceptor, InterceptorRegistrar, SharedInterceptor, }; use aws_smithy_runtime_api::client::orchestrator::{ - DynConnector, DynEndpointResolver, DynResponseDeserializer, SharedRequestSerializer, + BoxFuture, DynEndpointResolver, DynResponseDeserializer, Future, HttpRequest, + SharedRequestSerializer, }; use aws_smithy_runtime_api::client::retries::DynRetryStrategy; use aws_smithy_runtime_api::client::runtime_plugin::{RuntimePlugin, RuntimePlugins}; @@ -390,17 +424,17 @@ mod tests { } #[derive(Debug, Default)] - pub struct OkConnector {} + struct OkConnector {} impl OkConnector { - pub fn new() -> Self { + fn new() -> Self { Self::default() } } impl Connector for OkConnector { fn call(&self, _request: HttpRequest) -> BoxFuture { - Box::pin(Future::ready(Ok(http::Response::builder() + Box::pin(Future::ready(Ok(::http::Response::builder() .status(200) .body(SdkBody::empty()) .expect("OK response is valid")))) @@ -423,6 +457,10 @@ mod tests { )); cfg.set_endpoint_resolver_params(StaticUriEndpointResolverParams::new().into()); cfg.set_connector(DynConnector::new(OkConnector::new())); + cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("idontcare")); + cfg.set_auth_option_resolver(DynAuthOptionResolver::new( + StaticAuthOptionResolver::new(vec![NO_AUTH_SCHEME_ID]), + )); Some(cfg.freeze()) } @@ -482,7 +520,7 @@ mod tests { .with_operation_plugin(TestOperationRuntimePlugin) .with_operation_plugin(NoAuthRuntimePlugin::new()) .with_operation_plugin(FailingInterceptorsOperationRuntimePlugin); - let actual = invoke(input, &runtime_plugins) + let actual = invoke("test", "test", input, &runtime_plugins) .await .expect_err("should error"); let actual = format!("{:?}", actual); @@ -744,9 +782,9 @@ mod tests { let input = TypeErasedBox::new(Box::new(())); let runtime_plugins = RuntimePlugins::new() .with_operation_plugin(TestOperationRuntimePlugin) - .with_operation_plugin(AnonymousAuthRuntimePlugin::new()) + .with_operation_plugin(NoAuthRuntimePlugin::new()) .with_operation_plugin(InterceptorsTestOperationRuntimePlugin); - let actual = invoke(input, &runtime_plugins) + let actual = invoke("test", "test", input, &runtime_plugins) .await .expect_err("should error"); let actual = format!("{:?}", actual); @@ -992,11 +1030,13 @@ mod tests { let runtime_plugins = || { RuntimePlugins::new() .with_operation_plugin(TestOperationRuntimePlugin) - .with_operation_plugin(AnonymousAuthRuntimePlugin::new()) + .with_operation_plugin(NoAuthRuntimePlugin::new()) }; // StopPoint::None should result in a response getting set since orchestration doesn't stop let context = invoke_with_stop_point( + "test", + "test", TypedBox::new(()).erase(), &runtime_plugins(), StopPoint::None, @@ -1007,6 +1047,8 @@ mod tests { // StopPoint::BeforeTransmit will exit right before sending the request, so there should be no response let context = invoke_with_stop_point( + "test", + "test", TypedBox::new(()).erase(), &runtime_plugins(), StopPoint::BeforeTransmit, @@ -1079,7 +1121,7 @@ mod tests { let runtime_plugins = || { RuntimePlugins::new() .with_operation_plugin(TestOperationRuntimePlugin) - .with_operation_plugin(AnonymousAuthRuntimePlugin::new()) + .with_operation_plugin(NoAuthRuntimePlugin::new()) .with_operation_plugin(TestInterceptorRuntimePlugin { interceptor: interceptor.clone(), }) @@ -1087,6 +1129,8 @@ mod tests { // StopPoint::BeforeTransmit will exit right before sending the request, so there should be no response let context = invoke_with_stop_point( + "test", + "test", TypedBox::new(()).erase(), &runtime_plugins(), StopPoint::BeforeTransmit, diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 92191782bc..ce64570870 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -16,6 +16,7 @@ use aws_smithy_types::Document; use std::borrow::Cow; use std::error::Error as StdError; use std::fmt; +use tracing::trace; #[derive(Debug)] enum AuthOrchestrationError { @@ -71,7 +72,7 @@ pub(super) async fn orchestrate_auth( let auth_options = cfg.auth_option_resolver().resolve_auth_options(params)?; let identity_resolvers = cfg.identity_resolvers(); - tracing::trace!( + trace!( auth_option_resolver_params = ?params, auth_options = ?auth_options, identity_resolvers = ?identity_resolvers, @@ -82,13 +83,24 @@ pub(super) async fn orchestrate_auth( if let Some(auth_scheme) = cfg.http_auth_schemes().scheme(scheme_id) { if let Some(identity_resolver) = auth_scheme.identity_resolver(&identity_resolvers) { let request_signer = auth_scheme.request_signer(); + trace!( + auth_scheme = ?auth_scheme, + identity_resolver = ?identity_resolver, + request_signer = ?request_signer, + "resolved auth scheme, identity resolver, and signing implementation" + ); + let endpoint = cfg .load::() .expect("endpoint added to config bag by endpoint orchestrator"); let auth_scheme_endpoint_config = extract_endpoint_auth_scheme_config(endpoint, scheme_id)?; + trace!(auth_scheme_endpoint_config = ?auth_scheme_endpoint_config, "extracted auth scheme endpoint config"); let identity = identity_resolver.resolve_identity(cfg).await?; + trace!(identity = ?identity, "resolved identity"); + + trace!("signing request"); let request = ctx.request_mut().expect("set during serialization"); request_signer.sign_request( request, diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 12db9cb606..394cde080b 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -20,6 +20,7 @@ use http::header::HeaderName; use http::{HeaderValue, Uri}; use std::fmt::Debug; use std::str::FromStr; +use tracing::trace; #[derive(Debug, Clone)] pub struct StaticUriEndpointResolver { @@ -104,6 +105,8 @@ pub(super) async fn orchestrate_endpoint( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { + trace!("orchestrating endpoint resolution"); + let params = cfg.endpoint_resolver_params(); let endpoint_prefix = cfg.load::(); let request = ctx.request_mut().expect("set during serialization"); diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index 307297e0c5..ecb29234f4 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -241,7 +241,7 @@ impl RetryStrategy for StandardRetryStrategy { debug!( "attempt #{request_attempts} failed with {:?}; retrying after {:?}", retry_reason.expect("the match statement above ensures this is not None"), - backoff + backoff, ); Ok(ShouldAttempt::YesAfterDelay(backoff)) diff --git a/rust-runtime/aws-smithy-runtime/src/lib.rs b/rust-runtime/aws-smithy-runtime/src/lib.rs index 2c2eb6a9f8..cd4364a9b3 100644 --- a/rust-runtime/aws-smithy-runtime/src/lib.rs +++ b/rust-runtime/aws-smithy-runtime/src/lib.rs @@ -3,13 +3,26 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Runtime support logic and types for smithy-rs generated code. +//! +//! # Crate Features +//! +//! - `http-auth`: Enables auth scheme and identity resolver implementations for HTTP API Key, +//! Basic Auth, Bearer Token, and Digest Auth. +//! - `test-util`: Enables utilities for unit tests. DO NOT ENABLE IN PRODUCTION. + #![warn( // missing_docs, - // rustdoc::missing_crate_level_docs, + rustdoc::missing_crate_level_docs, unreachable_pub, rust_2018_idioms )] +/// Runtime support logic for generated clients. pub mod client; pub mod static_partition_map; + +/// General testing utilities. +#[cfg(feature = "test-util")] +pub mod test_util; diff --git a/rust-runtime/aws-smithy-runtime/src/test_util.rs b/rust-runtime/aws-smithy-runtime/src/test_util.rs new file mode 100644 index 0000000000..b3ec5e5dfb --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/test_util.rs @@ -0,0 +1,7 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// Utility for capturing and displaying logs during a unit test. +pub mod capture_test_logs; diff --git a/rust-runtime/aws-smithy-runtime/src/test_util/capture_test_logs.rs b/rust-runtime/aws-smithy-runtime/src/test_util/capture_test_logs.rs new file mode 100644 index 0000000000..a25c97c42f --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/test_util/capture_test_logs.rs @@ -0,0 +1,90 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use std::env; +use std::io::Write; +use std::sync::{Arc, Mutex}; +use tracing::subscriber::DefaultGuard; +use tracing::Level; +use tracing_subscriber::fmt::TestWriter; + +struct Tee { + buf: Arc>>, + quiet: bool, + inner: W, +} + +/// A guard that resets log capturing upon being dropped. +#[derive(Debug)] +pub struct LogCaptureGuard(DefaultGuard); + +/// Capture logs from this test. +/// +/// The logs will be captured until the `DefaultGuard` is dropped. +/// +/// *Why use this instead of traced_test?* +/// This captures _all_ logs, not just logs produced by the current crate. +#[must_use] // log capturing ceases the instant the `DefaultGuard` is dropped +pub fn capture_test_logs() -> (LogCaptureGuard, Rx) { + // it may be helpful to upstream this at some point + let (mut writer, rx) = Tee::stdout(); + if env::var("VERBOSE_TEST_LOGS").is_ok() { + eprintln!("Enabled verbose test logging."); + writer.loud(); + } else { + eprintln!("To see full logs from this test set VERBOSE_TEST_LOGS=true"); + } + let subscriber = tracing_subscriber::fmt() + .with_max_level(Level::TRACE) + .with_writer(Mutex::new(writer)) + .finish(); + let guard = tracing::subscriber::set_default(subscriber); + (LogCaptureGuard(guard), rx) +} + +pub struct Rx(Arc>>); +impl Rx { + pub fn contents(&self) -> String { + String::from_utf8(self.0.lock().unwrap().clone()).unwrap() + } +} + +impl Tee { + fn stdout() -> (Self, Rx) { + let buf: Arc>> = Default::default(); + ( + Tee { + buf: buf.clone(), + quiet: true, + inner: TestWriter::new(), + }, + Rx(buf), + ) + } +} + +impl Tee { + fn loud(&mut self) { + self.quiet = false; + } +} + +impl Write for Tee +where + W: Write, +{ + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.buf.lock().unwrap().extend_from_slice(buf); + if !self.quiet { + self.inner.write(buf) + } else { + Ok(buf.len()) + } + } + + fn flush(&mut self) -> std::io::Result<()> { + self.inner.flush() + } +} diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index 413082fa7c..a434b07761 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -18,11 +18,6 @@ services_that_dont_compile=(\ "timestreamwrite", ) -# TODO(enableNewSmithyRuntimeLaunch): Move these into `services_that_pass_tests` as more progress is made -services_that_compile=(\ - "s3"\ -) - services_that_pass_tests=(\ "aws-config"\ "config"\ @@ -45,15 +40,6 @@ services_that_pass_tests=(\ ./gradlew aws:sdk:assemble -Psmithy.runtime.mode=orchestrator cd aws/sdk/build/aws-sdk/sdk -for service in "${services_that_compile[@]}"; do - pushd "${service}" - echo -e "${C_YELLOW}# Running 'cargo check --all-features --all-targets' on '${service}'${C_RESET}" - RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo check --all-features --all-targets - echo -e "${C_YELLOW}# Running 'cargo clippy --all-features' on '${service}'${C_RESET}" - RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo clippy --all-features - popd -done - for service in "${services_that_pass_tests[@]}"; do pushd "${service}" echo -e "${C_YELLOW}# Running 'cargo test --all-features' on '${service}'${C_RESET}" From d75382789a3d58e6c832d59dd0cc7cce90477a58 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 30 Jun 2023 19:40:27 -0500 Subject: [PATCH 196/253] Implement the remaining part of supporting operation level configuration (#2814) ## Motivation and Context Implements the actual meat of `config_override` introduced [as a skeleton in the past](https://github.com/awslabs/smithy-rs/pull/2589). ## Description This PR enables operation-level config via `config_override` on a customizable operation (see [config-override.rs](https://github.com/awslabs/smithy-rs/blob/8105dd46b43854e7909aed82c85223414bc85df5/aws/sdk/integration-tests/s3/tests/config-override.rs) integration tests for example code snippets). The way it's implemented is through `ConfigOverrideRuntimePlugin`. The plugin holds onto two things: a `Builder` passed to `config_override` and a `FrozenLayer` derived from a service config (the latter is primarily for retrieving default values understood by a service config). The plugin then implements the `RuntimePlugin` trait to generate its own `FrozenLayer` that contains operation-level orchestrator components. That `FrozenLayer` will then be added to a config bag later in the orchestrator execution in a way that it takes precedence over the client-level configuration (see [register_default_runtime_plugins](https://github.com/awslabs/smithy-rs/blob/8105dd46b43854e7909aed82c85223414bc85df5/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt#L65-L71)). A couple of things to note: - The size of `ConfigOverrideRuntimePlugin::config` code-generated is getting large. Maybe each customization defines a helper function instead of inlining logic directly in `config` and we let the `config` method call those generated helpers. - The PR does not handle a case where `retry_partition` within `config_override` since it's currently `#[doc(hidden)]`, e.g. ``` client .some_operation() .customize() .await .unwrap() .config_override(Config::builder().retry_partition(/* ... */)) ... ``` ## Testing - Added tests in Kotlin [ConfigOverrideRuntimePluginGeneratorTest.kt](https://github.com/awslabs/smithy-rs/pull/2814/files#diff-04a76a43c2adb3a2ee37443157c68ec6dad77fab2484722b370a7ba14cf02086) and [CredentialCacheConfigTest.kt](https://github.com/awslabs/smithy-rs/pull/2814/files#diff-32246072688cd11391fa10cd9cb38a80ed88b587e95037225dbe9f1a482ebc5d). ~~These tests are minimal in that they only check if the appropriate orchestrator components are inserted into a config override layer when a user calls a certain builder method as part of `config_override`.~~ - Added integration tests [config-override.rs](https://github.com/awslabs/smithy-rs/pull/2814/files#diff-6fd7a1e70b1c3fa3e9c747925f3fc7a6ce0f7b801bd6bc46c54eec44150f5158) ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Yuki Saito --- .../smithy/rustsdk/AwsPresigningDecorator.kt | 3 +- .../amazon/smithy/rustsdk/CredentialCaches.kt | 30 +++ .../rustsdk/CredentialCacheConfigTest.kt | 188 +++++++++++++ .../amazon/smithy/rustsdk/TestUtil.kt | 7 + .../s3/tests/config-override.rs | 121 +++++++++ .../HttpConnectorConfigDecorator.kt | 39 +++ .../InterceptorConfigCustomization.kt | 2 +- .../ResiliencyConfigCustomization.kt | 26 +- .../endpoint/EndpointConfigCustomization.kt | 18 ++ .../ConfigOverrideRuntimePluginGenerator.kt | 82 ++++++ .../smithy/generators/OperationGenerator.kt | 6 +- .../smithy/generators/PaginatorGenerator.kt | 3 +- .../smithy/generators/ServiceGenerator.kt | 3 +- .../client/FluentClientGenerator.kt | 3 +- .../config/ServiceConfigGenerator.kt | 56 ++-- ...onfigOverrideRuntimePluginGeneratorTest.kt | 247 ++++++++++++++++++ .../generators/PaginatorGeneratorTest.kt | 13 +- .../codegen/core/rustlang/CargoDependency.kt | 2 + .../smithy/rust/codegen/core/testutil/Rust.kt | 2 + .../aws-smithy-types/src/config_bag.rs | 7 + 20 files changed, 800 insertions(+), 58 deletions(-) create mode 100644 aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt create mode 100644 aws/sdk/integration-tests/s3/tests/config-override.rs create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt create mode 100644 codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index 5ffd15a6a8..ef59287b35 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -322,7 +322,8 @@ class AwsPresignedFluentBuilderMethod( let runtime_plugins = #{Operation}::operation_runtime_plugins( self.handle.runtime_plugins.clone(), - self.config_override + &self.handle.conf, + self.config_override, ) .with_client_plugin(#{SigV4PresigningRuntimePlugin}::new(presigning_config, #{payload_override})) #{alternate_presigning_serializer_registration}; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt index 053e7ec7e6..b31c5329ea 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt @@ -191,6 +191,36 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom } } + is ServiceConfig.OperationConfigOverride -> { + rustTemplate( + """ + match ( + layer + .load::<#{CredentialsCache}>() + .cloned(), + layer + .load::<#{SharedCredentialsProvider}>() + .cloned(), + ) { + (#{None}, #{None}) => {} + (#{None}, _) => { + panic!("also specify `.credentials_cache` when overriding credentials provider for the operation"); + } + (_, #{None}) => { + panic!("also specify `.credentials_provider` when overriding credentials cache for the operation"); + } + ( + #{Some}(credentials_cache), + #{Some}(credentials_provider), + ) => { + layer.store_put(credentials_cache.create_cache(credentials_provider)); + } + } + """, + *codegenScope, + ) + } + else -> emptySection } } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt new file mode 100644 index 0000000000..c598c1ab0d --- /dev/null +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt @@ -0,0 +1,188 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +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.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.testModule +import software.amazon.smithy.rust.codegen.core.testutil.tokioTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest + +internal class CredentialCacheConfigTest { + private val model = """ + namespace com.example + use aws.protocols#awsJson1_0 + use aws.api#service + use smithy.rules#endpointRuleSet + + @service(sdkId: "Some Value") + @awsJson1_0 + @endpointRuleSet({ + "version": "1.0", + "rules": [{ + "type": "endpoint", + "conditions": [{"fn": "isSet", "argv": [{"ref": "Region"}]}], + "endpoint": { "url": "https://example.com" } + }], + "parameters": { + "Region": { "required": false, "type": "String", "builtIn": "AWS::Region" }, + } + }) + service HelloService { + operations: [SayHello], + version: "1" + } + + @optionalAuth + operation SayHello { input: TestInput } + structure TestInput { + foo: String, + } + """.asSmithyModel() + + @Test + fun `config override for credentials`() { + awsSdkIntegrationTest(model, defaultToOrchestrator = true) { clientCodegenContext, rustCrate -> + val runtimeConfig = clientCodegenContext.runtimeConfig + val codegenScope = arrayOf( + *RuntimeType.preludeScope, + "Credentials" to AwsRuntimeType.awsCredentialTypesTestUtil(runtimeConfig) + .resolve("Credentials"), + "CredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig) + .resolve("cache::CredentialsCache"), + "ProvideCachedCredentials" to AwsRuntimeType.awsCredentialTypes(runtimeConfig) + .resolve("cache::ProvideCachedCredentials"), + "RuntimePlugin" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::RuntimePlugin"), + "SharedCredentialsCache" to AwsRuntimeType.awsCredentialTypes(runtimeConfig) + .resolve("cache::SharedCredentialsCache"), + "SharedCredentialsProvider" to AwsRuntimeType.awsCredentialTypes(runtimeConfig) + .resolve("provider::SharedCredentialsProvider"), + ) + rustCrate.testModule { + unitTest( + "test_overriding_only_credentials_provider_should_panic", + additionalAttributes = listOf(Attribute.shouldPanic("also specify `.credentials_cache` when overriding credentials provider for the operation")), + ) { + rustTemplate( + """ + use #{RuntimePlugin}; + + let client_config = crate::config::Config::builder().build(); + let config_override = + crate::config::Config::builder().credentials_provider(#{Credentials}::for_tests()); + let sut = crate::config::ConfigOverrideRuntimePlugin { + client_config: client_config.config().unwrap(), + config_override, + }; + + // this should cause `panic!` + let _ = sut.config().unwrap(); + """, + *codegenScope, + ) + } + + unitTest( + "test_overriding_only_credentials_cache_should_panic", + additionalAttributes = listOf(Attribute.shouldPanic("also specify `.credentials_provider` when overriding credentials cache for the operation")), + ) { + rustTemplate( + """ + use #{RuntimePlugin}; + + let client_config = crate::config::Config::builder().build(); + let config_override = crate::config::Config::builder() + .credentials_cache(#{CredentialsCache}::no_caching()); + let sut = crate::config::ConfigOverrideRuntimePlugin { + client_config: client_config.config().unwrap(), + config_override, + }; + + // this should cause `panic!` + let _ = sut.config().unwrap(); + """, + *codegenScope, + ) + } + + tokioTest("test_overriding_cache_and_provider_leads_to_shared_credentials_cache_in_layer") { + rustTemplate( + """ + use #{ProvideCachedCredentials}; + use #{RuntimePlugin}; + + let client_config = crate::config::Config::builder() + .credentials_provider(#{Credentials}::for_tests()) + .build(); + let client_config_layer = client_config.config().unwrap(); + + // make sure test credentials are set in the client config level + assert_eq!(#{Credentials}::for_tests(), + client_config_layer + .load::<#{SharedCredentialsCache}>() + .unwrap() + .provide_cached_credentials() + .await + .unwrap() + ); + + let credentials = #{Credentials}::new( + "test", + "test", + #{None}, + #{None}, + "test", + ); + let config_override = crate::config::Config::builder() + .credentials_cache(#{CredentialsCache}::lazy()) + .credentials_provider(credentials.clone()); + let sut = crate::config::ConfigOverrideRuntimePlugin { + client_config: client_config_layer, + config_override, + }; + let sut_layer = sut.config().unwrap(); + + // make sure `.provide_cached_credentials` returns credentials set through `config_override` + assert_eq!(credentials, + sut_layer + .load::<#{SharedCredentialsCache}>() + .unwrap() + .provide_cached_credentials() + .await + .unwrap() + ); + """, + *codegenScope, + ) + } + + unitTest("test_not_overriding_cache_and_provider_leads_to_no_shared_credentials_cache_in_layer") { + rustTemplate( + """ + use #{RuntimePlugin}; + + let client_config = crate::config::Config::builder().build(); + let config_override = crate::config::Config::builder(); + let sut = crate::config::ConfigOverrideRuntimePlugin { + client_config: client_config.config().unwrap(), + config_override, + }; + let sut_layer = sut.config().unwrap(); + assert!(sut_layer + .load::<#{SharedCredentialsCache}>() + .is_none()); + """, + *codegenScope, + ) + } + } + } + } +} diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt index fc40eec7cd..0dabd0033e 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt @@ -7,6 +7,7 @@ package software.amazon.smithy.rustsdk import software.amazon.smithy.model.Model import software.amazon.smithy.model.node.ObjectNode +import software.amazon.smithy.model.node.StringNode import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest @@ -17,6 +18,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.util.letIf import java.io.File // In aws-sdk-codegen, the working dir when gradle runs tests is actually `./aws`. So, to find the smithy runtime, we need @@ -35,8 +37,10 @@ fun awsTestCodegenContext(model: Model? = null, settings: ClientRustSettings? = settings = settings ?: testClientRustSettings(runtimeConfig = AwsTestRuntimeConfig), ) +// TODO(enableNewSmithyRuntimeCleanup): Remove defaultToOrchestrator once the runtime switches to the orchestrator fun awsSdkIntegrationTest( model: Model, + defaultToOrchestrator: Boolean = false, test: (ClientCodegenContext, RustCrate) -> Unit = { _, _ -> }, ) = clientIntegrationTest( @@ -58,6 +62,9 @@ fun awsSdkIntegrationTest( "codegen", ObjectNode.builder() .withMember("includeFluentClient", false) + .letIf(defaultToOrchestrator) { + it.withMember("enableNewSmithyRuntime", StringNode.from("orchestrator")) + } .build(), ).build(), ), diff --git a/aws/sdk/integration-tests/s3/tests/config-override.rs b/aws/sdk/integration-tests/s3/tests/config-override.rs new file mode 100644 index 0000000000..b3d93be867 --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/config-override.rs @@ -0,0 +1,121 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_credential_types::provider::SharedCredentialsProvider; +use aws_sdk_s3::config::{Credentials, Region}; +use aws_sdk_s3::Client; +use aws_smithy_client::test_connection::{capture_request, CaptureRequestReceiver}; +use aws_types::SdkConfig; + +// TODO(enableNewSmithyRuntimeCleanup): Remove this attribute once #[cfg(aws_sdk_orchestrator_mode)] +// has been removed +#[allow(dead_code)] +fn test_client() -> (CaptureRequestReceiver, Client) { + let (conn, captured_request) = capture_request(None); + let sdk_config = SdkConfig::builder() + .credentials_provider(SharedCredentialsProvider::new(Credentials::for_tests())) + .region(Region::new("us-west-2")) + .http_connector(conn) + .build(); + let client = Client::new(&sdk_config); + (captured_request, client) +} + +#[cfg(aws_sdk_orchestrator_mode)] +#[tokio::test] +async fn operation_overrides_force_path_style() { + let (captured_request, client) = test_client(); + let _ = client + .list_objects_v2() + .bucket("test-bucket") + .customize() + .await + .unwrap() + .config_override(aws_sdk_s3::config::Config::builder().force_path_style(true)) + .send() + .await; + assert_eq!( + captured_request.expect_request().uri().to_string(), + "https://s3.us-west-2.amazonaws.com/test-bucket/?list-type=2" + ); +} + +#[cfg(aws_sdk_orchestrator_mode)] +#[tokio::test] +async fn operation_overrides_fips() { + let (captured_request, client) = test_client(); + let _ = client + .list_objects_v2() + .bucket("test-bucket") + .customize() + .await + .unwrap() + .config_override(aws_sdk_s3::config::Config::builder().use_fips(true)) + .send() + .await; + assert_eq!( + captured_request.expect_request().uri().to_string(), + "https://test-bucket.s3-fips.us-west-2.amazonaws.com/?list-type=2" + ); +} + +#[cfg(aws_sdk_orchestrator_mode)] +#[tokio::test] +async fn operation_overrides_dual_stack() { + let (captured_request, client) = test_client(); + let _ = client + .list_objects_v2() + .bucket("test-bucket") + .customize() + .await + .unwrap() + .config_override(aws_sdk_s3::config::Config::builder().use_dual_stack(true)) + .send() + .await; + assert_eq!( + captured_request.expect_request().uri().to_string(), + "https://test-bucket.s3.dualstack.us-west-2.amazonaws.com/?list-type=2" + ); +} + +// TODO(enableNewSmithyRuntimeCleanup): Comment in the following test once Handle is no longer +// accessed in ServiceRuntimePlugin::config. Currently, a credentials cache created for a single +// operation invocation is not picked up by an identity resolver. +/* +#[cfg(aws_sdk_orchestrator_mode)] +#[tokio::test] +async fn operation_overrides_credentials_provider() { + let (captured_request, client) = test_client(); + let _ = client + .list_objects_v2() + .bucket("test-bucket") + .customize() + .await + .unwrap() + .config_override(aws_sdk_s3::config::Config::builder().credentials_provider(Credentials::new( + "test", + "test", + Some("test".into()), + Some(std::time::UNIX_EPOCH + std::time::Duration::from_secs(1669257290 + 3600)), + "test", + ))) + .request_time_for_tests(std::time::UNIX_EPOCH + std::time::Duration::from_secs(1669257290)) + .send() + .await; + + let request = captured_request.expect_request(); + let actual_auth = + std::str::from_utf8(request.headers().get("authorization").unwrap().as_bytes()).unwrap(); + // signature would be f98cc3911dfba0daabf4343152f456bff9ecd3888a3068a1346d26949cb8f9e5 + // if we used `Credentials::for_tests()` + let expected_sig = "Signature=d7e7be63efc37c5bab5eda121999cd1c9a95efdde0cc1ce7c1b8761051cc3cbd"; + assert!( + actual_auth.contains(expected_sig), + "authorization header signature did not match expected signature: expected {} but not found in {}", + expected_sig, + actual_auth, + ); +} +*/ diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index 2c80bfd0cb..1153c27770 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -217,6 +217,45 @@ private class HttpConnectorConfigCustomization( } } + is ServiceConfig.OperationConfigOverride -> writable { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + if let #{Some}(http_connector) = + layer.load::<#{HttpConnector}>() + { + let sleep_impl = layer + .load::<#{SharedAsyncSleep}>() + .or_else(|| { + self.client_config + .load::<#{SharedAsyncSleep}>() + }) + .cloned(); + let timeout_config = layer + .load::<#{TimeoutConfig}>() + .or_else(|| { + self.client_config + .load::<#{TimeoutConfig}>() + }) + .expect("timeout config should be set either in `config_override` or in the client config"); + let connector_settings = + #{ConnectorSettings}::from_timeout_config( + timeout_config, + ); + if let #{Some}(conn) = http_connector.connector(&connector_settings, sleep_impl) { + let connection: #{DynConnector} = #{DynConnector}::new(#{DynConnectorAdapter}::new( + // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation + conn + )); + layer.set_connector(connection); + } + } + """, + *codegenScope, + ) + } + } + else -> emptySection } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt index b193172b55..692b874517 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt @@ -172,7 +172,7 @@ class InterceptorConfigCustomization(codegenContext: ClientCodegenContext) : Con is ServiceConfig.RuntimePluginInterceptors -> rust( """ - ${section.interceptors}.extend(self.interceptors.iter().cloned()); + ${section.interceptors}.extend(${section.interceptorsField}.interceptors.iter().cloned()); """, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index 9cb2f86950..dc20049c46 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -377,7 +377,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon } if retry_config.mode() == #{RetryMode}::Adaptive { - if let Some(time_source) = layer.load::<#{SharedTimeSource}>().cloned() { + if let #{Some}(time_source) = layer.load::<#{SharedTimeSource}>().cloned() { let seconds_since_unix_epoch = time_source .now() .duration_since(#{SystemTime}::UNIX_EPOCH) @@ -396,6 +396,12 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon let token_bucket = TOKEN_BUCKET.get_or_init(token_bucket_partition, #{TokenBucket}::default); layer.store_put(token_bucket); layer.set_retry_strategy(#{DynRetryStrategy}::new(#{StandardRetryStrategy}::new(&retry_config))); + + // TODO(enableNewSmithyRuntimeCleanup): Should not need to provide a default once smithy-rs##2770 + // is resolved + if layer.load::<#{TimeoutConfig}>().is_none() { + layer.store_put(#{TimeoutConfig}::disabled()); + } """, *codegenScope, ) @@ -414,6 +420,24 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon } } + is ServiceConfig.OperationConfigOverride -> { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + if let #{Some}(retry_config) = layer + .load::<#{RetryConfig}>() + .cloned() + { + layer.set_retry_strategy( + #{DynRetryStrategy}::new(#{StandardRetryStrategy}::new(&retry_config)) + ); + } + """, + *codegenScope, + ) + } + } + else -> emptySection } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index a6b07ecaaa..a55c5b3e7d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -240,6 +240,24 @@ internal class EndpointConfigCustomization( } } + is ServiceConfig.OperationConfigOverride -> { + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + if let #{Some}(resolver) = layer + .load::<$sharedEndpointResolver>() + .cloned() + { + let endpoint_resolver = #{DynEndpointResolver}::new( + #{DefaultEndpointResolver}::<#{Params}>::new(resolver)); + layer.set_endpoint_resolver(endpoint_resolver); + } + """, + *codegenScope, + ) + } + } + else -> emptySection } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt new file mode 100644 index 0000000000..edfe94af95 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt @@ -0,0 +1,82 @@ +/* + * 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 + +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +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.core.rustlang.RustWriter +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.writeCustomizations + +class ConfigOverrideRuntimePluginGenerator( + codegenContext: ClientCodegenContext, +) { + private val moduleUseName = codegenContext.moduleUseName() + private val codegenScope = codegenContext.runtimeConfig.let { rc -> + val runtimeApi = RuntimeType.smithyRuntimeApi(rc) + val smithyTypes = RuntimeType.smithyTypes(rc) + arrayOf( + *RuntimeType.preludeScope, + "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), + "ConfigBagAccessors" to runtimeApi.resolve("client::config_bag_accessors::ConfigBagAccessors"), + "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), + "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), + "Layer" to smithyTypes.resolve("config_bag::Layer"), + "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), + ) + } + + fun render(writer: RustWriter, customizations: List) { + writer.rustTemplate( + """ + /// A plugin that enables configuration for a single operation invocation + /// + /// The `config` method will return a `FrozenLayer` by storing values from `config_override`. + /// In the case of default values requested, they will be obtained from `client_config`. + ##[derive(Debug)] + pub(crate) struct ConfigOverrideRuntimePlugin { + pub(crate) config_override: Builder, + pub(crate) client_config: #{FrozenLayer}, + } + + impl #{RuntimePlugin} for ConfigOverrideRuntimePlugin { + fn config(&self) -> #{Option}<#{FrozenLayer}> { + use #{ConfigBagAccessors}; + + ##[allow(unused_mut)] + let layer: #{Layer} = self + .config_override + .inner + .clone() + .into(); + let mut layer = layer.with_name("$moduleUseName::config::ConfigOverrideRuntimePlugin"); + #{config} + + #{Some}(layer.freeze()) + } + + fn interceptors(&self, _interceptors: &mut #{InterceptorRegistrar}) { + #{interceptors} + } + } + + """, + *codegenScope, + "config" to writable { + writeCustomizations( + customizations, + ServiceConfig.OperationConfigOverride("layer"), + ) + }, + "interceptors" to writable { + writeCustomizations(customizations, ServiceConfig.RuntimePluginInterceptors("_interceptors", "self.config_override")) + }, + ) + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 5bf9c8615b..a0087b3d2a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -155,11 +155,15 @@ open class OperationGenerator( pub(crate) fn operation_runtime_plugins( client_runtime_plugins: #{RuntimePlugins}, + client_config: &crate::config::Config, config_override: #{Option}, ) -> #{RuntimePlugins} { let mut runtime_plugins = client_runtime_plugins.with_operation_plugin(Self::new()); if let Some(config_override) = config_override { - runtime_plugins = runtime_plugins.with_operation_plugin(config_override); + runtime_plugins = runtime_plugins.with_operation_plugin(crate::config::ConfigOverrideRuntimePlugin { + config_override, + client_config: #{RuntimePlugin}::config(client_config).expect("frozen layer should exist in client config"), + }) } runtime_plugins #{additional_runtime_plugins} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt index 5e18a99114..3f86fe720d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt @@ -242,7 +242,8 @@ class PaginatorGenerator private constructor( """ let runtime_plugins = #{operation}::operation_runtime_plugins( handle.runtime_plugins.clone(), - None, + &handle.conf, + #{None}, ); """, *codegenScope, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index 571abbd508..1c85d9047b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -55,7 +55,8 @@ class ServiceGenerator( .render(this, decorator.serviceRuntimePluginCustomizations(codegenContext, emptyList())) serviceConfigGenerator.renderRuntimePluginImplForSelf(this) - serviceConfigGenerator.renderRuntimePluginImplForBuilder(this) + ConfigOverrideRuntimePluginGenerator(codegenContext) + .render(this, decorator.configCustomizations(codegenContext, listOf())) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 3292ae10bb..6fe9978f78 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -518,7 +518,8 @@ class FluentClientGenerator( let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; let runtime_plugins = #{Operation}::operation_runtime_plugins( self.handle.runtime_plugins.clone(), - self.config_override + &self.handle.conf, + self.config_override, ); #{Operation}::orchestrate(&runtime_plugins, input).await } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 69634adba4..148353269f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -13,7 +13,6 @@ import software.amazon.smithy.model.shapes.ServiceShape import software.amazon.smithy.model.traits.IdempotencyTokenTrait 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.customizations.codegenScope import software.amazon.smithy.rust.codegen.client.smithy.customize.TestUtilFeature import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter @@ -92,20 +91,20 @@ sealed class ServiceConfig(name: String) : Section(name) { */ object BuilderBuild : ServiceConfig("BuilderBuild") - // TODO(enableNewSmithyRuntimeLaunch): This is temporary until config builder is backed by a CloneableLayer. - // It is needed because certain config fields appear explicitly regardless of the smithy runtime mode, e.g. - // interceptors. The [BuilderBuild] section is bifurcated depending on the runtime mode (in the orchestrator mode, - // storing a field into a frozen layer and in the middleware moving it into a corresponding service config field) - // so we need a different temporary section to always move a field from a builder to service config within the - // build method. + /** + * A section for customizing individual fields in the initializer of Config + */ object BuilderBuildExtras : ServiceConfig("BuilderBuildExtras") /** - * A section for setting up a field to be used by RuntimePlugin + * A section for setting up a field to be used by ConfigOverrideRuntimePlugin */ - data class RuntimePluginConfig(val cfg: String) : ServiceConfig("ToRuntimePlugin") + data class OperationConfigOverride(val cfg: String) : ServiceConfig("ToRuntimePlugin") - data class RuntimePluginInterceptors(val interceptors: String) : ServiceConfig("ToRuntimePluginInterceptors") + /** + * A section for appending additional runtime plugins, stored in [interceptorsField], to [interceptors] + */ + data class RuntimePluginInterceptors(val interceptors: String, val interceptorsField: String) : ServiceConfig("ToRuntimePluginInterceptors") /** * A section for extra functionality that needs to be defined with the config module @@ -262,7 +261,7 @@ fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext } } - is ServiceConfig.RuntimePluginConfig -> emptySection + is ServiceConfig.OperationConfigOverride -> emptySection else -> emptySection } @@ -434,7 +433,10 @@ class ServiceConfigGenerator( // requiring that items created and stored _during_ the build method be `Clone`, since they // will soon be part of a `FrozenLayer` owned by the service config. So we will convert the // current `CloneableLayer` into a `Layer` that does not impose the `Clone` requirement. - let mut layer: #{Layer} = self.inner.into(); + let layer: #{Layer} = self + .inner + .into(); + let mut layer = layer.with_name("$moduleUseName::config::config"); """, *codegenScope, ) @@ -478,35 +480,9 @@ class ServiceConfigGenerator( """, *codegenScope, - "config" to writable { writeCustomizations(customizations, ServiceConfig.RuntimePluginConfig("cfg")) }, - "interceptors" to writable { - writeCustomizations(customizations, ServiceConfig.RuntimePluginInterceptors("_interceptors")) - }, - ) - } - - fun renderRuntimePluginImplForBuilder(writer: RustWriter) { - writer.rustTemplate( - """ - impl #{RuntimePlugin} for Builder { - fn config(&self) -> #{Option}<#{FrozenLayer}> { - // TODO(enableNewSmithyRuntimeLaunch): Put into `cfg` the fields in `self.config_override` that are not `None` - ##[allow(unused_mut)] - let mut cfg = #{CloneableLayer}::new("service config"); - #{config} - #{Some}(cfg.freeze()) - } - - fn interceptors(&self, _interceptors: &mut #{InterceptorRegistrar}) { - #{interceptors} - } - } - - """, - *codegenScope, - "config" to writable { writeCustomizations(customizations, ServiceConfig.RuntimePluginConfig("cfg")) }, + "config" to writable { writeCustomizations(customizations, ServiceConfig.OperationConfigOverride("cfg")) }, "interceptors" to writable { - writeCustomizations(customizations, ServiceConfig.RuntimePluginInterceptors("_interceptors")) + writeCustomizations(customizations, ServiceConfig.RuntimePluginInterceptors("_interceptors", "self")) }, ) } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt new file mode 100644 index 0000000000..b1ee6311a6 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt @@ -0,0 +1,247 @@ +/* + * 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 + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency.Companion.smithyRuntimeApiTestUtil +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.IntegrationTestParams +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.testutil.testModule +import software.amazon.smithy.rust.codegen.core.testutil.tokioTest +import software.amazon.smithy.rust.codegen.core.testutil.unitTest + +internal class ConfigOverrideRuntimePluginGeneratorTest { + 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 `operation overrides endpoint resolver`() { + clientIntegrationTest( + model, + params = IntegrationTestParams(additionalSettings = TestCodegenSettings.orchestratorMode()), + ) { clientCodegenContext, rustCrate -> + val runtimeConfig = clientCodegenContext.runtimeConfig + val codegenScope = arrayOf( + *preludeScope, + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), + "EndpointResolverParams" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::EndpointResolverParams"), + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + ) + rustCrate.testModule { + addDependency(CargoDependency.Tokio.withFeature("test-util").toDevDependency()) + tokioTest("test_operation_overrides_endpoint_resolver") { + rustTemplate( + """ + use #{ConfigBagAccessors}; + use #{RuntimePlugin}; + + let expected_url = "http://localhost:1234/"; + let client_config = crate::config::Config::builder().build(); + let config_override = + crate::config::Config::builder().endpoint_resolver(expected_url); + let sut = crate::config::ConfigOverrideRuntimePlugin { + client_config: client_config.config().unwrap(), + config_override, + }; + let sut_layer = sut.config().unwrap(); + let endpoint_resolver = sut_layer.endpoint_resolver(); + let endpoint = endpoint_resolver + .resolve_endpoint(&#{EndpointResolverParams}::new(crate::config::endpoint::Params {})) + .await + .unwrap(); + + assert_eq!(expected_url, endpoint.url()); + """, + *codegenScope, + ) + } + } + } + } + + @Test + fun `operation overrides http connector`() { + clientIntegrationTest( + model, + params = IntegrationTestParams(additionalSettings = TestCodegenSettings.orchestratorMode()), + ) { clientCodegenContext, rustCrate -> + val runtimeConfig = clientCodegenContext.runtimeConfig + val codegenScope = arrayOf( + *preludeScope, + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + ) + rustCrate.testModule { + addDependency(CargoDependency.Tokio.withFeature("test-util").toDevDependency()) + tokioTest("test_operation_overrides_http_connection") { + rustTemplate( + """ + use #{AsyncSleep}; + + let (conn, captured_request) = #{capture_request}(#{None}); + let expected_url = "http://localhost:1234/"; + let client_config = crate::config::Config::builder() + .endpoint_resolver(expected_url) + .http_connector(#{NeverConnector}::new()) + .build(); + let client = crate::client::Client::from_conf(client_config.clone()); + let sleep = #{TokioSleep}::new(); + + let send = client.say_hello().send(); + let timeout = #{Timeout}::new( + send, + sleep.sleep(::std::time::Duration::from_millis(100)), + ); + + // sleep future should win because the other future `send` is backed by `NeverConnector`, + // yielding `Err` + matches!(timeout.await, Err(_)); + + let client = crate::client::Client::from_conf(client_config); + let customizable_send = client + .say_hello() + .customize() + .await + .unwrap() + .config_override(crate::config::Config::builder().http_connector(conn)) + .send(); + + let timeout = #{Timeout}::new( + customizable_send, + sleep.sleep(::std::time::Duration::from_millis(100)), + ); + + // `conn` should shadow `NeverConnector` by virtue of `config_override` + match timeout.await { + Ok(_) => { + assert_eq!( + expected_url, + captured_request.expect_request().uri().to_string() + ); + } + Err(_) => { + panic!("this arm should not be reached since the `customizable_send` future should win"); + } + } + """, + *codegenScope, + "AsyncSleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep::AsyncSleep"), + "capture_request" to RuntimeType.captureRequest(runtimeConfig), + "NeverConnector" to RuntimeType.smithyClient(runtimeConfig) + .resolve("never::NeverConnector"), + "Timeout" to RuntimeType.smithyAsync(runtimeConfig).resolve("future::timeout::Timeout"), + "TokioSleep" to RuntimeType.smithyAsync(runtimeConfig) + .resolve("rt::sleep::TokioSleep"), + ) + } + } + } + } + + @Test + fun `operation overrides retry strategy`() { + clientIntegrationTest( + model, + params = IntegrationTestParams(additionalSettings = TestCodegenSettings.orchestratorMode()), + ) { clientCodegenContext, rustCrate -> + val runtimeConfig = clientCodegenContext.runtimeConfig + val codegenScope = arrayOf( + *preludeScope, + "AlwaysRetry" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::retries::AlwaysRetry"), + "ConfigBag" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag"), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), + "ErrorKind" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::ErrorKind"), + "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), + "Layer" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::Layer"), + "OrchestratorError" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::orchestrator::OrchestratorError"), + "RetryConfig" to RuntimeType.smithyTypes(clientCodegenContext.runtimeConfig) + .resolve("retry::RetryConfig"), + "RequestAttempts" to smithyRuntimeApiTestUtil(runtimeConfig).toType() + .resolve("client::request_attempts::RequestAttempts"), + "RetryClassifiers" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::retries::RetryClassifiers"), + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "ShouldAttempt" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::retries::ShouldAttempt"), + "TypeErasedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypeErasedBox"), + ) + rustCrate.testModule { + unitTest("test_operation_overrides_retry_strategy") { + rustTemplate( + """ + use #{ConfigBagAccessors}; + use #{RuntimePlugin}; + + let client_config = crate::config::Config::builder() + .retry_config(#{RetryConfig}::standard().with_max_attempts(3)) + .build(); + + let client_config_layer = client_config.config().unwrap(); + + let mut ctx = #{InterceptorContext}::new(#{TypeErasedBox}::new(())); + ctx.set_output_or_error(#{Err}(#{OrchestratorError}::other("doesn't matter"))); + let mut layer = #{Layer}::new("test"); + layer.store_put(#{RequestAttempts}::new(1)); + layer.set_retry_classifiers( + #{RetryClassifiers}::new().with_classifier(#{AlwaysRetry}(#{ErrorKind}::TransientError)), + ); + + let mut cfg = #{ConfigBag}::of_layers(vec![layer]); + cfg.push_shared_layer(client_config_layer.clone()); + + let retry = cfg.retry_strategy().unwrap(); + assert!(matches!( + retry.should_attempt_retry(&ctx, &cfg).unwrap(), + #{ShouldAttempt}::YesAfterDelay(_) + )); + + // sets `max_attempts` to 1 implicitly by using `disabled()`, forcing it to run out of + // attempts with respect to `RequestAttempts` set to 1 above + let config_override = crate::config::Config::builder() + .retry_config(#{RetryConfig}::disabled()); + let sut = crate::config::ConfigOverrideRuntimePlugin { + client_config: client_config_layer, + config_override, + }; + let sut_layer = sut.config().unwrap(); + cfg.push_shared_layer(sut_layer); + let retry = cfg.retry_strategy().unwrap(); + + assert!(matches!( + retry.should_attempt_retry(&ctx, &cfg).unwrap(), + #{ShouldAttempt}::No + )); + """, + *codegenScope, + ) + } + } + } + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt index 97f114fda0..0726c6870c 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGeneratorTest.kt @@ -6,8 +6,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import org.junit.jupiter.api.Test -import software.amazon.smithy.model.node.ObjectNode -import software.amazon.smithy.model.node.StringNode +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -83,19 +82,11 @@ internal class PaginatorGeneratorTest { } } - private fun enableNewSmithyRuntime(): ObjectNode = ObjectNode.objectNodeBuilder() - .withMember( - "codegen", - ObjectNode.objectNodeBuilder() - .withMember("enableNewSmithyRuntime", StringNode.from("orchestrator")).build(), - ) - .build() - @Test fun `generate paginators that compile`() { clientIntegrationTest( model, - params = IntegrationTestParams(additionalSettings = enableNewSmithyRuntime()), + params = IntegrationTestParams(additionalSettings = TestCodegenSettings.orchestratorMode()), ) { clientCodegenContext, rustCrate -> rustCrate.integrationTest("paginators_generated") { Attribute.AllowUnusedImports.render(this) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index d850b2cc26..b91020fe3f 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -289,6 +289,8 @@ data class CargoDependency( fun smithyQuery(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-query") fun smithyRuntime(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-runtime") fun smithyRuntimeApi(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-runtime-api") + fun smithyRuntimeApiTestUtil(runtimeConfig: RuntimeConfig) = + smithyRuntimeApi(runtimeConfig).toDevDependency().withFeature("test-util") fun smithyTypes(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-types") fun smithyXml(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-xml") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt index fa98e87fcb..b40d5b8a06 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt @@ -215,10 +215,12 @@ fun RustWriter.unitTest( name: String, vararg args: Any, attribute: Attribute = Attribute.Test, + additionalAttributes: List = emptyList(), async: Boolean = false, block: Writable, ): RustWriter { attribute.render(this) + additionalAttributes.forEach { it.render(this) } if (async) { rust("async") } diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index 8a993e3114..12fefbe3b1 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -242,6 +242,13 @@ impl Layer { } } + pub fn with_name(self, name: impl Into>) -> Self { + Self { + name: name.into(), + props: self.props, + } + } + /// Load a storable item from the bag pub fn load(&self) -> ::ReturnedType<'_> { T::Storer::merge_iter(ItemIter { From ddba46086a9754c01f3b11d7521c49d4489de84b Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 4 Jul 2023 14:19:15 +0200 Subject: [PATCH 197/253] Better distinguish model and HTTP plugins (#2827) So far, servers have tacitly worked with the notion that plugins come in two flavors: "HTTP plugins" and "model plugins": - A HTTP plugin acts on the HTTP request before it is deserialized, and acts on the HTTP response after it is serialized. - A model plugin acts on the modeled operation input after it is deserialized, and acts on the modeled operation output or the modeled operation error before it is serialized. However, this notion was never reified in the type system. Thus, users who pass in a model plugin where a HTTP plugin is expected or viceversa in several APIs: ```rust let pipeline = PluginPipeline::new().push(http_plugin).push(model_plugin); let app = SimpleService::builder_with_plugins(http_plugins, IdentityPlugin) .post_operation(handler) /* ... */ .build() .unwrap(); ``` would get the typical Tower service compilation errors, which can get very confusing: ``` error[E0631]: type mismatch in function arguments --> simple/rust-server-codegen/src/main.rs:47:6 | 15 | fn new_svc(inner: S) -> model_plugin::PostOperationService { | -------------------------------------------------------------------------- found signature defined here ... 47 | .post_operation(post_operation) | ^^^^^^^^^^^^^^ expected due to this | = note: expected function signature `fn(Upgrade, _>>) -> _` found function signature `fn(aws_smithy_http_server::operation::IntoService) -> _` = note: required for `LayerFn) -> ... {new_svc::<..., ...>}>` to implement `Layer, _>>>` = note: the full type name has been written to '/local/home/davidpz/workplace/smithy-ws/src/SmithyRsSource/target/debug/deps/simple-6577f9f79749ceb9.long-type-4938700695428041031.txt' ``` This commit introduces the `HttpPlugin` and `ModelPlugin` marker traits, allowing plugins to be marked as an HTTP plugin, a model plugin, or both. It also removes the primary way of concatenating plugins, `PluginPipeline`, in favor of `HttpPlugins` and `ModelPlugins`, which eagerly check that whenever a plugin is `push`ed, it is of the expected type. The generated service type in the server SDKs' `builder_with_plugins` constructor now takes an `HttpPlugin` as its first parameter, and a `ModelPlugin` as its second parameter. I think this change perhaps goes counter to the generally accepted wisdom that trait bounds in Rust should be enforced "at the latest possible moment", that is, only when the behavior encoded in the trait implementation is relied upon in the code (citation needed). However, the result is that exposing the concepts of HTTP plugin and model plugin in the type system makes for code that is easier to reason about, and produces more helpful compiler error messages. Documentation about the plugin system has been expanded, particularly on how to implement model plugins. ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ --- CHANGELOG.next.toml | 40 ++- .../smithy/generators/ServerRootGenerator.kt | 9 +- .../ServerRuntimeTypesReExportsGenerator.kt | 7 +- .../generators/ServerServiceGenerator.kt | 35 +- design/src/server/anatomy.md | 32 +- design/src/server/instrumentation.md | 4 +- design/src/server/middleware.md | 28 +- .../tests/plugins_execution_order.rs | 16 +- examples/pokemon-service/src/main.rs | 6 +- examples/pokemon-service/src/plugin.rs | 13 +- .../aws-smithy-http-server/src/extension.rs | 14 +- .../src/instrumentation/plugin.rs | 10 +- .../src/plugin/alb_health_check.rs | 4 +- .../src/plugin/filter.rs | 11 +- .../plugin/{pipeline.rs => http_plugins.rs} | 113 ++++--- .../src/plugin/identity.rs | 6 +- .../src/plugin/layer.rs | 8 +- .../aws-smithy-http-server/src/plugin/mod.rs | 305 +++++++++++++++++- .../src/plugin/model_plugins.rs | 94 ++++++ .../src/plugin/scoped.rs | 7 +- .../src/plugin/stack.rs | 21 +- 21 files changed, 627 insertions(+), 156 deletions(-) rename rust-runtime/aws-smithy-http-server/src/plugin/{pipeline.rs => http_plugins.rs} (50%) create mode 100644 rust-runtime/aws-smithy-http-server/src/plugin/model_plugins.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 35349116c0..f3ac9bdc43 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -210,6 +210,7 @@ message = """The middleware system has been reworked as we push for a unified, s - A `ServiceShape` trait has been added. - The `Plugin` trait has been simplified. +- The `HttpMarker` and `ModelMarker` marker traits have been added to better distinguish when plugins run and what they have access to. - The `Operation` structure has been removed. - A `Scoped` `Plugin` has been added. @@ -371,6 +372,8 @@ where PrintService { inner, name: Op::ID.name() } } } + +impl HttpMarker for PrintPlugin { } ``` Alternatively, using the new `ServiceShape`, implemented on `Ser`: @@ -397,6 +400,11 @@ let app = PokemonService::builder_with_plugins(/* HTTP plugins */, /* model plug .unwrap(); ``` +To better distinguish when a plugin runs and what it has access to, `Plugin`s now have to additionally implement the `HttpMarker` marker trait, the `ModelMarker` marker trait, or both: + +- A HTTP plugin acts on the HTTP request before it is deserialized, and acts on the HTTP response after it is serialized. +- A model plugin acts on the modeled operation input after it is deserialized, and acts on the modeled operation output or the modeled operation error before it is serialized. + The motivation behind this change is to simplify the job of middleware authors, separate concerns, accomodate common cases better, and to improve composition internally. Because `Plugin` is now closer to `tower::Layer` we have two canonical converters: @@ -413,6 +421,34 @@ let plugin = /* some plugin */; let layer = LayerPlugin::new::(plugin); ``` +## Removal of `PluginPipeline` + +Since plugins now come in two flavors (those marked with `HttpMarker` and those marked with `ModelMarker`) that shouldn't be mixed in a collection of plugins, the primary way of concatenating plugins, `PluginPipeline` has been removed in favor of the `HttpPlugins` and `ModelPlugins` types, which eagerly check that whenever a plugin is pushed, it is of the expected type. + +This worked before, but you wouldn't be able to do apply this collection of plugins anywhere; if you tried to, the compilation error messages would not be very helpful: + +```rust +use aws_smithy_http_server::plugin::PluginPipeline; + +let pipeline = PluginPipeline::new().push(http_plugin).push(model_plugin); +``` + +Now collections of plugins must contain plugins of the same flavor: + +```rust +use aws_smithy_http_server::plugin::{HttpPlugins, ModelPlugins}; + +let http_plugins = HttpPlugins::new() + .push(http_plugin) + // .push(model_plugin) // This fails to compile with a helpful error message. + .push(&http_and_model_plugin); +let model_plugins = ModelPlugins::new() + .push(model_plugin) + .push(&http_and_model_plugin); +``` + +In the above example, `&http_and_model_plugin` implements both `HttpMarker` and `ModelMarker`, so we can add it to both collections. + ## Removal of `Operation` The `aws_smithy_http_server::operation::Operation` structure has now been removed. Previously, there existed a `{operation_name}_operation` setter on the service builder, which accepted an `Operation`. This allowed users to @@ -495,8 +531,8 @@ let scoped_plugin = Scoped::new::(plugin); ``` """ -references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779"] -meta = { "breaking" = true, "tada" = false, "bug" = false } +references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779", "smithy-rs#2827"] +meta = { "breaking" = true, "tada" = true, "bug" = false } author = "hlbarber" [[smithy-rs]] diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt index c8c1dd8450..e300d248b1 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRootGenerator.kt @@ -106,7 +106,8 @@ open class ServerRootGenerator( //! #### Plugins //! //! The [`$serviceName::builder_with_plugins`] method, returning [`$builderName`], - //! accepts a [`Plugin`](aws_smithy_http_server::plugin::Plugin). + //! accepts a plugin marked with [`HttpMarker`](aws_smithy_http_server::plugin::HttpMarker) and a + //! plugin marked with [`ModelMarker`](aws_smithy_http_server::plugin::ModelMarker). //! Plugins allow you to build middleware which is aware of the operation it is being applied to. //! //! ```rust @@ -114,13 +115,13 @@ open class ServerRootGenerator( //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as LoggingPlugin; //! ## use #{SmithyHttpServer}::plugin::IdentityPlugin as MetricsPlugin; //! ## use #{Hyper}::Body; - //! use #{SmithyHttpServer}::plugin::PluginPipeline; + //! use #{SmithyHttpServer}::plugin::HttpPlugins; //! use $crateName::{$serviceName, $builderName}; //! - //! let plugins = PluginPipeline::new() + //! let http_plugins = HttpPlugins::new() //! .push(LoggingPlugin) //! .push(MetricsPlugin); - //! let builder: $builderName = $serviceName::builder_with_plugins(plugins, IdentityPlugin); + //! let builder: $builderName = $serviceName::builder_with_plugins(http_plugins, IdentityPlugin); //! ``` //! //! Check out [`#{SmithyHttpServer}::plugin`] to learn more about plugins. diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt index 85a9507648..a6f62246d8 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerRuntimeTypesReExportsGenerator.kt @@ -9,14 +9,12 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.smithy.CodegenContext import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency -import software.amazon.smithy.rust.codegen.server.smithy.ServerRuntimeType class ServerRuntimeTypesReExportsGenerator( codegenContext: CodegenContext, ) { private val runtimeConfig = codegenContext.runtimeConfig private val codegenScope = arrayOf( - "Router" to ServerRuntimeType.router(runtimeConfig), "SmithyHttpServer" to ServerCargoDependency.smithyHttpServer(runtimeConfig).toType(), ) @@ -30,8 +28,11 @@ class ServerRuntimeTypesReExportsGenerator( pub use #{SmithyHttpServer}::operation::OperationShape; } pub mod plugin { + pub use #{SmithyHttpServer}::plugin::HttpPlugins; + pub use #{SmithyHttpServer}::plugin::ModelPlugins; + pub use #{SmithyHttpServer}::plugin::HttpMarker; + pub use #{SmithyHttpServer}::plugin::ModelMarker; pub use #{SmithyHttpServer}::plugin::Plugin; - pub use #{SmithyHttpServer}::plugin::PluginPipeline; pub use #{SmithyHttpServer}::plugin::PluginStack; } pub mod request { diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt index 2852734b3f..2b4e2ee2e9 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ServerServiceGenerator.kt @@ -136,7 +136,7 @@ class ServerServiceGenerator( where HandlerType: #{SmithyHttpServer}::operation::Handler, - ModelPlugin: #{SmithyHttpServer}::plugin::Plugin< + ModelPl: #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, #{SmithyHttpServer}::operation::IntoService @@ -144,9 +144,9 @@ class ServerServiceGenerator( #{SmithyHttpServer}::operation::UpgradePlugin::: #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, - ModelPlugin::Output + ModelPl::Output >, - HttpPlugin: #{SmithyHttpServer}::plugin::Plugin< + HttpPl: #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, < @@ -154,13 +154,13 @@ class ServerServiceGenerator( as #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, - ModelPlugin::Output + ModelPl::Output > >::Output >, - HttpPlugin::Output: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, - >>::Future: Send + 'static, + HttpPl::Output: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, + >>::Future: Send + 'static, { use #{SmithyHttpServer}::operation::OperationShapeExt; @@ -199,7 +199,7 @@ class ServerServiceGenerator( where S: #{SmithyHttpServer}::operation::OperationService, - ModelPlugin: #{SmithyHttpServer}::plugin::Plugin< + ModelPl: #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, #{SmithyHttpServer}::operation::Normalize @@ -207,9 +207,9 @@ class ServerServiceGenerator( #{SmithyHttpServer}::operation::UpgradePlugin::: #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, - ModelPlugin::Output + ModelPl::Output >, - HttpPlugin: #{SmithyHttpServer}::plugin::Plugin< + HttpPl: #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, < @@ -217,13 +217,13 @@ class ServerServiceGenerator( as #{SmithyHttpServer}::plugin::Plugin< $serviceName, crate::operation_shape::$structName, - ModelPlugin::Output + ModelPl::Output > >::Output >, - HttpPlugin::Output: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, - >>::Future: Send + 'static, + HttpPl::Output: #{Tower}::Service<#{Http}::Request, Response = #{Http}::Response<#{SmithyHttpServer}::body::BoxBody>, Error = ::std::convert::Infallible> + Clone + Send + 'static, + >>::Future: Send + 'static, { use #{SmithyHttpServer}::operation::OperationShapeExt; @@ -394,7 +394,7 @@ class ServerServiceGenerator( /** Returns a `Writable` containing the builder struct definition and its implementations. */ private fun builder(): Writable = writable { - val builderGenerics = listOf(builderBodyGenericTypeName, "HttpPlugin", "ModelPlugin").joinToString(", ") + val builderGenerics = listOf(builderBodyGenericTypeName, "HttpPl", "ModelPl").joinToString(", ") rustTemplate( """ /// The service builder for [`$serviceName`]. @@ -402,8 +402,8 @@ class ServerServiceGenerator( /// Constructed via [`$serviceName::builder_with_plugins`] or [`$serviceName::builder_without_plugins`]. pub struct $builderName<$builderGenerics> { ${builderFields.joinToString(", ")}, - http_plugin: HttpPlugin, - model_plugin: ModelPlugin + http_plugin: HttpPl, + model_plugin: ModelPl } impl<$builderGenerics> $builderName<$builderGenerics> { @@ -473,9 +473,10 @@ class ServerServiceGenerator( /// /// Use [`$serviceName::builder_without_plugins`] if you don't need to apply plugins. /// - /// Check out [`PluginPipeline`](#{SmithyHttpServer}::plugin::PluginPipeline) if you need to apply + /// Check out [`HttpPlugins`](#{SmithyHttpServer}::plugin::HttpPlugins) and + /// [`ModelPlugins`](#{SmithyHttpServer}::plugin::ModelPlugins) if you need to apply /// multiple plugins. - pub fn builder_with_plugins(http_plugin: HttpPlugin, model_plugin: ModelPlugin) -> $builderName { + pub fn builder_with_plugins(http_plugin: HttpPl, model_plugin: ModelPl) -> $builderName { $builderName { #{NotSetFields:W}, http_plugin, diff --git a/design/src/server/anatomy.md b/design/src/server/anatomy.md index bc7a550c95..d15f4fbcc0 100644 --- a/design/src/server/anatomy.md +++ b/design/src/server/anatomy.md @@ -466,40 +466,40 @@ stateDiagram-v2 The service builder API requires plugins to be specified upfront - they must be passed as an argument to `builder_with_plugins` and cannot be modified afterwards. You might find yourself wanting to apply _multiple_ plugins to your service. -This can be accommodated via [`PluginPipeline`]. +This can be accommodated via [`HttpPlugins`] and [`ModelPlugins`]. ```rust # extern crate aws_smithy_http_server; -use aws_smithy_http_server::plugin::PluginPipeline; +use aws_smithy_http_server::plugin::HttpPlugins; # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; -let pipeline = PluginPipeline::new().push(LoggingPlugin).push(MetricsPlugin); +let http_plugins = HttpPlugins::new().push(LoggingPlugin).push(MetricsPlugin); ``` The plugins' runtime logic is executed in registration order. In the example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last. -If you are vending a plugin, you can leverage `PluginPipeline` as an extension point: you can add custom methods to it using an extension trait. +If you are vending a plugin, you can leverage `HttpPlugins` or `ModelPlugins` as an extension point: you can add custom methods to it using an extension trait. For example: ```rust # extern crate aws_smithy_http_server; -use aws_smithy_http_server::plugin::{PluginPipeline, PluginStack}; +use aws_smithy_http_server::plugin::{HttpPlugins, PluginStack}; # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; pub trait AuthPluginExt { - fn with_auth(self) -> PluginPipeline>; + fn with_auth(self) -> HttpPlugins>; } -impl AuthPluginExt for PluginPipeline { - fn with_auth(self) -> PluginPipeline> { +impl AuthPluginExt for HttpPlugins { + fn with_auth(self) -> HttpPlugins> { self.push(AuthPlugin) } } -let pipeline = PluginPipeline::new() +let http_plugins = HttpPlugins::new() .push(LoggingPlugin) // Our custom method! .with_auth(); @@ -518,15 +518,15 @@ You can create an instance of a service builder by calling either `builder_witho /// The service builder for [`PokemonService`]. /// /// Constructed via [`PokemonService::builder`]. -pub struct PokemonServiceBuilder { +pub struct PokemonServiceBuilder { capture_pokemon_operation: Option>, empty_operation: Option>, get_pokemon_species: Option>, get_server_statistics: Option>, get_storage: Option>, health_check_operation: Option>, - http_plugin: HttpPlugin, - model_plugin: ModelPlugin + http_plugin: HttpPl, + model_plugin: ModelPl, } ``` @@ -537,7 +537,7 @@ The builder has two setter methods for each [Smithy Operation](https://awslabs.g where HandlerType:Handler, - ModelPlugin: Plugin< + ModelPl: Plugin< PokemonService, GetPokemonSpecies, IntoService @@ -547,7 +547,7 @@ The builder has two setter methods for each [Smithy Operation](https://awslabs.g GetPokemonSpecies, ModelPlugin::Output >, - HttpPlugin: Plugin< + HttpPl: Plugin< PokemonService, GetPokemonSpecies, UpgradePlugin::::Output @@ -565,7 +565,7 @@ The builder has two setter methods for each [Smithy Operation](https://awslabs.g where S: OperationService, - ModelPlugin: Plugin< + ModelPl: Plugin< PokemonService, GetPokemonSpecies, Normalize @@ -575,7 +575,7 @@ The builder has two setter methods for each [Smithy Operation](https://awslabs.g GetPokemonSpecies, ModelPlugin::Output >, - HttpPlugin: Plugin< + HttpPl: Plugin< PokemonService, GetPokemonSpecies, UpgradePlugin::::Output diff --git a/design/src/server/instrumentation.md b/design/src/server/instrumentation.md index dfb5ee5252..08c72a2098 100644 --- a/design/src/server/instrumentation.md +++ b/design/src/server/instrumentation.md @@ -66,11 +66,11 @@ This is enabled via the `instrument` method provided by the `aws_smithy_http_ser # let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; use aws_smithy_http_server::{ instrumentation::InstrumentExt, - plugin::{IdentityPlugin, PluginPipeline} + plugin::{IdentityPlugin, HttpPlugins} }; use pokemon_service_server_sdk::PokemonService; -let http_plugins = PluginPipeline::new().instrument(); +let http_plugins = HttpPlugins::new().instrument(); let app = PokemonService::builder_with_plugins(http_plugins, IdentityPlugin) .get_pokemon_species(handler) /* ... */ diff --git a/design/src/server/middleware.md b/design/src/server/middleware.md index 409c8700fa..af394b1bca 100644 --- a/design/src/server/middleware.md +++ b/design/src/server/middleware.md @@ -226,7 +226,7 @@ scope! { // Construct `LoggingLayer`. let logging_plugin = LayerPlugin(LoggingLayer::new()); let logging_plugin = Scoped::new::(logging_plugin); -let http_plugins = PluginPipeline::new().push(logging_plugin); +let http_plugins = HttpPlugins::new().push(logging_plugin); let app /* : PokemonService> */ = PokemonService::builder_with_plugins(http_plugins, IdentityPlugin) .get_pokemon_species(handler) @@ -265,7 +265,7 @@ scope! { // Construct `BufferLayer`. let buffer_plugin = LayerPlugin(BufferLayer::new(3)); let buffer_plugin = Scoped::new::(buffer_plugin); -let model_plugins = PluginPipeline::new().push(buffer_plugin); +let model_plugins = ModelPlugins::new().push(buffer_plugin); let app /* : PokemonService> */ = PokemonService::builder_with_plugins(IdentityPlugin, model_plugins) .get_pokemon_species(handler) @@ -347,23 +347,24 @@ where } ``` -You can provide a custom method to add your plugin to a `PluginPipeline` via an extension trait: +You can provide a custom method to add your plugin to a collection of `HttpPlugins` or `ModelPlugins` via an extension trait. For example, for `HttpPlugins`: ```rust # extern crate aws_smithy_http_server; # pub struct PrintPlugin; -use aws_smithy_http_server::plugin::{PluginPipeline, PluginStack}; +# impl aws_smithy_http_server::plugin::HttpMarker for PrintPlugin { } +use aws_smithy_http_server::plugin::{HttpPlugins, PluginStack}; -/// This provides a [`print`](PrintExt::print) method on [`PluginPipeline`]. +/// This provides a [`print`](PrintExt::print) method on [`HttpPlugins`]. pub trait PrintExt { /// Causes all operations to print the operation name when called. /// /// This works by applying the [`PrintPlugin`]. - fn print(self) -> PluginPipeline>; + fn print(self) -> HttpPlugins>; } -impl PrintExt for PluginPipeline { - fn print(self) -> PluginPipeline> { +impl PrintExt for HttpPlugins { + fn print(self) -> HttpPlugins> { self.push(PrintPlugin) } } @@ -377,14 +378,15 @@ This allows for: # use aws_smithy_http_server::plugin::{PluginStack, Plugin}; # struct PrintPlugin; # impl Plugin for PrintPlugin { type Output = T; fn apply(&self, svc: T) -> Self::Output { svc }} -# trait PrintExt { fn print(self) -> PluginPipeline>; } -# impl PrintExt for PluginPipeline { fn print(self) -> PluginPipeline> { self.push(PrintPlugin) }} +# impl aws_smithy_http_server::plugin::HttpMarker for PrintPlugin { } +# trait PrintExt { fn print(self) -> HttpPlugins>; } +# impl PrintExt for HttpPlugins { fn print(self) -> HttpPlugins> { self.push(PrintPlugin) }} # use pokemon_service_server_sdk::{operation_shape::GetPokemonSpecies, input::*, output::*, error::*}; # let handler = |req: GetPokemonSpeciesInput| async { Result::::Ok(todo!()) }; -use aws_smithy_http_server::plugin::{IdentityPlugin, PluginPipeline}; +use aws_smithy_http_server::plugin::{IdentityPlugin, HttpPlugins}; use pokemon_service_server_sdk::PokemonService; -let http_plugins = PluginPipeline::new() +let http_plugins = HttpPlugins::new() // [..other plugins..] // The custom method! .print(); @@ -397,4 +399,4 @@ let app /* : PokemonService> */ = PokemonService::builder_with_plugins( ``` The custom `print` method hides the details of the `Plugin` trait from the average consumer. -They interact with the utility methods on `PluginPipeline` and enjoy the self-contained documentation. +They interact with the utility methods on `HttpPlugins` and enjoy the self-contained documentation. diff --git a/examples/pokemon-service-common/tests/plugins_execution_order.rs b/examples/pokemon-service-common/tests/plugins_execution_order.rs index de9f2632f1..7a768cb005 100644 --- a/examples/pokemon-service-common/tests/plugins_execution_order.rs +++ b/examples/pokemon-service-common/tests/plugins_execution_order.rs @@ -11,7 +11,7 @@ use std::{ }; use aws_smithy_http::body::SdkBody; -use aws_smithy_http_server::plugin::{IdentityPlugin, Plugin, PluginPipeline}; +use aws_smithy_http_server::plugin::{HttpMarker, HttpPlugins, IdentityPlugin, Plugin}; use tower::{Layer, Service}; use pokemon_service_client::{operation::do_nothing::DoNothingInput, Config}; @@ -34,13 +34,15 @@ async fn plugin_layers_are_executed_in_registration_order() { // We can then check the vector content to verify the invocation order let output = Arc::new(Mutex::new(Vec::new())); - let pipeline = PluginPipeline::new() + let http_plugins = HttpPlugins::new() .push(SentinelPlugin::new("first", output.clone())) .push(SentinelPlugin::new("second", output.clone())); - let mut app = - pokemon_service_server_sdk::PokemonService::builder_with_plugins(pipeline, IdentityPlugin) - .do_nothing(do_nothing) - .build_unchecked(); + let mut app = pokemon_service_server_sdk::PokemonService::builder_with_plugins( + http_plugins, + IdentityPlugin, + ) + .do_nothing(do_nothing) + .build_unchecked(); let request = DoNothingInput::builder() .build() .unwrap() @@ -77,6 +79,8 @@ impl Plugin for SentinelPlugin { } } +impl HttpMarker for SentinelPlugin {} + /// A [`Service`] that adds a print log. #[derive(Clone, Debug)] pub struct SentinelService { diff --git a/examples/pokemon-service/src/main.rs b/examples/pokemon-service/src/main.rs index b3aa767f50..b7566baa2e 100644 --- a/examples/pokemon-service/src/main.rs +++ b/examples/pokemon-service/src/main.rs @@ -10,7 +10,7 @@ use std::{net::SocketAddr, sync::Arc}; use aws_smithy_http_server::{ extension::OperationExtensionExt, instrumentation::InstrumentExt, - plugin::{alb_health_check::AlbHealthCheckLayer, IdentityPlugin, PluginPipeline, Scoped}, + plugin::{alb_health_check::AlbHealthCheckLayer, HttpPlugins, IdentityPlugin, Scoped}, request::request_id::ServerRequestIdProviderLayer, AddExtensionLayer, }; @@ -51,9 +51,9 @@ pub async fn main() { } } // Scope the `PrintPlugin`, defined in `plugin.rs`, to `PrintScope` - let print_plugin = Scoped::new::(PluginPipeline::new().print()); + let print_plugin = Scoped::new::(HttpPlugins::new().print()); - let plugins = PluginPipeline::new() + let plugins = HttpPlugins::new() // Apply the scoped `PrintPlugin` .push(print_plugin) // Apply the `OperationExtensionPlugin` defined in `aws_smithy_http_server::extension`. This allows other diff --git a/examples/pokemon-service/src/plugin.rs b/examples/pokemon-service/src/plugin.rs index e030d3a472..971aebb015 100644 --- a/examples/pokemon-service/src/plugin.rs +++ b/examples/pokemon-service/src/plugin.rs @@ -7,7 +7,7 @@ use aws_smithy_http_server::{ operation::OperationShape, - plugin::{Plugin, PluginPipeline, PluginStack}, + plugin::{HttpMarker, HttpPlugins, Plugin, PluginStack}, service::ServiceShape, shape_id::ShapeId, }; @@ -63,16 +63,19 @@ where } } } -/// This provides a [`print`](PrintExt::print) method on [`PluginPipeline`]. + +impl HttpMarker for PrintPlugin {} + +/// This provides a [`print`](PrintExt::print) method on [`HttpPlugins`]. pub trait PrintExt { /// Causes all operations to print the operation name when called. /// /// This works by applying the [`PrintPlugin`]. - fn print(self) -> PluginPipeline>; + fn print(self) -> HttpPlugins>; } -impl PrintExt for PluginPipeline { - fn print(self) -> PluginPipeline> { +impl PrintExt for HttpPlugins { + fn print(self) -> HttpPlugins> { self.push(PrintPlugin) } } diff --git a/rust-runtime/aws-smithy-http-server/src/extension.rs b/rust-runtime/aws-smithy-http-server/src/extension.rs index a2a54affb2..17ff01e961 100644 --- a/rust-runtime/aws-smithy-http-server/src/extension.rs +++ b/rust-runtime/aws-smithy-http-server/src/extension.rs @@ -28,7 +28,7 @@ use thiserror::Error; use tower::Service; use crate::operation::OperationShape; -use crate::plugin::{Plugin, PluginPipeline, PluginStack}; +use crate::plugin::{HttpMarker, HttpPlugins, Plugin, PluginStack}; use crate::shape_id::ShapeId; pub use crate::request::extension::{Extension, MissingExtension}; @@ -128,16 +128,18 @@ where } } -/// An extension trait on [`PluginPipeline`] allowing the application of [`OperationExtensionPlugin`]. +impl HttpMarker for OperationExtensionPlugin {} + +/// An extension trait on [`HttpPlugins`] allowing the application of [`OperationExtensionPlugin`]. /// /// See [`module`](crate::extension) documentation for more info. pub trait OperationExtensionExt { /// Apply the [`OperationExtensionPlugin`], which inserts the [`OperationExtension`] into every [`http::Response`]. - fn insert_operation_extension(self) -> PluginPipeline>; + fn insert_operation_extension(self) -> HttpPlugins>; } -impl OperationExtensionExt for PluginPipeline { - fn insert_operation_extension(self) -> PluginPipeline> { +impl OperationExtensionExt for HttpPlugins { + fn insert_operation_extension(self) -> HttpPlugins> { self.push(OperationExtensionPlugin) } } @@ -221,7 +223,7 @@ mod tests { } // Apply `Plugin`. - let plugins = PluginPipeline::new().insert_operation_extension(); + let plugins = HttpPlugins::new().insert_operation_extension(); // Apply `Plugin`s `Layer`. let layer = PluginLayer::new::(plugins); diff --git a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs index ee88555d73..f2dcca9cac 100644 --- a/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs +++ b/rust-runtime/aws-smithy-http-server/src/instrumentation/plugin.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::plugin::{PluginPipeline, PluginStack}; +use crate::plugin::{HttpMarker, HttpPlugins, PluginStack}; use crate::{operation::OperationShape, plugin::Plugin}; use super::sensitivity::Sensitivity; @@ -27,17 +27,19 @@ where } } +impl HttpMarker for InstrumentPlugin {} + /// An extension trait for applying [`InstrumentPlugin`]. pub trait InstrumentExt { /// Applies an [`InstrumentOperation`] to every operation, respecting the [@sensitive] trait given on the input and /// output models. See [`InstrumentOperation`](super::InstrumentOperation) for more information. /// /// [@sensitive]: https://awslabs.github.io/smithy/2.0/spec/documentation-traits.html#sensitive-trait - fn instrument(self) -> PluginPipeline>; + fn instrument(self) -> HttpPlugins>; } -impl InstrumentExt for PluginPipeline { - fn instrument(self) -> PluginPipeline> { +impl InstrumentExt for HttpPlugins { + fn instrument(self) -> HttpPlugins> { self.push(InstrumentPlugin) } } diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs b/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs index c87e86619c..47b5460a3f 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs @@ -9,9 +9,9 @@ //! # Example //! //! ```no_run -//! # use aws_smithy_http_server::{body, plugin::{PluginPipeline, alb_health_check::AlbHealthCheckLayer}}; +//! # use aws_smithy_http_server::{body, plugin::{HttpPlugins, alb_health_check::AlbHealthCheckLayer}}; //! # use hyper::{Body, Response, StatusCode}; -//! let plugins = PluginPipeline::new() +//! let plugins = HttpPlugins::new() //! // Handle all `/ping` health check requests by returning a `200 OK`. //! .layer(AlbHealthCheckLayer::from_handler("/ping", |_req| async { //! StatusCode::OK diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs index 0e4a195e6d..f29fac8e50 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/filter.rs @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::{either::Either, IdentityPlugin}; +use super::{either::Either, IdentityPlugin, ModelMarker}; use crate::operation::OperationShape; use crate::service::ContainsOperation; -use super::Plugin; +use super::{HttpMarker, Plugin}; /// Filters the application of an inner [`Plugin`] using a predicate over the /// [`ServiceShape::Operations`](crate::service::ServiceShape::Operations). @@ -41,11 +41,14 @@ where } } +impl HttpMarker for FilterByOperation where Inner: HttpMarker {} +impl ModelMarker for FilterByOperation where Inner: ModelMarker {} + /// Filters the application of an inner [`Plugin`] using a predicate over the /// [`ServiceShape::Operations`](crate::service::ServiceShape::Operations). /// -/// Users should prefer [`Scoped`](crate::plugin::Scoped) and fallback to [`filter_by_operation`] in cases where -/// [`Plugin`] application must be decided at runtime. +/// Users should prefer [`Scoped`](crate::plugin::Scoped) and fallback to [`filter_by_operation`] +/// in cases where [`Plugin`] application must be decided at runtime. /// /// # Example /// diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs b/rust-runtime/aws-smithy-http-server/src/plugin/http_plugins.rs similarity index 50% rename from rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs rename to rust-runtime/aws-smithy-http-server/src/plugin/http_plugins.rs index acac792c3b..63aece88a6 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/pipeline.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/http_plugins.rs @@ -3,25 +3,28 @@ * SPDX-License-Identifier: Apache-2.0 */ +// If you make any updates to this file (including Rust docs), make sure you make them to +// `model_plugins.rs` too! + use crate::plugin::{IdentityPlugin, Plugin, PluginStack}; -use super::LayerPlugin; +use super::{HttpMarker, LayerPlugin}; -/// A wrapper struct for composing [`Plugin`]s. -/// It is used as input for the `builder_with_plugins` method on the generate service struct +/// A wrapper struct for composing HTTP plugins. +/// It can be used as input for the `builder_with_plugins` method on the generated service struct /// (e.g. `PokemonService::builder_with_plugins`). /// /// ## Applying plugins in a sequence /// -/// You can use the [`push`](PluginPipeline::push) method to apply a new plugin after the ones that +/// You can use the [`push`](HttpPlugins::push) method to apply a new HTTP plugin after the ones that /// have already been registered. /// /// ```rust -/// use aws_smithy_http_server::plugin::PluginPipeline; +/// use aws_smithy_http_server::plugin::HttpPlugins; /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; /// -/// let pipeline = PluginPipeline::new().push(LoggingPlugin).push(MetricsPlugin); +/// let http_plugins = HttpPlugins::new().push(LoggingPlugin).push(MetricsPlugin); /// ``` /// /// The plugins' runtime logic is executed in registration order. @@ -32,13 +35,14 @@ use super::LayerPlugin; /// From time to time, you might have a need to transform the entire pipeline that has been built /// so far - e.g. you only want to apply those plugins for a specific operation. /// -/// `PluginPipeline` is itself a [`Plugin`]: you can apply any transformation that expects a -/// [`Plugin`] to an entire pipeline. In this case, we want to use -/// [`filter_by_operation`](crate::plugin::filter_by_operation) to limit the scope of -/// the logging and metrics plugins to the `CheckHealth` operation: +/// `HttpPlugins` is itself a [`Plugin`]: you can apply any transformation that expects a +/// [`Plugin`] to an entire pipeline. In this case, we could use a [scoped +/// plugin](crate::plugin::Scoped) to limit the scope of the logging and metrics plugins to the +/// `CheckHealth` operation: /// /// ```rust -/// use aws_smithy_http_server::plugin::{filter_by_operation, PluginPipeline}; +/// use aws_smithy_http_server::scope; +/// use aws_smithy_http_server::plugin::{HttpPlugins, Scoped}; /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; @@ -49,91 +53,101 @@ use super::LayerPlugin; /// # impl CheckHealth { const ID: ShapeId = ShapeId::new("namespace#MyName", "namespace", "MyName"); } /// /// // The logging and metrics plugins will only be applied to the `CheckHealth` operation. -/// let plugin = PluginPipeline::new() -/// .push(LoggingPlugin) -/// .push(MetricsPlugin); -/// let filtered_plugin = filter_by_operation(plugin, |operation: Operation| operation == Operation::CheckHealth); -/// let pipeline = PluginPipeline::new() +/// let plugin = HttpPlugins::new() +/// .push(LoggingPlugin) +/// .push(MetricsPlugin); +/// +/// scope! { +/// struct OnlyCheckHealth { +/// includes: [CheckHealth], +/// excludes: [/* The rest of the operations go here */] +/// } +/// } +/// +/// let filtered_plugin = Scoped::new::(&plugin); +/// let http_plugins = HttpPlugins::new() /// .push(filtered_plugin) -/// // The auth plugin will be applied to all operations +/// // The auth plugin will be applied to all operations. /// .push(AuthPlugin); /// ``` /// -/// ## Concatenating two plugin pipelines +/// ## Concatenating two collections of HTTP plugins /// -/// `PluginPipeline` is a good way to bundle together multiple plugins, ensuring they are all +/// `HttpPlugins` is a good way to bundle together multiple plugins, ensuring they are all /// registered in the correct order. /// -/// Since `PluginPipeline` is itself a [`Plugin`], you can use the [`push`](PluginPipeline::push) to -/// append, at once, all the plugins in another pipeline to the current pipeline: +/// Since `HttpPlugins` is itself a HTTP plugin (it implements the `HttpMarker` trait), you can use +/// the [`push`](HttpPlugins::push) to append, at once, all the HTTP plugins in another +/// `HttpPlugins` to the current `HttpPlugins`: /// /// ```rust -/// use aws_smithy_http_server::plugin::{IdentityPlugin, PluginPipeline, PluginStack}; +/// use aws_smithy_http_server::plugin::{IdentityPlugin, HttpPlugins, PluginStack}; /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; /// -/// pub fn get_bundled_pipeline() -> PluginPipeline>> { -/// PluginPipeline::new().push(LoggingPlugin).push(MetricsPlugin) +/// pub fn get_bundled_http_plugins() -> HttpPlugins>> { +/// HttpPlugins::new().push(LoggingPlugin).push(MetricsPlugin) /// } /// -/// let pipeline = PluginPipeline::new() +/// let http_plugins = HttpPlugins::new() /// .push(AuthPlugin) -/// .push(get_bundled_pipeline()); +/// .push(get_bundled_http_plugins()); /// ``` /// -/// ## Providing custom methods on `PluginPipeline` +/// ## Providing custom methods on `HttpPlugins` /// -/// You use an **extension trait** to add custom methods on `PluginPipeline`. +/// You use an **extension trait** to add custom methods on `HttpPlugins`. /// /// This is a simple example using `AuthPlugin`: /// /// ```rust -/// use aws_smithy_http_server::plugin::{PluginPipeline, PluginStack}; +/// use aws_smithy_http_server::plugin::{HttpPlugins, PluginStack}; /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as AuthPlugin; /// /// pub trait AuthPluginExt { -/// fn with_auth(self) -> PluginPipeline>; +/// fn with_auth(self) -> HttpPlugins>; /// } /// -/// impl AuthPluginExt for PluginPipeline { -/// fn with_auth(self) -> PluginPipeline> { +/// impl AuthPluginExt for HttpPlugins { +/// fn with_auth(self) -> HttpPlugins> { /// self.push(AuthPlugin) /// } /// } /// -/// let pipeline = PluginPipeline::new() +/// let http_plugins = HttpPlugins::new() /// .push(LoggingPlugin) /// // Our custom method! /// .with_auth(); /// ``` -pub struct PluginPipeline

(pub(crate) P); +#[derive(Debug)] +pub struct HttpPlugins

(pub(crate) P); -impl Default for PluginPipeline { +impl Default for HttpPlugins { fn default() -> Self { Self(IdentityPlugin) } } -impl PluginPipeline { - /// Create an empty [`PluginPipeline`]. +impl HttpPlugins { + /// Create an empty [`HttpPlugins`]. /// - /// You can use [`PluginPipeline::push`] to add plugins to it. + /// You can use [`HttpPlugins::push`] to add plugins to it. pub fn new() -> Self { Self::default() } } -impl

PluginPipeline

{ - /// Apply a new plugin after the ones that have already been registered. +impl

HttpPlugins

{ + /// Apply a new HTTP plugin after the ones that have already been registered. /// /// ```rust - /// use aws_smithy_http_server::plugin::PluginPipeline; + /// use aws_smithy_http_server::plugin::HttpPlugins; /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; /// - /// let pipeline = PluginPipeline::new().push(LoggingPlugin).push(MetricsPlugin); + /// let http_plugins = HttpPlugins::new().push(LoggingPlugin).push(MetricsPlugin); /// ``` /// /// The plugins' runtime logic is executed in registration order. @@ -163,18 +177,19 @@ impl

PluginPipeline

{ /// } /// } /// ``` - /// - pub fn push(self, new_plugin: NewPlugin) -> PluginPipeline> { - PluginPipeline(PluginStack::new(new_plugin, self.0)) + // We eagerly require `NewPlugin: HttpMarker`, despite not really needing it, because compiler + // errors get _substantially_ better if the user makes a mistake. + pub fn push(self, new_plugin: NewPlugin) -> HttpPlugins> { + HttpPlugins(PluginStack::new(new_plugin, self.0)) } /// Applies a single [`tower::Layer`] to all operations _before_ they are deserialized. - pub fn layer(self, layer: L) -> PluginPipeline, P>> { - PluginPipeline(PluginStack::new(LayerPlugin(layer), self.0)) + pub fn layer(self, layer: L) -> HttpPlugins, P>> { + HttpPlugins(PluginStack::new(LayerPlugin(layer), self.0)) } } -impl Plugin for PluginPipeline +impl Plugin for HttpPlugins where InnerPlugin: Plugin, { @@ -184,3 +199,5 @@ where self.0.apply(input) } } + +impl HttpMarker for HttpPlugins where InnerPlugin: HttpMarker {} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs b/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs index affbd9f6b9..6ec684a532 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/identity.rs @@ -3,9 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::Plugin; +use super::{HttpMarker, ModelMarker, Plugin}; /// A [`Plugin`] that maps a service to itself. +#[derive(Debug)] pub struct IdentityPlugin; impl Plugin for IdentityPlugin { @@ -15,3 +16,6 @@ impl Plugin for IdentityPlugin { svc } } + +impl ModelMarker for IdentityPlugin {} +impl HttpMarker for IdentityPlugin {} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs b/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs index b1a025b4cb..0a2f31bc48 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/layer.rs @@ -7,7 +7,7 @@ use std::marker::PhantomData; use tower::Layer; -use super::Plugin; +use super::{HttpMarker, ModelMarker, Plugin}; /// A [`Plugin`] which acts as a [`Layer`] `L`. pub struct LayerPlugin(pub L); @@ -23,6 +23,12 @@ where } } +// Without more information about what the layer `L` does, we can't know whether it's appropriate +// to run this plugin as a HTTP plugin or a model plugin, so we implement both marker traits. + +impl HttpMarker for LayerPlugin {} +impl ModelMarker for LayerPlugin {} + /// A [`Layer`] which acts as a [`Plugin`] `Pl` for specific protocol `P` and operation `Op`. pub struct PluginLayer { plugin: Pl, diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs index 106dc9200c..5bbeda3eba 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -5,13 +5,42 @@ //! The plugin system allows you to build middleware with an awareness of the operation it is applied to. //! -//! The system centers around the [`Plugin`] trait. In addition, this module provides helpers for composing and -//! combining [`Plugin`]s. +//! The system centers around the [`Plugin`], [`HttpMarker`], and [`ModelMarker`] traits. In +//! addition, this module provides helpers for composing and combining [`Plugin`]s. +//! +//! # HTTP plugins vs model plugins +//! +//! Plugins come in two flavors: _HTTP_ plugins and _model_ plugins. The key difference between +//! them is _when_ they run: +//! +//! - A HTTP plugin acts on the HTTP request before it is deserialized, and acts on the HTTP response +//! after it is serialized. +//! - A model plugin acts on the modeled operation input after it is deserialized, and acts on the +//! modeled operation output or the modeled operation error before it is serialized. +//! +//! See the relevant section in [the book], which contains an illustrative diagram. +//! +//! Both kinds of plugins implement the [`Plugin`] trait, but only HTTP plugins implement the +//! [`HttpMarker`] trait and only model plugins implement the [`ModelMarker`] trait. There is no +//! difference in how an HTTP plugin or a model plugin is applied, so both the [`HttpMarker`] trait +//! and the [`ModelMarker`] trait are _marker traits_, they carry no behavior. Their only purpose +//! is to mark a plugin, at the type system leve, as allowed to run at a certain time. A plugin can be +//! _both_ a HTTP plugin and a model plugin by implementing both traits; in this case, when the +//! plugin runs is decided by you when you register it in your application. [`IdentityPlugin`], +//! [`Scoped`], and [`LayerPlugin`] are examples of plugins that implement both traits. +//! +//! In practice, most plugins are HTTP plugins. Since HTTP plugins run before a request has been +//! correctly deserialized, HTTP plugins should be fast and lightweight. Only use model plugins if +//! you absolutely require your middleware to run after deserialization, or to act on particular +//! fields of your deserialized operation's input/output/errors. +//! +//! [the book]: https://awslabs.github.io/smithy-rs/design/server/anatomy.html //! //! # Filtered application of a HTTP [`Layer`](tower::Layer) //! //! ``` //! # use aws_smithy_http_server::plugin::*; +//! # use aws_smithy_http_server::scope; //! # use aws_smithy_http_server::shape_id::ShapeId; //! # let layer = (); //! # #[derive(PartialEq)] @@ -21,8 +50,18 @@ //! // Create a `Plugin` from a HTTP `Layer` //! let plugin = LayerPlugin(layer); //! -//! // Only apply the layer to operations with name "GetPokemonSpecies" -//! let plugin = filter_by_operation(plugin, |operation: Operation| operation == Operation::GetPokemonSpecies); +//! scope! { +//! struct OnlyGetPokemonSpecies { +//! includes: [GetPokemonSpecies], +//! excludes: [/* The rest of the operations go here */] +//! } +//! } +//! +//! // Only apply the layer to operations with name "GetPokemonSpecies". +//! let filtered_plugin = Scoped::new::(&plugin); +//! +//! // The same effect can be achieved at runtime. +//! let filtered_plugin = filter_by_operation(&plugin, |operation: Operation| operation == Operation::GetPokemonSpecies); //! ``` //! //! # Construct a [`Plugin`] from a closure that takes as input the operation name @@ -68,25 +107,35 @@ //! //! # Combine [`Plugin`]s //! -//! ``` +//! ```no_run //! # use aws_smithy_http_server::plugin::*; -//! # let a = (); let b = (); -//! // Combine `Plugin`s `a` and `b` -//! let plugin = PluginPipeline::new() +//! # struct Foo; +//! # impl HttpMarker for Foo { } +//! # let a = Foo; let b = Foo; +//! // Combine `Plugin`s `a` and `b`. Both need to implement `HttpMarker`. +//! let plugin = HttpPlugins::new() //! .push(a) //! .push(b); //! ``` //! -//! As noted in the [`PluginPipeline`] documentation, the plugins' runtime logic is executed in registration order, +//! As noted in the [`HttpPlugins`] documentation, the plugins' runtime logic is executed in registration order, //! meaning that `a` is run _before_ `b` in the example above. //! -//! # Example Implementation +//! Similarly, you can use [`ModelPlugins`] to combine model plugins. //! -//! ```rust +//! # Example implementation of a [`Plugin`] +//! +//! The following is an example implementation of a [`Plugin`] that prints out the service's name +//! and the name of the operation that was hit every time it runs. Since it doesn't act on the HTTP +//! request nor the modeled operation input/output/errors, this plugin can be both an HTTP plugin +//! and a model plugin. In practice, however, you'd only want to register it once, as either an +//! HTTP plugin or a model plugin. +//! +//! ```no_run //! use aws_smithy_http_server::{ //! operation::OperationShape, //! service::ServiceShape, -//! plugin::{Plugin, PluginPipeline, PluginStack}, +//! plugin::{Plugin, HttpMarker, HttpPlugins, ModelMarker}, //! shape_id::ShapeId, //! }; //! # use tower::{layer::util::Stack, Layer, Service}; @@ -137,16 +186,22 @@ //! } //! } //! } -//! ``` //! +//! // This plugin could be registered as an HTTP plugin and a model plugin, so we implement both +//! // marker traits. +//! +//! impl HttpMarker for PrintPlugin { } +//! impl ModelMarker for PrintPlugin { } +//! ``` pub mod alb_health_check; mod closure; mod either; mod filter; +mod http_plugins; mod identity; mod layer; -mod pipeline; +mod model_plugins; #[doc(hidden)] pub mod scoped; mod stack; @@ -154,9 +209,10 @@ mod stack; pub use closure::{plugin_from_operation_fn, OperationFn}; pub use either::Either; pub use filter::{filter_by_operation, FilterByOperation}; +pub use http_plugins::HttpPlugins; pub use identity::IdentityPlugin; pub use layer::{LayerPlugin, PluginLayer}; -pub use pipeline::PluginPipeline; +pub use model_plugins::ModelPlugins; pub use scoped::Scoped; pub use stack::PluginStack; @@ -188,3 +244,222 @@ where >::apply(self, inner) } } + +/// A HTTP plugin is a plugin that acts on the HTTP request before it is deserialized, and acts on +/// the HTTP response after it is serialized. +/// +/// This trait is a _marker_ trait to indicate that a plugin can be registered as an HTTP plugin. +/// +/// Compare with [`ModelMarker`] in the [module](crate::plugin) documentation, which contains an +/// example implementation too. +pub trait HttpMarker {} +impl<'a, Pl> HttpMarker for &'a Pl where Pl: HttpMarker {} + +/// A model plugin is a plugin that acts on the modeled operation input after it is deserialized, +/// and acts on the modeled operation output or the modeled operation error before it is +/// serialized. +/// +/// This trait is a _marker_ trait to indicate that a plugin can be registered as a model plugin. +/// +/// Compare with [`HttpMarker`] in the [module](crate::plugin) documentation. +/// +/// # Example implementation of a model plugin +/// +/// Model plugins are most useful when you really need to rely on the actual shape of your +/// modeled operation input, operation output, and/or operation errors. For this reason, most +/// model plugins' implementation are _operation-specific_: somewhere in the type signature +/// of their definition, they'll rely on a operation shape's types. It is therefore important +/// that you scope application of model plugins to the operations they are meant to work on, via +/// [`Scoped`](crate::plugin::Scoped) or [`filter_by_operation`](crate::plugin::filter_by_operation). +/// +/// Below is an example implementation of a model plugin that can only be applied to the +/// `CheckHealth` operation: note how in the `Service` trait implementation, we require access to +/// the operation's input, where we log the `health_info` field. +/// +/// ```no_run +/// use std::marker::PhantomData; +/// +/// use aws_smithy_http_server::{operation::OperationShape, plugin::{ModelMarker, Plugin}}; +/// use tower::Service; +/// # pub struct SimpleService; +/// # pub struct CheckHealth; +/// # pub struct CheckHealthInput { +/// # health_info: (), +/// # } +/// # pub struct CheckHealthOutput; +/// # impl aws_smithy_http_server::operation::OperationShape for CheckHealth { +/// # const ID: aws_smithy_http_server::shape_id::ShapeId = aws_smithy_http_server::shape_id::ShapeId::new( +/// # "com.amazonaws.simple#CheckHealth", +/// # "com.amazonaws.simple", +/// # "CheckHealth", +/// # ); +/// # type Input = CheckHealthInput; +/// # type Output = CheckHealthOutput; +/// # type Error = std::convert::Infallible; +/// # } +/// +/// /// A model plugin that can only be applied to the `CheckHealth` operation. +/// pub struct CheckHealthPlugin { +/// pub _exts: PhantomData, +/// } +/// +/// impl CheckHealthPlugin { +/// pub fn new() -> Self { +/// Self { _exts: PhantomData } +/// } +/// } +/// +/// impl Plugin for CheckHealthPlugin { +/// type Output = CheckHealthService; +/// +/// fn apply(&self, input: T) -> Self::Output { +/// CheckHealthService { +/// inner: input, +/// _exts: PhantomData, +/// } +/// } +/// } +/// +/// impl ModelMarker for CheckHealthPlugin { } +/// +/// #[derive(Clone)] +/// pub struct CheckHealthService { +/// inner: S, +/// _exts: PhantomData, +/// } +/// +/// impl Service<(::Input, Exts)> for CheckHealthService +/// where +/// S: Service<(::Input, Exts)>, +/// { +/// type Response = S::Response; +/// type Error = S::Error; +/// type Future = S::Future; +/// +/// fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { +/// self.inner.poll_ready(cx) +/// } +/// +/// fn call(&mut self, req: (::Input, Exts)) -> Self::Future { +/// let (input, _exts) = &req; +/// +/// // We have access to `CheckHealth`'s modeled operation input! +/// dbg!(&input.health_info); +/// +/// self.inner.call(req) +/// } +/// } +/// +/// // In `main.rs` or wherever we register plugins, we have to make sure we only apply this plugin +/// // to the the only operation it can be applied to, the `CheckHealth` operation. If we apply the +/// // plugin to other operations, we will get a compilation error. +/// +/// use aws_smithy_http_server::plugin::Scoped; +/// use aws_smithy_http_server::scope; +/// +/// pub fn main() { +/// scope! { +/// struct OnlyCheckHealth { +/// includes: [CheckHealth], +/// excludes: [/* The rest of the operations go here */] +/// } +/// } +/// +/// let model_plugin = CheckHealthPlugin::new(); +/// # _foo(&model_plugin); +/// +/// // Scope the plugin to the `CheckHealth` operation. +/// let scoped_plugin = Scoped::new::(model_plugin); +/// # fn _foo(model_plugin: &CheckHealthPlugin<()>) {} +/// } +/// ``` +/// +/// If you are a service owner and don't care about giving a name to the model plugin, you can +/// simplify this down to: +/// +/// ```no_run +/// use std::marker::PhantomData; +/// +/// use aws_smithy_http_server::operation::OperationShape; +/// use tower::Service; +/// # pub struct SimpleService; +/// # pub struct CheckHealth; +/// # pub struct CheckHealthInput { +/// # health_info: (), +/// # } +/// # pub struct CheckHealthOutput; +/// # impl aws_smithy_http_server::operation::OperationShape for CheckHealth { +/// # const ID: aws_smithy_http_server::shape_id::ShapeId = aws_smithy_http_server::shape_id::ShapeId::new( +/// # "com.amazonaws.simple#CheckHealth", +/// # "com.amazonaws.simple", +/// # "CheckHealth", +/// # ); +/// # type Input = CheckHealthInput; +/// # type Output = CheckHealthOutput; +/// # type Error = std::convert::Infallible; +/// # } +/// +/// #[derive(Clone)] +/// pub struct CheckHealthService { +/// inner: S, +/// _exts: PhantomData, +/// } +/// +/// impl Service<(::Input, Exts)> for CheckHealthService +/// where +/// S: Service<(::Input, Exts)>, +/// { +/// type Response = S::Response; +/// type Error = S::Error; +/// type Future = S::Future; +/// +/// fn poll_ready(&mut self, cx: &mut std::task::Context<'_>) -> std::task::Poll> { +/// self.inner.poll_ready(cx) +/// } +/// +/// fn call(&mut self, req: (::Input, Exts)) -> Self::Future { +/// let (input, _exts) = &req; +/// +/// // We have access to `CheckHealth`'s modeled operation input! +/// dbg!(&input.health_info); +/// +/// self.inner.call(req) +/// } +/// } +/// +/// // In `main.rs`: +/// +/// use aws_smithy_http_server::plugin::LayerPlugin; +/// use aws_smithy_http_server::plugin::Scoped; +/// use aws_smithy_http_server::scope; +/// +/// fn new_check_health_service(inner: S) -> CheckHealthService { +/// CheckHealthService { +/// inner, +/// _exts: PhantomData, +/// } +/// } +/// +/// pub fn main() { +/// scope! { +/// struct OnlyCheckHealth { +/// includes: [CheckHealth], +/// excludes: [/* The rest of the operations go here */] +/// } +/// } +/// +/// # fn new_check_health_service(inner: ()) -> CheckHealthService<(), ()> { +/// # CheckHealthService { +/// # inner, +/// # _exts: PhantomData, +/// # } +/// # } +/// let layer = tower::layer::layer_fn(new_check_health_service); +/// let model_plugin = LayerPlugin(layer); +/// +/// // Scope the plugin to the `CheckHealth` operation. +/// let scoped_plugin = Scoped::new::(model_plugin); +/// } +/// ``` +pub trait ModelMarker {} +impl<'a, Pl> ModelMarker for &'a Pl where Pl: ModelMarker {} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/model_plugins.rs b/rust-runtime/aws-smithy-http-server/src/plugin/model_plugins.rs new file mode 100644 index 0000000000..05dc8568d7 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/plugin/model_plugins.rs @@ -0,0 +1,94 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +// If you make any updates to this file (including Rust docs), make sure you make them to +// `http_plugins.rs` too! + +use crate::plugin::{IdentityPlugin, Plugin, PluginStack}; + +use super::{LayerPlugin, ModelMarker}; + +/// A wrapper struct for composing model plugins. +/// It operates identically to [`HttpPlugins`](crate::plugin::HttpPlugins); see its documentation. +#[derive(Debug)] +pub struct ModelPlugins

(pub(crate) P); + +impl Default for ModelPlugins { + fn default() -> Self { + Self(IdentityPlugin) + } +} + +impl ModelPlugins { + /// Create an empty [`ModelPlugins`]. + /// + /// You can use [`ModelPlugins::push`] to add plugins to it. + pub fn new() -> Self { + Self::default() + } +} + +impl

ModelPlugins

{ + /// Apply a new model plugin after the ones that have already been registered. + /// + /// ```rust + /// use aws_smithy_http_server::plugin::ModelPlugins; + /// # use aws_smithy_http_server::plugin::IdentityPlugin as LoggingPlugin; + /// # use aws_smithy_http_server::plugin::IdentityPlugin as MetricsPlugin; + /// + /// let model_plugins = ModelPlugins::new().push(LoggingPlugin).push(MetricsPlugin); + /// ``` + /// + /// The plugins' runtime logic is executed in registration order. + /// In our example above, `LoggingPlugin` would run first, while `MetricsPlugin` is executed last. + /// + /// ## Implementation notes + /// + /// Plugins are applied to the underlying [`Service`](tower::Service) in opposite order compared + /// to their registration order. + /// + /// As an example: + /// + /// ```rust,compile_fail + /// #[derive(Debug)] + /// pub struct PrintPlugin; + /// + /// impl Plugin for PrintPlugin + /// // [...] + /// { + /// // [...] + /// fn apply(&self, inner: T) -> Self::Service { + /// PrintService { + /// inner, + /// service_id: Ser::ID, + /// operation_id: Op::ID + /// } + /// } + /// } + /// ``` + // We eagerly require `NewPlugin: ModelMarker`, despite not really needing it, because compiler + // errors get _substantially_ better if the user makes a mistake. + pub fn push(self, new_plugin: NewPlugin) -> ModelPlugins> { + ModelPlugins(PluginStack::new(new_plugin, self.0)) + } + + /// Applies a single [`tower::Layer`] to all operations _before_ they are deserialized. + pub fn layer(self, layer: L) -> ModelPlugins, P>> { + ModelPlugins(PluginStack::new(LayerPlugin(layer), self.0)) + } +} + +impl Plugin for ModelPlugins +where + InnerPlugin: Plugin, +{ + type Output = InnerPlugin::Output; + + fn apply(&self, input: T) -> Self::Output { + self.0.apply(input) + } +} + +impl ModelMarker for ModelPlugins where InnerPlugin: ModelMarker {} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs b/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs index a3761b2b1a..d4b7e82e51 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/scoped.rs @@ -5,7 +5,7 @@ use std::marker::PhantomData; -use super::Plugin; +use super::{HttpMarker, ModelMarker, Plugin}; /// Marker struct for `true`. /// @@ -102,12 +102,15 @@ where } } +impl HttpMarker for Scoped where Pl: HttpMarker {} +impl ModelMarker for Scoped where Pl: ModelMarker {} + /// A macro to help with scoping [plugins](crate::plugin) to a subset of all operations. /// /// The scope must partition _all_ operations, that is, each and every operation must be included or excluded, but not /// both. /// -/// The generated server SDK exports a similar `scope` macro which is aware of a services operations and can complete +/// The generated server SDK exports a similar `scope` macro which is aware of a service's operations and can complete /// underspecified scopes automatically. /// /// # Example diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs b/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs index 63f8e44865..6c96ebaca0 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/stack.rs @@ -3,13 +3,16 @@ * SPDX-License-Identifier: Apache-2.0 */ -use super::Plugin; +use super::{HttpMarker, ModelMarker, Plugin}; /// A wrapper struct which composes an `Inner` and an `Outer` [`Plugin`]. /// /// The `Inner::map` is run _then_ the `Outer::map`. /// -/// Note that the primary tool for composing plugins is [`PluginPipeline`](crate::plugin::PluginPipeline). +/// Note that the primary tool for composing HTTP plugins is +/// [`HttpPlugins`](crate::plugin::HttpPlugins), and the primary tool for composing HTTP plugins is +/// [`ModelPlugins`](crate::plugin::ModelPlugins); if you are an application writer, you should +/// prefer composing plugins using these. pub struct PluginStack { inner: Inner, outer: Outer, @@ -34,3 +37,17 @@ where self.outer.apply(svc) } } + +impl HttpMarker for PluginStack +where + Inner: HttpMarker, + Outer: HttpMarker, +{ +} + +impl ModelMarker for PluginStack +where + Inner: ModelMarker, + Outer: ModelMarker, +{ +} From 735b635190b4dab3546edc7c2b74713b247c3e12 Mon Sep 17 00:00:00 2001 From: Burak Date: Mon, 10 Jul 2023 10:31:01 +0100 Subject: [PATCH 198/253] Python: Allow configuring logging formatter (#2829) ## Motivation and Context Allow Python users to configure their logging formatter to either `json`, `pretty` or `compact` (default). ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --- examples/python/pokemon_service.py | 9 +- .../aws-smithy-http-server-python/Cargo.toml | 2 +- .../src/logging.rs | 83 ++++++++++++++----- .../src/tls/listener.rs | 2 +- 4 files changed, 72 insertions(+), 24 deletions(-) diff --git a/examples/python/pokemon_service.py b/examples/python/pokemon_service.py index eeb6445eb3..552d892ff3 100644 --- a/examples/python/pokemon_service.py +++ b/examples/python/pokemon_service.py @@ -50,7 +50,14 @@ # Logging can bee setup using standard Python tooling. We provide # fast logging handler, Tracingandler based on Rust tracing crate. -logging.basicConfig(handlers=[TracingHandler(level=logging.DEBUG).handler()]) +logging.basicConfig( + handlers=[ + TracingHandler( + level=logging.DEBUG, + format="pretty", # You can also use "json" or "compact" (default) + ).handler() + ] +) class SafeCounter: diff --git a/rust-runtime/aws-smithy-http-server-python/Cargo.toml b/rust-runtime/aws-smithy-http-server-python/Cargo.toml index b454eab626..61ce57d7a7 100644 --- a/rust-runtime/aws-smithy-http-server-python/Cargo.toml +++ b/rust-runtime/aws-smithy-http-server-python/Cargo.toml @@ -38,7 +38,7 @@ tokio = { version = "1.20.1", features = ["full"] } tokio-stream = "0.1" tower = { version = "0.4.13", features = ["util"] } tracing = "0.1.36" -tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } +tracing-subscriber = { version = "0.3.15", features = ["json", "env-filter"] } tracing-appender = { version = "0.2.2"} [dev-dependencies] diff --git a/rust-runtime/aws-smithy-http-server-python/src/logging.rs b/rust-runtime/aws-smithy-http-server-python/src/logging.rs index 80d4966ac0..57fa2873aa 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/logging.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/logging.rs @@ -4,7 +4,8 @@ */ //! Rust `tracing` and Python `logging` setup and utilities. -use std::path::PathBuf; + +use std::{path::PathBuf, str::FromStr}; use pyo3::prelude::*; #[cfg(not(test))] @@ -15,15 +16,49 @@ use tracing_subscriber::{ fmt::{self, writer::MakeWriterExt}, layer::SubscriberExt, util::SubscriberInitExt, + Layer, }; use crate::error::PyException; +#[derive(Debug, Default)] +enum Format { + Json, + Pretty, + #[default] + Compact, +} + +#[derive(Debug, PartialEq, Eq)] +struct InvalidFormatError; + +impl FromStr for Format { + type Err = InvalidFormatError; + + fn from_str(s: &str) -> Result { + match s { + "pretty" => Ok(Self::Pretty), + "json" => Ok(Self::Json), + "compact" => Ok(Self::Compact), + _ => Err(InvalidFormatError), + } + } +} + /// Setup tracing-subscriber to log on console or to a hourly rolling file. fn setup_tracing_subscriber( level: Option, logfile: Option, + format: Option, ) -> PyResult> { + let format = match format { + Some(format) => Format::from_str(&format).unwrap_or_else(|_| { + tracing::error!("unknown format '{format}', falling back to default formatter"); + Format::default() + }), + None => Format::default(), + }; + let appender = match logfile { Some(logfile) => { let parent = logfile.parent().ok_or_else(|| { @@ -54,27 +89,27 @@ fn setup_tracing_subscriber( _ => Level::TRACE, }; + let formatter = fmt::Layer::new().with_line_number(true).with_level(true); + match appender { Some((appender, guard)) => { - let layer = Some( - fmt::Layer::new() - .with_writer(appender.with_max_level(tracing_level)) - .with_ansi(true) - .with_line_number(true) - .with_level(true), - ); - tracing_subscriber::registry().with(layer).init(); + let formatter = formatter.with_writer(appender.with_max_level(tracing_level)); + let formatter = match format { + Format::Json => formatter.json().boxed(), + Format::Compact => formatter.compact().boxed(), + Format::Pretty => formatter.pretty().boxed(), + }; + tracing_subscriber::registry().with(formatter).init(); Ok(Some(guard)) } None => { - let layer = Some( - fmt::Layer::new() - .with_writer(std::io::stdout.with_max_level(tracing_level)) - .with_ansi(true) - .with_line_number(true) - .with_level(true), - ); - tracing_subscriber::registry().with(layer).init(); + let formatter = formatter.with_writer(std::io::stdout.with_max_level(tracing_level)); + let formatter = match format { + Format::Json => formatter.json().boxed(), + Format::Compact => formatter.compact().boxed(), + Format::Pretty => formatter.pretty().boxed(), + }; + tracing_subscriber::registry().with(formatter).init(); Ok(None) } } @@ -89,9 +124,10 @@ fn setup_tracing_subscriber( /// /// :param level typing.Optional\[int\]: /// :param logfile typing.Optional\[pathlib.Path\]: +/// :param format typing.Optional\[typing.Literal\['compact', 'pretty', 'json'\]\]: /// :rtype None: #[pyclass(name = "TracingHandler")] -#[pyo3(text_signature = "($self, level=None, logfile=None)")] +#[pyo3(text_signature = "($self, level=None, logfile=None, format=None)")] #[derive(Debug)] pub struct PyTracingHandler { _guard: Option, @@ -100,8 +136,13 @@ pub struct PyTracingHandler { #[pymethods] impl PyTracingHandler { #[new] - fn newpy(py: Python, level: Option, logfile: Option) -> PyResult { - let _guard = setup_tracing_subscriber(level, logfile)?; + fn newpy( + py: Python, + level: Option, + logfile: Option, + format: Option, + ) -> PyResult { + let _guard = setup_tracing_subscriber(level, logfile, format)?; let logging = py.import("logging")?; let root = logging.getattr("root")?; root.setattr("level", level)?; @@ -190,7 +231,7 @@ mod tests { fn tracing_handler_is_injected_in_python() { crate::tests::initialize(); Python::with_gil(|py| { - let handler = PyTracingHandler::newpy(py, Some(10), None).unwrap(); + let handler = PyTracingHandler::newpy(py, Some(10), None, None).unwrap(); let kwargs = PyDict::new(py); kwargs .set_item("handlers", vec![handler.handler(py).unwrap()]) diff --git a/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs b/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs index fb3aa7ac91..9e6dbed364 100644 --- a/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs +++ b/rust-runtime/aws-smithy-http-server-python/src/tls/listener.rs @@ -144,7 +144,7 @@ mod tests { assert!(response .unwrap_err() .to_string() - .contains("invalid peer certificate: UnknownIssuer")); + .contains("invalid peer certificate")); } { From 8abc946328e1913ca36e3c7f600237a2bb2c6dc3 Mon Sep 17 00:00:00 2001 From: "Nate McMaster (AWS)" <88004173+mcmasn-amzn@users.noreply.github.com> Date: Mon, 10 Jul 2023 07:56:56 -0700 Subject: [PATCH 199/253] Update OperationInputTestDecorator to find operation shape by name (#2782) ## Motivation and Context Addresses https://github.com/awslabs/smithy-rs/issues/2767 This fixes an issue for users who write endpoint tests that rely on an operation and service shape from different namespaces. ## Description Instead of assuming operation namespace matches service namespace, ## Testing `./gradlew :aws:sdk-codegen:test` ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Zelda Hessler --- CHANGELOG.next.toml | 6 ++ .../endpoints/OperationInputTestGenerator.kt | 8 +-- .../OperationInputTestGeneratorTests.kt | 71 +++++++++++++++++++ 3 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/OperationInputTestGeneratorTests.kt diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index f3ac9bdc43..d603759359 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -611,6 +611,12 @@ references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "hlbarber" +[[smithy-rs]] +message = "Fix bug in client generation when using smithy.rules#endpointTests and operation and service shapes are in different namespaces." +author = "mcmasn-amzn" +references = ["smithy-rs#2767"] +meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client" } + [[smithy-rs]] message = "The naming `make_token` for fields and the API of `IdempotencyTokenProvider` in service configs and their builders has now been updated to `idempotency_token_provider`." references = ["smithy-rs#2783"] diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt index 698e732623..f1a957be61 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/endpoints/OperationInputTestGenerator.kt @@ -122,9 +122,6 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test: private val model = ctx.model private val instantiator = ClientInstantiator(ctx) - private fun EndpointTestOperationInput.operationId() = - ShapeId.fromOptionalNamespace(ctx.serviceShape.id.namespace, operationName) - /** the Rust SDK doesn't support SigV4a — search endpoint.properties.authSchemes[].name */ private fun EndpointTestCase.isSigV4a() = expect.endpoint.orNull()?.properties?.get("authSchemes")?.asArrayNode()?.orNull() @@ -183,7 +180,7 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test: private fun operationInvocation(testOperationInput: EndpointTestOperationInput) = writable { rust("client.${testOperationInput.operationName.toSnakeCase()}()") val operationInput = - model.expectShape(testOperationInput.operationId(), OperationShape::class.java).inputShape(model) + model.expectShape(ctx.operationId(testOperationInput), OperationShape::class.java).inputShape(model) testOperationInput.operationParams.members.forEach { (key, value) -> val member = operationInput.expectMember(key.value) rustTemplate( @@ -217,3 +214,6 @@ class OperationInputTestGenerator(_ctx: ClientCodegenContext, private val test: } } } + +fun ClientCodegenContext.operationId(testOperationInput: EndpointTestOperationInput): ShapeId = + this.serviceShape.allOperations.first { it.name == testOperationInput.operationName } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/OperationInputTestGeneratorTests.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/OperationInputTestGeneratorTests.kt new file mode 100644 index 0000000000..2fde9a6866 --- /dev/null +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/OperationInputTestGeneratorTests.kt @@ -0,0 +1,71 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import software.amazon.smithy.model.Model +import software.amazon.smithy.rulesengine.traits.EndpointTestOperationInput +import software.amazon.smithy.rust.codegen.client.testutil.testClientCodegenContext +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rustsdk.endpoints.operationId + +class OperationInputTestGeneratorTests { + @Test + fun `finds operation shape by name`() { + val prefix = "\$version: \"2\"" + val operationModel = """ + $prefix + namespace operations + + operation Ping {} + """.trimIndent() + val serviceModel = """ + $prefix + namespace service + + use operations#Ping + + service MyService { + operations: [Ping] + } + """.trimIndent() + + val model = Model.assembler() + .discoverModels() + .addUnparsedModel("operation.smithy", operationModel) + .addUnparsedModel("main.smithy", serviceModel) + .assemble() + .unwrap() + + val context = testClientCodegenContext(model) + val testOperationInput = EndpointTestOperationInput.builder() + .operationName("Ping") + .build() + + val operationId = context.operationId(testOperationInput) + assertEquals("operations#Ping", operationId.toString()) + } + + @Test + fun `fails for operation name not found`() { + val model = """ + namespace test + operation Ping {} + service MyService { + operations: [Ping] + } + """.trimIndent().asSmithyModel() + + val context = testClientCodegenContext(model) + val testOperationInput = EndpointTestOperationInput.builder() + .operationName("Pong") + .build() + + assertThrows { context.operationId(testOperationInput) } + } +} From 7c9c283a389f73d77cbc9c445b8a980422543ae5 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Mon, 10 Jul 2023 09:57:34 -0500 Subject: [PATCH 200/253] Add `Metadata` to a config layer through operation runtime plugin (#2830) ## Motivation and Context Adds `Metadata` to a config layer through `OperationRuntimePlugin`. ## Description We have had a customer's use case where a service name and a operation name are obtained from [Metadata](https://github.com/awslabs/smithy-rs/blob/ddba46086a9754c01f3b11d7521c49d4489de84b/rust-runtime/aws-smithy-http/src/operation.rs#L17-L22). The end goal was to use their names as part of metrics collection. Previously, it was done using `map_operation` on a `CustomizableOperation`, e.g. ``` client .some_operation() .customize() .await? .map_operation(|operation| { operation.try_clone().map(|operation| { let (_, parts) = operation.into_request_response(); parts.metadata.map(|metadata| { let service_name = metadata.service().to_string().to_uppercase(); let operation_name = metadata.name().to_string(); /* * do something with `service_name` and `operation_name` */ }) }); Ok(operation) })? .send() .await; ``` The orchestrator no longer supports `map_operation` on `CustomizableOperation`. We therefore add `Metadata` to a config layer through `OperationRuntimePlugin`. See the added integration test for how `Metadata` is retrieved from within an interceptor. ## Testing Added an integration-test to verify `Metadata` is properly set. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: ysaito1001 Co-authored-by: Zelda Hessler --- .../customizations/MetadataCustomization.kt | 48 ++++++++ .../customize/RequiredCustomizations.kt | 6 +- .../MetadataCustomizationTest.kt | 113 ++++++++++++++++++ rust-runtime/aws-smithy-http/src/operation.rs | 5 + 4 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomization.kt create mode 100644 codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomization.kt new file mode 100644 index 0000000000..03c62fb049 --- /dev/null +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomization.kt @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +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.util.dq +import software.amazon.smithy.rust.codegen.core.util.sdkId + +class MetadataCustomization( + private val codegenContext: ClientCodegenContext, + operation: OperationShape, +) : OperationCustomization() { + private val operationName = codegenContext.symbolProvider.toSymbol(operation).name + private val runtimeConfig = codegenContext.runtimeConfig + private val codegenScope by lazy { + arrayOf( + "Metadata" to RuntimeType.operationModule(runtimeConfig).resolve("Metadata"), + ) + } + + override fun section(section: OperationSection): Writable = writable { + when (section) { + is OperationSection.AdditionalRuntimePluginConfig -> { + rustTemplate( + """ + ${section.newLayerName}.store_put(#{Metadata}::new( + ${operationName.dq()}, + ${codegenContext.serviceShape.sdkId().dq()}, + )); + """, + *codegenScope, + ) + } + + else -> {} + } + } +} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index e26e228c7e..9c64b5e8fd 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -15,6 +15,7 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpVers import software.amazon.smithy.rust.codegen.client.smithy.customizations.IdempotencyTokenGenerator import software.amazon.smithy.rust.codegen.client.smithy.customizations.IdentityConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.InterceptorConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.customizations.MetadataCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyReExportCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyServiceRuntimePluginCustomization @@ -30,6 +31,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.customizations.CrateVersi import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyErrorTypes import software.amazon.smithy.rust.codegen.core.smithy.customizations.pubUseSmithyPrimitives import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization +import software.amazon.smithy.rust.codegen.core.util.letIf val TestUtilFeature = Feature("test-util", false, listOf()) @@ -47,7 +49,9 @@ class RequiredCustomizations : ClientCodegenDecorator { operation: OperationShape, baseCustomizations: List, ): List = - baseCustomizations + + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + MetadataCustomization(codegenContext, operation) + } + IdempotencyTokenGenerator(codegenContext, operation) + EndpointPrefixGenerator(codegenContext, operation) + HttpChecksumRequiredGenerator(codegenContext, operation) + diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt new file mode 100644 index 0000000000..b827551bdd --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt @@ -0,0 +1,113 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.client.smithy.customizations + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +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.IntegrationTestParams +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +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, + params = IntegrationTestParams(additionalSettings = TestCodegenSettings.orchestratorMode()), + ) { clientCodegenContext, rustCrate -> + val runtimeConfig = clientCodegenContext.runtimeConfig + val codegenScope = arrayOf( + *preludeScope, + "BeforeTransmitInterceptorContextMut" to RuntimeType.beforeTransmitInterceptorContextMut(runtimeConfig), + "BoxError" to RuntimeType.boxError(runtimeConfig), + "ConfigBag" to RuntimeType.configBag(runtimeConfig), + "Interceptor" to RuntimeType.interceptor(runtimeConfig), + "Metadata" to RuntimeType.operationModule(runtimeConfig).resolve("Metadata"), + "capture_request" to RuntimeType.captureRequest(runtimeConfig), + ) + rustCrate.testModule { + addDependency(CargoDependency.Tokio.withFeature("test-util").toDevDependency()) + tokioTest("test_extract_metadata_via_customizable_operation") { + rustTemplate( + """ + // Interceptors aren’t supposed to store states, but it is done this way for a testing purpose. + ##[derive(Debug)] + struct ExtractMetadataInterceptor( + ::std::sync::Mutex<#{Option}<::std::sync::mpsc::Sender<(String, String)>>>, + ); + + impl #{Interceptor} for ExtractMetadataInterceptor { + fn modify_before_signing( + &self, + _context: &mut #{BeforeTransmitInterceptorContextMut}<'_>, + cfg: &mut #{ConfigBag}, + ) -> #{Result}<(), #{BoxError}> { + let metadata = cfg + .load::<#{Metadata}>() + .expect("metadata should exist"); + let service_name = metadata.service().to_string(); + let operation_name = metadata.name().to_string(); + let tx = self.0.lock().unwrap().take().unwrap(); + tx.send((service_name, operation_name)).unwrap(); + #{Ok}(()) + } + } + + let (tx, rx) = ::std::sync::mpsc::channel(); + + let (conn, _captured_request) = #{capture_request}(#{None}); + let client_config = crate::config::Config::builder() + .endpoint_resolver("http://localhost:1234/") + .http_connector(conn) + .build(); + let client = crate::client::Client::from_conf(client_config); + let _ = client + .say_hello() + .customize() + .await + .expect("operation should be customizable") + .interceptor(ExtractMetadataInterceptor(::std::sync::Mutex::new(#{Some}(tx)))) + .send() + .await; + + match rx.recv() { + #{Ok}((service_name, operation_name)) => { + assert_eq!("HelloService", &service_name); + assert_eq!("SayHello", &operation_name); + } + #{Err}(_) => panic!( + "failed to receive service name and operation name from `ExtractMetadataInterceptor`" + ), + } + """, + *codegenScope, + ) + } + } + } + } +} diff --git a/rust-runtime/aws-smithy-http/src/operation.rs b/rust-runtime/aws-smithy-http/src/operation.rs index dadc22b282..2ddbd28070 100644 --- a/rust-runtime/aws-smithy-http/src/operation.rs +++ b/rust-runtime/aws-smithy-http/src/operation.rs @@ -9,6 +9,7 @@ use crate::body::SdkBody; use crate::property_bag::{PropertyBag, SharedPropertyBag}; use crate::retry::DefaultResponseRetryClassifier; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::borrow::Cow; use std::ops::{Deref, DerefMut}; @@ -44,6 +45,10 @@ impl Metadata { } } +impl Storable for Metadata { + type Storer = StoreReplace; +} + /// Non-request parts of an [`Operation`]. /// /// Generics: From 3a133b180787c2ac83af31db46e361bbfa6c5c1d Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 10 Jul 2023 14:13:50 -0400 Subject: [PATCH 201/253] Prevent non-clone types from entering cloneable layer (#2834) ## Motivation and Context There is a bug in cloneable layer that allows non-clone types to enter ## Description Remove `DerefMut` and resolve the consequences ## Testing - new doc test - existing orchestrator CI ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --------- Co-authored-by: John DiSanti --- .../aws-smithy-types/src/config_bag.rs | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index 12fefbe3b1..f293dbea18 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -19,7 +19,7 @@ use std::borrow::Cow; use std::fmt::{Debug, Formatter}; use std::iter::Rev; use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; +use std::ops::Deref; use std::slice::Iter; use std::sync::Arc; @@ -64,6 +64,21 @@ impl Default for Value { /// /// While [`FrozenLayer`] is also cloneable, which is a shallow clone via `Arc`, `CloneableLayer` /// performs a deep clone that newly allocates all the items stored in it. +/// +/// Cloneable enforces that non clone items cannot be added +/// ```rust,compile_fail +/// use aws_smithy_types::config_bag::Storable; +/// use aws_smithy_types::config_bag::StoreReplace; +/// use aws_smithy_types::config_bag::CloneableLayer; +/// #[derive(Debug)] +/// struct MyNotCloneStruct; +/// +/// impl Storable for MyNotCloneStruct { +/// type Storer = StoreReplace; +/// } +/// let mut layer = CloneableLayer::new("layer"); +/// layer.store_put(MyNotCloneStruct); +/// ``` #[derive(Debug, Default)] pub struct CloneableLayer(Layer); @@ -75,12 +90,6 @@ impl Deref for CloneableLayer { } } -impl DerefMut for CloneableLayer { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - impl Clone for CloneableLayer { fn clone(&self) -> Self { Self( @@ -110,15 +119,16 @@ impl CloneableLayer { /// Removes `T` from this bag pub fn unset(&mut self) -> &mut Self { - self.put_directly::>(Value::ExplicitlyUnset(type_name::())); + self.0.unset::(); self } - fn put_directly(&mut self, value: T::StoredType) -> &mut Self + fn put_directly_cloneable(&mut self, value: T::StoredType) -> &mut Self where T::StoredType: Clone, { - self.props + self.0 + .props .insert(TypeId::of::(), TypeErasedBox::new_with_clone(value)); self } @@ -128,7 +138,7 @@ impl CloneableLayer { where T: Storable> + Clone, { - self.put_directly::>(Value::Set(item)); + self.put_directly_cloneable::>(Value::Set(item)); self } @@ -142,7 +152,7 @@ impl CloneableLayer { Some(item) => Value::Set(item), None => Value::ExplicitlyUnset(type_name::()), }; - self.put_directly::>(item); + self.put_directly_cloneable::>(item); self } @@ -164,14 +174,15 @@ impl CloneableLayer { where T: Storable> + Clone, { - self.put_directly::>(Value::ExplicitlyUnset(type_name::())); + self.put_directly_cloneable::>(Value::ExplicitlyUnset(type_name::())); } fn get_mut_or_default(&mut self) -> &mut T::StoredType where T::StoredType: Default + Clone, { - self.props + self.0 + .props .entry(TypeId::of::()) .or_insert_with(|| TypeErasedBox::new_with_clone(T::StoredType::default())) .downcast_mut() From 705f0f07005b64fcfe796c388f5dc2a20813f543 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 10 Jul 2023 11:36:15 -0700 Subject: [PATCH 202/253] Increase max line length for generated client code (#2835) This PR increases the max line length on generated client code to reduce the likelihood of rustfmt failing to format files. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index 9ddbbd4dc8..fee242cbe1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -170,7 +170,8 @@ class ClientCodegenVisitor( ), ) try { - "cargo fmt".runCommand(fileManifest.baseDir, timeout = settings.codegenConfig.formatTimeoutSeconds.toLong()) + // use an increased max_width to make rustfmt fail less frequently + "cargo fmt -- --config max_width=150".runCommand(fileManifest.baseDir, timeout = settings.codegenConfig.formatTimeoutSeconds.toLong()) } catch (err: CommandFailed) { logger.warning("Failed to run cargo fmt: [${service.id}]\n${err.output}") } From 5ae61a7195930efc379557990604cc24792e0aae Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Mon, 10 Jul 2023 14:30:25 -0500 Subject: [PATCH 203/253] =?UTF-8?q?add=20re=C3=ABxport=20for=20SharedInter?= =?UTF-8?q?ceptor=20to=20SDK=20config=20modules=20(#2837)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We weren't already doing this but we should b/c it's needed to use the `add_interceptor` and `set_interceptors` config methods --- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../smithy/generators/ClientRuntimeTypesReExportGenerator.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt index 408ff44390..12fa3e264b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt @@ -28,9 +28,11 @@ class ClientRuntimeTypesReExportGenerator( """ pub use #{ConfigBag}; pub use #{Interceptor}; + pub use #{SharedInterceptor}; """, "ConfigBag" to RuntimeType.configBag(rc), "Interceptor" to RuntimeType.interceptor(rc), + "SharedInterceptor" to RuntimeType.sharedInterceptor(rc), ) } rustCrate.withModule(ClientRustModule.endpoint(codegenContext)) { From 655684b6b7093764640772bb8d16b15619dd271b Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 12 Jul 2023 12:22:04 -0400 Subject: [PATCH 204/253] Include the old stable version in the dockerfile (#2839) ## Motivation and Context The previous rust stable version is required to compile older versions of the SDK ## Testing built the docker image locally _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- tools/ci-build/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/ci-build/Dockerfile b/tools/ci-build/Dockerfile index 3e04f15c7d..ef3cfbef56 100644 --- a/tools/ci-build/Dockerfile +++ b/tools/ci-build/Dockerfile @@ -6,6 +6,7 @@ # This is the base Docker build image used by CI ARG base_image=public.ecr.aws/amazonlinux/amazonlinux:2 +ARG prev_rust_stable_version=1.67.1 ARG rust_stable_version=1.68.2 ARG rust_nightly_version=nightly-2023-05-31 @@ -25,6 +26,7 @@ RUN curl https://musl.libc.org/releases/musl-1.2.3.tar.gz -o musl-1.2.3.tar.gz \ FROM bare_base_image AS install_rust ARG rust_stable_version ARG rust_nightly_version +ARG prev_rust_stable_version ENV RUSTUP_HOME=/opt/rustup \ CARGO_HOME=/opt/cargo \ PATH=/opt/cargo/bin/:${PATH} \ @@ -60,6 +62,7 @@ RUN set -eux; \ rustup component add rustfmt; \ rustup component add clippy; \ rustup toolchain install ${rust_nightly_version} --component clippy; \ + rustup toolchain install ${prev_rust_stable_version} --component clippy; \ rustup target add x86_64-unknown-linux-musl; \ rustup target add wasm32-unknown-unknown; \ rustup target add wasm32-wasi; \ From abd01157faca31a9e327e59c822c5c62d968ce9f Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 12 Jul 2023 14:30:41 -0400 Subject: [PATCH 205/253] Update the S3 benchmark for correctness and performance (#2838) ## Motivation and Context Gain a better understanding of S3 performance with the Rust SDK ## Description Updates the S3 benchmark to: 1. Add verify step (untimed) 2. Improve performance by reading and writing from the disk concurrently 3. Add support for using multiple clients simultaeneously 4. Fix correctness issues & simplify 5. Add timeouts on the parts ## Testing Ran the benchmark locally and on a c5n.18xlarge ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/sdk/s3-benchmark/benchmark/Cargo.lock | 31 +++- aws/sdk/s3-benchmark/benchmark/Cargo.toml | 4 + .../s3-benchmark/benchmark/src/get_test.rs | 49 +++++ .../s3-benchmark/benchmark/src/latencies.rs | 2 +- aws/sdk/s3-benchmark/benchmark/src/main.rs | 155 ++++++++++++---- .../benchmark/src/multipart_get.rs | 172 ++++++++++++++---- .../benchmark/src/multipart_put.rs | 99 ++++++++-- .../s3-benchmark/benchmark/src/put_test.rs | 63 +++++++ aws/sdk/s3-benchmark/benchmark/src/verify.rs | 28 +++ 9 files changed, 501 insertions(+), 102 deletions(-) create mode 100644 aws/sdk/s3-benchmark/benchmark/src/get_test.rs create mode 100644 aws/sdk/s3-benchmark/benchmark/src/put_test.rs create mode 100644 aws/sdk/s3-benchmark/benchmark/src/verify.rs diff --git a/aws/sdk/s3-benchmark/benchmark/Cargo.lock b/aws/sdk/s3-benchmark/benchmark/Cargo.lock index d53f4b830a..a435022a58 100644 --- a/aws/sdk/s3-benchmark/benchmark/Cargo.lock +++ b/aws/sdk/s3-benchmark/benchmark/Cargo.lock @@ -17,6 +17,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +[[package]] +name = "async-trait" +version = "0.1.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79fa67157abdfd688a259b6648808757db9347af834624f27ec646da976aee5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -404,11 +415,15 @@ dependencies = [ name = "benchmark" version = "0.1.0" dependencies = [ + "async-trait", "aws-config", "aws-sdk-s3", + "aws-smithy-client", "aws-smithy-http", "clap", + "hyper", "tokio", + "tracing", "tracing-subscriber", ] @@ -735,9 +750,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -1005,18 +1020,18 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" -version = "1.0.59" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.28" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" dependencies = [ "proc-macro2", ] @@ -1264,9 +1279,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.18" +version = "2.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" dependencies = [ "proc-macro2", "quote", diff --git a/aws/sdk/s3-benchmark/benchmark/Cargo.toml b/aws/sdk/s3-benchmark/benchmark/Cargo.toml index 2fe087c0c2..5ac77a1f81 100644 --- a/aws/sdk/s3-benchmark/benchmark/Cargo.toml +++ b/aws/sdk/s3-benchmark/benchmark/Cargo.toml @@ -12,6 +12,10 @@ publish = false aws-config = "0.55.3" aws-sdk-s3 = "0.28.0" aws-smithy-http = "0.55.3" +aws-smithy-client= { version = "0.55.3", features = ["client-hyper"] } clap = { version = "4.3.2", default-features = false, features = ["derive", "std", "help"] } tokio = { version = "1.28.2", features = ["full"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +tracing = "0.1" +async-trait = "0.1.68" +hyper = { version = "0.14.27", features = ["client"] } diff --git a/aws/sdk/s3-benchmark/benchmark/src/get_test.rs b/aws/sdk/s3-benchmark/benchmark/src/get_test.rs new file mode 100644 index 0000000000..a9588255a6 --- /dev/null +++ b/aws/sdk/s3-benchmark/benchmark/src/get_test.rs @@ -0,0 +1,49 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::{verify, Args, BoxError}; +use async_trait::async_trait; +use aws_config::SdkConfig; +use aws_sdk_s3::Client; +use std::path::{Path, PathBuf}; + +pub(crate) struct GetTestResult { + pub(crate) expected: PathBuf, + pub(crate) actual: PathBuf, +} + +#[async_trait] +pub(crate) trait GetBenchmark { + type Setup: Send; + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup; + async fn do_get( + &self, + state: Self::Setup, + target_path: &Path, + args: &Args, + ) -> Result; + async fn do_bench( + &self, + state: Self::Setup, + args: &Args, + expected_path: &Path, + ) -> Result { + let target_path = expected_path.with_extension("downloaded"); + let downloaded_path = self.do_get(state, &target_path, args).await?; + Ok(GetTestResult { + expected: expected_path.to_path_buf(), + actual: downloaded_path, + }) + } + + async fn verify( + &self, + _client: &Client, + _args: &Args, + result: GetTestResult, + ) -> Result<(), BoxError> { + verify::diff(&result.actual, &result.expected).await + } +} diff --git a/aws/sdk/s3-benchmark/benchmark/src/latencies.rs b/aws/sdk/s3-benchmark/benchmark/src/latencies.rs index 8ff3879b41..50b54a273d 100644 --- a/aws/sdk/s3-benchmark/benchmark/src/latencies.rs +++ b/aws/sdk/s3-benchmark/benchmark/src/latencies.rs @@ -6,7 +6,7 @@ use std::fmt; use std::time; -const ONE_GIGABYTE: u64 = 1024 * 1024 * 1024; +const ONE_GIGABYTE: u64 = 1000 * 1000 * 1000; #[derive(Debug)] pub struct Latencies { diff --git a/aws/sdk/s3-benchmark/benchmark/src/main.rs b/aws/sdk/s3-benchmark/benchmark/src/main.rs index 91a6214a7b..ad9e6aefb1 100644 --- a/aws/sdk/s3-benchmark/benchmark/src/main.rs +++ b/aws/sdk/s3-benchmark/benchmark/src/main.rs @@ -4,9 +4,11 @@ */ use crate::latencies::Latencies; -use crate::multipart_get::get_object_multipart; -use crate::multipart_put::put_object_multipart; +use crate::multipart_put::{put_object_multipart, PutObjectMultipart}; +use async_trait::async_trait; +use aws_config::SdkConfig; use aws_sdk_s3 as s3; +use aws_sdk_s3::Client; use clap::Parser as _; use s3::error::DisplayErrorContext; use s3::primitives::ByteStream; @@ -14,16 +16,24 @@ use std::error::Error as StdError; use std::path::Path; use std::path::PathBuf; use std::process; +use std::process::{Command, Stdio}; use std::time; +mod get_test; mod latencies; mod multipart_get; mod multipart_put; +mod put_test; +mod verify; pub type BoxError = Box; pub const BENCH_KEY: &str = "s3_bench_file"; +use crate::get_test::GetBenchmark; +use crate::put_test::PutBenchmark; +use tracing::Instrument; + #[derive(Copy, Clone, Debug, clap::ValueEnum)] pub enum Fs { #[cfg(target_os = "linux")] @@ -41,7 +51,7 @@ pub enum Bench { GetObjectMultipart, } -#[derive(Debug, clap::Parser)] +#[derive(Debug, Clone, clap::Parser)] #[command()] pub struct Args { /// Which benchmark to run. @@ -79,6 +89,9 @@ pub struct Args { /// Number of concurrent uploads/downloads to perform. #[arg(long, default_value_t = 4)] concurrency: usize, + + #[arg(long, default_value_t = 1000)] + part_upload_timeout_millis: u64, } #[tokio::main] @@ -94,13 +107,12 @@ async fn main() { } loader.load().await }; - let client = s3::Client::new(&config); let result = match args.bench { - Bench::PutObject => benchmark_put_object(&client, &args).await, - Bench::GetObject => benchmark_get_object(&client, &args).await, - Bench::PutObjectMultipart => benchmark_put_object_multipart(&client, &args).await, - Bench::GetObjectMultipart => benchmark_get_object_multipart(&client, &args).await, + Bench::PutObject => benchmark_put_object(&config, &args).await, + Bench::GetObject => benchmark_get_object(&config, &args).await, + Bench::PutObjectMultipart => benchmark_put_object_multipart(&config, &args).await, + Bench::GetObjectMultipart => benchmark_get_object_multipart(&config, &args).await, }; match result { Ok(latencies) => { @@ -117,15 +129,31 @@ async fn main() { } macro_rules! benchmark { - ($client:ident, $args:ident, setup => $setup:expr, operation => $operation:expr) => {{ + ($sdk_config:ident, $args:ident, setup => $setup:expr, operation => $operation:expr) => {{ + #[allow(unused)] + use crate::get_test::GetBenchmark; + #[allow(unused)] + use crate::put_test::PutBenchmark; + println!("setting up..."); let test_file_path = generate_test_file($args)?; - $setup($client, $args, &test_file_path).await?; + let setup_client = aws_sdk_s3::Client::new(&$sdk_config); + $setup(&setup_client, $args, &test_file_path).await?; + println!("setup complete"); let mut latencies = Latencies::new($args.size_bytes); for i in 0..$args.iterations { + let span = tracing::info_span!("run operation"); + let bench = $operation; + let client = bench.prepare($sdk_config).await; let start = time::Instant::now(); - $operation($client, $args, &test_file_path).await?; + let result = bench + .do_bench(client, $args, &test_file_path) + .instrument(span) + .await?; let latency = start.elapsed(); + if let Err(e) = bench.verify(&setup_client, $args, result).await { + println!("benchmark did not finish correctly: {}", e); + } latencies.push(latency); println!( "finished iteration {i} in {} seconds", @@ -137,38 +165,79 @@ macro_rules! benchmark { }}; } -async fn benchmark_put_object(client: &s3::Client, args: &Args) -> Result { - benchmark!(client, args, setup => no_setup, operation => put_object) +async fn benchmark_put_object(conf: &SdkConfig, args: &Args) -> Result { + struct PutObject; + #[async_trait] + impl PutBenchmark for PutObject { + type Setup = Client; + + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup { + Client::new(conf) + } + + async fn do_put( + &self, + state: Self::Setup, + target_key: &str, + local_file: &Path, + args: &Args, + ) -> Result<(), BoxError> { + state + .put_object() + .bucket(&args.bucket) + .key(target_key) + .body(ByteStream::from_path(local_file).await?) + .send() + .await?; + Ok(()) + } + } + benchmark!(conf, args, setup => no_setup, operation => PutObject) } -async fn benchmark_get_object(client: &s3::Client, args: &Args) -> Result { - async fn operation(client: &s3::Client, args: &Args, path: &Path) -> Result<(), BoxError> { - let output = client - .get_object() - .bucket(&args.bucket) - .key(BENCH_KEY) - .send() - .await?; - let mut body = output.body.into_async_read(); - let mut file = tokio::fs::File::create(path).await?; - tokio::io::copy(&mut body, &mut file).await?; - Ok(()) +async fn benchmark_get_object(client: &SdkConfig, args: &Args) -> Result { + struct GetObject; + #[async_trait] + impl GetBenchmark for GetObject { + type Setup = Client; + + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup { + Client::new(&conf) + } + + async fn do_get( + &self, + state: Self::Setup, + target_path: &Path, + args: &Args, + ) -> Result { + let output = state + .get_object() + .bucket(&args.bucket) + .key(BENCH_KEY) + .send() + .await?; + let mut body = output.body.into_async_read(); + let mut file = tokio::fs::File::create(target_path).await?; + tokio::io::copy(&mut body, &mut file).await?; + Ok(target_path.to_path_buf()) + } } - benchmark!(client, args, setup => put_object_intelligent, operation => operation) + benchmark!(client, args, setup => put_object_intelligent, operation => GetObject) } async fn benchmark_put_object_multipart( - client: &s3::Client, + conf: &SdkConfig, args: &Args, ) -> Result { - benchmark!(client, args, setup => no_setup, operation => put_object_multipart) + benchmark!(conf, args, setup => no_setup, operation => PutObjectMultipart) } async fn benchmark_get_object_multipart( - client: &s3::Client, + config: &SdkConfig, args: &Args, ) -> Result { - benchmark!(client, args, setup => put_object_intelligent, operation => get_object_multipart) + benchmark!(config, args, setup => put_object_intelligent, operation => multipart_get::GetObjectMultipart::new()) } fn generate_test_file(args: &Args) -> Result { @@ -183,11 +252,27 @@ fn generate_test_file(args: &Args) -> Result { } }; - process::Command::new("truncate") - .arg("-s") + let mut yes_process = Command::new("yes") + .arg("01234567890abcdefghijklmnopqrstuvwxyz") + .stdout(Stdio::piped()) + .spawn()?; + + let mut head_process = Command::new("head") + .arg("-c") .arg(format!("{}", args.size_bytes)) - .arg(&path) - .output()?; + .stdin(yes_process.stdout.take().unwrap()) + .stdout(Stdio::piped()) + .spawn()?; + + let mut file = std::fs::File::create(&path)?; + head_process.stdout.as_mut().unwrap(); + std::io::copy(&mut head_process.stdout.take().unwrap(), &mut file)?; + + let exit_status = head_process.wait()?; + + if !exit_status.success() { + Err("failed to generate temp file")? + } Ok(path) } @@ -202,7 +287,7 @@ async fn put_object_intelligent( path: &Path, ) -> Result<(), BoxError> { if args.size_bytes > args.part_size_bytes { - put_object_multipart(client, args, path).await + put_object_multipart(&[client.clone()], args, BENCH_KEY, path).await } else { put_object(client, args, path).await } diff --git a/aws/sdk/s3-benchmark/benchmark/src/multipart_get.rs b/aws/sdk/s3-benchmark/benchmark/src/multipart_get.rs index 08e0f3a9c2..ab8832816c 100644 --- a/aws/sdk/s3-benchmark/benchmark/src/multipart_get.rs +++ b/aws/sdk/s3-benchmark/benchmark/src/multipart_get.rs @@ -4,80 +4,174 @@ */ use crate::{Args, BoxError, BENCH_KEY}; +use async_trait::async_trait; +use aws_config::SdkConfig; use aws_sdk_s3 as s3; +use aws_sdk_s3::Client; +use aws_smithy_http::byte_stream::AggregatedBytes; use std::fmt; -use std::path::Path; +use std::fs::File; +use std::os::unix::fs::FileExt; +use std::path::{Path, PathBuf}; use std::sync::Arc; -use tokio::sync::watch::channel; +use std::time::{Duration, SystemTime}; + +use crate::get_test::GetBenchmark; use tokio::sync::Semaphore; +use tokio::task::spawn_blocking; +use tokio::time::timeout; +use tracing::{info_span, Instrument}; + +pub(crate) struct GetObjectMultipart {} +impl GetObjectMultipart { + pub(crate) fn new() -> Self { + Self {} + } +} + +#[async_trait] +impl GetBenchmark for GetObjectMultipart { + type Setup = Vec; + + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup { + let clients = (0..32).map(|_| Client::new(&conf)).collect::>(); + for client in &clients { + let _ = client.list_buckets().send().await; + } + clients + } + + async fn do_get( + &self, + state: Self::Setup, + target_path: &Path, + args: &Args, + ) -> Result { + get_object_multipart(&state, args, target_path, &args.bucket, BENCH_KEY).await?; + Ok(target_path.to_path_buf()) + } +} pub async fn get_object_multipart( - client: &s3::Client, + clients: &[s3::Client], args: &Args, - path: &Path, + target_path: &Path, + bucket: &str, + key: &str, ) -> Result<(), BoxError> { - let mut part_count = (args.size_bytes / args.part_size_bytes + 1) as i64; - let mut size_of_last_part = (args.size_bytes % args.part_size_bytes) as i64; + let mut part_count = (args.size_bytes / args.part_size_bytes + 1) as u64; + let mut size_of_last_part = (args.size_bytes % args.part_size_bytes) as u64; if size_of_last_part == 0 { - size_of_last_part = args.part_size_bytes as i64; + size_of_last_part = args.part_size_bytes as u64; part_count -= 1; } - let mut ranges = (0..part_count).map(|i| { + let ranges = (0..part_count).map(|i| { if i == part_count - 1 { - let start = i * args.part_size_bytes as i64; + let start = i * args.part_size_bytes as u64; ContentRange::new(start, start + size_of_last_part - 1) } else { ContentRange::new( - i * args.part_size_bytes as i64, - (i + 1) * args.part_size_bytes as i64 - 1, + i * args.part_size_bytes as u64, + (i + 1) * args.part_size_bytes as u64 - 1, ) } }); - let (tx, rx) = channel(ranges.next().unwrap()); - for range in ranges { - tx.send(range)?; - } let semaphore = Arc::new(Semaphore::new(args.concurrency)); let mut tasks = Vec::new(); - for _ in 0..part_count { + let file = Arc::new(File::create(target_path)?); + for (id, range) in ranges.enumerate() { let semaphore = semaphore.clone(); - let client = client.clone(); - let bucket = args.bucket.clone(); - let mut rx = rx.clone(); - tasks.push(tokio::spawn(async move { - let _permit = semaphore.acquire().await?; - let range = rx.borrow_and_update().to_string(); - - let part = client - .get_object() - .bucket(bucket) - .key(BENCH_KEY) - .range(range) - .send() - .await?; - - Result::<_, BoxError>::Ok(part.body) - })); + let client = clients[id % clients.len()].clone(); + let file = file.clone(); + let bucket = bucket.to_string(); + let key = key.to_string(); + tasks.push(tokio::spawn( + async move { + let _permit = semaphore.acquire_owned().await?; + + let start = SystemTime::now(); + tracing::debug!(range = ?range); + + let body = + download_part_retry_on_timeout(id, &range, &client, &bucket, &key).await?; + tracing::debug!(id =? id, load_duration = ?start.elapsed().unwrap()); + let mut offset = range.start; + let write_duration = SystemTime::now(); + spawn_blocking(move || { + for part in body.into_segments() { + file.write_all_at(&part, offset)?; + offset += part.len() as u64; + } + Ok::<_, BoxError>(()) + }) + .await??; + tracing::debug!(id =? id, write_duration = ?write_duration.elapsed().unwrap()); + Result::<_, BoxError>::Ok(()) + } + .instrument(info_span!("run-collect-part", id = id)), + )); } for task in tasks { - let mut body = task.await??.into_async_read(); - let mut file = tokio::fs::File::create(path).await?; - tokio::io::copy(&mut body, &mut file).await?; + task.await??; } Ok(()) } +async fn download_part_retry_on_timeout( + id: usize, + range: &ContentRange, + client: &Client, + bucket: &str, + key: &str, +) -> Result { + loop { + match timeout( + Duration::from_millis(1000), + download_part(id, range, client, bucket, key), + ) + .await + { + Ok(result) => return result, + Err(_) => tracing::warn!("get part timeout"), + } + } +} + +async fn download_part( + id: usize, + range: &ContentRange, + client: &Client, + bucket: &str, + key: &str, +) -> Result { + let part = client + .get_object() + .bucket(bucket) + .key(key) + .range(range.to_string()) + .send() + .instrument(info_span!("get_object", id = id)) + .await?; + + let body = part + .body + .collect() + .instrument(info_span!("collect-body", id = id)) + .await?; + Ok(body) +} + #[derive(Debug)] struct ContentRange { - start: i64, - end: i64, + start: u64, + end: u64, } impl ContentRange { - fn new(start: i64, end: i64) -> Self { + fn new(start: u64, end: u64) -> Self { Self { start, end } } } diff --git a/aws/sdk/s3-benchmark/benchmark/src/multipart_put.rs b/aws/sdk/s3-benchmark/benchmark/src/multipart_put.rs index 4c8213b0c6..942c3d8020 100644 --- a/aws/sdk/s3-benchmark/benchmark/src/multipart_put.rs +++ b/aws/sdk/s3-benchmark/benchmark/src/multipart_put.rs @@ -3,26 +3,60 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::put_test::PutBenchmark; use crate::{Args, BoxError, BENCH_KEY}; +use async_trait::async_trait; +use aws_config::SdkConfig; use aws_sdk_s3 as s3; +use aws_sdk_s3::Client; use aws_smithy_http::byte_stream::ByteStream; -use aws_smithy_http::byte_stream::Length; use s3::types::CompletedMultipartUpload; use s3::types::CompletedPart; +use std::io::SeekFrom; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use tokio::sync::Semaphore; +use std::time::{Duration, SystemTime}; +use tokio::fs::File; +use tokio::io::{AsyncReadExt, AsyncSeekExt}; +use tokio::sync::{OwnedSemaphorePermit, Semaphore}; +use tokio::time::timeout; + +pub(crate) struct PutObjectMultipart; + +#[async_trait] +impl PutBenchmark for PutObjectMultipart { + type Setup = Vec; + + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup { + let clients = (0..32).map(|_| Client::new(&conf)).collect::>(); + for client in &clients { + let _ = client.list_buckets().send().await; + } + clients + } + + async fn do_put( + &self, + state: Self::Setup, + target_key: &str, + local_file: &Path, + args: &Args, + ) -> Result<(), BoxError> { + put_object_multipart(&state, args, target_key, local_file).await + } +} pub async fn put_object_multipart( - client: &s3::Client, + client: &[s3::Client], args: &Args, + target_key: &str, path: &Path, ) -> Result<(), BoxError> { - let upload_id = client + let upload_id = client[0] .create_multipart_upload() .bucket(&args.bucket) - .key(BENCH_KEY) + .key(target_key) .send() .await? .upload_id @@ -44,15 +78,17 @@ pub async fn put_object_multipart( } else { args.part_size_bytes }; - tasks.push(tokio::spawn(upload_part( - semaphore.clone(), - client.clone(), + let permit = semaphore.clone().acquire_owned().await?; + tasks.push(tokio::spawn(upload_part_retry_on_timeout( + permit, + client[part as usize % client.len()].clone(), args.bucket.clone(), upload_id.clone(), path.to_path_buf(), offset, length, part, + Duration::from_millis(args.part_upload_timeout_millis), ))); } let mut parts = Vec::new(); @@ -60,7 +96,7 @@ pub async fn put_object_multipart( parts.push(task.await??); } - client + client[0] .complete_multipart_upload() .bucket(&args.bucket) .key(BENCH_KEY) @@ -76,9 +112,8 @@ pub async fn put_object_multipart( Ok(()) } -#[allow(clippy::too_many_arguments)] -async fn upload_part( - semaphore: Arc, +async fn upload_part_retry_on_timeout( + permit: OwnedSemaphorePermit, client: s3::Client, bucket: String, upload_id: String, @@ -86,15 +121,40 @@ async fn upload_part( offset: u64, length: u64, part: u64, + timeout_dur: Duration, ) -> Result { - let _permit = semaphore.acquire().await?; + loop { + match timeout( + timeout_dur, + upload_part(&client, &bucket, &upload_id, &path, offset, length, part), + ) + .await + { + Ok(res) => { + drop(permit); + return res; + } + Err(_) => tracing::warn!(id = ?part, "timeout!"), + } + } +} - let stream = ByteStream::read_from() - .path(path) - .offset(offset) - .length(Length::Exact(length)) - .build() - .await?; +#[allow(clippy::too_many_arguments)] +async fn upload_part( + client: &s3::Client, + bucket: &str, + upload_id: &str, + path: &Path, + offset: u64, + length: u64, + part: u64, +) -> Result { + let start = SystemTime::now(); + let mut file = File::open(path).await?; + file.seek(SeekFrom::Start(offset)).await?; + let mut buf = vec![0; length as usize]; + file.read_exact(&mut buf).await?; + let stream = ByteStream::from(buf); let part_output = client .upload_part() .key(BENCH_KEY) @@ -104,6 +164,7 @@ async fn upload_part( .part_number(part as i32 + 1) // S3 takes a 1-based index .send() .await?; + tracing::debug!(part = ?part, upload_duration = ?start.elapsed().unwrap(), "upload-part"); Ok(CompletedPart::builder() .part_number(part as i32 + 1) .e_tag(part_output.e_tag.expect("must have an e-tag")) diff --git a/aws/sdk/s3-benchmark/benchmark/src/put_test.rs b/aws/sdk/s3-benchmark/benchmark/src/put_test.rs new file mode 100644 index 0000000000..606778a216 --- /dev/null +++ b/aws/sdk/s3-benchmark/benchmark/src/put_test.rs @@ -0,0 +1,63 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::multipart_get::get_object_multipart; +use crate::{verify, Args, BoxError, BENCH_KEY}; +use async_trait::async_trait; +use aws_config::SdkConfig; +use aws_sdk_s3::Client; +use std::env::temp_dir; +use std::path::{Path, PathBuf}; + +pub(crate) struct PutTestResult { + local_file: PathBuf, + bucket: String, + key: String, +} + +#[async_trait] +pub(crate) trait PutBenchmark { + type Setup: Send; + async fn prepare(&self, conf: &SdkConfig) -> Self::Setup; + async fn do_put( + &self, + state: Self::Setup, + target_key: &str, + local_file: &Path, + args: &Args, + ) -> Result<(), BoxError>; + async fn do_bench( + &self, + state: Self::Setup, + args: &Args, + file: &Path, + ) -> Result { + self.do_put(state, BENCH_KEY, file, args).await?; + Ok(PutTestResult { + local_file: file.to_path_buf(), + bucket: args.bucket.clone(), + key: BENCH_KEY.to_string(), + }) + } + + async fn verify( + &self, + client: &Client, + args: &Args, + result: PutTestResult, + ) -> Result<(), BoxError> { + let dir = temp_dir(); + let downloaded_path = dir.join("downloaded_file"); + get_object_multipart( + &[client.clone()][..], + args, + &downloaded_path, + &result.bucket, + &result.key, + ) + .await?; + verify::diff(&result.local_file, &downloaded_path).await + } +} diff --git a/aws/sdk/s3-benchmark/benchmark/src/verify.rs b/aws/sdk/s3-benchmark/benchmark/src/verify.rs new file mode 100644 index 0000000000..6b6c50904f --- /dev/null +++ b/aws/sdk/s3-benchmark/benchmark/src/verify.rs @@ -0,0 +1,28 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::BoxError; +use std::path::Path; +use std::time::SystemTime; +use tokio::process::Command; + +pub(crate) async fn diff(a: &Path, b: &Path) -> Result<(), BoxError> { + let start_diff = SystemTime::now(); + let diff_ok = Command::new("diff") + .arg(a) + .arg(b) + .arg("-q") + .spawn() + .unwrap() + .wait() + .await + .unwrap(); + tracing::info!(diff_duration = ?start_diff.elapsed().unwrap()); + if !diff_ok.success() { + Err("files differ")? + } else { + Ok(()) + } +} From 3ee63a84860f0dfadbbccfffe24c1fec83cab9ca Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 12 Jul 2023 15:23:56 -0400 Subject: [PATCH 206/253] Fix the middleware benchmark and rerun (#2840) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation and Context Rerun the middleware benchmark. Results: ``` Finished bench [optimized + debuginfo] target(s) in 3.77s Running benches/middleware_vs_orchestrator.rs (/Users/rcoh/code/smithy-rs/target/release/deps/middleware_vs_orchestrator-eec2a5ed375836b0) compare/middleware (HEAD)/S3 ListObjectsV2 time: [27.887 µs 27.956 µs 28.030 µs] change: [+22.004% +22.457% +22.923%] (p = 0.00 < 0.05) Performance has regressed. Found 6 outliers among 100 measurements (6.00%) 5 (5.00%) high mild 1 (1.00%) high severe compare/middleware (last_release)/S3 ListObjectsV2 time: [22.076 µs 22.122 µs 22.176 µs] change: [-3.1947% -2.7875% -2.4454%] (p = 0.00 < 0.05) Performance has improved. Found 7 outliers among 100 measurements (7.00%) 5 (5.00%) high mild 2 (2.00%) high severe compare/orchestrator/S3 ListObjectsV2 time: [27.711 µs 27.764 µs 27.821 µs] change: [-11.548% -11.313% -11.097%] (p = 0.00 < 0.05) Performance has improved. Found 9 outliers among 100 measurements (9.00%) 6 (6.00%) high mild 3 (3.00%) high severe ``` ## Description - The fixup plugin is not required anymore. - Added a readme with instructions ## Testing - ran the benchmark ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-sdk-s3/benches/README.md | 6 +++++ .../benches/middleware_vs_orchestrator.rs | 26 +------------------ 2 files changed, 7 insertions(+), 25 deletions(-) create mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/benches/README.md diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/README.md b/aws/sra-test/integration-tests/aws-sdk-s3/benches/README.md new file mode 100644 index 0000000000..cf8f97680c --- /dev/null +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/README.md @@ -0,0 +1,6 @@ +### Middleware vs. Orchestrator Benchmark + +To run the benchmark: +```bash +./gradlew :aws:sra-test:assemble && (cd aws/sra-test/integration-tests/aws-sdk-s3 && cargo bench) +``` diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs index 23af022b79..fd4e5440a6 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs @@ -88,35 +88,11 @@ macro_rules! middleware_bench_fn { } async fn orchestrator(client: &s3::Client) { - #[derive(Debug)] - struct FixupPlugin { - region: String, - } - impl RuntimePlugin for FixupPlugin { - fn configure( - &self, - cfg: &mut ConfigBag, - _interceptors: &mut InterceptorRegistrar, - ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { - let params_builder = s3::endpoint::Params::builder() - .set_region(Some(self.region.clone())) - .bucket("test-bucket"); - - cfg.put(params_builder); - Ok(()) - } - } let _output = client .list_objects_v2() .bucket("test-bucket") .prefix("prefix~") - .send_orchestrator_with_plugin(Some(FixupPlugin { - region: client - .conf() - .region() - .map(|c| c.as_ref().to_string()) - .unwrap(), - })) + .send_orchestrator() .await .expect("successful execution"); } From c1a1daeee00be0a246c68e0c32eac1a5498edfaa Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 12 Jul 2023 16:59:54 -0700 Subject: [PATCH 207/253] Split runtime components out of config in the orchestrator impl (#2832) This PR moves all the "runtime components", pieces that are core to the operation of the orchestrator, into a separate `RuntimeComponents` type for the orchestrator to reference directly. The reason for this is so that these core components cannot be changed by interceptors while the orchestrator is executing a request. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-inlineable/Cargo.toml | 1 + .../src/apigateway_interceptors.rs | 2 + .../src/glacier_interceptors.rs | 13 +- .../src/http_request_checksum.rs | 3 + .../src/http_response_checksum.rs | 3 + .../src/presigning_interceptors.rs | 34 +- .../src/route53_resource_id_preprocessor.rs | 2 + .../aws-runtime/src/auth/sigv4.rs | 13 +- .../aws-runtime/src/invocation_id.rs | 9 +- .../aws-runtime/src/recursion_detection.rs | 6 +- .../aws-runtime/src/request_info.rs | 6 +- .../aws-runtime/src/user_agent.rs | 15 +- aws/sdk-codegen/build.gradle.kts | 3 +- .../AwsCustomizableOperationDecorator.kt | 18 +- .../amazon/smithy/rustsdk/CredentialCaches.kt | 6 +- .../smithy/rustsdk/CredentialProviders.kt | 6 +- .../HttpConnectorConfigCustomization.kt | 4 +- .../smithy/rustsdk/InvocationIdDecorator.kt | 2 +- .../rustsdk/RecursionDetectionDecorator.kt | 2 +- .../amazon/smithy/rustsdk/RegionDecorator.kt | 4 +- .../RetryInformationHeaderDecorator.kt | 2 +- .../smithy/rustsdk/SigV4AuthDecorator.kt | 4 +- .../smithy/rustsdk/UserAgentDecorator.kt | 6 +- .../apigateway/ApiGatewayDecorator.kt | 2 +- .../customize/glacier/GlacierDecorator.kt | 2 +- .../timestream/TimestreamDecorator.kt | 5 +- .../test/kotlin/SdkCodegenIntegrationTest.kt | 62 ++ .../rustsdk/CredentialCacheConfigTest.kt | 30 +- codegen-client-test/build.gradle.kts | 2 + codegen-client/build.gradle.kts | 3 +- .../client/smithy/ClientCodegenVisitor.kt | 4 +- .../customizations/ApiKeyAuthDecorator.kt | 2 +- .../ConnectionPoisoningConfigCustomization.kt | 2 +- .../customizations/HttpAuthDecorator.kt | 19 +- .../HttpChecksumRequiredGenerator.kt | 2 +- .../HttpConnectorConfigDecorator.kt | 109 ++- .../IdentityConfigCustomization.kt | 32 - .../InterceptorConfigCustomization.kt | 33 +- .../ResiliencyConfigCustomization.kt | 55 +- .../customizations/TimeSourceCustomization.kt | 14 +- .../customize/RequiredCustomizations.kt | 4 +- .../endpoint/EndpointConfigCustomization.kt | 181 ++-- .../ConfigOverrideRuntimePluginGenerator.kt | 49 +- .../generators/OperationCustomization.kt | 7 +- .../smithy/generators/OperationGenerator.kt | 32 +- .../OperationRuntimePluginGenerator.kt | 37 +- .../smithy/generators/ServiceGenerator.kt | 1 - .../ServiceRuntimePluginGenerator.kt | 60 +- .../client/CustomizableOperationGenerator.kt | 14 +- .../client/FluentClientGenerator.kt | 21 +- .../IdempotencyTokenProviderCustomization.kt | 10 +- .../config/ServiceConfigGenerator.kt | 142 ++-- .../testutil/TestConfigCustomization.kt | 7 +- .../MetadataCustomizationTest.kt | 3 + .../ClientContextConfigCustomizationTest.kt | 12 +- .../smithy/endpoint/EndpointsDecoratorTest.kt | 4 +- ...onfigOverrideRuntimePluginGeneratorTest.kt | 63 +- .../config/ServiceConfigGeneratorTest.kt | 4 +- .../protocol/ProtocolTestGeneratorTest.kt | 14 +- .../rust/codegen/core/smithy/RuntimeType.kt | 9 +- .../smithy/rust/codegen/core/testutil/Rust.kt | 4 +- .../smithy/rust/codegen/core/util/Exec.kt | 10 +- .../RecursiveShapesIntegrationTest.kt | 4 +- .../rust/codegen/core/util/ExecKtTest.kt | 2 +- .../server/smithy/ServerCodegenVisitor.kt | 4 +- .../ConstrainedStringGeneratorTest.kt | 4 +- rust-runtime/aws-smithy-async/Cargo.toml | 1 - rust-runtime/aws-smithy-async/src/rt/sleep.rs | 5 - rust-runtime/aws-smithy-async/src/time.rs | 5 - .../aws-smithy-client/external-types.toml | 2 +- .../aws-smithy-runtime-api/src/client.rs | 2 + .../aws-smithy-runtime-api/src/client/auth.rs | 77 +- .../src/client/config_bag_accessors.rs | 292 +------ .../src/client/connectors.rs | 16 +- .../src/client/identity.rs | 32 +- .../src/client/interceptors.rs | 301 +++---- .../src/client/orchestrator.rs | 14 +- .../src/client/retries.rs | 49 +- .../src/client/runtime_components.rs | 786 ++++++++++++++++++ .../src/client/runtime_plugin.rs | 72 +- rust-runtime/aws-smithy-runtime/src/client.rs | 3 + .../src/client/auth/http.rs | 26 +- .../src/client/auth/no_auth.rs | 35 +- .../src/client/config_override.rs | 254 ++++++ .../client/connectors/connection_poisoning.rs | 8 +- .../src/client/interceptors.rs | 3 + .../src/client/orchestrator.rs | 363 +++++--- .../src/client/orchestrator/auth.rs | 106 ++- .../src/client/orchestrator/endpoints.rs | 8 +- .../interceptors/service_clock_skew.rs | 2 + .../src/client/retries/client_rate_limiter.rs | 32 +- .../client/retries/strategy/fixed_delay.rs | 14 +- .../src/client/retries/strategy/never.rs | 8 +- .../src/client/retries/strategy/standard.rs | 164 ++-- .../src/client/test_util/interceptors.rs | 33 +- .../aws-smithy-runtime/src/client/timeout.rs | 72 +- .../aws-smithy-types/src/config_bag.rs | 6 +- .../src/client_http_checksum_required.rs | 24 +- .../src/client_idempotency_token.rs | 18 +- .../check-aws-sdk-orchestrator-impl | 1 + 100 files changed, 2504 insertions(+), 1574 deletions(-) create mode 100644 aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt delete mode 100644 codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs create mode 100644 rust-runtime/aws-smithy-runtime/src/client/config_override.rs diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml index e061a335e6..4a6630ea6f 100644 --- a/aws/rust-runtime/aws-inlineable/Cargo.toml +++ b/aws/rust-runtime/aws-inlineable/Cargo.toml @@ -43,6 +43,7 @@ tracing = "0.1" aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client", features = ["test-util"] } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http", features = ["rt-tokio"] } +aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["test-util"] } tempfile = "3.6.0" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["test-util"] } diff --git a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs index 06398a325b..261932f3fb 100644 --- a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs @@ -8,6 +8,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use http::header::ACCEPT; use http::HeaderValue; @@ -20,6 +21,7 @@ impl Interceptor for AcceptHeaderInterceptor { fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { context diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs index 18bf422f49..b9a6165017 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -17,6 +17,7 @@ use aws_smithy_runtime_api::client::interceptors::context::{ }; use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::orchestrator::LoadedRequestBody; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use bytes::Bytes; use http::header::{HeaderName, HeaderValue}; @@ -71,6 +72,7 @@ impl Interceptor fn modify_before_serialization( &self, context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let erased_input = context.input_mut(); @@ -99,6 +101,7 @@ impl Interceptor for GlacierApiVersionInterceptor { fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { context.request_mut().headers_mut().insert( @@ -117,6 +120,7 @@ impl Interceptor for GlacierTreeHashHeaderInterceptor { fn modify_before_serialization( &self, _context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { // Request the request body to be loaded into memory immediately after serialization @@ -129,6 +133,7 @@ impl Interceptor for GlacierTreeHashHeaderInterceptor { fn modify_before_retry_loop( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let maybe_loaded_body = cfg.load::(); @@ -237,6 +242,7 @@ fn compute_hash_tree(mut hashes: Vec) -> Digest { mod account_id_autofill_tests { use super::*; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::type_erasure::TypedBox; #[test] @@ -251,13 +257,14 @@ mod account_id_autofill_tests { } } + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut cfg = ConfigBag::base(); let mut context = InterceptorContext::new(TypedBox::new(SomeInput { account_id: None }).erase()); let mut context = BeforeSerializationInterceptorContextMut::from(&mut context); let interceptor = GlacierAccountIdAutofillInterceptor::::new(); interceptor - .modify_before_serialization(&mut context, &mut cfg) + .modify_before_serialization(&mut context, &rc, &mut cfg) .expect("success"); assert_eq!( DEFAULT_ACCOUNT_ID, @@ -276,10 +283,12 @@ mod account_id_autofill_tests { mod api_version_tests { use super::*; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::type_erasure::TypedBox; #[test] fn api_version_interceptor() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut cfg = ConfigBag::base(); let mut context = InterceptorContext::new(TypedBox::new("dontcare").erase()); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); @@ -287,7 +296,7 @@ mod api_version_tests { let interceptor = GlacierApiVersionInterceptor::new("some-version"); interceptor - .modify_before_signing(&mut context, &mut cfg) + .modify_before_signing(&mut context, &rc, &mut cfg) .expect("success"); assert_eq!( diff --git a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs index e438032bdc..b97b2d2bd0 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs @@ -19,6 +19,7 @@ use aws_smithy_runtime_api::client::interceptors::context::{ BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, Input, }; use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; use http::HeaderValue; use http_body::Body; @@ -81,6 +82,7 @@ where fn read_before_serialization( &self, context: &BeforeSerializationInterceptorContextRef<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let checksum_algorithm = (self.algorithm_provider)(context.input())?; @@ -98,6 +100,7 @@ where fn modify_before_retry_loop( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let state = cfg diff --git a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs index 4f3f9dfea5..9a54d00191 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs @@ -14,6 +14,7 @@ use aws_smithy_runtime_api::client::interceptors::context::{ BeforeDeserializationInterceptorContextMut, BeforeSerializationInterceptorContextRef, Input, }; use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; use http::HeaderValue; use std::{fmt, mem}; @@ -58,6 +59,7 @@ where fn read_before_serialization( &self, context: &BeforeSerializationInterceptorContextRef<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let validation_enabled = (self.validation_enabled)(context.input()); @@ -72,6 +74,7 @@ where fn modify_before_deserialization( &self, context: &mut BeforeDeserializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let state = cfg diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs index ddc969ac23..7161134ac8 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs @@ -15,16 +15,19 @@ use aws_sigv4::http_request::SignableBody; use aws_smithy_async::time::{SharedTimeSource, StaticTimeSource}; use aws_smithy_runtime::client::retries::strategy::NeverRetryStrategy; use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{ BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, }; use aws_smithy_runtime_api::client::interceptors::{ - disable_interceptor, Interceptor, InterceptorRegistrar, SharedInterceptor, + disable_interceptor, Interceptor, SharedInterceptor, +}; +use aws_smithy_runtime_api::client::retries::SharedRetryStrategy; +use aws_smithy_runtime_api::client::runtime_components::{ + RuntimeComponents, RuntimeComponentsBuilder, }; -use aws_smithy_runtime_api::client::retries::DynRetryStrategy; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; +use std::borrow::Cow; /// Interceptor that tells the SigV4 signer to add the signature to query params, /// and sets the request expiration time from the presigning config. @@ -47,6 +50,7 @@ impl Interceptor for SigV4PresigningInterceptor { fn modify_before_serialization( &self, _context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { cfg.interceptor_state() @@ -55,16 +59,13 @@ impl Interceptor for SigV4PresigningInterceptor { .omit_default_content_length() .omit_default_content_type(), ); - cfg.interceptor_state() - .set_request_time(SharedTimeSource::new(StaticTimeSource::new( - self.config.start_time(), - ))); Ok(()) } fn modify_before_signing( &self, _context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { if let Some(mut config) = cfg.load::().cloned() { @@ -86,16 +87,20 @@ impl Interceptor for SigV4PresigningInterceptor { /// Runtime plugin that registers the SigV4PresigningInterceptor. #[derive(Debug)] pub(crate) struct SigV4PresigningRuntimePlugin { - interceptor: SharedInterceptor, + runtime_components: RuntimeComponentsBuilder, } impl SigV4PresigningRuntimePlugin { pub(crate) fn new(config: PresigningConfig, payload_override: SignableBody<'static>) -> Self { + let time_source = SharedTimeSource::new(StaticTimeSource::new(config.start_time())); Self { - interceptor: SharedInterceptor::new(SigV4PresigningInterceptor::new( - config, - payload_override, - )), + runtime_components: RuntimeComponentsBuilder::new("SigV4PresigningRuntimePlugin") + .with_interceptor(SharedInterceptor::new(SigV4PresigningInterceptor::new( + config, + payload_override, + ))) + .with_retry_strategy(Some(SharedRetryStrategy::new(NeverRetryStrategy::new()))) + .with_time_source(Some(time_source)), } } } @@ -103,14 +108,13 @@ impl SigV4PresigningRuntimePlugin { impl RuntimePlugin for SigV4PresigningRuntimePlugin { fn config(&self) -> Option { let mut layer = Layer::new("Presigning"); - layer.set_retry_strategy(DynRetryStrategy::new(NeverRetryStrategy::new())); layer.store_put(disable_interceptor::("presigning")); layer.store_put(disable_interceptor::("presigning")); layer.store_put(disable_interceptor::("presigning")); Some(layer.freeze()) } - fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { - interceptors.register(self.interceptor.clone()); + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.runtime_components) } } diff --git a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs index 0ed2a354d2..d8fa3f4c97 100644 --- a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs +++ b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs @@ -8,6 +8,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeSerializationInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use std::fmt; use std::marker::PhantomData; @@ -74,6 +75,7 @@ where fn modify_before_serialization( &self, context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let input: &mut T = context.input_mut().downcast_mut().expect("correct type"); diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs index 366e3ce0db..3a5a2e85ef 100644 --- a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -12,11 +12,9 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::{ AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, }; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; -use aws_smithy_runtime_api::client::identity::{ - Identity, IdentityResolvers, SharedIdentityResolver, -}; +use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver}; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; +use aws_smithy_runtime_api::client::runtime_components::{GetIdentityResolver, RuntimeComponents}; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::Document; use aws_types::region::{Region, SigningRegion}; @@ -99,7 +97,7 @@ impl HttpAuthScheme for SigV4HttpAuthScheme { fn identity_resolver( &self, - identity_resolvers: &IdentityResolvers, + identity_resolvers: &dyn GetIdentityResolver, ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -319,11 +317,12 @@ impl HttpRequestSigner for SigV4HttpRequestSigner { request: &mut HttpRequest, identity: &Identity, auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + runtime_components: &RuntimeComponents, config_bag: &ConfigBag, ) -> Result<(), BoxError> { let operation_config = Self::extract_operation_config(auth_scheme_endpoint_config, config_bag)?; - let request_time = config_bag.request_time().unwrap_or_default().now(); + let request_time = runtime_components.time_source().unwrap_or_default().now(); let credentials = if let Some(creds) = identity.data::() { creds @@ -373,7 +372,7 @@ impl HttpRequestSigner for SigV4HttpRequestSigner { use event_stream::SigV4MessageSigner; if let Some(signer_sender) = config_bag.load::() { - let time_source = config_bag.request_time().unwrap_or_default(); + let time_source = runtime_components.time_source().unwrap_or_default(); signer_sender .send(Box::new(SigV4MessageSigner::new( _signature, diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 6a5c178757..55cf9fd9e9 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -11,6 +11,7 @@ use http::{HeaderName, HeaderValue}; use std::fmt::Debug; use uuid::Uuid; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; #[cfg(feature = "test-util")] pub use test_util::{NoInvocationIdGenerator, PredefinedInvocationIdGenerator}; @@ -61,6 +62,7 @@ impl Interceptor for InvocationIdInterceptor { fn modify_before_retry_loop( &self, _ctx: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let id = cfg @@ -77,6 +79,7 @@ impl Interceptor for InvocationIdInterceptor { fn modify_before_transmit( &self, ctx: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let headers = ctx.request_mut().headers_mut(); @@ -184,6 +187,7 @@ mod tests { BeforeTransmitInterceptorContextMut, InterceptorContext, }; use aws_smithy_runtime_api::client::interceptors::Interceptor; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::type_erasure::TypeErasedBox; use http::HeaderValue; @@ -197,6 +201,7 @@ mod tests { #[test] fn test_id_is_generated_and_set() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); ctx.enter_serialization_phase(); ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); @@ -207,10 +212,10 @@ mod tests { let interceptor = InvocationIdInterceptor::new(); let mut ctx = Into::into(&mut ctx); interceptor - .modify_before_retry_loop(&mut ctx, &mut cfg) + .modify_before_retry_loop(&mut ctx, &rc, &mut cfg) .unwrap(); interceptor - .modify_before_transmit(&mut ctx, &mut cfg) + .modify_before_transmit(&mut ctx, &rc, &mut cfg) .unwrap(); let expected = cfg.load::().expect("invocation ID was set"); diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs index c8b28b07b4..3cbda55c4d 100644 --- a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -6,6 +6,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use aws_types::os_shim_internal::Env; use http::HeaderValue; @@ -42,6 +43,7 @@ impl Interceptor for RecursionDetectionInterceptor { fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let request = context.request_mut(); @@ -75,6 +77,7 @@ mod tests { use aws_smithy_http::body::SdkBody; use aws_smithy_protocol_test::{assert_ok, validate_headers}; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::type_erasure::TypeErasedBox; use aws_types::os_shim_internal::Env; use http::HeaderValue; @@ -142,6 +145,7 @@ mod tests { } fn check(test_case: TestCase) { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let env = test_case.env(); let mut request = http::Request::builder(); for (name, value) in test_case.request_headers_before() { @@ -157,7 +161,7 @@ mod tests { let mut ctx = Into::into(&mut context); RecursionDetectionInterceptor { env } - .modify_before_signing(&mut ctx, &mut config) + .modify_before_signing(&mut ctx, &rc, &mut config) .expect("interceptor must succeed"); let mutated_request = context.request().expect("request is set"); for name in mutated_request.headers().keys() { diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index d2ea28cc35..0fb9a929ee 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -8,6 +8,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::date_time::Format; use aws_smithy_types::retry::RetryConfig; @@ -89,6 +90,7 @@ impl Interceptor for RequestInfoInterceptor { fn modify_before_transmit( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let mut pairs = RequestPairs::new(); @@ -166,6 +168,7 @@ mod tests { use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::interceptors::Interceptor; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; @@ -186,6 +189,7 @@ mod tests { #[test] fn test_request_pairs_for_initial_attempt() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut context = InterceptorContext::new(TypeErasedBox::doesnt_matter()); context.enter_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); @@ -204,7 +208,7 @@ mod tests { let interceptor = RequestInfoInterceptor::new(); let mut ctx = (&mut context).into(); interceptor - .modify_before_transmit(&mut ctx, &mut config) + .modify_before_transmit(&mut ctx, &rc, &mut config) .unwrap(); assert_eq!( diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 3527fbe582..5e91bd8d23 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -7,6 +7,7 @@ use aws_http::user_agent::{ApiMetadata, AwsUserAgent}; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use aws_types::app_name::AppName; use aws_types::os_shim_internal::Env; @@ -74,6 +75,7 @@ impl Interceptor for UserAgentInterceptor { fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let api_metadata = cfg @@ -110,6 +112,7 @@ mod tests { use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::interceptors::Interceptor; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::error::display::DisplayErrorContext; use aws_smithy_types::type_erasure::TypeErasedBox; @@ -136,6 +139,7 @@ mod tests { #[test] fn test_overridden_ua() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut context = context(); let mut layer = Layer::new("test"); @@ -146,7 +150,7 @@ mod tests { let interceptor = UserAgentInterceptor::new(); let mut ctx = Into::into(&mut context); interceptor - .modify_before_signing(&mut ctx, &mut cfg) + .modify_before_signing(&mut ctx, &rc, &mut cfg) .unwrap(); let header = expect_header(&context, "user-agent"); @@ -161,6 +165,7 @@ mod tests { #[test] fn test_default_ua() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut context = context(); let api_metadata = ApiMetadata::new("some-service", "some-version"); @@ -171,7 +176,7 @@ mod tests { let interceptor = UserAgentInterceptor::new(); let mut ctx = Into::into(&mut context); interceptor - .modify_before_signing(&mut ctx, &mut config) + .modify_before_signing(&mut ctx, &rc, &mut config) .unwrap(); let expected_ua = AwsUserAgent::new_from_environment(Env::real(), api_metadata); @@ -191,6 +196,7 @@ mod tests { #[test] fn test_app_name() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut context = context(); let api_metadata = ApiMetadata::new("some-service", "some-version"); @@ -202,7 +208,7 @@ mod tests { let interceptor = UserAgentInterceptor::new(); let mut ctx = Into::into(&mut context); interceptor - .modify_before_signing(&mut ctx, &mut config) + .modify_before_signing(&mut ctx, &rc, &mut config) .unwrap(); let app_value = "app/my_awesome_app"; @@ -221,6 +227,7 @@ mod tests { #[test] fn test_api_metadata_missing() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut context = context(); let mut config = ConfigBag::base(); @@ -231,7 +238,7 @@ mod tests { "{}", DisplayErrorContext( &*interceptor - .modify_before_signing(&mut ctx, &mut config) + .modify_before_signing(&mut ctx, &rc, &mut config) .expect_err("it should error") ) ); diff --git a/aws/sdk-codegen/build.gradle.kts b/aws/sdk-codegen/build.gradle.kts index bb6f2925a7..b7a6471bed 100644 --- a/aws/sdk-codegen/build.gradle.kts +++ b/aws/sdk-codegen/build.gradle.kts @@ -87,12 +87,11 @@ if (isTestingEnabled.toBoolean()) { tasks.test { useJUnitPlatform() testLogging { - events("passed", "skipped", "failed") + events("failed") exceptionFormat = TestExceptionFormat.FULL showCauses = true showExceptions = true showStackTraces = true - showStandardStreams = true } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index 29718fa3cd..08d58134d4 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -25,6 +25,9 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "http" to CargoDependency.Http.toType(), "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), + "StaticRuntimePlugin" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::StaticRuntimePlugin"), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(runtimeConfig), "SharedTimeSource" to CargoDependency.smithyAsync(runtimeConfig).withFeature("test-util").toType() .resolve("time::SharedTimeSource"), "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) @@ -41,13 +44,14 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : """ ##[doc(hidden)] // This is a temporary method for testing. NEVER use it in production - pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self { - use #{ConfigBagAccessors}; - let interceptor = #{TestParamsSetterInterceptor}::new(move |_: &mut #{BeforeTransmitInterceptorContextMut}<'_>, cfg: &mut #{ConfigBag}| { - cfg.interceptor_state().set_request_time(#{SharedTimeSource}::new(request_time)); - }); - self.interceptors.push(#{SharedInterceptor}::new(interceptor)); - self + pub fn request_time_for_tests(self, request_time: ::std::time::SystemTime) -> Self { + self.runtime_plugin( + #{StaticRuntimePlugin}::new() + .with_runtime_components( + #{RuntimeComponentsBuilder}::new("request_time_for_tests") + .with_time_source(Some(#{SharedTimeSource}::new(request_time))) + ) + ) } ##[doc(hidden)] diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt index b31c5329ea..41fad244ef 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt @@ -80,7 +80,7 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom """ /// Returns the credentials cache. pub fn credentials_cache(&self) -> #{Option}<#{SharedCredentialsCache}> { - self.inner.load::<#{SharedCredentialsCache}>().cloned() + self.config.load::<#{SharedCredentialsCache}>().cloned() } """, *codegenScope, @@ -121,7 +121,7 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom """ /// Sets the credentials cache for this service pub fn set_credentials_cache(&mut self, credentials_cache: #{Option}<#{CredentialsCache}>) -> &mut Self { - self.inner.store_or_unset(credentials_cache); + self.config.store_or_unset(credentials_cache); self } """, @@ -148,7 +148,7 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom if let Some(credentials_provider) = layer.load::<#{SharedCredentialsProvider}>().cloned() { let cache_config = layer.load::<#{CredentialsCache}>().cloned() .unwrap_or_else({ - let sleep = layer.load::<#{SharedAsyncSleep}>().cloned(); + let sleep = self.runtime_components.sleep_impl(); || match sleep { Some(sleep) => { #{CredentialsCache}::lazy_builder() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index f73d1f8483..6be60295d5 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -101,7 +101,7 @@ class CredentialProviderConfig(codegenContext: ClientCodegenContext) : ConfigCus """ /// Sets the credentials provider for this service pub fn set_credentials_provider(&mut self, credentials_provider: #{Option}<#{SharedCredentialsProvider}>) -> &mut Self { - self.inner.store_or_unset(credentials_provider); + self.config.store_or_unset(credentials_provider); self } """, @@ -138,9 +138,9 @@ class CredentialsIdentityResolverRegistration( override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { - is ServiceRuntimePluginSection.AdditionalConfig -> { + is ServiceRuntimePluginSection.RegisterRuntimeComponents -> { rustBlockTemplate("if let Some(credentials_cache) = ${section.serviceConfigName}.credentials_cache()") { - section.registerIdentityResolver(this, runtimeConfig) { + section.registerIdentityResolver(this) { rustTemplate( """ #{SIGV4_SCHEME_ID}, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt index 5be6feefbd..911ec7ea27 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt @@ -55,7 +55,7 @@ class HttpConnectorConfigCustomization( """ /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. pub fn http_connector(&self) -> Option<&#{HttpConnector}> { - self.inner.load::<#{HttpConnector}>() + self.config.load::<#{HttpConnector}>() } """, *codegenScope, @@ -161,7 +161,7 @@ class HttpConnectorConfigCustomization( rustTemplate( """ pub fn set_http_connector(&mut self, http_connector: #{Option}>) -> &mut Self { - http_connector.map(|c| self.inner.store_put(c.into())); + http_connector.map(|c| self.config.store_put(c.into())); self } """, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt index 71c9d9ff62..9b9fcccb49 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt @@ -36,7 +36,7 @@ private class InvocationIdRuntimePluginCustomization( ) override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.RegisterInterceptor) { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { section.registerInterceptor(codegenContext.runtimeConfig, this) { rustTemplate("#{InvocationIdInterceptor}::new()", *codegenScope) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt index 509be8d295..56092dab81 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RecursionDetectionDecorator.kt @@ -31,7 +31,7 @@ private class RecursionDetectionRuntimePluginCustomization( private val codegenContext: ClientCodegenContext, ) : ServiceRuntimePluginCustomization() { override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.RegisterInterceptor) { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { section.registerInterceptor(codegenContext.runtimeConfig, this) { rust( "#T::new()", diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt index 494a08f8ed..5c9a0669f6 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt @@ -181,7 +181,7 @@ class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomi """ /// Returns the AWS region, if it was provided. pub fn region(&self) -> #{Option}<&#{Region}> { - self.inner.load::<#{Region}>() + self.config.load::<#{Region}>() } """, *codegenScope, @@ -232,7 +232,7 @@ class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomi """ /// Sets the AWS region to use when making requests. pub fn set_region(&mut self, region: #{Option}<#{Region}>) -> &mut Self { - self.inner.store_or_unset(region); + self.config.store_or_unset(region); self } """, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt index 77185e0667..221c2ec430 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt @@ -35,7 +35,7 @@ private class AddRetryInformationHeaderInterceptors(codegenContext: ClientCodege private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.RegisterInterceptor) { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { // Track the latency between client and server. section.registerInterceptor(runtimeConfig, this) { rust( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 7d469b4d98..6163d78a2f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -78,13 +78,13 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext: override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { - is ServiceRuntimePluginSection.AdditionalConfig -> { + is ServiceRuntimePluginSection.RegisterRuntimeComponents -> { val serviceHasEventStream = codegenContext.serviceShape.hasEventStreamOperations(codegenContext.model) if (serviceHasEventStream) { // enable the aws-runtime `sign-eventstream` feature addDependency(AwsCargoDependency.awsRuntime(runtimeConfig).withFeature("event-stream").toType().toSymbol()) } - section.registerHttpAuthScheme(this, runtimeConfig) { + section.registerHttpAuthScheme(this) { rustTemplate("#{SharedHttpAuthScheme}::new(#{SigV4HttpAuthScheme}::new())", *codegenScope) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index 19395ebea6..0ca5ce335c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -104,7 +104,7 @@ class UserAgentDecorator : ClientCodegenDecorator { override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { - is ServiceRuntimePluginSection.RegisterInterceptor -> { + is ServiceRuntimePluginSection.RegisterRuntimeComponents -> { section.registerInterceptor(runtimeConfig, this) { rust("#T::new()", awsRuntime.resolve("user_agent::UserAgentInterceptor")) } @@ -182,7 +182,7 @@ class UserAgentDecorator : ClientCodegenDecorator { /// This _optional_ name is used to identify the application in the user agent that /// gets sent along with requests. pub fn set_app_name(&mut self, app_name: #{Option}<#{AppName}>) -> &mut Self { - self.inner.store_or_unset(app_name); + self.config.store_or_unset(app_name); self } """, @@ -228,7 +228,7 @@ class UserAgentDecorator : ClientCodegenDecorator { /// This _optional_ name is used to identify the application in the user agent that /// gets sent along with requests. pub fn app_name(&self) -> #{Option}<&#{AppName}> { - self.inner.load::<#{AppName}>() + self.config.load::<#{AppName}>() } """, *codegenScope, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt index 3f699f5973..05087534b7 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/apigateway/ApiGatewayDecorator.kt @@ -66,7 +66,7 @@ private class ApiGatewayAddAcceptHeader : OperationCustomization() { private class ApiGatewayAcceptHeaderInterceptorCustomization(private val codegenContext: ClientCodegenContext) : ServiceRuntimePluginCustomization() { override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.RegisterInterceptor) { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { section.registerInterceptor(codegenContext.runtimeConfig, this) { rustTemplate( "#{Interceptor}::default()", diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt index 525acffd0a..ee3f18c175 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/glacier/GlacierDecorator.kt @@ -101,7 +101,7 @@ private class GlacierAccountIdCustomization(private val codegenContext: ClientCo private class GlacierApiVersionCustomization(private val codegenContext: ClientCodegenContext) : ServiceRuntimePluginCustomization() { override fun section(section: ServiceRuntimePluginSection): Writable = writable { - if (section is ServiceRuntimePluginSection.RegisterInterceptor) { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { val apiVersion = codegenContext.serviceShape.version section.registerInterceptor(codegenContext.runtimeConfig, this) { rustTemplate( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt index 57893b1c0a..55a9e7f7bd 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt @@ -62,7 +62,8 @@ class TimestreamDecorator : ClientCodegenDecorator { #{ResolveEndpointError}::from_source("failed to call describe_endpoints", e) })?; let endpoint = describe_endpoints.endpoints().unwrap().get(0).unwrap(); - let expiry = client.conf().time_source().now() + #{Duration}::from_secs(endpoint.cache_period_in_minutes() as u64 * 60); + let expiry = client.conf().time_source().expect("checked when ep discovery was enabled").now() + + #{Duration}::from_secs(endpoint.cache_period_in_minutes() as u64 * 60); Ok(( #{Endpoint}::builder() .url(format!("https://{}", endpoint.address().unwrap())) @@ -78,7 +79,7 @@ class TimestreamDecorator : ClientCodegenDecorator { pub async fn enable_endpoint_discovery(self) -> #{Result}<(Self, #{endpoint_discovery}::ReloadEndpoint), #{ResolveEndpointError}> { let mut new_conf = self.conf().clone(); let sleep = self.conf().sleep_impl().expect("sleep impl must be provided"); - let time = self.conf().time_source(); + let time = self.conf().time_source().expect("time source must be provided"); let (resolver, reloader) = #{endpoint_discovery}::create_cache( move || { let client = self.clone(); diff --git a/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt b/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt new file mode 100644 index 0000000000..fde2f32ae7 --- /dev/null +++ b/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rustsdk.awsSdkIntegrationTest + +class SdkCodegenIntegrationTest { + val model = """ + namespace test + + use aws.api#service + use aws.auth#sigv4 + use aws.protocols#restJson1 + use smithy.rules#endpointRuleSet + + @service(sdkId: "dontcare") + @restJson1 + @sigv4(name: "dontcare") + @auth([sigv4]) + @endpointRuleSet({ + "version": "1.0", + "rules": [{ "type": "endpoint", "conditions": [], "endpoint": { "url": "https://example.com" } }], + "parameters": { + "Region": { "required": false, "type": "String", "builtIn": "AWS::Region" }, + } + }) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String + } + + @http(uri: "/SomeOperation", method: "GET") + @optionalAuth + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + + @Test + fun smokeTestSdkCodegen() { + awsSdkIntegrationTest( + model, + defaultToOrchestrator = true, + ) { _, _ -> /* it should compile */ } + } + + @Test + fun smokeTestSdkCodegenMiddleware() { + awsSdkIntegrationTest( + model, + defaultToOrchestrator = false, + ) { _, _ -> /* it should compile */ } + } +} diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt index c598c1ab0d..6d1d7f8adc 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt @@ -77,10 +77,11 @@ internal class CredentialCacheConfigTest { let client_config = crate::config::Config::builder().build(); let config_override = crate::config::Config::builder().credentials_provider(#{Credentials}::for_tests()); - let sut = crate::config::ConfigOverrideRuntimePlugin { - client_config: client_config.config().unwrap(), + let sut = crate::config::ConfigOverrideRuntimePlugin::new( config_override, - }; + client_config.config, + &client_config.runtime_components, + ); // this should cause `panic!` let _ = sut.config().unwrap(); @@ -100,10 +101,11 @@ internal class CredentialCacheConfigTest { let client_config = crate::config::Config::builder().build(); let config_override = crate::config::Config::builder() .credentials_cache(#{CredentialsCache}::no_caching()); - let sut = crate::config::ConfigOverrideRuntimePlugin { - client_config: client_config.config().unwrap(), + let sut = crate::config::ConfigOverrideRuntimePlugin::new( config_override, - }; + client_config.config, + &client_config.runtime_components, + ); // this should cause `panic!` let _ = sut.config().unwrap(); @@ -121,7 +123,7 @@ internal class CredentialCacheConfigTest { let client_config = crate::config::Config::builder() .credentials_provider(#{Credentials}::for_tests()) .build(); - let client_config_layer = client_config.config().unwrap(); + let client_config_layer = client_config.config; // make sure test credentials are set in the client config level assert_eq!(#{Credentials}::for_tests(), @@ -143,10 +145,11 @@ internal class CredentialCacheConfigTest { let config_override = crate::config::Config::builder() .credentials_cache(#{CredentialsCache}::lazy()) .credentials_provider(credentials.clone()); - let sut = crate::config::ConfigOverrideRuntimePlugin { - client_config: client_config_layer, + let sut = crate::config::ConfigOverrideRuntimePlugin::new( config_override, - }; + client_config_layer, + &client_config.runtime_components, + ); let sut_layer = sut.config().unwrap(); // make sure `.provide_cached_credentials` returns credentials set through `config_override` @@ -170,10 +173,11 @@ internal class CredentialCacheConfigTest { let client_config = crate::config::Config::builder().build(); let config_override = crate::config::Config::builder(); - let sut = crate::config::ConfigOverrideRuntimePlugin { - client_config: client_config.config().unwrap(), + let sut = crate::config::ConfigOverrideRuntimePlugin::new( config_override, - }; + client_config.config, + &client_config.runtime_components, + ); let sut_layer = sut.config().unwrap(); assert!(sut_layer .load::<#{SharedCredentialsCache}>() diff --git a/codegen-client-test/build.gradle.kts b/codegen-client-test/build.gradle.kts index bb906e9968..8ccf6f6ad4 100644 --- a/codegen-client-test/build.gradle.kts +++ b/codegen-client-test/build.gradle.kts @@ -114,6 +114,8 @@ project.registerGenerateSmithyBuildTask(rootProject, pluginName, allCodegenTests project.registerGenerateCargoWorkspaceTask(rootProject, pluginName, allCodegenTests, workingDirUnderBuildDir) project.registerGenerateCargoConfigTomlTask(buildDir.resolve(workingDirUnderBuildDir)) +tasks["generateSmithyBuild"].inputs.property("smithy.runtime.mode", getSmithyRuntimeMode()) + tasks["smithyBuildJar"].dependsOn("generateSmithyBuild") tasks["assemble"].finalizedBy("generateCargoWorkspace") diff --git a/codegen-client/build.gradle.kts b/codegen-client/build.gradle.kts index 62d543beb4..919c1fe9bb 100644 --- a/codegen-client/build.gradle.kts +++ b/codegen-client/build.gradle.kts @@ -77,12 +77,11 @@ if (isTestingEnabled.toBoolean()) { tasks.test { useJUnitPlatform() testLogging { - events("passed", "skipped", "failed") + events("failed") exceptionFormat = TestExceptionFormat.FULL showCauses = true showExceptions = true showStackTraces = true - showStandardStreams = true } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt index fee242cbe1..b8cf2605de 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenVisitor.kt @@ -45,7 +45,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGenerat import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer 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.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.inputShape @@ -172,7 +172,7 @@ class ClientCodegenVisitor( try { // use an increased max_width to make rustfmt fail less frequently "cargo fmt -- --config max_width=150".runCommand(fileManifest.baseDir, timeout = settings.codegenConfig.formatTimeoutSeconds.toLong()) - } catch (err: CommandFailed) { + } catch (err: CommandError) { logger.warning("Failed to run cargo fmt: [${service.id}]\n${err.output}") } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt index 4cf45a49ef..8c48d4ce1d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt @@ -206,7 +206,7 @@ private class ApiKeyConfigCustomization(codegenContext: ClientCodegenContext) : """ /// Returns API key used by the client, if it was provided. pub fn api_key(&self) -> #{Option}<&#{ApiKey}> { - self.inner.load::<#{ApiKey}>() + self.config.load::<#{ApiKey}>() } """, *codegenScope, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ConnectionPoisoningConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ConnectionPoisoningConfigCustomization.kt index eaed687aeb..cd797f938f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ConnectionPoisoningConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ConnectionPoisoningConfigCustomization.kt @@ -20,7 +20,7 @@ class ConnectionPoisoningRuntimePluginCustomization( override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { - is ServiceRuntimePluginSection.RegisterInterceptor -> { + is ServiceRuntimePluginSection.RegisterRuntimeComponents -> { // This interceptor assumes that a compatible Connector is set. Otherwise, connection poisoning // won't work and an error message will be logged. section.registerInterceptor(runtimeConfig, this) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index 4a8f6057f5..ead864dc32 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -32,7 +32,7 @@ import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.letIf -fun codegenScope(runtimeConfig: RuntimeConfig): Array> { +private fun codegenScope(runtimeConfig: RuntimeConfig): Array> { val smithyRuntime = CargoDependency.smithyRuntime(runtimeConfig).withFeature("http-auth").toType() val smithyRuntimeApi = CargoDependency.smithyRuntimeApi(runtimeConfig).withFeature("http-auth").toType() @@ -42,7 +42,6 @@ fun codegenScope(runtimeConfig: RuntimeConfig): Array> { "AuthSchemeId" to smithyRuntimeApi.resolve("client::auth::AuthSchemeId"), "ApiKeyAuthScheme" to authHttp.resolve("ApiKeyAuthScheme"), "ApiKeyLocation" to authHttp.resolve("ApiKeyLocation"), - "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "BasicAuthScheme" to authHttp.resolve("BasicAuthScheme"), "BearerAuthScheme" to authHttp.resolve("BearerAuthScheme"), "DigestAuthScheme" to authHttp.resolve("DigestAuthScheme"), @@ -163,9 +162,9 @@ private class HttpAuthServiceRuntimePluginCustomization( override fun section(section: ServiceRuntimePluginSection): Writable = writable { when (section) { - is ServiceRuntimePluginSection.AdditionalConfig -> { + is ServiceRuntimePluginSection.RegisterRuntimeComponents -> { fun registerAuthScheme(scheme: Writable) { - section.registerHttpAuthScheme(this, codegenContext.runtimeConfig) { + section.registerHttpAuthScheme(this) { rustTemplate("#{SharedHttpAuthScheme}::new(#{Scheme})", *codegenScope, "Scheme" to scheme) } } @@ -236,8 +235,7 @@ private class HttpAuthConfigCustomization( /// Sets an API key resolver will be used for authentication. pub fn api_key_resolver(mut self, api_key_resolver: impl #{IdentityResolver} + 'static) -> Self { - #{ConfigBagAccessors}::push_identity_resolver( - &mut self.inner, + self.runtime_components.push_identity_resolver( #{HTTP_API_KEY_AUTH_SCHEME_ID}, #{SharedIdentityResolver}::new(api_key_resolver) ); @@ -257,8 +255,7 @@ private class HttpAuthConfigCustomization( /// Sets a bearer token provider that will be used for HTTP bearer auth. pub fn bearer_token_resolver(mut self, bearer_token_resolver: impl #{IdentityResolver} + 'static) -> Self { - #{ConfigBagAccessors}::push_identity_resolver( - &mut self.inner, + self.runtime_components.push_identity_resolver( #{HTTP_BEARER_AUTH_SCHEME_ID}, #{SharedIdentityResolver}::new(bearer_token_resolver) ); @@ -278,8 +275,7 @@ private class HttpAuthConfigCustomization( /// Sets a login resolver that will be used for HTTP basic auth. pub fn basic_auth_login_resolver(mut self, basic_auth_resolver: impl #{IdentityResolver} + 'static) -> Self { - #{ConfigBagAccessors}::push_identity_resolver( - &mut self.inner, + self.runtime_components.push_identity_resolver( #{HTTP_BASIC_AUTH_SCHEME_ID}, #{SharedIdentityResolver}::new(basic_auth_resolver) ); @@ -299,8 +295,7 @@ private class HttpAuthConfigCustomization( /// Sets a login resolver that will be used for HTTP digest auth. pub fn digest_auth_login_resolver(mut self, digest_auth_resolver: impl #{IdentityResolver} + 'static) -> Self { - #{ConfigBagAccessors}::push_identity_resolver( - &mut self.inner, + self.runtime_components.push_identity_resolver( #{HTTP_DIGEST_AUTH_SCHEME_ID}, #{SharedIdentityResolver}::new(digest_auth_resolver) ); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt index fc5c1d6298..e654b48421 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpChecksumRequiredGenerator.kt @@ -40,7 +40,7 @@ class HttpChecksumRequiredGenerator( is OperationSection.AdditionalRuntimePlugins -> writable { section.addOperationRuntimePlugin(this) { rustTemplate( - "#{HttpChecksumRequiredRuntimePlugin}", + "#{HttpChecksumRequiredRuntimePlugin}::new()", "HttpChecksumRequiredRuntimePlugin" to InlineDependency.forRustFile( RustModule.pubCrate("client_http_checksum_required", parent = ClientRustModule.root), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index 1153c27770..e2d71aeae7 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations 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.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig @@ -40,13 +41,52 @@ private class HttpConnectorConfigCustomization( *preludeScope, "Connection" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::Connection"), "ConnectorSettings" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::ConnectorSettings"), - "DynConnector" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::connectors::DynConnector"), + "default_connector" to RuntimeType.smithyClient(runtimeConfig).resolve("conns::default_connector"), "DynConnectorAdapter" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::connectors::adapter::DynConnectorAdapter"), "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), + "Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"), "SharedAsyncSleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep::SharedAsyncSleep"), + "SharedConnector" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::connectors::SharedConnector"), "TimeoutConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout::TimeoutConfig"), ) + private fun setConnectorFn(): RuntimeType = RuntimeType.forInlineFun("set_connector", ClientRustModule.config) { + rustTemplate( + """ + fn set_connector(resolver: &mut #{Resolver}<'_>) { + // Initial configuration needs to set a default if no connector is given, so it + // should always get into the condition below. + // + // Override configuration should set the connector if the override config + // contains a connector, sleep impl, or a timeout config since these are all + // incorporated into the final connector. + let must_set_connector = resolver.is_initial() + || resolver.is_latest_set::<#{HttpConnector}>() + || resolver.latest_sleep_impl().is_some() + || resolver.is_latest_set::<#{TimeoutConfig}>(); + if must_set_connector { + let sleep_impl = resolver.sleep_impl(); + let timeout_config = resolver.resolve_config::<#{TimeoutConfig}>() + .cloned() + .unwrap_or_else(#{TimeoutConfig}::disabled); + let connector_settings = #{ConnectorSettings}::from_timeout_config(&timeout_config); + let http_connector = resolver.resolve_config::<#{HttpConnector}>(); + + // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation + let connector = + http_connector + .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) + .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) + .map(|c| #{SharedConnector}::new(#{DynConnectorAdapter}::new(c))); + + resolver.runtime_components_mut().set_connector(connector); + } + } + """, + *codegenScope, + ) + } + override fun section(section: ServiceConfig): Writable { return when (section) { is ServiceConfig.ConfigStruct -> writable { @@ -59,9 +99,15 @@ private class HttpConnectorConfigCustomization( if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ + // TODO(enableNewSmithyRuntimeCleanup): Remove this function /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. pub fn http_connector(&self) -> Option<&#{HttpConnector}> { - self.inner.load::<#{HttpConnector}>() + self.config.load::<#{HttpConnector}>() + } + + /// Return the [`SharedConnector`](#{SharedConnector}) to use when making requests, if any. + pub fn connector(&self) -> Option<#{SharedConnector}> { + self.runtime_components.connector() } """, *codegenScope, @@ -164,12 +210,11 @@ private class HttpConnectorConfigCustomization( """, *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ pub fn set_http_connector(&mut self, http_connector: Option>) -> &mut Self { - http_connector.map(|c| self.inner.store_put(c.into())); + http_connector.map(|c| self.config.store_put(c.into())); self } """, @@ -191,26 +236,8 @@ private class HttpConnectorConfigCustomization( is ServiceConfig.BuilderBuild -> writable { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - """ - let sleep_impl = layer.load::<#{SharedAsyncSleep}>().cloned(); - let timeout_config = layer.load::<#{TimeoutConfig}>().cloned().unwrap_or_else(#{TimeoutConfig}::disabled); - - let connector_settings = #{ConnectorSettings}::from_timeout_config(&timeout_config); - - if let Some(connector) = layer.load::<#{HttpConnector}>() - .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) - .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) { - let connector: #{DynConnector} = #{DynConnector}::new(#{DynConnectorAdapter}::new( - // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation - connector - )); - #{ConfigBagAccessors}::set_connector(&mut layer, connector); - } - - """, - *codegenScope, - "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), - "default_connector" to RuntimeType.smithyClient(runtimeConfig).resolve("conns::default_connector"), + "#{set_connector}(&mut resolver);", + "set_connector" to setConnectorFn(), ) } else { rust("http_connector: self.http_connector,") @@ -220,38 +247,8 @@ private class HttpConnectorConfigCustomization( is ServiceConfig.OperationConfigOverride -> writable { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - """ - if let #{Some}(http_connector) = - layer.load::<#{HttpConnector}>() - { - let sleep_impl = layer - .load::<#{SharedAsyncSleep}>() - .or_else(|| { - self.client_config - .load::<#{SharedAsyncSleep}>() - }) - .cloned(); - let timeout_config = layer - .load::<#{TimeoutConfig}>() - .or_else(|| { - self.client_config - .load::<#{TimeoutConfig}>() - }) - .expect("timeout config should be set either in `config_override` or in the client config"); - let connector_settings = - #{ConnectorSettings}::from_timeout_config( - timeout_config, - ); - if let #{Some}(conn) = http_connector.connector(&connector_settings, sleep_impl) { - let connection: #{DynConnector} = #{DynConnector}::new(#{DynConnectorAdapter}::new( - // TODO(enableNewSmithyRuntimeCleanup): Replace the tower-based DynConnector and remove DynConnectorAdapter when deleting the middleware implementation - conn - )); - layer.set_connector(connection); - } - } - """, - *codegenScope, + "#{set_connector}(&mut resolver);", + "set_connector" to setConnectorFn(), ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt deleted file mode 100644 index d08afe493b..0000000000 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/IdentityConfigCustomization.kt +++ /dev/null @@ -1,32 +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.customizations - -import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -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.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 - -class IdentityConfigCustomization(private val codegenContext: ClientCodegenContext) : ConfigCustomization() { - override fun section(section: ServiceConfig): Writable = writable { - if (section is ServiceConfig.ConfigImpl) { - rustTemplate( - """ - /// Returns the identity resolvers. - pub fn identity_resolvers(&self) -> #{IdentityResolvers} { - #{ConfigBagAccessors}::identity_resolvers(&self.inner) - } - """, - "IdentityResolvers" to RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) - .resolve("client::identity::IdentityResolvers"), - "ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig), - ) - } - } -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt index 692b874517..2dfec5159e 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt @@ -32,23 +32,12 @@ class InterceptorConfigCustomization(codegenContext: ClientCodegenContext) : Con override fun section(section: ServiceConfig) = writable { when (section) { - ServiceConfig.ConfigStruct -> rustTemplate( - "pub(crate) interceptors: Vec<#{SharedInterceptor}>,", - *codegenScope, - ) - - ServiceConfig.BuilderStruct -> - rustTemplate( - "interceptors: Vec<#{SharedInterceptor}>,", - *codegenScope, - ) - ServiceConfig.ConfigImpl -> rustTemplate( """ #{maybe_hide_orchestrator_code} /// Returns interceptors currently registered by the user. - pub fn interceptors(&self) -> impl Iterator + '_ { - self.interceptors.iter() + pub fn interceptors(&self) -> impl Iterator + '_ { + self.runtime_components.interceptors() } """, *codegenScope, @@ -103,7 +92,7 @@ class InterceptorConfigCustomization(codegenContext: ClientCodegenContext) : Con /// ## } /// ``` pub fn interceptor(mut self, interceptor: impl #{Interceptor} + Send + Sync + 'static) -> Self { - self.add_interceptor(#{SharedInterceptor}::new(interceptor)); + self.push_interceptor(#{SharedInterceptor}::new(interceptor)); self } @@ -146,7 +135,7 @@ class InterceptorConfigCustomization(codegenContext: ClientCodegenContext) : Con /// Ok(()) /// } /// } - /// builder.add_interceptor(SharedInterceptor::new(UriModifierInterceptor)); + /// builder.push_interceptor(SharedInterceptor::new(UriModifierInterceptor)); /// } /// /// let mut builder = Config::builder(); @@ -155,29 +144,21 @@ class InterceptorConfigCustomization(codegenContext: ClientCodegenContext) : Con /// ## } /// ## } /// ``` - pub fn add_interceptor(&mut self, interceptor: #{SharedInterceptor}) -> &mut Self { - self.interceptors.push(interceptor); + pub fn push_interceptor(&mut self, interceptor: #{SharedInterceptor}) -> &mut Self { + self.runtime_components.push_interceptor(interceptor); self } #{maybe_hide_orchestrator_code} /// Set [`SharedInterceptor`](#{SharedInterceptor})s for the builder. pub fn set_interceptors(&mut self, interceptors: impl IntoIterator) -> &mut Self { - self.interceptors = interceptors.into_iter().collect(); + self.runtime_components.set_interceptors(interceptors.into_iter()); self } """, *codegenScope, ) - is ServiceConfig.RuntimePluginInterceptors -> rust( - """ - ${section.interceptors}.extend(${section.interceptorsField}.interceptors.iter().cloned()); - """, - ) - - is ServiceConfig.BuilderBuildExtras -> rust("interceptors: self.interceptors,") - else -> emptySection } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index dc20049c46..fc74d456c3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -30,21 +30,21 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon private val moduleUseName = codegenContext.moduleUseName() private val codegenScope = arrayOf( *preludeScope, - "DynRetryStrategy" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::retries::DynRetryStrategy"), + "ClientRateLimiter" to retries.resolve("ClientRateLimiter"), + "ClientRateLimiterPartition" to retries.resolve("ClientRateLimiterPartition"), + "debug" to RuntimeType.Tracing.resolve("debug"), "RetryConfig" to retryConfig.resolve("RetryConfig"), + "RetryMode" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::RetryMode"), + "RetryPartition" to retries.resolve("RetryPartition"), "SharedAsyncSleep" to sleepModule.resolve("SharedAsyncSleep"), + "SharedRetryStrategy" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::retries::SharedRetryStrategy"), + "SharedTimeSource" to RuntimeType.smithyAsync(runtimeConfig).resolve("time::SharedTimeSource"), "Sleep" to sleepModule.resolve("Sleep"), "StandardRetryStrategy" to retries.resolve("strategy::StandardRetryStrategy"), "SystemTime" to RuntimeType.std.resolve("time::SystemTime"), "TimeoutConfig" to timeoutModule.resolve("TimeoutConfig"), - "RetryMode" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::RetryMode"), "TokenBucket" to retries.resolve("TokenBucket"), - "ClientRateLimiter" to retries.resolve("ClientRateLimiter"), - "SharedTimeSource" to RuntimeType.smithyAsync(runtimeConfig).resolve("time::SharedTimeSource"), - "ClientRateLimiterPartition" to retries.resolve("ClientRateLimiterPartition"), "TokenBucketPartition" to retries.resolve("TokenBucketPartition"), - "RetryPartition" to retries.resolve("RetryPartition"), - "debug" to RuntimeType.Tracing.resolve("debug"), ) override fun section(section: ServiceConfig) = @@ -69,17 +69,17 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon """ /// Return a reference to the retry configuration contained in this config, if any. pub fn retry_config(&self) -> #{Option}<&#{RetryConfig}> { - self.inner.load::<#{RetryConfig}>() + self.config.load::<#{RetryConfig}>() } /// Return a cloned shared async sleep implementation from this config, if any. pub fn sleep_impl(&self) -> #{Option}<#{SharedAsyncSleep}> { - self.inner.load::<#{SharedAsyncSleep}>().cloned() + self.runtime_components.sleep_impl() } /// Return a reference to the timeout configuration contained in this config, if any. pub fn timeout_config(&self) -> #{Option}<&#{TimeoutConfig}> { - self.inner.load::<#{TimeoutConfig}>() + self.config.load::<#{TimeoutConfig}>() } ##[doc(hidden)] @@ -88,7 +88,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon /// WARNING: This method is unstable and may be removed at any time. Do not rely on this /// method for anything! pub fn retry_partition(&self) -> #{Option}<&#{RetryPartition}> { - self.inner.load::<#{RetryPartition}>() + self.config.load::<#{RetryPartition}>() } """, *codegenScope, @@ -171,7 +171,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon rustTemplate( """ pub fn set_retry_config(&mut self, retry_config: #{Option}<#{RetryConfig}>) -> &mut Self { - retry_config.map(|r| self.inner.store_put(r)); + retry_config.map(|r| self.config.store_put(r)); self } """, @@ -249,7 +249,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon rustTemplate( """ pub fn set_sleep_impl(&mut self, sleep_impl: #{Option}<#{SharedAsyncSleep}>) -> &mut Self { - sleep_impl.map(|s| self.inner.store_put(s)); + self.runtime_components.set_sleep_impl(sleep_impl); self } """, @@ -317,7 +317,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon rustTemplate( """ pub fn set_timeout_config(&mut self, timeout_config: #{Option}<#{TimeoutConfig}>) -> &mut Self { - timeout_config.map(|t| self.inner.store_put(t)); + timeout_config.map(|t| self.config.store_put(t)); self } """, @@ -357,7 +357,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon /// also share things like token buckets and client rate limiters. By default, all clients /// for the same service will share a partition. pub fn set_retry_partition(&mut self, retry_partition: #{Option}<#{RetryPartition}>) -> &mut Self { - retry_partition.map(|r| self.inner.store_put(r)); + retry_partition.map(|r| self.config.store_put(r)); self } """, @@ -377,7 +377,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon } if retry_config.mode() == #{RetryMode}::Adaptive { - if let #{Some}(time_source) = layer.load::<#{SharedTimeSource}>().cloned() { + if let #{Some}(time_source) = self.runtime_components.time_source() { let seconds_since_unix_epoch = time_source .now() .duration_since(#{SystemTime}::UNIX_EPOCH) @@ -395,13 +395,16 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon let token_bucket_partition = #{TokenBucketPartition}::new(retry_partition); let token_bucket = TOKEN_BUCKET.get_or_init(token_bucket_partition, #{TokenBucket}::default); layer.store_put(token_bucket); - layer.set_retry_strategy(#{DynRetryStrategy}::new(#{StandardRetryStrategy}::new(&retry_config))); // TODO(enableNewSmithyRuntimeCleanup): Should not need to provide a default once smithy-rs##2770 // is resolved if layer.load::<#{TimeoutConfig}>().is_none() { layer.store_put(#{TimeoutConfig}::disabled()); } + + self.runtime_components.set_retry_strategy(#{Some}( + #{SharedRetryStrategy}::new(#{StandardRetryStrategy}::new(&retry_config))) + ); """, *codegenScope, ) @@ -420,24 +423,6 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon } } - is ServiceConfig.OperationConfigOverride -> { - if (runtimeMode.defaultToOrchestrator) { - rustTemplate( - """ - if let #{Some}(retry_config) = layer - .load::<#{RetryConfig}>() - .cloned() - { - layer.set_retry_strategy( - #{DynRetryStrategy}::new(#{StandardRetryStrategy}::new(&retry_config)) - ); - } - """, - *codegenScope, - ) - } - } - else -> emptySection } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt index 0dacbd7290..44579aec51 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt @@ -42,16 +42,16 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust is ServiceConfig.ConfigImpl -> { rust("/// Return time source used for this service.") rustBlockTemplate( - "pub fn time_source(&self) -> #{SharedTimeSource}", + "pub fn time_source(&self) -> #{Option}<#{SharedTimeSource}>", *codegenScope, ) { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - """self.inner.load::<#{SharedTimeSource}>().expect("time source should be set").clone()""", + """self.runtime_components.time_source()""", *codegenScope, ) } else { - rust("self.time_source.clone()") + rustTemplate("#{Some}(self.time_source.clone())", *codegenScope) } } } @@ -88,7 +88,7 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust &mut self, time_source: #{Option}<#{SharedTimeSource}>, ) -> &mut Self { - self.inner.store_or_unset(time_source); + self.runtime_components.set_time_source(time_source); self } """, @@ -114,7 +114,11 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust ServiceConfig.BuilderBuild -> { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - "layer.store_put(layer.load::<#{SharedTimeSource}>().cloned().unwrap_or_default());", + """ + if self.runtime_components.time_source().is_none() { + self.runtime_components.set_time_source(#{Default}::default()); + } + """, *codegenScope, ) } else { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt index 9c64b5e8fd..46de4940a0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/RequiredCustomizations.kt @@ -13,7 +13,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.customizations.Endpoint import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpChecksumRequiredGenerator import software.amazon.smithy.rust.codegen.client.smithy.customizations.HttpVersionListCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.IdempotencyTokenGenerator -import software.amazon.smithy.rust.codegen.client.smithy.customizations.IdentityConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.InterceptorConfigCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.MetadataCustomization import software.amazon.smithy.rust.codegen.client.smithy.customizations.ResiliencyConfigCustomization @@ -66,8 +65,7 @@ class RequiredCustomizations : ClientCodegenDecorator { baseCustomizations + ResiliencyConfigCustomization(codegenContext) + InterceptorConfigCustomization(codegenContext) + - TimeSourceCustomization(codegenContext) + - IdentityConfigCustomization(codegenContext) + TimeSourceCustomization(codegenContext) } else { baseCustomizations + ResiliencyConfigCustomization(codegenContext) + diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index a55c5b3e7d..81b89d989d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -28,18 +28,20 @@ internal class EndpointConfigCustomization( private val runtimeMode = codegenContext.smithyRuntimeMode private val types = Types(runtimeConfig) + private val codegenScope = arrayOf( + *preludeScope, + "DefaultEndpointResolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::endpoints::DefaultEndpointResolver"), + "OldSharedEndpointResolver" to types.sharedEndpointResolver, + "Params" to typesGenerator.paramsStruct(), + "Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"), + "SharedEndpointResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::SharedEndpointResolver"), + "SmithyResolver" to types.resolveEndpoint, + ) + override fun section(section: ServiceConfig): Writable { return writable { - val sharedEndpointResolver = "#{SharedEndpointResolver}<#{Params}>" + val sharedEndpointResolver = "#{OldSharedEndpointResolver}<#{Params}>" val resolverTrait = "#{SmithyResolver}<#{Params}>" - val codegenScope = arrayOf( - *preludeScope, - "DefaultEndpointResolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::endpoints::DefaultEndpointResolver"), - "DynEndpointResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::DynEndpointResolver"), - "SharedEndpointResolver" to types.sharedEndpointResolver, - "SmithyResolver" to types.resolveEndpoint, - "Params" to typesGenerator.paramsStruct(), - ) when (section) { is ServiceConfig.ConfigStruct -> { if (runtimeMode.defaultToMiddleware) { @@ -55,8 +57,8 @@ internal class EndpointConfigCustomization( rustTemplate( """ /// Returns the endpoint resolver. - pub fn endpoint_resolver(&self) -> $sharedEndpointResolver { - self.inner.load::<$sharedEndpointResolver>().expect("endpoint resolver should be set").clone() + pub fn endpoint_resolver(&self) -> #{SharedEndpointResolver} { + self.runtime_components.endpoint_resolver().expect("resolver defaulted if not set") } """, *codegenScope, @@ -128,7 +130,7 @@ internal class EndpointConfigCustomization( /// Sets the endpoint resolver to use when making requests. $defaultResolverDocs pub fn endpoint_resolver(mut self, endpoint_resolver: impl $resolverTrait + 'static) -> Self { - self.set_endpoint_resolver(#{Some}(#{SharedEndpointResolver}::new(endpoint_resolver))); + self.set_endpoint_resolver(#{Some}(#{OldSharedEndpointResolver}::new(endpoint_resolver))); self } @@ -144,7 +146,7 @@ internal class EndpointConfigCustomization( rustTemplate( """ pub fn set_endpoint_resolver(&mut self, endpoint_resolver: #{Option}<$sharedEndpointResolver>) -> &mut Self { - self.inner.store_or_unset(endpoint_resolver); + self.config.store_or_unset(endpoint_resolver); self } """, @@ -164,96 +166,29 @@ internal class EndpointConfigCustomization( } ServiceConfig.BuilderBuild -> { - val defaultResolver = typesGenerator.defaultResolver() - if (defaultResolver != null) { - if (runtimeMode.defaultToOrchestrator) { - rustTemplate( - // TODO(enableNewSmithyRuntimeCleanup): Simplify the endpoint resolvers - """ - let endpoint_resolver = #{DynEndpointResolver}::new( - #{DefaultEndpointResolver}::<#{Params}>::new( - layer.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(|| - #{SharedEndpointResolver}::new(#{DefaultResolver}::new()) - ) - ) - ); - layer.set_endpoint_resolver(endpoint_resolver); - """, - *codegenScope, - "DefaultResolver" to defaultResolver, - ) - } else { - rustTemplate( - """ - endpoint_resolver: self.endpoint_resolver.unwrap_or_else(|| - #{SharedEndpointResolver}::new(#{DefaultResolver}::new()) - ), - """, - *codegenScope, - "DefaultResolver" to defaultResolver, - ) - } + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + "#{set_endpoint_resolver}(&mut resolver);", + "set_endpoint_resolver" to setEndpointResolverFn(), + ) } else { - val alwaysFailsResolver = - RuntimeType.forInlineFun("MissingResolver", ClientRustModule.endpoint(codegenContext)) { - rustTemplate( - """ - ##[derive(Debug)] - pub(crate) struct MissingResolver; - impl #{ResolveEndpoint} for MissingResolver { - fn resolve_endpoint(&self, _params: &T) -> #{Result} { - Err(#{ResolveEndpointError}::message("an endpoint resolver must be provided.")) - } - } - """, - "ResolveEndpoint" to types.resolveEndpoint, - "ResolveEndpointError" to types.resolveEndpointError, - "Result" to types.smithyHttpEndpointModule.resolve("Result"), - ) - } - // To keep this diff under control, rather than `.expect` here, insert a resolver that will - // always fail. In the future, this will be changed to an `expect()` - if (runtimeMode.defaultToOrchestrator) { - rustTemplate( - """ - let endpoint_resolver = #{DynEndpointResolver}::new( - #{DefaultEndpointResolver}::<#{Params}>::new( - layer.load::<$sharedEndpointResolver>().cloned().unwrap_or_else(|| - #{SharedEndpointResolver}::new(#{FailingResolver}) - ).clone() - ) - ); - layer.set_endpoint_resolver(endpoint_resolver); - """, - *codegenScope, - "FailingResolver" to alwaysFailsResolver, - ) - } else { - rustTemplate( - """ - endpoint_resolver: self.endpoint_resolver.unwrap_or_else(||#{SharedEndpointResolver}::new(#{FailingResolver})), - """, - *codegenScope, - "FailingResolver" to alwaysFailsResolver, - ) - } + rustTemplate( + """ + endpoint_resolver: self.endpoint_resolver.unwrap_or_else(|| + #{OldSharedEndpointResolver}::new(#{DefaultResolver}::new()) + ), + """, + *codegenScope, + "DefaultResolver" to defaultResolver(), + ) } } is ServiceConfig.OperationConfigOverride -> { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - """ - if let #{Some}(resolver) = layer - .load::<$sharedEndpointResolver>() - .cloned() - { - let endpoint_resolver = #{DynEndpointResolver}::new( - #{DefaultEndpointResolver}::<#{Params}>::new(resolver)); - layer.set_endpoint_resolver(endpoint_resolver); - } - """, - *codegenScope, + "#{set_endpoint_resolver}(&mut resolver);", + "set_endpoint_resolver" to setEndpointResolverFn(), ) } } @@ -262,4 +197,58 @@ internal class EndpointConfigCustomization( } } } + + private fun defaultResolver(): RuntimeType { + // For now, fallback to a default endpoint resolver that always fails. In the future, + // the endpoint resolver will be required (so that it can be unwrapped). + return typesGenerator.defaultResolver() ?: RuntimeType.forInlineFun( + "MissingResolver", + ClientRustModule.endpoint(codegenContext), + ) { + rustTemplate( + """ + ##[derive(Debug)] + pub(crate) struct MissingResolver; + impl MissingResolver { + pub(crate) fn new() -> Self { Self } + } + impl #{ResolveEndpoint} for MissingResolver { + fn resolve_endpoint(&self, _params: &T) -> #{Result} { + Err(#{ResolveEndpointError}::message("an endpoint resolver must be provided.")) + } + } + """, + "ResolveEndpoint" to types.resolveEndpoint, + "ResolveEndpointError" to types.resolveEndpointError, + "Result" to types.smithyHttpEndpointModule.resolve("Result"), + ) + } + } + + private fun setEndpointResolverFn(): RuntimeType = RuntimeType.forInlineFun("set_endpoint_resolver", ClientRustModule.config) { + // TODO(enableNewSmithyRuntimeCleanup): Simplify the endpoint resolvers + rustTemplate( + """ + fn set_endpoint_resolver(resolver: &mut #{Resolver}<'_>) { + let endpoint_resolver = if resolver.is_initial() { + Some(resolver.resolve_config::<#{OldSharedEndpointResolver}<#{Params}>>().cloned().unwrap_or_else(|| + #{OldSharedEndpointResolver}::new(#{DefaultResolver}::new()) + )) + } else if resolver.is_latest_set::<#{OldSharedEndpointResolver}<#{Params}>>() { + resolver.resolve_config::<#{OldSharedEndpointResolver}<#{Params}>>().cloned() + } else { + None + }; + if let Some(endpoint_resolver) = endpoint_resolver { + let shared = #{SharedEndpointResolver}::new( + #{DefaultEndpointResolver}::<#{Params}>::new(endpoint_resolver) + ); + resolver.runtime_components_mut().set_endpoint_resolver(#{Some}(shared)); + } + } + """, + *codegenScope, + "DefaultResolver" to defaultResolver(), + ) + } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt index edfe94af95..5a37375e56 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt @@ -23,12 +23,15 @@ class ConfigOverrideRuntimePluginGenerator( val smithyTypes = RuntimeType.smithyTypes(rc) arrayOf( *RuntimeType.preludeScope, + "Cow" to RuntimeType.Cow, "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), "ConfigBagAccessors" to runtimeApi.resolve("client::config_bag_accessors::ConfigBagAccessors"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "Layer" to smithyTypes.resolve("config_bag::Layer"), - "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), + "Resolver" to RuntimeType.smithyRuntime(rc).resolve("client::config_override::Resolver"), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(rc), + "RuntimePlugin" to RuntimeType.runtimePlugin(rc), ) } @@ -41,31 +44,40 @@ class ConfigOverrideRuntimePluginGenerator( /// In the case of default values requested, they will be obtained from `client_config`. ##[derive(Debug)] pub(crate) struct ConfigOverrideRuntimePlugin { - pub(crate) config_override: Builder, - pub(crate) client_config: #{FrozenLayer}, + pub(crate) config: #{FrozenLayer}, + pub(crate) components: #{RuntimeComponentsBuilder}, } - impl #{RuntimePlugin} for ConfigOverrideRuntimePlugin { - fn config(&self) -> #{Option}<#{FrozenLayer}> { - use #{ConfigBagAccessors}; + impl ConfigOverrideRuntimePlugin { + pub(crate) fn new( + config_override: Builder, + initial_config: #{FrozenLayer}, + initial_components: &#{RuntimeComponentsBuilder} + ) -> Self { + let mut layer = #{Layer}::from(config_override.config) + .with_name("$moduleUseName::config::ConfigOverrideRuntimePlugin"); + let mut components = config_override.runtime_components; + let mut resolver = #{Resolver}::overrid(initial_config, initial_components, &mut layer, &mut components); - ##[allow(unused_mut)] - let layer: #{Layer} = self - .config_override - .inner - .clone() - .into(); - let mut layer = layer.with_name("$moduleUseName::config::ConfigOverrideRuntimePlugin"); #{config} - #{Some}(layer.freeze()) + let _ = resolver; + Self { + config: layer.freeze(), + components, + } } + } - fn interceptors(&self, _interceptors: &mut #{InterceptorRegistrar}) { - #{interceptors} + impl #{RuntimePlugin} for ConfigOverrideRuntimePlugin { + fn config(&self) -> #{Option}<#{FrozenLayer}> { + Some(self.config.clone()) } - } + fn runtime_components(&self) -> #{Cow}<'_, #{RuntimeComponentsBuilder}> { + #{Cow}::Borrowed(&self.components) + } + } """, *codegenScope, "config" to writable { @@ -74,9 +86,6 @@ class ConfigOverrideRuntimePluginGenerator( ServiceConfig.OperationConfigOverride("layer"), ) }, - "interceptors" to writable { - writeCustomizations(customizations, ServiceConfig.RuntimePluginInterceptors("_interceptors", "self.config_override")) - }, ) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt index 87fead7142..90552c3659 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationCustomization.kt @@ -107,14 +107,17 @@ sealed class OperationSection(name: String) : Section(name) { data class AdditionalInterceptors( override val customizations: List, - val interceptorRegistrarName: String, val operationShape: OperationShape, ) : OperationSection("AdditionalInterceptors") { fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { val smithyRuntimeApi = RuntimeType.smithyRuntimeApi(runtimeConfig) writer.rustTemplate( """ - $interceptorRegistrarName.register(#{SharedInterceptor}::new(#{interceptor}) as _); + .with_interceptor( + #{SharedInterceptor}::new( + #{interceptor} + ) as _ + ) """, "interceptor" to interceptor, "SharedInterceptor" to smithyRuntimeApi.resolve("client::interceptors::SharedInterceptor"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index a0087b3d2a..39795ed503 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -18,6 +18,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.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.implBlock +import software.amazon.smithy.rust.codegen.core.rustlang.isNotEmpty import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate @@ -118,6 +119,12 @@ open class OperationGenerator( "SdkError" to RuntimeType.sdkError(runtimeConfig), ) if (codegenContext.smithyRuntimeMode.generateOrchestrator) { + val additionalPlugins = writable { + writeCustomizations( + operationCustomizations, + OperationSection.AdditionalRuntimePlugins(operationCustomizations, operationShape), + ) + } rustTemplate( """ pub(crate) async fn orchestrate( @@ -159,14 +166,16 @@ open class OperationGenerator( config_override: #{Option}, ) -> #{RuntimePlugins} { let mut runtime_plugins = client_runtime_plugins.with_operation_plugin(Self::new()); + #{additional_runtime_plugins} if let Some(config_override) = config_override { - runtime_plugins = runtime_plugins.with_operation_plugin(crate::config::ConfigOverrideRuntimePlugin { - config_override, - client_config: #{RuntimePlugin}::config(client_config).expect("frozen layer should exist in client config"), - }) + for plugin in config_override.runtime_plugins.iter().cloned() { + runtime_plugins = runtime_plugins.with_operation_plugin(plugin); + } + runtime_plugins = runtime_plugins.with_operation_plugin( + crate::config::ConfigOverrideRuntimePlugin::new(config_override, client_config.config.clone(), &client_config.runtime_components) + ); } runtime_plugins - #{additional_runtime_plugins} } """, *codegenScope, @@ -179,10 +188,15 @@ open class OperationGenerator( "StopPoint" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::StopPoint"), "invoke_with_stop_point" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::invoke_with_stop_point"), "additional_runtime_plugins" to writable { - writeCustomizations( - operationCustomizations, - OperationSection.AdditionalRuntimePlugins(operationCustomizations, operationShape), - ) + if (additionalPlugins.isNotEmpty()) { + rustTemplate( + """ + runtime_plugins = runtime_plugins + #{additional_runtime_plugins}; + """, + "additional_runtime_plugins" to additionalPlugins, + ) + } }, ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index febdb95825..a798a9753b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -34,17 +34,19 @@ class OperationRuntimePluginGenerator( val runtimeApi = RuntimeType.smithyRuntimeApi(rc) val smithyTypes = RuntimeType.smithyTypes(rc) arrayOf( + *preludeScope, "AuthOptionResolverParams" to runtimeApi.resolve("client::auth::AuthOptionResolverParams"), "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), "ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig), - "DynAuthOptionResolver" to runtimeApi.resolve("client::auth::DynAuthOptionResolver"), + "Cow" to RuntimeType.Cow, + "SharedAuthOptionResolver" to runtimeApi.resolve("client::auth::SharedAuthOptionResolver"), "DynResponseDeserializer" to runtimeApi.resolve("client::orchestrator::DynResponseDeserializer"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), - "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "Layer" to smithyTypes.resolve("config_bag::Layer"), "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), - "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), + "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(codegenContext.runtimeConfig), "SharedRequestSerializer" to runtimeApi.resolve("client::orchestrator::SharedRequestSerializer"), "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), "StaticAuthOptionResolverParams" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolverParams"), @@ -68,22 +70,25 @@ class OperationRuntimePluginGenerator( cfg.set_request_serializer(#{SharedRequestSerializer}::new(${operationStructName}RequestSerializer)); cfg.set_response_deserializer(#{DynResponseDeserializer}::new(${operationStructName}ResponseDeserializer)); - // Retry classifiers are operation-specific because they need to downcast operation-specific error types. - let retry_classifiers = #{RetryClassifiers}::new() - #{retry_classifier_customizations}; - cfg.set_retry_classifiers(retry_classifiers); - ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StaticAuthOptionResolverParams}::new())); - #{auth_options} #{additional_config} Some(cfg.freeze()) } - fn interceptors(&self, _interceptors: &mut #{InterceptorRegistrar}) { - #{interceptors} + fn runtime_components(&self) -> #{Cow}<'_, #{RuntimeComponentsBuilder}> { + // Retry classifiers are operation-specific because they need to downcast operation-specific error types. + let retry_classifiers = #{RetryClassifiers}::new() + #{retry_classifier_customizations}; + + #{Cow}::Owned( + #{RuntimeComponentsBuilder}::new(${operationShape.id.name.dq()}) + .with_retry_classifiers(Some(retry_classifiers)) + #{auth_options} + #{interceptors} + ) } } @@ -117,7 +122,7 @@ class OperationRuntimePluginGenerator( "interceptors" to writable { writeCustomizations( customizations, - OperationSection.AdditionalInterceptors(customizations, "_interceptors", operationShape), + OperationSection.AdditionalInterceptors(customizations, operationShape), ) }, ) @@ -135,8 +140,12 @@ class OperationRuntimePluginGenerator( option.schemeShapeId to option } withBlockTemplate( - "cfg.set_auth_option_resolver(#{DynAuthOptionResolver}::new(#{StaticAuthOptionResolver}::new(vec![", - "])));", + """ + .with_auth_option_resolver(#{Some}( + #{SharedAuthOptionResolver}::new( + #{StaticAuthOptionResolver}::new(vec![ + """, + "]))))", *codegenScope, ) { var noSupportedAuthSchemes = true diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt index 1c85d9047b..01cecc49f3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceGenerator.kt @@ -54,7 +54,6 @@ class ServiceGenerator( ServiceRuntimePluginGenerator(codegenContext) .render(this, decorator.serviceRuntimePluginCustomizations(codegenContext, emptyList())) - serviceConfigGenerator.renderRuntimePluginImplForSelf(this) ConfigOverrideRuntimePluginGenerator(codegenContext) .render(this, decorator.configCustomizations(codegenContext, listOf())) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index c77e95940a..b5da3eaba9 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -36,43 +36,35 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { fun putConfigValue(writer: RustWriter, value: Writable) { writer.rust("$newLayerName.store_put(#T);", value) } + } - fun registerHttpAuthScheme(writer: RustWriter, runtimeConfig: RuntimeConfig, authScheme: Writable) { + data class RegisterRuntimeComponents(val serviceConfigName: String) : ServiceRuntimePluginSection("RegisterRuntimeComponents") { + /** Generates the code to register an interceptor */ + fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { writer.rustTemplate( """ - #{ConfigBagAccessors}::push_http_auth_scheme( - &mut $newLayerName, - #{auth_scheme} - ); + runtime_components.push_interceptor(#{SharedInterceptor}::new(#{interceptor}) as _); """, - "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), - "auth_scheme" to authScheme, + "interceptor" to interceptor, + "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::SharedInterceptor"), ) } - fun registerIdentityResolver(writer: RustWriter, runtimeConfig: RuntimeConfig, identityResolver: Writable) { + fun registerHttpAuthScheme(writer: RustWriter, authScheme: Writable) { writer.rustTemplate( """ - #{ConfigBagAccessors}::push_identity_resolver( - &mut $newLayerName, - #{identity_resolver} - ); + runtime_components.push_http_auth_scheme(#{auth_scheme}); """, - "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), - "identity_resolver" to identityResolver, + "auth_scheme" to authScheme, ) } - } - data class RegisterInterceptor(val interceptorRegistrarName: String) : ServiceRuntimePluginSection("RegisterInterceptor") { - /** Generates the code to register an interceptor */ - fun registerInterceptor(runtimeConfig: RuntimeConfig, writer: RustWriter, interceptor: Writable) { + fun registerIdentityResolver(writer: RustWriter, identityResolver: Writable) { writer.rustTemplate( """ - $interceptorRegistrarName.register(#{SharedInterceptor}::new(#{interceptor}) as _); + runtime_components.push_identity_resolver(#{identity_resolver}); """, - "interceptor" to interceptor, - "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::SharedInterceptor"), + "identity_resolver" to identityResolver, ) } } @@ -92,12 +84,11 @@ class ServiceRuntimePluginGenerator( *preludeScope, "Arc" to RuntimeType.Arc, "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), - "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), + "Cow" to RuntimeType.Cow, "Layer" to smithyTypes.resolve("config_bag::Layer"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), - "ConfigBagAccessors" to RuntimeType.configBagAccessors(rc), - "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), - "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(rc), + "RuntimePlugin" to RuntimeType.runtimePlugin(rc), ) } @@ -113,15 +104,15 @@ class ServiceRuntimePluginGenerator( ##[derive(Debug)] pub(crate) struct ServiceRuntimePlugin { config: #{Option}<#{FrozenLayer}>, + runtime_components: #{RuntimeComponentsBuilder}, } impl ServiceRuntimePlugin { pub fn new(_service_config: crate::config::Config) -> Self { - Self { - config: { - #{config} - }, - } + let config = { #{config} }; + let mut runtime_components = #{RuntimeComponentsBuilder}::new("ServiceRuntimePlugin"); + #{runtime_components} + Self { config, runtime_components } } } @@ -130,9 +121,8 @@ class ServiceRuntimePluginGenerator( self.config.clone() } - fn interceptors(&self, interceptors: &mut #{InterceptorRegistrar}) { - let _interceptors = interceptors; - #{additional_interceptors} + fn runtime_components(&self) -> #{Cow}<'_, #{RuntimeComponentsBuilder}> { + #{Cow}::Borrowed(&self.runtime_components) } } @@ -155,8 +145,8 @@ class ServiceRuntimePluginGenerator( rust("None") } }, - "additional_interceptors" to writable { - writeCustomizations(customizations, ServiceRuntimePluginSection.RegisterInterceptor("_interceptors")) + "runtime_components" to writable { + writeCustomizations(customizations, ServiceRuntimePluginSection.RegisterRuntimeComponents("_service_config")) }, "declare_singletons" to writable { writeCustomizations(customizations, ServiceRuntimePluginSection.DeclareSingletons()) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index c8afcb7a96..633a50985a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -156,6 +156,7 @@ class CustomizableOperationGenerator( "MutateRequestInterceptor" to RuntimeType.smithyRuntime(runtimeConfig) .resolve("client::interceptors::MutateRequestInterceptor"), "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "SharedRuntimePlugin" to RuntimeType.sharedRuntimePlugin(runtimeConfig), "SendResult" to ClientRustModule.Client.customize.toType() .resolve("internal::SendResult"), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), @@ -188,6 +189,7 @@ class CustomizableOperationGenerator( pub(crate) customizable_send: #{Box}>, pub(crate) config_override: #{Option}, pub(crate) interceptors: Vec<#{SharedInterceptor}>, + pub(crate) runtime_plugins: Vec<#{SharedRuntimePlugin}>, } impl CustomizableOperation { @@ -202,6 +204,13 @@ class CustomizableOperationGenerator( self } + /// Adds a runtime plugin. + ##[allow(unused)] + pub(crate) fn runtime_plugin(mut self, runtime_plugin: impl #{RuntimePlugin} + 'static) -> Self { + self.runtime_plugins.push(#{SharedRuntimePlugin}::new(runtime_plugin)); + self + } + /// Allows for customizing the operation's request. pub fn map_request(mut self, f: F) -> Self where @@ -264,7 +273,10 @@ class CustomizableOperationGenerator( }; self.interceptors.into_iter().for_each(|interceptor| { - config_override.add_interceptor(interceptor); + config_override.push_interceptor(interceptor); + }); + self.runtime_plugins.into_iter().for_each(|plugin| { + config_override.push_runtime_plugin(plugin); }); (self.customizable_send)(config_override).await diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 6fe9978f78..368d70ae43 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -543,6 +543,7 @@ class FluentClientGenerator( }), config_override: None, interceptors: vec![], + runtime_plugins: vec![], } } """, @@ -652,18 +653,30 @@ private fun baseClientRuntimePluginsFn(runtimeConfig: RuntimeConfig): RuntimeTyp rustTemplate( """ pub(crate) fn base_client_runtime_plugins( - config: crate::Config, + mut config: crate::Config, ) -> #{RuntimePlugins} { - #{RuntimePlugins}::new() - .with_client_plugin(config.clone()) + let mut configured_plugins = #{Vec}::new(); + ::std::mem::swap(&mut config.runtime_plugins, &mut configured_plugins); + let mut plugins = #{RuntimePlugins}::new() + .with_client_plugin( + #{StaticRuntimePlugin}::new() + .with_config(config.config.clone()) + .with_runtime_components(config.runtime_components.clone()) + ) .with_client_plugin(crate::config::ServiceRuntimePlugin::new(config)) - .with_client_plugin(#{NoAuthRuntimePlugin}::new()) + .with_client_plugin(#{NoAuthRuntimePlugin}::new()); + for plugin in configured_plugins { + plugins = plugins.with_client_plugin(plugin); + } + plugins } """, *preludeScope, "RuntimePlugins" to RuntimeType.runtimePlugins(runtimeConfig), "NoAuthRuntimePlugin" to RuntimeType.smithyRuntime(runtimeConfig) .resolve("client::auth::no_auth::NoAuthRuntimePlugin"), + "StaticRuntimePlugin" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_plugin::StaticRuntimePlugin"), ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt index 7e03e1ece5..58a3a67438 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt @@ -42,7 +42,7 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext /// If a random token provider was configured, /// a newly-randomized token provider will be returned. pub fn idempotency_token_provider(&self) -> #{IdempotencyTokenProvider} { - self.inner.load::<#{IdempotencyTokenProvider}>().expect("the idempotency provider should be set").clone() + self.config.load::<#{IdempotencyTokenProvider}>().expect("the idempotency provider should be set").clone() } """, *codegenScope, @@ -85,7 +85,7 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext """ /// Sets the idempotency token provider to use for service calls that require tokens. pub fn set_idempotency_token_provider(&mut self, idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>) -> &mut Self { - self.inner.store_or_unset(idempotency_token_provider); + self.config.store_or_unset(idempotency_token_provider); self } """, @@ -108,7 +108,11 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext ServiceConfig.BuilderBuild -> writable { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - "layer.store_put(layer.load::<#{IdempotencyTokenProvider}>().cloned().unwrap_or_else(#{default_provider}));", + """ + if !resolver.is_set::<#{IdempotencyTokenProvider}>() { + resolver.config_mut().store_put(#{default_provider}()); + } + """, *codegenScope, ) } else { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 148353269f..f3cbe633af 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -29,7 +29,6 @@ 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.smithy.customize.NamedCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.Section -import software.amazon.smithy.rust.codegen.core.smithy.customize.writeCustomizations import software.amazon.smithy.rust.codegen.core.smithy.makeOptional import software.amazon.smithy.rust.codegen.core.util.hasTrait import software.amazon.smithy.rust.codegen.core.util.letIf @@ -101,11 +100,6 @@ sealed class ServiceConfig(name: String) : Section(name) { */ data class OperationConfigOverride(val cfg: String) : ServiceConfig("ToRuntimePlugin") - /** - * A section for appending additional runtime plugins, stored in [interceptorsField], to [interceptors] - */ - data class RuntimePluginInterceptors(val interceptors: String, val interceptorsField: String) : ServiceConfig("ToRuntimePluginInterceptors") - /** * A section for extra functionality that needs to be defined with the config module */ @@ -234,7 +228,7 @@ fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext rustTemplate( """ pub fn set_${param.name}(&mut self, ${param.name}: Option<#{T}>) -> &mut Self { - self.inner.store_or_unset(${param.name}.map(#{newtype})); + self.config.store_or_unset(${param.name}.map(#{newtype})); self } """, @@ -261,8 +255,6 @@ fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext } } - is ServiceConfig.OperationConfigOverride -> emptySection - else -> emptySection } } @@ -312,18 +304,20 @@ class ServiceConfigGenerator( } } - private val runtimeApi = RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) private val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig) val codegenScope = arrayOf( + *preludeScope, "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), "ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig), + "Cow" to RuntimeType.Cow, "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), - "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "Layer" to smithyTypes.resolve("config_bag::Layer"), - "RuntimePlugin" to runtimeApi.resolve("client::runtime_plugin::RuntimePlugin"), - *preludeScope, + "Resolver" to RuntimeType.smithyRuntime(codegenContext.runtimeConfig).resolve("client::config_override::Resolver"), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(codegenContext.runtimeConfig), + "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), + "SharedRuntimePlugin" to RuntimeType.sharedRuntimePlugin(codegenContext.runtimeConfig), ) private val moduleUseName = codegenContext.moduleUseName() private val runtimeMode = codegenContext.smithyRuntimeMode @@ -334,10 +328,17 @@ class ServiceConfigGenerator( it.section(ServiceConfig.ConfigStructAdditionalDocs)(writer) } Attribute(Attribute.derive(RuntimeType.Clone)).render(writer) + if (runtimeMode.generateOrchestrator) { + Attribute(Attribute.derive(RuntimeType.Debug)).render(writer) + } writer.rustBlock("pub struct Config") { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - "inner: #{FrozenLayer},", + """ + pub(crate) config: #{FrozenLayer}, + pub(crate) runtime_components: #{RuntimeComponentsBuilder}, + pub(crate) runtime_plugins: #{Vec}<#{SharedRuntimePlugin}>, + """, *codegenScope, ) } @@ -346,16 +347,18 @@ class ServiceConfigGenerator( } } - // Custom implementation for Debug so we don't need to enforce Debug down the chain - writer.rustBlock("impl std::fmt::Debug for Config") { - writer.rustTemplate( - """ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut config = f.debug_struct("Config"); - config.finish() - } - """, - ) + if (runtimeMode.defaultToMiddleware) { + // Custom implementation for Debug so we don't need to enforce Debug down the chain + writer.rustBlock("impl std::fmt::Debug for Config") { + writer.rustTemplate( + """ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut config = f.debug_struct("Config"); + config.finish() + } + """, + ) + } } writer.rustBlock("impl Config") { @@ -372,10 +375,17 @@ class ServiceConfigGenerator( writer.docs("Builder for creating a `Config`.") writer.raw("#[derive(Clone, Default)]") + if (runtimeMode.defaultToOrchestrator) { + Attribute(Attribute.derive(RuntimeType.Debug)).render(writer) + } writer.rustBlock("pub struct Builder") { if (runtimeMode.defaultToOrchestrator) { rustTemplate( - "inner: #{CloneableLayer},", + """ + pub(crate) config: #{CloneableLayer}, + pub(crate) runtime_components: #{RuntimeComponentsBuilder}, + pub(crate) runtime_plugins: #{Vec}<#{SharedRuntimePlugin}>, + """, *codegenScope, ) } @@ -384,16 +394,18 @@ class ServiceConfigGenerator( } } - // Custom implementation for Debug so we don't need to enforce Debug down the chain - writer.rustBlock("impl std::fmt::Debug for Builder") { - writer.rustTemplate( - """ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut config = f.debug_struct("Builder"); - config.finish() - } - """, - ) + if (runtimeMode.defaultToMiddleware) { + // Custom implementation for Debug so we don't need to enforce Debug down the chain + writer.rustBlock("impl std::fmt::Debug for Builder") { + writer.rustTemplate( + """ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut config = f.debug_struct("Builder"); + config.finish() + } + """, + ) + } } writer.rustBlock("impl Builder") { @@ -403,6 +415,27 @@ class ServiceConfigGenerator( it.section(ServiceConfig.BuilderImpl)(this) } + if (runtimeMode.defaultToOrchestrator) { + rustTemplate( + """ + /// Adds a runtime plugin to the config. + ##[allow(unused)] + pub(crate) fn runtime_plugin(mut self, plugin: impl #{RuntimePlugin} + 'static) -> Self { + self.push_runtime_plugin(#{SharedRuntimePlugin}::new(plugin)); + self + } + + /// Adds a runtime plugin to the config. + ##[allow(unused)] + pub(crate) fn push_runtime_plugin(&mut self, plugin: #{SharedRuntimePlugin}) -> &mut Self { + self.runtime_plugins.push(plugin); + self + } + """, + *codegenScope, + ) + } + val testUtilOnly = Attribute(Attribute.cfg(Attribute.any(Attribute.feature(TestUtilFeature.name), writable("test")))) @@ -427,16 +460,13 @@ class ServiceConfigGenerator( rustBlock("pub fn build(mut self) -> Config") { rustTemplate( """ - ##[allow(unused_imports)] - use #{ConfigBagAccessors}; // The builder is being turned into a service config. While doing so, we'd like to avoid // requiring that items created and stored _during_ the build method be `Clone`, since they // will soon be part of a `FrozenLayer` owned by the service config. So we will convert the // current `CloneableLayer` into a `Layer` that does not impose the `Clone` requirement. - let layer: #{Layer} = self - .inner - .into(); - let mut layer = layer.with_name("$moduleUseName::config::config"); + let mut layer = #{Layer}::from(self.config).with_name("$moduleUseName::config::Config"); + ##[allow(unused)] + let mut resolver = #{Resolver}::initial(&mut layer, &mut self.runtime_components); """, *codegenScope, ) @@ -447,7 +477,13 @@ class ServiceConfigGenerator( customizations.forEach { it.section(ServiceConfig.BuilderBuildExtras)(this) } - rust("inner: layer.freeze(),") + rust( + """ + config: layer.freeze(), + runtime_components: self.runtime_components, + runtime_plugins: self.runtime_plugins, + """, + ) } } } else { @@ -464,26 +500,4 @@ class ServiceConfigGenerator( } } } - - fun renderRuntimePluginImplForSelf(writer: RustWriter) { - writer.rustTemplate( - """ - impl #{RuntimePlugin} for Config { - fn config(&self) -> #{Option}<#{FrozenLayer}> { - #{Some}(self.inner.clone()) - } - - fn interceptors(&self, _interceptors: &mut #{InterceptorRegistrar}) { - #{interceptors} - } - } - - """, - *codegenScope, - "config" to writable { writeCustomizations(customizations, ServiceConfig.OperationConfigOverride("cfg")) }, - "interceptors" to writable { - writeCustomizations(customizations, ServiceConfig.RuntimePluginInterceptors("_interceptors", "self")) - }, - ) - } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt index e449fe448c..0f96cf3e0f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt @@ -41,7 +41,7 @@ fun stubConfigCustomization(name: String, codegenContext: ClientCodegenContext): """ ##[allow(missing_docs)] pub fn $name(&self) -> u64 { - self.inner.load::<#{T}>().map(|u| u.0).unwrap() + self.config.load::<#{T}>().map(|u| u.0).unwrap() } """, "T" to configParamNewtype( @@ -71,7 +71,7 @@ fun stubConfigCustomization(name: String, codegenContext: ClientCodegenContext): """ /// docs! pub fn $name(mut self, $name: u64) -> Self { - self.inner.store_put(#{T}($name)); + self.config.store_put(#{T}($name)); self } """, @@ -129,9 +129,6 @@ fun stubConfigProject(codegenContext: ClientCodegenContext, customization: Confi val generator = ServiceConfigGenerator(codegenContext, customizations = customizations.toList()) project.withModule(ClientRustModule.config) { generator.render(this) - if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { - generator.renderRuntimePluginImplForSelf(this) - } unitTest( "config_send_sync", """ diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt index b827551bdd..97d88c1680 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt @@ -48,6 +48,8 @@ class MetadataCustomizationTest { "Interceptor" to RuntimeType.interceptor(runtimeConfig), "Metadata" to RuntimeType.operationModule(runtimeConfig).resolve("Metadata"), "capture_request" to RuntimeType.captureRequest(runtimeConfig), + "RuntimeComponents" to RuntimeType.smithyRuntimeApi(runtimeConfig) + .resolve("client::runtime_components::RuntimeComponents"), ) rustCrate.testModule { addDependency(CargoDependency.Tokio.withFeature("test-util").toDevDependency()) @@ -64,6 +66,7 @@ class MetadataCustomizationTest { fn modify_before_signing( &self, _context: &mut #{BeforeTransmitInterceptorContextMut}<'_>, + _runtime_components: &#{RuntimeComponents}, cfg: &mut #{ConfigBag}, ) -> #{Result}<(), #{BoxError}> { let metadata = cfg diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt index c467189c24..94bd6524a1 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt @@ -47,16 +47,14 @@ class ClientContextConfigCustomizationTest { use #{RuntimePlugin}; let conf = crate::Config::builder().a_string_param("hello!").a_bool_param(true).build(); assert_eq!( - conf.config() - .unwrap() + conf.config .load::() .map(|u| u.0.clone()) .unwrap(), "hello!" ); assert_eq!( - conf.config() - .unwrap() + conf.config .load::() .map(|u| u.0), Some(true) @@ -82,16 +80,14 @@ class ClientContextConfigCustomizationTest { use #{RuntimePlugin}; let conf = crate::Config::builder().a_string_param("hello!").build(); assert_eq!( - conf.config() - .unwrap() + conf.config .load::() .map(|u| u.0.clone()) .unwrap(), "hello!" ); assert_eq!( - conf.config() - .unwrap() + conf.config .load::() .map(|u| u.0), None, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt index 56cd00f0ee..62fff162aa 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt @@ -15,7 +15,7 @@ import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest import software.amazon.smithy.rust.codegen.core.testutil.runWithWarnings -import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError /** * End-to-end test of endpoint resolvers, attaching a real resolver to a fully generated service @@ -162,7 +162,7 @@ class EndpointsDecoratorTest { } } // the model has an intentionally failing test—ensure it fails - val failure = shouldThrow { "cargo test".runWithWarnings(testDir) } + val failure = shouldThrow { "cargo test".runWithWarnings(testDir) } failure.output shouldContain "endpoint::test::test_1" failure.output shouldContain "https://failingtest.com" "cargo clippy".runWithWarnings(testDir) diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt index b1ee6311a6..086a211915 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt @@ -56,19 +56,20 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { tokioTest("test_operation_overrides_endpoint_resolver") { rustTemplate( """ - use #{ConfigBagAccessors}; use #{RuntimePlugin}; + use ::aws_smithy_runtime_api::client::orchestrator::EndpointResolver; let expected_url = "http://localhost:1234/"; let client_config = crate::config::Config::builder().build(); let config_override = crate::config::Config::builder().endpoint_resolver(expected_url); - let sut = crate::config::ConfigOverrideRuntimePlugin { - client_config: client_config.config().unwrap(), + let sut = crate::config::ConfigOverrideRuntimePlugin::new( config_override, - }; - let sut_layer = sut.config().unwrap(); - let endpoint_resolver = sut_layer.endpoint_resolver(); + client_config.config, + &client_config.runtime_components, + ); + let sut_components = sut.runtime_components(); + let endpoint_resolver = sut_components.endpoint_resolver().unwrap(); let endpoint = endpoint_resolver .resolve_endpoint(&#{EndpointResolverParams}::new(crate::config::endpoint::Params {})) .await @@ -186,6 +187,7 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { .resolve("client::request_attempts::RequestAttempts"), "RetryClassifiers" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::retries::RetryClassifiers"), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(runtimeConfig), "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), "ShouldAttempt" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::retries::ShouldAttempt"), @@ -195,46 +197,65 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { unitTest("test_operation_overrides_retry_strategy") { rustTemplate( """ - use #{ConfigBagAccessors}; use #{RuntimePlugin}; + use ::aws_smithy_runtime_api::client::retries::RetryStrategy; let client_config = crate::config::Config::builder() .retry_config(#{RetryConfig}::standard().with_max_attempts(3)) .build(); - let client_config_layer = client_config.config().unwrap(); - let mut ctx = #{InterceptorContext}::new(#{TypeErasedBox}::new(())); ctx.set_output_or_error(#{Err}(#{OrchestratorError}::other("doesn't matter"))); + let mut layer = #{Layer}::new("test"); layer.store_put(#{RequestAttempts}::new(1)); - layer.set_retry_classifiers( - #{RetryClassifiers}::new().with_classifier(#{AlwaysRetry}(#{ErrorKind}::TransientError)), - ); let mut cfg = #{ConfigBag}::of_layers(vec![layer]); + let client_config_layer = client_config.config; cfg.push_shared_layer(client_config_layer.clone()); - let retry = cfg.retry_strategy().unwrap(); + let retry_classifiers_component = #{RuntimeComponentsBuilder}::new("retry_classifier") + .with_retry_classifiers(#{Some}( + #{RetryClassifiers}::new().with_classifier(#{AlwaysRetry}(#{ErrorKind}::TransientError)), + )); + + // Emulate the merging of runtime components from runtime plugins that the orchestrator does + let runtime_components = #{RuntimeComponentsBuilder}::for_tests() + .merge_from(&client_config.runtime_components) + .merge_from(&retry_classifiers_component) + .build() + .unwrap(); + + let retry = runtime_components.retry_strategy(); assert!(matches!( - retry.should_attempt_retry(&ctx, &cfg).unwrap(), + retry.should_attempt_retry(&ctx, &runtime_components, &cfg).unwrap(), #{ShouldAttempt}::YesAfterDelay(_) )); // sets `max_attempts` to 1 implicitly by using `disabled()`, forcing it to run out of // attempts with respect to `RequestAttempts` set to 1 above - let config_override = crate::config::Config::builder() + let config_override_builder = crate::config::Config::builder() .retry_config(#{RetryConfig}::disabled()); - let sut = crate::config::ConfigOverrideRuntimePlugin { - client_config: client_config_layer, - config_override, - }; + let config_override = config_override_builder.clone().build(); + let sut = crate::config::ConfigOverrideRuntimePlugin::new( + config_override_builder, + client_config_layer, + &client_config.runtime_components, + ); let sut_layer = sut.config().unwrap(); cfg.push_shared_layer(sut_layer); - let retry = cfg.retry_strategy().unwrap(); + // Emulate the merging of runtime components from runtime plugins that the orchestrator does + let runtime_components = #{RuntimeComponentsBuilder}::for_tests() + .merge_from(&client_config.runtime_components) + .merge_from(&retry_classifiers_component) + .merge_from(&config_override.runtime_components) + .build() + .unwrap(); + + let retry = runtime_components.retry_strategy(); assert!(matches!( - retry.should_attempt_retry(&ctx, &cfg).unwrap(), + retry.should_attempt_retry(&ctx, &runtime_components, &cfg).unwrap(), #{ShouldAttempt}::No )); """, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt index 646d6863e9..b41cff293c 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt @@ -106,7 +106,7 @@ internal class ServiceConfigGeneratorTest { """ ##[allow(missing_docs)] pub fn config_field(&self) -> u64 { - self.inner.load::<#{T}>().map(|u| u.0).unwrap() + self.config.load::<#{T}>().map(|u| u.0).unwrap() } """, "T" to configParamNewtype( @@ -137,7 +137,7 @@ internal class ServiceConfigGeneratorTest { """ ##[allow(missing_docs)] pub fn config_field(mut self, config_field: u64) -> Self { - self.inner.store_put(#{T}(config_field)); + self.config.store_put(#{T}(config_field)); self } """, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt index c80ed9ba04..28ffe74561 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt @@ -31,7 +31,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGenerat import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel -import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.outputShape import java.nio.file.Path @@ -257,7 +257,7 @@ class ProtocolTestGeneratorTest { @Test fun `test incorrect response parsing`() { - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?Hi=Hello%20there&required") @@ -273,7 +273,7 @@ class ProtocolTestGeneratorTest { @Test fun `test invalid body`() { - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?Hi=Hello%20there&required") @@ -290,7 +290,7 @@ class ProtocolTestGeneratorTest { @Test fun `test invalid url parameter`() { - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?Hi=INCORRECT&required") @@ -306,7 +306,7 @@ class ProtocolTestGeneratorTest { @Test fun `test forbidden url parameter`() { - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?goodbye&Hi=Hello%20there&required") @@ -323,7 +323,7 @@ class ProtocolTestGeneratorTest { @Test fun `test required url parameter`() { // Hard coded implementation for this 1 test - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?Hi=Hello%20there") @@ -340,7 +340,7 @@ class ProtocolTestGeneratorTest { @Test fun `invalid header`() { - val err = assertThrows { + val err = assertThrows { testService( """ .uri("/?Hi=Hello%20there&required") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index cbf2514157..fbd4ed69dd 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -338,8 +338,14 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag") fun configBagAccessors(runtimeConfig: RuntimeConfig): RuntimeType = smithyRuntimeApi(runtimeConfig).resolve("client::config_bag_accessors::ConfigBagAccessors") + fun runtimeComponentsBuilder(runtimeConfig: RuntimeConfig) = + smithyRuntimeApi(runtimeConfig).resolve("client::runtime_components::RuntimeComponentsBuilder") fun runtimePlugins(runtimeConfig: RuntimeConfig): RuntimeType = smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugins") + fun runtimePlugin(runtimeConfig: RuntimeConfig) = + smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugin") + fun sharedRuntimePlugin(runtimeConfig: RuntimeConfig) = + smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::SharedRuntimePlugin") fun boxError(runtimeConfig: RuntimeConfig): RuntimeType = smithyRuntimeApi(runtimeConfig).resolve("box_error::BoxError") fun interceptor(runtimeConfig: RuntimeConfig): RuntimeType = @@ -468,8 +474,5 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun idempotencyToken(runtimeConfig: RuntimeConfig) = forInlineDependency(InlineDependency.idempotencyToken(runtimeConfig)) - - fun runtimePlugin(runtimeConfig: RuntimeConfig) = - RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::RuntimePlugin") } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt index b40d5b8a06..2bf386a519 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt @@ -32,7 +32,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider -import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.orNullIfEmpty @@ -389,7 +389,7 @@ fun RustWriter.compileAndTest( println("Test sources for debugging: file://${testModule.absolutePath}") } return testOutput - } catch (e: CommandFailed) { + } catch (e: CommandError) { if (!expectFailure) { println("Test sources for debugging: file://${testModule.absolutePath}") } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Exec.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Exec.kt index 7609e950a2..f7a08f62f3 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Exec.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/util/Exec.kt @@ -9,7 +9,7 @@ import java.io.IOException import java.nio.file.Path import java.util.concurrent.TimeUnit -data class CommandFailed(val output: String) : Exception("Command Failed\n$output") +data class CommandError(val output: String) : Exception("Command Error\n$output") fun String.runCommand(workdir: Path? = null, environment: Map = mapOf(), timeout: Long = 3600): String { val parts = this.split("\\s".toRegex()) @@ -30,13 +30,13 @@ fun String.runCommand(workdir: Path? = null, environment: Map = val output = "$stdErr\n$stdOut" return when (proc.exitValue()) { 0 -> output - else -> throw CommandFailed("Command Failed\n$output") + else -> throw CommandError("Command Error\n$output") } } catch (_: IllegalThreadStateException) { - throw CommandFailed("Timeout") + throw CommandError("Timeout") } catch (err: IOException) { - throw CommandFailed("$this was not a valid command.\n Hint: is everything installed?\n$err") + throw CommandError("$this was not a valid command.\n Hint: is everything installed?\n$err") } catch (other: Exception) { - throw CommandFailed("Unexpected exception thrown when executing subprocess:\n$other") + throw CommandError("Unexpected exception thrown when executing subprocess:\n$other") } } diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt index 1dfc41795d..d204a605c4 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/transformers/RecursiveShapesIntegrationTest.kt @@ -17,7 +17,7 @@ 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.core.testutil.testSymbolProvider -import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.lookup class RecursiveShapesIntegrationTest { @@ -61,7 +61,7 @@ class RecursiveShapesIntegrationTest { project } val unmodifiedProject = check(model) - val output = assertThrows { + val output = assertThrows { unmodifiedProject.compileAndTest(expectFailure = true) } // THIS IS A LOAD-BEARING shouldContain! If the compiler error changes then this will break! diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/ExecKtTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/ExecKtTest.kt index aab2b70613..5041166fb7 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/ExecKtTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/util/ExecKtTest.kt @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test internal class ExecKtTest { @Test fun `missing command throws CommandFailed`() { - shouldThrow { + shouldThrow { "notaprogram run".runCommand() } } diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt index 0c6d27f564..156cc455eb 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ServerCodegenVisitor.kt @@ -45,7 +45,7 @@ import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGenerat import software.amazon.smithy.rust.codegen.core.smithy.transformers.EventStreamNormalizer 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.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.getTrait import software.amazon.smithy.rust.codegen.core.util.hasEventStreamMember import software.amazon.smithy.rust.codegen.core.util.hasTrait @@ -266,7 +266,7 @@ open class ServerCodegenVisitor( fileManifest.baseDir, timeout = settings.codegenConfig.formatTimeoutSeconds.toLong(), ) - } catch (err: CommandFailed) { + } catch (err: CommandError) { logger.info( "[rust-server-codegen] Failed to run cargo fmt: [${service.id}]\n${err.output}", ) diff --git a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt index eb0ba5ed34..5e2f828e1b 100644 --- a/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt +++ b/codegen-server/src/test/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/ConstrainedStringGeneratorTest.kt @@ -20,7 +20,7 @@ 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.core.testutil.unitTest -import software.amazon.smithy.rust.codegen.core.util.CommandFailed +import software.amazon.smithy.rust.codegen.core.util.CommandError import software.amazon.smithy.rust.codegen.core.util.lookup import software.amazon.smithy.rust.codegen.server.smithy.ServerRustModule import software.amazon.smithy.rust.codegen.server.smithy.createTestInlineModuleCreator @@ -237,7 +237,7 @@ class ConstrainedStringGeneratorTest { ).render() } - assertThrows { + assertThrows { project.compileAndTest() } } diff --git a/rust-runtime/aws-smithy-async/Cargo.toml b/rust-runtime/aws-smithy-async/Cargo.toml index ca703aaf65..c95862d9ff 100644 --- a/rust-runtime/aws-smithy-async/Cargo.toml +++ b/rust-runtime/aws-smithy-async/Cargo.toml @@ -12,7 +12,6 @@ rt-tokio = ["tokio/time"] test-util = [] [dependencies] -aws-smithy-types = { path = "../aws-smithy-types" } pin-project-lite = "0.2" tokio = { version = "1.23.1", features = ["sync"] } tokio-stream = { version = "0.1.5", default-features = false } diff --git a/rust-runtime/aws-smithy-async/src/rt/sleep.rs b/rust-runtime/aws-smithy-async/src/rt/sleep.rs index 6776790ee0..076db95c97 100644 --- a/rust-runtime/aws-smithy-async/src/rt/sleep.rs +++ b/rust-runtime/aws-smithy-async/src/rt/sleep.rs @@ -6,7 +6,6 @@ //! Provides an [`AsyncSleep`] trait that returns a future that sleeps for a given duration, //! and implementations of `AsyncSleep` for different async runtimes. -use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::fmt::{Debug, Formatter}; use std::future::Future; use std::pin::Pin; @@ -69,10 +68,6 @@ impl AsyncSleep for SharedAsyncSleep { } } -impl Storable for SharedAsyncSleep { - type Storer = StoreReplace; -} - #[cfg(feature = "rt-tokio")] /// Returns a default sleep implementation based on the features enabled pub fn default_async_sleep() -> Option { diff --git a/rust-runtime/aws-smithy-async/src/time.rs b/rust-runtime/aws-smithy-async/src/time.rs index 0b43a0af74..2abe332c88 100644 --- a/rust-runtime/aws-smithy-async/src/time.rs +++ b/rust-runtime/aws-smithy-async/src/time.rs @@ -4,7 +4,6 @@ */ //! Time source abstraction to support WASM and testing -use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::fmt::Debug; use std::sync::Arc; use std::time::SystemTime; @@ -87,7 +86,3 @@ impl TimeSource for SharedTimeSource { self.0.now() } } - -impl Storable for SharedTimeSource { - type Storer = StoreReplace; -} diff --git a/rust-runtime/aws-smithy-client/external-types.toml b/rust-runtime/aws-smithy-client/external-types.toml index 0ca5028416..74daaad215 100644 --- a/rust-runtime/aws-smithy-client/external-types.toml +++ b/rust-runtime/aws-smithy-client/external-types.toml @@ -23,7 +23,7 @@ allowed_external_types = [ "tokio::io::async_write::AsyncWrite", - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Once tooling permits it, only allow the following types in the `test-utils` feature + # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Once tooling permits it, only allow the following types in the `test-util` feature "bytes::bytes::Bytes", "serde::ser::Serialize", "serde::de::Deserialize", diff --git a/rust-runtime/aws-smithy-runtime-api/src/client.rs b/rust-runtime/aws-smithy-runtime-api/src/client.rs index cdcfb847a5..106cc5087c 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +pub mod runtime_components; + /// Client orchestrator configuration accessors for the [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag). pub mod config_bag_accessors; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 517e5a47e0..5241f5187c 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -4,9 +4,10 @@ */ use crate::box_error::BoxError; -use crate::client::identity::{Identity, IdentityResolvers, SharedIdentityResolver}; +use crate::client::identity::{Identity, SharedIdentityResolver}; use crate::client::orchestrator::HttpRequest; -use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; +use crate::client::runtime_components::{GetIdentityResolver, RuntimeComponents}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use aws_smithy_types::Document; use std::borrow::Cow; @@ -66,20 +67,16 @@ pub trait AuthOptionResolver: Send + Sync + fmt::Debug { ) -> Result, BoxError>; } -#[derive(Debug)] -pub struct DynAuthOptionResolver(Box); +#[derive(Clone, Debug)] +pub struct SharedAuthOptionResolver(Arc); -impl DynAuthOptionResolver { +impl SharedAuthOptionResolver { pub fn new(auth_option_resolver: impl AuthOptionResolver + 'static) -> Self { - Self(Box::new(auth_option_resolver)) + Self(Arc::new(auth_option_resolver)) } } -impl Storable for DynAuthOptionResolver { - type Storer = StoreReplace; -} - -impl AuthOptionResolver for DynAuthOptionResolver { +impl AuthOptionResolver for SharedAuthOptionResolver { fn resolve_auth_options( &self, params: &AuthOptionResolverParams, @@ -93,7 +90,7 @@ pub trait HttpAuthScheme: Send + Sync + fmt::Debug { fn identity_resolver( &self, - identity_resolvers: &IdentityResolvers, + identity_resolvers: &dyn GetIdentityResolver, ) -> Option; fn request_signer(&self) -> &dyn HttpRequestSigner; @@ -117,7 +114,7 @@ impl HttpAuthScheme for SharedHttpAuthScheme { fn identity_resolver( &self, - identity_resolvers: &IdentityResolvers, + identity_resolvers: &dyn GetIdentityResolver, ) -> Option { self.0.identity_resolver(identity_resolvers) } @@ -127,10 +124,6 @@ impl HttpAuthScheme for SharedHttpAuthScheme { } } -impl Storable for SharedHttpAuthScheme { - type Storer = StoreAppend; -} - pub trait HttpRequestSigner: Send + Sync + fmt::Debug { /// Return a signed version of the given request using the given identity. /// @@ -140,6 +133,7 @@ pub trait HttpRequestSigner: Send + Sync + fmt::Debug { request: &mut HttpRequest, identity: &Identity, auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + runtime_components: &RuntimeComponents, config_bag: &ConfigBag, ) -> Result<(), BoxError>; } @@ -166,52 +160,3 @@ impl<'a> AuthSchemeEndpointConfig<'a> { self.0 } } - -#[cfg(test)] -mod tests { - use super::*; - use aws_smithy_types::config_bag::{ConfigBag, Layer}; - - #[test] - fn test_shared_http_auth_scheme_configuration() { - #[derive(Debug)] - struct TestHttpAuthScheme(&'static str); - impl HttpAuthScheme for TestHttpAuthScheme { - fn scheme_id(&self) -> AuthSchemeId { - AuthSchemeId::new(self.0) - } - - fn identity_resolver(&self, _: &IdentityResolvers) -> Option { - unreachable!("this shouldn't get called in this test") - } - - fn request_signer(&self) -> &dyn HttpRequestSigner { - unreachable!("this shouldn't get called in this test") - } - } - - let mut config_bag = ConfigBag::base(); - - let mut layer = Layer::new("first"); - layer.store_append(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_1"))); - config_bag.push_layer(layer); - - let mut layer = Layer::new("second"); - layer.store_append(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_2"))); - layer.store_append(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_3"))); - config_bag.push_layer(layer); - - let auth_schemes = config_bag.load::(); - let encountered_scheme_ids: Vec = - auth_schemes.map(|s| s.scheme_id()).collect(); - - assert_eq!( - vec![ - AuthSchemeId::new("scheme_3"), - AuthSchemeId::new("scheme_2"), - AuthSchemeId::new("scheme_1") - ], - encountered_scheme_ids - ); - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs index 54832acf80..5f338cd93f 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs @@ -3,23 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::client::auth::{ - AuthOptionResolver, AuthOptionResolverParams, AuthSchemeId, DynAuthOptionResolver, - SharedHttpAuthScheme, -}; -use crate::client::connectors::{Connector, DynConnector}; -use crate::client::identity::{ - ConfiguredIdentityResolver, IdentityResolvers, SharedIdentityResolver, -}; +use crate::client::auth::AuthOptionResolverParams; use crate::client::orchestrator::{ - DynEndpointResolver, DynResponseDeserializer, EndpointResolver, EndpointResolverParams, - LoadedRequestBody, ResponseDeserializer, SharedRequestSerializer, NOT_NEEDED, + DynResponseDeserializer, EndpointResolverParams, LoadedRequestBody, ResponseDeserializer, + SharedRequestSerializer, NOT_NEEDED, }; -use crate::client::retries::{DynRetryStrategy, RetryClassifiers, RetryStrategy}; -use aws_smithy_async::rt::sleep::SharedAsyncSleep; -use aws_smithy_async::time::{SharedTimeSource, TimeSource}; -use aws_smithy_types::config_bag::{AppendItemIter, CloneableLayer, ConfigBag, FrozenLayer, Layer}; -use std::fmt::Debug; +use aws_smithy_types::config_bag::{CloneableLayer, ConfigBag, FrozenLayer, Layer}; // Place traits in a private module so that they can be used in the public API without being a part of the public API. mod internal { @@ -60,51 +49,6 @@ mod internal { } } - pub trait CloneableSettable { - fn store_put(&mut self, value: T) - where - T: Storable> + Clone; - - fn store_append(&mut self, item: T) - where - T: Storable> + Clone; - } - - impl CloneableSettable for S - where - S: Settable, - { - fn store_put(&mut self, value: T) - where - T: Storable> + Clone, - { - Settable::store_put(self, value); - } - - fn store_append(&mut self, item: T) - where - T: Storable> + Clone, - { - Settable::store_append(self, item); - } - } - - impl CloneableSettable for CloneableLayer { - fn store_put(&mut self, value: T) - where - T: Storable> + Clone, - { - CloneableLayer::store_put(self, value); - } - - fn store_append(&mut self, item: T) - where - T: Storable> + Clone, - { - CloneableLayer::store_append(self, item); - } - } - pub trait Gettable { fn load(&self) -> ::ReturnedType<'_>; } @@ -134,7 +78,7 @@ mod internal { } } -use internal::{CloneableSettable, Gettable, Settable}; +use internal::{Gettable, Settable}; pub trait ConfigBagAccessors { fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams @@ -153,21 +97,6 @@ pub trait ConfigBagAccessors { self.store_put::(auth_option_resolver_params); } - fn auth_option_resolver(&self) -> &dyn AuthOptionResolver - where - Self: Gettable, - { - self.load::() - .expect("an auth option resolver must be set") - } - - fn set_auth_option_resolver(&mut self, auth_option_resolver: DynAuthOptionResolver) - where - Self: Settable, - { - self.store_put::(auth_option_resolver); - } - fn endpoint_resolver_params(&self) -> &EndpointResolverParams where Self: Gettable, @@ -183,73 +112,6 @@ pub trait ConfigBagAccessors { self.store_put::(endpoint_resolver_params); } - fn endpoint_resolver(&self) -> &dyn EndpointResolver - where - Self: Gettable, - { - self.load::() - .expect("an endpoint resolver must be set") - } - - fn set_endpoint_resolver(&mut self, endpoint_resolver: DynEndpointResolver) - where - Self: Settable, - { - self.store_put::(endpoint_resolver); - } - - /// Returns the configured identity resolvers. - fn identity_resolvers(&self) -> IdentityResolvers - where - Self: Gettable, - { - IdentityResolvers::new(self.load::()) - } - - /// Adds an identity resolver to the config. - fn push_identity_resolver( - &mut self, - auth_scheme_id: AuthSchemeId, - identity_resolver: SharedIdentityResolver, - ) where - Self: CloneableSettable, - { - self.store_append::(ConfiguredIdentityResolver::new( - auth_scheme_id, - identity_resolver, - )); - } - - fn connector(&self) -> &dyn Connector - where - Self: Gettable, - { - self.load::().expect("missing connector") - } - - fn set_connector(&mut self, connection: DynConnector) - where - Self: Settable, - { - self.store_put::(connection); - } - - /// Returns the configured HTTP auth schemes. - fn http_auth_schemes(&self) -> HttpAuthSchemes<'_> - where - Self: Gettable, - { - HttpAuthSchemes::new(self.load::()) - } - - /// Adds a HTTP auth scheme to the config. - fn push_http_auth_scheme(&mut self, auth_scheme: SharedHttpAuthScheme) - where - Self: Settable, - { - self.store_append::(auth_scheme); - } - fn request_serializer(&self) -> SharedRequestSerializer where Self: Gettable, @@ -279,63 +141,6 @@ pub trait ConfigBagAccessors { self.store_put::(response_deserializer); } - fn retry_classifiers(&self) -> &RetryClassifiers - where - Self: Gettable, - { - self.load::() - .expect("retry classifiers must be set") - } - fn set_retry_classifiers(&mut self, retry_classifiers: RetryClassifiers) - where - Self: Settable, - { - self.store_put::(retry_classifiers); - } - - fn retry_strategy(&self) -> Option<&dyn RetryStrategy> - where - Self: Gettable, - { - self.load::().map(|rs| rs as _) - } - fn set_retry_strategy(&mut self, retry_strategy: DynRetryStrategy) - where - Self: Settable, - { - self.store_put::(retry_strategy); - } - - fn request_time(&self) -> Option - where - Self: Gettable, - { - self.load::().cloned() - } - fn set_request_time(&mut self, time_source: impl TimeSource + 'static) - where - Self: Settable, - { - self.store_put::(SharedTimeSource::new(time_source)); - } - - fn sleep_impl(&self) -> Option - where - Self: Gettable, - { - self.load::().cloned() - } - fn set_sleep_impl(&mut self, async_sleep: Option) - where - Self: Settable, - { - if let Some(sleep_impl) = async_sleep { - self.store_put::(sleep_impl); - } else { - self.unset::(); - } - } - fn loaded_request_body(&self) -> &LoadedRequestBody where Self: Gettable, @@ -354,90 +159,3 @@ impl ConfigBagAccessors for ConfigBag {} impl ConfigBagAccessors for FrozenLayer {} impl ConfigBagAccessors for CloneableLayer {} impl ConfigBagAccessors for Layer {} - -/// Accessor for HTTP auth schemes. -#[derive(Debug)] -pub struct HttpAuthSchemes<'a> { - inner: AppendItemIter<'a, SharedHttpAuthScheme>, -} - -impl<'a> HttpAuthSchemes<'a> { - pub(crate) fn new(inner: AppendItemIter<'a, SharedHttpAuthScheme>) -> Self { - Self { inner } - } - - /// Returns the HTTP auth scheme with the given ID, if there is one. - pub fn scheme(mut self, scheme_id: AuthSchemeId) -> Option { - use crate::client::auth::HttpAuthScheme; - self.inner - .find(|&scheme| scheme.scheme_id() == scheme_id) - .cloned() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::client::auth::{HttpAuthScheme, HttpRequestSigner}; - use crate::client::config_bag_accessors::ConfigBagAccessors; - use aws_smithy_types::config_bag::{ConfigBag, Layer}; - - #[test] - fn test_shared_http_auth_scheme_configuration() { - #[derive(Debug)] - struct TestHttpAuthScheme(&'static str); - impl HttpAuthScheme for TestHttpAuthScheme { - fn scheme_id(&self) -> AuthSchemeId { - AuthSchemeId::new(self.0) - } - - fn identity_resolver(&self, _: &IdentityResolvers) -> Option { - unreachable!("this shouldn't get called in this test") - } - - fn request_signer(&self) -> &dyn HttpRequestSigner { - unreachable!("this shouldn't get called in this test") - } - } - - let mut config_bag = ConfigBag::base(); - - let mut layer = Layer::new("first"); - layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_1"))); - config_bag.push_layer(layer); - - let mut layer = Layer::new("second"); - layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_2"))); - layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestHttpAuthScheme("scheme_3"))); - config_bag.push_layer(layer); - - assert!(config_bag - .http_auth_schemes() - .scheme(AuthSchemeId::new("does-not-exist")) - .is_none()); - assert_eq!( - AuthSchemeId::new("scheme_1"), - config_bag - .http_auth_schemes() - .scheme(AuthSchemeId::new("scheme_1")) - .unwrap() - .scheme_id() - ); - assert_eq!( - AuthSchemeId::new("scheme_2"), - config_bag - .http_auth_schemes() - .scheme(AuthSchemeId::new("scheme_2")) - .unwrap() - .scheme_id() - ); - assert_eq!( - AuthSchemeId::new("scheme_3"), - config_bag - .http_auth_schemes() - .scheme(AuthSchemeId::new("scheme_3")) - .unwrap() - .scheme_id() - ); - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs index fd63eea38c..a7d10a2dae 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs @@ -4,28 +4,24 @@ */ use crate::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse}; -use aws_smithy_types::config_bag::{Storable, StoreReplace}; use std::fmt; +use std::sync::Arc; pub trait Connector: Send + Sync + fmt::Debug { fn call(&self, request: HttpRequest) -> BoxFuture; } -#[derive(Debug)] -pub struct DynConnector(Box); +#[derive(Clone, Debug)] +pub struct SharedConnector(Arc); -impl DynConnector { +impl SharedConnector { pub fn new(connection: impl Connector + 'static) -> Self { - Self(Box::new(connection)) + Self(Arc::new(connection)) } } -impl Connector for DynConnector { +impl Connector for SharedConnector { fn call(&self, request: HttpRequest) -> BoxFuture { (*self.0).call(request) } } - -impl Storable for DynConnector { - type Storer = StoreReplace; -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index a8ab993960..60a110fbb9 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -5,7 +5,7 @@ use crate::client::auth::AuthSchemeId; use crate::client::orchestrator::Future; -use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; +use aws_smithy_types::config_bag::ConfigBag; use std::any::Any; use std::fmt; use std::fmt::Debug; @@ -67,36 +67,6 @@ impl ConfiguredIdentityResolver { } } -impl Storable for ConfiguredIdentityResolver { - type Storer = StoreAppend; -} - -#[derive(Clone, Debug, Default)] -pub struct IdentityResolvers { - identity_resolvers: Vec, -} - -impl Storable for IdentityResolvers { - type Storer = StoreReplace; -} - -impl IdentityResolvers { - pub(crate) fn new<'a>(resolvers: impl Iterator) -> Self { - let identity_resolvers: Vec<_> = resolvers.cloned().collect(); - if identity_resolvers.is_empty() { - tracing::warn!("no identity resolvers available for this request"); - } - Self { identity_resolvers } - } - - pub fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option { - self.identity_resolvers - .iter() - .find(|pair| pair.scheme_id() == scheme_id) - .map(|pair| pair.identity_resolver()) - } -} - #[derive(Clone)] pub struct Identity { data: Arc, diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index 5d93c64566..5bf1ade4b1 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -11,7 +11,8 @@ use crate::client::interceptors::context::{ BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, InterceptorContext, }; -use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreAppend, StoreReplace}; +use crate::client::runtime_components::RuntimeComponents; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::error::display::DisplayErrorContext; use context::{Error, Input, Output}; use std::fmt; @@ -27,16 +28,28 @@ pub use error::InterceptorError; macro_rules! interceptor_trait_fn { ($name:ident, $phase:ident, $docs:tt) => { #[doc = $docs] - fn $name(&self, context: &$phase<'_>, cfg: &mut ConfigBag) -> Result<(), BoxError> { + fn $name( + &self, + context: &$phase<'_>, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { let _ctx = context; + let _rc = runtime_components; let _cfg = cfg; Ok(()) } }; (mut $name:ident, $phase:ident, $docs:tt) => { #[doc = $docs] - fn $name(&self, context: &mut $phase<'_>, cfg: &mut ConfigBag) -> Result<(), BoxError> { + fn $name( + &self, + context: &mut $phase<'_>, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { let _ctx = context; + let _rc = runtime_components; let _cfg = cfg; Ok(()) } @@ -54,29 +67,32 @@ macro_rules! interceptor_trait_fn { /// to read in-flight request or response messages, or "read/write" hooks, which make it possible /// to modify in-flight request or output messages. pub trait Interceptor: fmt::Debug { - interceptor_trait_fn!( - read_before_execution, - BeforeSerializationInterceptorContextRef, - " - A hook called at the start of an execution, before the SDK - does anything else. - - **When:** This will **ALWAYS** be called once per execution. The duration - between invocation of this hook and `after_execution` is very close - to full duration of the execution. - - **Available Information:** The [InterceptorContext::input()] is - **ALWAYS** available. Other information **WILL NOT** be available. - - **Error Behavior:** Errors raised by this hook will be stored - until all interceptors have had their `before_execution` invoked. - Other hooks will then be skipped and execution will jump to - `modify_before_completion` with the raised error as the - [InterceptorContext::output_or_error()]. If multiple - `before_execution` methods raise errors, the latest - will be used and earlier ones will be logged and dropped. - " - ); + /// A hook called at the start of an execution, before the SDK + /// does anything else. + /// + /// **When:** This will **ALWAYS** be called once per execution. The duration + /// between invocation of this hook and `after_execution` is very close + /// to full duration of the execution. + /// + /// **Available Information:** The [InterceptorContext::input()] is + /// **ALWAYS** available. Other information **WILL NOT** be available. + /// + /// **Error Behavior:** Errors raised by this hook will be stored + /// until all interceptors have had their `before_execution` invoked. + /// Other hooks will then be skipped and execution will jump to + /// `modify_before_completion` with the raised error as the + /// [InterceptorContext::output_or_error()]. If multiple + /// `before_execution` methods raise errors, the latest + /// will be used and earlier ones will be logged and dropped. + fn read_before_execution( + &self, + context: &BeforeSerializationInterceptorContextRef<'_>, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let _ctx = context; + let _cfg = cfg; + Ok(()) + } interceptor_trait_fn!( mut modify_before_serialization, @@ -96,7 +112,6 @@ pub trait Interceptor: fmt::Debug { later hooks. Other information **WILL NOT** be available. **Error Behavior:** If errors are raised by this hook, - execution will jump to `modify_before_completion` with the raised error as the [InterceptorContext::output_or_error()]. @@ -478,9 +493,11 @@ pub trait Interceptor: fmt::Debug { fn modify_before_attempt_completion( &self, context: &mut FinalizerInterceptorContextMut<'_>, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let _ctx = context; + let _rc = runtime_components; let _cfg = cfg; Ok(()) } @@ -510,9 +527,11 @@ pub trait Interceptor: fmt::Debug { fn read_after_attempt( &self, context: &FinalizerInterceptorContextRef<'_>, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let _ctx = context; + let _rc = runtime_components; let _cfg = cfg; Ok(()) } @@ -540,9 +559,11 @@ pub trait Interceptor: fmt::Debug { fn modify_before_completion( &self, context: &mut FinalizerInterceptorContextMut<'_>, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let _ctx = context; + let _rc = runtime_components; let _cfg = cfg; Ok(()) } @@ -568,9 +589,11 @@ pub trait Interceptor: fmt::Debug { fn read_after_execution( &self, context: &FinalizerInterceptorContextRef<'_>, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let _ctx = context; + let _rc = runtime_components; let _cfg = cfg; Ok(()) } @@ -607,18 +630,6 @@ impl SharedInterceptor { } } -/// A interceptor wrapper to conditionally enable the interceptor based on [`DisableInterceptor`] -struct ConditionallyEnabledInterceptor<'a>(&'a SharedInterceptor); -impl ConditionallyEnabledInterceptor<'_> { - fn if_enabled(&self, cfg: &ConfigBag) -> Option<&dyn Interceptor> { - if self.0.enabled(cfg) { - Some(self.0.as_ref()) - } else { - None - } - } -} - impl AsRef for SharedInterceptor { fn as_ref(&self) -> &(dyn Interceptor + 'static) { self.interceptor.as_ref() @@ -632,45 +643,29 @@ impl Deref for SharedInterceptor { } } -impl Storable for SharedInterceptor { - type Storer = StoreAppend; -} - -/// Collection of [`SharedInterceptor`] that allows for only registration -#[derive(Debug, Clone, Default)] -pub struct InterceptorRegistrar { - interceptors: Vec, -} - -impl InterceptorRegistrar { - /// Register an interceptor with this `InterceptorRegistrar`. - /// - /// When this `InterceptorRegistrar` is passed to an orchestrator, the orchestrator will run the - /// registered interceptor for all the "hooks" that it implements. - pub fn register(&mut self, interceptor: SharedInterceptor) { - self.interceptors.push(interceptor); - } -} - -impl Extend for InterceptorRegistrar { - fn extend>(&mut self, iter: T) { - for interceptor in iter { - self.register(interceptor); +/// A interceptor wrapper to conditionally enable the interceptor based on [`DisableInterceptor`] +struct ConditionallyEnabledInterceptor(SharedInterceptor); +impl ConditionallyEnabledInterceptor { + fn if_enabled(&self, cfg: &ConfigBag) -> Option<&dyn Interceptor> { + if self.0.enabled(cfg) { + Some(self.0.as_ref()) + } else { + None } } } -#[derive(Debug, Clone, Default)] -pub struct Interceptors { - client_interceptors: InterceptorRegistrar, - operation_interceptors: InterceptorRegistrar, +#[derive(Debug)] +pub struct Interceptors { + interceptors: I, } macro_rules! interceptor_impl_fn { (mut $interceptor:ident) => { pub fn $interceptor( - &self, + self, ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { tracing::trace!(concat!( @@ -680,9 +675,11 @@ macro_rules! interceptor_impl_fn { )); let mut result: Result<(), BoxError> = Ok(()); let mut ctx = ctx.into(); - for interceptor in self.interceptors() { + for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = interceptor.$interceptor(&mut ctx, cfg) { + if let Err(new_error) = + interceptor.$interceptor(&mut ctx, runtime_components, cfg) + { if let Err(last_error) = result { tracing::debug!("{}", DisplayErrorContext(&*last_error)); } @@ -695,15 +692,17 @@ macro_rules! interceptor_impl_fn { }; (ref $interceptor:ident) => { pub fn $interceptor( - &self, + self, ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { let mut result: Result<(), BoxError> = Ok(()); let ctx = ctx.into(); - for interceptor in self.interceptors() { + for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = interceptor.$interceptor(&ctx, cfg) { + if let Err(new_error) = interceptor.$interceptor(&ctx, runtime_components, cfg) + { if let Err(last_error) = result { tracing::debug!("{}", DisplayErrorContext(&*last_error)); } @@ -742,68 +741,31 @@ pub fn disable_interceptor(cause: &'static str) -> DisableInterc } } -impl Interceptors { - pub fn new() -> Self { - Self::default() - } - - fn interceptors(&self) -> impl Iterator> { - self.client_interceptors() - .chain(self.operation_interceptors()) - } - - fn client_interceptors(&self) -> impl Iterator> { - self.client_interceptors - .interceptors - .iter() - .map(ConditionallyEnabledInterceptor) - } - - fn operation_interceptors(&self) -> impl Iterator> { - self.operation_interceptors - .interceptors - .iter() - .map(ConditionallyEnabledInterceptor) - } - - pub fn client_interceptors_mut(&mut self) -> &mut InterceptorRegistrar { - &mut self.client_interceptors +impl Interceptors +where + I: Iterator, +{ + pub fn new(interceptors: I) -> Self { + Self { interceptors } } - pub fn operation_interceptors_mut(&mut self) -> &mut InterceptorRegistrar { - &mut self.operation_interceptors + fn into_iter(self) -> impl Iterator { + self.interceptors.map(ConditionallyEnabledInterceptor) } - pub fn client_read_before_execution( - &self, + pub fn read_before_execution( + self, + operation: bool, ctx: &InterceptorContext, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { - tracing::trace!("running `client_read_before_execution` interceptors"); - let mut result: Result<(), BoxError> = Ok(()); - let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); - for interceptor in self.client_interceptors() { - if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); - } - result = Err(new_error); - } - } - } - result.map_err(InterceptorError::read_before_execution) - } - - pub fn operation_read_before_execution( - &self, - ctx: &InterceptorContext, - cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - tracing::trace!("running `operation_read_before_execution` interceptors"); + tracing::trace!( + "running {} `read_before_execution` interceptors", + if operation { "operation" } else { "client" } + ); let mut result: Result<(), BoxError> = Ok(()); let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); - for interceptor in self.operation_interceptors() { + for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { if let Err(last_error) = result { @@ -832,16 +794,18 @@ impl Interceptors { interceptor_impl_fn!(ref read_after_deserialization); pub fn modify_before_attempt_completion( - &self, + self, ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { tracing::trace!("running `modify_before_attempt_completion` interceptors"); let mut result: Result<(), BoxError> = Ok(()); let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); - for interceptor in self.interceptors() { + for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = interceptor.modify_before_attempt_completion(&mut ctx, cfg) + if let Err(new_error) = + interceptor.modify_before_attempt_completion(&mut ctx, runtime_components, cfg) { if let Err(last_error) = result { tracing::debug!("{}", DisplayErrorContext(&*last_error)); @@ -854,16 +818,19 @@ impl Interceptors { } pub fn read_after_attempt( - &self, + self, ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { tracing::trace!("running `read_after_attempt` interceptors"); let mut result: Result<(), BoxError> = Ok(()); let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); - for interceptor in self.interceptors() { + for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = interceptor.read_after_attempt(&ctx, cfg) { + if let Err(new_error) = + interceptor.read_after_attempt(&ctx, runtime_components, cfg) + { if let Err(last_error) = result { tracing::debug!("{}", DisplayErrorContext(&*last_error)); } @@ -875,16 +842,19 @@ impl Interceptors { } pub fn modify_before_completion( - &self, + self, ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { tracing::trace!("running `modify_before_completion` interceptors"); let mut result: Result<(), BoxError> = Ok(()); let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); - for interceptor in self.interceptors() { + for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = interceptor.modify_before_completion(&mut ctx, cfg) { + if let Err(new_error) = + interceptor.modify_before_completion(&mut ctx, runtime_components, cfg) + { if let Err(last_error) = result { tracing::debug!("{}", DisplayErrorContext(&*last_error)); } @@ -896,16 +866,19 @@ impl Interceptors { } pub fn read_after_execution( - &self, + self, ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { tracing::trace!("running `read_after_execution` interceptors"); let mut result: Result<(), BoxError> = Ok(()); let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); - for interceptor in self.interceptors() { + for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = interceptor.read_after_execution(&ctx, cfg) { + if let Err(new_error) = + interceptor.read_after_execution(&ctx, runtime_components, cfg) + { if let Err(last_error) = result { tracing::debug!("{}", DisplayErrorContext(&*last_error)); } @@ -917,35 +890,20 @@ impl Interceptors { } } -#[cfg(test)] +#[cfg(all(test, feature = "test-util"))] mod tests { use crate::client::interceptors::context::Input; use crate::client::interceptors::{ disable_interceptor, BeforeTransmitInterceptorContextRef, BoxError, Interceptor, - InterceptorContext, InterceptorRegistrar, Interceptors, SharedInterceptor, + InterceptorContext, Interceptors, SharedInterceptor, }; + use crate::client::runtime_components::{RuntimeComponents, RuntimeComponentsBuilder}; use aws_smithy_types::config_bag::ConfigBag; #[derive(Debug)] struct TestInterceptor; impl Interceptor for TestInterceptor {} - #[test] - fn register_interceptor() { - let mut registrar = InterceptorRegistrar::default(); - registrar.register(SharedInterceptor::new(TestInterceptor)); - assert_eq!(1, registrar.interceptors.len()); - } - - #[test] - fn bulk_register_interceptors() { - let mut registrar = InterceptorRegistrar::default(); - let number_of_interceptors = 3; - let interceptors = vec![SharedInterceptor::new(TestInterceptor); number_of_interceptors]; - registrar.extend(interceptors); - assert_eq!(number_of_interceptors, registrar.interceptors.len()); - } - #[test] fn test_disable_interceptors() { #[derive(Debug)] @@ -954,42 +912,43 @@ mod tests { fn read_before_transmit( &self, _context: &BeforeTransmitInterceptorContextRef<'_>, + _rc: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { Err("boom".into()) } } - let mut interceptors = Interceptors::new(); - let interceptors_vec = vec![ - SharedInterceptor::new(PanicInterceptor), - SharedInterceptor::new(TestInterceptor), - ]; - interceptors - .client_interceptors_mut() - .extend(interceptors_vec); + let rc = RuntimeComponentsBuilder::for_tests() + .with_interceptor(SharedInterceptor::new(PanicInterceptor)) + .with_interceptor(SharedInterceptor::new(TestInterceptor)) + .build() + .unwrap(); + let mut cfg = ConfigBag::base(); + let interceptors = Interceptors::new(rc.interceptors()); assert_eq!( interceptors - .interceptors() + .into_iter() .filter(|i| i.if_enabled(&cfg).is_some()) .count(), 2 ); - interceptors - .read_before_transmit(&InterceptorContext::new(Input::new(5)), &mut cfg) + + Interceptors::new(rc.interceptors()) + .read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg) .expect_err("interceptor returns error"); cfg.interceptor_state() .store_put(disable_interceptor::("test")); assert_eq!( - interceptors - .interceptors() + Interceptors::new(rc.interceptors()) + .into_iter() .filter(|i| i.if_enabled(&cfg).is_some()) .count(), 1 ); // shouldn't error because interceptors won't run - interceptors - .read_before_transmit(&InterceptorContext::new(Input::new(5)), &mut cfg) + Interceptors::new(rc.interceptors()) + .read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg) .expect("interceptor is now disabled"); } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index 9384509900..ed68be978d 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -114,25 +114,21 @@ pub trait EndpointResolver: Send + Sync + fmt::Debug { fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future; } -#[derive(Debug)] -pub struct DynEndpointResolver(Box); +#[derive(Clone, Debug)] +pub struct SharedEndpointResolver(Arc); -impl DynEndpointResolver { +impl SharedEndpointResolver { pub fn new(endpoint_resolver: impl EndpointResolver + 'static) -> Self { - Self(Box::new(endpoint_resolver)) + Self(Arc::new(endpoint_resolver)) } } -impl EndpointResolver for DynEndpointResolver { +impl EndpointResolver for SharedEndpointResolver { fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future { self.0.resolve_endpoint(params) } } -impl Storable for DynEndpointResolver { - type Storer = StoreReplace; -} - /// Informs the orchestrator on whether or not the request body needs to be loaded into memory before transmit. /// /// This enum gets placed into the `ConfigBag` to change the orchestrator behavior. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index 1e1ea497e3..37aa72d9ce 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -4,7 +4,7 @@ */ use crate::client::interceptors::context::InterceptorContext; -use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use aws_smithy_types::config_bag::ConfigBag; use std::fmt::Debug; use std::time::Duration; use tracing::trace; @@ -30,42 +30,50 @@ impl ShouldAttempt { } pub trait RetryStrategy: Send + Sync + Debug { - fn should_attempt_initial_request(&self, cfg: &ConfigBag) -> Result; + fn should_attempt_initial_request( + &self, + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + ) -> Result; fn should_attempt_retry( &self, context: &InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &ConfigBag, ) -> Result; } -#[derive(Debug)] -pub struct DynRetryStrategy(Box); +#[derive(Clone, Debug)] +pub struct SharedRetryStrategy(Arc); -impl DynRetryStrategy { +impl SharedRetryStrategy { pub fn new(retry_strategy: impl RetryStrategy + 'static) -> Self { - Self(Box::new(retry_strategy)) + Self(Arc::new(retry_strategy)) } } -impl RetryStrategy for DynRetryStrategy { - fn should_attempt_initial_request(&self, cfg: &ConfigBag) -> Result { - self.0.should_attempt_initial_request(cfg) +impl RetryStrategy for SharedRetryStrategy { + fn should_attempt_initial_request( + &self, + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + ) -> Result { + self.0 + .should_attempt_initial_request(runtime_components, cfg) } fn should_attempt_retry( &self, context: &InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &ConfigBag, ) -> Result { - self.0.should_attempt_retry(context, cfg) + self.0 + .should_attempt_retry(context, runtime_components, cfg) } } -impl Storable for DynRetryStrategy { - type Storer = StoreReplace; -} - #[non_exhaustive] #[derive(Clone, Eq, PartialEq, Debug)] pub enum RetryReason { @@ -83,9 +91,9 @@ pub trait ClassifyRetry: Send + Sync + Debug { fn name(&self) -> &'static str; } -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct RetryClassifiers { - inner: Vec>, + inner: Vec>, } impl RetryClassifiers { @@ -98,8 +106,7 @@ impl RetryClassifiers { } pub fn with_classifier(mut self, retry_classifier: impl ClassifyRetry + 'static) -> Self { - self.inner.push(Box::new(retry_classifier)); - + self.inner.push(Arc::new(retry_classifier)); self } @@ -107,10 +114,6 @@ impl RetryClassifiers { // pub fn map_classifiers(mut self, fun: Fn() -> RetryClassifiers) } -impl Storable for RetryClassifiers { - type Storer = StoreReplace; -} - impl ClassifyRetry for RetryClassifiers { fn classify_retry(&self, ctx: &InterceptorContext) -> Option { // return the first non-None result @@ -160,5 +163,7 @@ mod test_util { } use crate::box_error::BoxError; +use crate::client::runtime_components::RuntimeComponents; +use std::sync::Arc; #[cfg(feature = "test-util")] pub use test_util::AlwaysRetry; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs new file mode 100644 index 0000000000..9b24628997 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs @@ -0,0 +1,786 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::client::auth::{ + AuthSchemeId, HttpAuthScheme, SharedAuthOptionResolver, SharedHttpAuthScheme, +}; +use crate::client::connectors::SharedConnector; +use crate::client::identity::{ConfiguredIdentityResolver, SharedIdentityResolver}; +use crate::client::interceptors::SharedInterceptor; +use crate::client::orchestrator::SharedEndpointResolver; +use crate::client::retries::{RetryClassifiers, SharedRetryStrategy}; +use aws_smithy_async::rt::sleep::SharedAsyncSleep; +use aws_smithy_async::time::SharedTimeSource; +use std::fmt; + +pub(crate) static EMPTY_RUNTIME_COMPONENTS_BUILDER: RuntimeComponentsBuilder = + RuntimeComponentsBuilder::new("empty"); + +/// Internal to `declare_runtime_components!`. +/// +/// Merges a field from one builder into another. +macro_rules! merge { + (Option $other:ident . $name:ident => $self:ident) => { + $self.$name = $other.$name.clone().or($self.$name.take()); + }; + (Vec $other:ident . $name:ident => $self:ident) => { + if !$other.$name.is_empty() { + $self.$name.extend($other.$name.iter().cloned()); + } + }; +} +/// Internal to `declare_runtime_components!`. +/// +/// This is used when creating the builder's `build` method +/// to populate each individual field value. The `required`/`atLeastOneRequired` +/// validations are performed here. +macro_rules! builder_field_value { + (Option $self:ident . $name:ident) => { + $self.$name + }; + (Option $self:ident . $name:ident required) => { + $self.$name.ok_or(BuildError(concat!( + "the `", + stringify!($name), + "` runtime component is required" + )))? + }; + (Vec $self:ident . $name:ident) => { + $self.$name + }; + (Vec $self:ident . $name:ident atLeastOneRequired) => {{ + if $self.$name.is_empty() { + return Err(BuildError(concat!( + "at least one `", + stringify!($name), + "` runtime component is required" + ))); + } + $self.$name + }}; +} +/// Internal to `declare_runtime_components!`. +/// +/// Converts the field type from `Option` or `Vec` into `Option>` or `Vec>` respectively. +/// Also removes the `Option` wrapper for required fields in the non-builder struct. +macro_rules! runtime_component_field_type { + (Option $inner_type:ident) => { + Option> + }; + (Option $inner_type:ident required) => { + Tracked<$inner_type> + }; + (Vec $inner_type:ident) => { + Vec> + }; + (Vec $inner_type:ident atLeastOneRequired) => { + Vec> + }; +} +/// Internal to `declare_runtime_components!`. +/// +/// Converts an `$outer_type` into an empty instantiation for that type. +/// This is needed since `Default::default()` can't be used in a `const` function, +/// and `RuntimeComponentsBuilder::new()` is `const`. +macro_rules! empty_builder_value { + (Option) => { + None + }; + (Vec) => { + Vec::new() + }; +} + +/// Macro to define the structs for both `RuntimeComponents` and `RuntimeComponentsBuilder`. +/// +/// This is a macro in order to keep the fields consistent between the two, and to automatically +/// update the `merge_from` and `build` methods when new components are added. +/// +/// It also facilitates unit testing since the overall mechanism can be unit tested with different +/// fields that are easy to check in tests (testing with real components makes it hard +/// to tell that the correct component was selected when merging builders). +/// +/// # Example usage +/// +/// The two identifiers after "fields for" become the names of the struct and builder respectively. +/// Following that, all the fields are specified. Fields MUST be wrapped in `Option` or `Vec`. +/// To make a field required in the non-builder struct, add `#[required]` for `Option` fields, or +/// `#[atLeastOneRequired]` for `Vec` fields. +/// +/// ```no_compile +/// declare_runtime_components! { +/// fields for TestRc and TestRcBuilder { +/// some_optional_string: Option, +/// +/// some_optional_vec: Vec, +/// +/// #[required] +/// some_required_string: Option, +/// +/// #[atLeastOneRequired] +/// some_required_vec: Vec, +/// } +/// } +/// ``` +macro_rules! declare_runtime_components { + (fields for $rc_name:ident and $builder_name:ident { + $($(#[$option:ident])? $field_name:ident : $outer_type:ident<$inner_type:ident> ,)+ + }) => { + /// Components that can only be set in runtime plugins that the orchestrator uses directly to call an operation. + #[derive(Clone, Debug)] + pub struct $rc_name { + $($field_name: runtime_component_field_type!($outer_type $inner_type $($option)?),)+ + } + + #[derive(Clone, Debug, Default)] + pub struct $builder_name { + builder_name: &'static str, + $($field_name: $outer_type>,)+ + } + impl $builder_name { + /// Creates a new builder. + /// + /// Since multiple builders are merged together to make the final [`RuntimeComponents`], + /// all components added by this builder are associated with the given `name` so that + /// the origin of a component can be easily found when debugging. + pub const fn new(name: &'static str) -> Self { + Self { + builder_name: name, + $($field_name: empty_builder_value!($outer_type),)+ + } + } + + /// Merge in components from another builder. + pub fn merge_from(mut self, other: &Self) -> Self { + $(merge!($outer_type other.$field_name => self);)+ + self + } + + /// Builds [`RuntimeComponents`] from this builder. + pub fn build(self) -> Result<$rc_name, BuildError> { + Ok($rc_name { + $($field_name: builder_field_value!($outer_type self.$field_name $($option)?),)+ + }) + } + } + }; +} + +declare_runtime_components! { + fields for RuntimeComponents and RuntimeComponentsBuilder { + #[required] + auth_option_resolver: Option, + + // A connector is not required since a client could technically only be used for presigning + connector: Option, + + #[required] + endpoint_resolver: Option, + + #[atLeastOneRequired] + http_auth_schemes: Vec, + + #[atLeastOneRequired] + identity_resolvers: Vec, + + interceptors: Vec, + + retry_classifiers: Option, + + #[required] + retry_strategy: Option, + + time_source: Option, + + sleep_impl: Option, + } +} + +impl RuntimeComponents { + /// Returns a builder for runtime components. + pub fn builder() -> RuntimeComponentsBuilder { + Default::default() + } + + /// Returns the auth option resolver. + pub fn auth_option_resolver(&self) -> SharedAuthOptionResolver { + self.auth_option_resolver.value.clone() + } + + /// Returns the connector. + pub fn connector(&self) -> Option { + self.connector.as_ref().map(|s| s.value.clone()) + } + + /// Returns the endpoint resolver. + pub fn endpoint_resolver(&self) -> SharedEndpointResolver { + self.endpoint_resolver.value.clone() + } + + /// Returns the requested auth scheme if it is set. + pub fn http_auth_scheme(&self, scheme_id: AuthSchemeId) -> Option { + self.http_auth_schemes + .iter() + .find(|s| s.value.scheme_id() == scheme_id) + .map(|s| s.value.clone()) + } + + /// Returns an iterator over the interceptors. + pub fn interceptors(&self) -> impl Iterator + '_ { + self.interceptors.iter().map(|s| s.value.clone()) + } + + /// Returns the retry classifiers. + pub fn retry_classifiers(&self) -> Option<&RetryClassifiers> { + self.retry_classifiers.as_ref().map(|s| &s.value) + } + + /// Returns the retry strategy. + pub fn retry_strategy(&self) -> SharedRetryStrategy { + self.retry_strategy.value.clone() + } + + /// Returns the async sleep implementation. + pub fn sleep_impl(&self) -> Option { + self.sleep_impl.as_ref().map(|s| s.value.clone()) + } + + /// Returns the time source. + pub fn time_source(&self) -> Option { + self.time_source.as_ref().map(|s| s.value.clone()) + } +} + +impl RuntimeComponentsBuilder { + /// Returns the auth option resolver. + pub fn auth_option_resolver(&self) -> Option { + self.auth_option_resolver.as_ref().map(|s| s.value.clone()) + } + + /// Sets the auth option resolver. + pub fn set_auth_option_resolver( + &mut self, + auth_option_resolver: Option, + ) -> &mut Self { + self.auth_option_resolver = + auth_option_resolver.map(|r| Tracked::new(self.builder_name, r)); + self + } + + /// Sets the auth option resolver. + pub fn with_auth_option_resolver( + mut self, + auth_option_resolver: Option, + ) -> Self { + self.set_auth_option_resolver(auth_option_resolver); + self + } + + /// Returns the connector. + pub fn connector(&self) -> Option { + self.connector.as_ref().map(|s| s.value.clone()) + } + + /// Sets the connector. + pub fn set_connector(&mut self, connector: Option) -> &mut Self { + self.connector = connector.map(|c| Tracked::new(self.builder_name, c)); + self + } + + /// Sets the connector. + pub fn with_connector(mut self, connector: Option) -> Self { + self.set_connector(connector); + self + } + + /// Returns the endpoint resolver. + pub fn endpoint_resolver(&self) -> Option { + self.endpoint_resolver.as_ref().map(|s| s.value.clone()) + } + + /// Sets the endpoint resolver. + pub fn set_endpoint_resolver( + &mut self, + endpoint_resolver: Option, + ) -> &mut Self { + self.endpoint_resolver = endpoint_resolver.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Sets the endpoint resolver. + pub fn with_endpoint_resolver( + mut self, + endpoint_resolver: Option, + ) -> Self { + self.set_endpoint_resolver(endpoint_resolver); + self + } + + /// Returns the HTTP auth schemes. + pub fn http_auth_schemes(&self) -> impl Iterator + '_ { + self.http_auth_schemes.iter().map(|s| s.value.clone()) + } + + /// Adds a HTTP auth scheme. + pub fn push_http_auth_scheme(&mut self, auth_scheme: SharedHttpAuthScheme) -> &mut Self { + self.http_auth_schemes + .push(Tracked::new(self.builder_name, auth_scheme)); + self + } + + /// Adds a HTTP auth scheme. + pub fn with_http_auth_scheme(mut self, auth_scheme: SharedHttpAuthScheme) -> Self { + self.push_http_auth_scheme(auth_scheme); + self + } + + /// Adds an identity resolver. + pub fn push_identity_resolver( + &mut self, + scheme_id: AuthSchemeId, + identity_resolver: SharedIdentityResolver, + ) -> &mut Self { + self.identity_resolvers.push(Tracked::new( + self.builder_name, + ConfiguredIdentityResolver::new(scheme_id, identity_resolver), + )); + self + } + + /// Adds an identity resolver. + pub fn with_identity_resolver( + mut self, + scheme_id: AuthSchemeId, + identity_resolver: SharedIdentityResolver, + ) -> Self { + self.push_identity_resolver(scheme_id, identity_resolver); + self + } + + /// Returns the interceptors. + pub fn interceptors(&self) -> impl Iterator + '_ { + self.interceptors.iter().map(|s| s.value.clone()) + } + + /// Adds all the given interceptors. + pub fn extend_interceptors( + &mut self, + interceptors: impl Iterator, + ) -> &mut Self { + self.interceptors + .extend(interceptors.map(|s| Tracked::new(self.builder_name, s))); + self + } + + /// Adds an interceptor. + pub fn push_interceptor(&mut self, interceptor: SharedInterceptor) -> &mut Self { + self.interceptors + .push(Tracked::new(self.builder_name, interceptor)); + self + } + + /// Adds an interceptor. + pub fn with_interceptor(mut self, interceptor: SharedInterceptor) -> Self { + self.push_interceptor(interceptor); + self + } + + /// Directly sets the interceptors and clears out any that were previously pushed. + pub fn set_interceptors( + &mut self, + interceptors: impl Iterator, + ) -> &mut Self { + self.interceptors.clear(); + self.interceptors + .extend(interceptors.map(|s| Tracked::new(self.builder_name, s))); + self + } + + /// Directly sets the interceptors and clears out any that were previously pushed. + pub fn with_interceptors( + mut self, + interceptors: impl Iterator, + ) -> Self { + self.set_interceptors(interceptors); + self + } + + /// Returns the retry classifiers. + pub fn retry_classifiers(&self) -> Option<&RetryClassifiers> { + self.retry_classifiers.as_ref().map(|s| &s.value) + } + + /// Sets the retry classifiers. + pub fn set_retry_classifiers( + &mut self, + retry_classifiers: Option, + ) -> &mut Self { + self.retry_classifiers = retry_classifiers.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Sets the retry classifiers. + pub fn with_retry_classifiers(mut self, retry_classifiers: Option) -> Self { + self.retry_classifiers = retry_classifiers.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Returns the retry strategy. + pub fn retry_strategy(&self) -> Option { + self.retry_strategy.as_ref().map(|s| s.value.clone()) + } + + /// Sets the retry strategy. + pub fn set_retry_strategy(&mut self, retry_strategy: Option) -> &mut Self { + self.retry_strategy = retry_strategy.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Sets the retry strategy. + pub fn with_retry_strategy(mut self, retry_strategy: Option) -> Self { + self.retry_strategy = retry_strategy.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Returns the async sleep implementation. + pub fn sleep_impl(&self) -> Option { + self.sleep_impl.as_ref().map(|s| s.value.clone()) + } + + /// Sets the async sleep implementation. + pub fn set_sleep_impl(&mut self, sleep_impl: Option) -> &mut Self { + self.sleep_impl = sleep_impl.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Sets the async sleep implementation. + pub fn with_sleep_impl(mut self, sleep_impl: Option) -> Self { + self.sleep_impl = sleep_impl.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Returns the time source. + pub fn time_source(&self) -> Option { + self.time_source.as_ref().map(|s| s.value.clone()) + } + + /// Sets the time source. + pub fn set_time_source(&mut self, time_source: Option) -> &mut Self { + self.time_source = time_source.map(|s| Tracked::new(self.builder_name, s)); + self + } + + /// Sets the time source. + pub fn with_time_source(mut self, time_source: Option) -> Self { + self.time_source = time_source.map(|s| Tracked::new(self.builder_name, s)); + self + } +} + +#[derive(Clone, Debug)] +#[cfg_attr(test, derive(Eq, PartialEq))] +struct Tracked { + _origin: &'static str, + value: T, +} + +impl Tracked { + fn new(origin: &'static str, value: T) -> Self { + Self { + _origin: origin, + value, + } + } +} + +impl RuntimeComponentsBuilder { + /// Creates a runtime components builder with all the required components filled in with fake (panicking) implementations. + #[cfg(feature = "test-util")] + pub fn for_tests() -> Self { + use crate::client::auth::AuthOptionResolver; + use crate::client::connectors::Connector; + use crate::client::identity::Identity; + use crate::client::identity::IdentityResolver; + use crate::client::orchestrator::{EndpointResolver, EndpointResolverParams, Future}; + use crate::client::retries::RetryStrategy; + use aws_smithy_async::rt::sleep::AsyncSleep; + use aws_smithy_async::time::TimeSource; + use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::endpoint::Endpoint; + + #[derive(Debug)] + struct FakeAuthOptionResolver; + impl AuthOptionResolver for FakeAuthOptionResolver { + fn resolve_auth_options( + &self, + _: &crate::client::auth::AuthOptionResolverParams, + ) -> Result, crate::box_error::BoxError> + { + unreachable!("fake auth option resolver must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeConnector; + impl Connector for FakeConnector { + fn call( + &self, + _: crate::client::orchestrator::HttpRequest, + ) -> crate::client::orchestrator::BoxFuture + { + unreachable!("fake connector must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeEndpointResolver; + impl EndpointResolver for FakeEndpointResolver { + fn resolve_endpoint(&self, _: &EndpointResolverParams) -> Future { + unreachable!("fake endpoint resolver must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeHttpAuthScheme; + impl HttpAuthScheme for FakeHttpAuthScheme { + fn scheme_id(&self) -> AuthSchemeId { + AuthSchemeId::new("fake") + } + + fn identity_resolver( + &self, + _: &dyn GetIdentityResolver, + ) -> Option { + None + } + + fn request_signer(&self) -> &dyn crate::client::auth::HttpRequestSigner { + unreachable!("fake http auth scheme must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeIdentityResolver; + impl IdentityResolver for FakeIdentityResolver { + fn resolve_identity(&self, _: &ConfigBag) -> Future { + unreachable!("fake identity resolver must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeRetryStrategy; + impl RetryStrategy for FakeRetryStrategy { + fn should_attempt_initial_request( + &self, + _: &RuntimeComponents, + _: &ConfigBag, + ) -> Result + { + unreachable!("fake retry strategy must be overridden for this test") + } + + fn should_attempt_retry( + &self, + _: &crate::client::interceptors::context::InterceptorContext, + _: &RuntimeComponents, + _: &ConfigBag, + ) -> Result + { + unreachable!("fake retry strategy must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeTimeSource; + impl TimeSource for FakeTimeSource { + fn now(&self) -> std::time::SystemTime { + unreachable!("fake time source must be overridden for this test") + } + } + + #[derive(Debug)] + struct FakeSleep; + impl AsyncSleep for FakeSleep { + fn sleep(&self, _: std::time::Duration) -> aws_smithy_async::rt::sleep::Sleep { + unreachable!("fake sleep must be overridden for this test") + } + } + + Self::new("aws_smithy_runtime_api::client::runtime_components::RuntimeComponentBuilder::for_tests") + .with_auth_option_resolver(Some(SharedAuthOptionResolver::new(FakeAuthOptionResolver))) + .with_connector(Some(SharedConnector::new(FakeConnector))) + .with_endpoint_resolver(Some(SharedEndpointResolver::new(FakeEndpointResolver))) + .with_http_auth_scheme(SharedHttpAuthScheme::new(FakeHttpAuthScheme)) + .with_identity_resolver(AuthSchemeId::new("fake"), SharedIdentityResolver::new(FakeIdentityResolver)) + .with_retry_classifiers(Some(RetryClassifiers::new())) + .with_retry_strategy(Some(SharedRetryStrategy::new(FakeRetryStrategy))) + .with_time_source(Some(SharedTimeSource::new(FakeTimeSource))) + .with_sleep_impl(Some(SharedAsyncSleep::new(FakeSleep))) + } +} + +#[derive(Debug)] +pub struct BuildError(&'static str); + +impl std::error::Error for BuildError {} + +impl fmt::Display for BuildError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +/// A trait for retrieving a shared identity resolver. +/// +/// This trait exists so that [`HttpAuthScheme::identity_resolver`](crate::client::auth::HttpAuthScheme::identity_resolver) +/// can have access to configured identity resolvers without having access to all the runtime components. +pub trait GetIdentityResolver: Send + Sync { + /// Returns the requested identity resolver if it is set. + fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option; +} + +impl GetIdentityResolver for RuntimeComponents { + fn identity_resolver(&self, scheme_id: AuthSchemeId) -> Option { + self.identity_resolvers + .iter() + .find(|s| s.value.scheme_id() == scheme_id) + .map(|s| s.value.identity_resolver()) + } +} + +#[cfg(all(test, feature = "test-util"))] +mod tests { + use super::*; + + #[test] + #[allow(unreachable_pub)] + #[allow(dead_code)] + fn the_builders_should_merge() { + declare_runtime_components! { + fields for TestRc and TestRcBuilder { + #[required] + some_required_string: Option, + + some_optional_string: Option, + + #[atLeastOneRequired] + some_required_vec: Vec, + + some_optional_vec: Vec, + } + } + + let builder1 = TestRcBuilder { + builder_name: "builder1", + some_required_string: Some(Tracked::new("builder1", "override_me".into())), + some_optional_string: Some(Tracked::new("builder1", "override_me optional".into())), + some_required_vec: vec![Tracked::new("builder1", "first".into())], + some_optional_vec: vec![Tracked::new("builder1", "first optional".into())], + }; + let builder2 = TestRcBuilder { + builder_name: "builder2", + some_required_string: Some(Tracked::new("builder2", "override_me_too".into())), + some_optional_string: Some(Tracked::new("builder2", "override_me_too optional".into())), + some_required_vec: vec![Tracked::new("builder2", "second".into())], + some_optional_vec: vec![Tracked::new("builder2", "second optional".into())], + }; + let builder3 = TestRcBuilder { + builder_name: "builder3", + some_required_string: Some(Tracked::new("builder3", "correct".into())), + some_optional_string: Some(Tracked::new("builder3", "correct optional".into())), + some_required_vec: vec![Tracked::new("builder3", "third".into())], + some_optional_vec: vec![Tracked::new("builder3", "third optional".into())], + }; + let rc = TestRcBuilder::new("root") + .merge_from(&builder1) + .merge_from(&builder2) + .merge_from(&builder3) + .build() + .expect("success"); + assert_eq!( + Tracked::new("builder3", "correct".to_string()), + rc.some_required_string + ); + assert_eq!( + Some(Tracked::new("builder3", "correct optional".to_string())), + rc.some_optional_string + ); + assert_eq!( + vec![ + Tracked::new("builder1", "first".to_string()), + Tracked::new("builder2", "second".into()), + Tracked::new("builder3", "third".into()) + ], + rc.some_required_vec + ); + assert_eq!( + vec![ + Tracked::new("builder1", "first optional".to_string()), + Tracked::new("builder2", "second optional".into()), + Tracked::new("builder3", "third optional".into()) + ], + rc.some_optional_vec + ); + } + + #[test] + #[allow(unreachable_pub)] + #[allow(dead_code)] + #[should_panic(expected = "the `_some_string` runtime component is required")] + fn require_field_singular() { + declare_runtime_components! { + fields for TestRc and TestRcBuilder { + #[required] + _some_string: Option, + } + } + + let rc = TestRcBuilder::new("test").build().unwrap(); + + // Ensure the correct types were used + let _: Tracked = rc._some_string; + } + + #[test] + #[allow(unreachable_pub)] + #[allow(dead_code)] + #[should_panic(expected = "at least one `_some_vec` runtime component is required")] + fn require_field_plural() { + declare_runtime_components! { + fields for TestRc and TestRcBuilder { + #[atLeastOneRequired] + _some_vec: Vec, + } + } + + let rc = TestRcBuilder::new("test").build().unwrap(); + + // Ensure the correct types were used + let _: Vec> = rc._some_vec; + } + + #[test] + #[allow(unreachable_pub)] + #[allow(dead_code)] + fn optional_fields_dont_panic() { + declare_runtime_components! { + fields for TestRc and TestRcBuilder { + _some_optional_string: Option, + _some_optional_vec: Vec, + } + } + + let rc = TestRcBuilder::new("test").build().unwrap(); + + // Ensure the correct types were used + let _: Option> = rc._some_optional_string; + let _: Vec> = rc._some_optional_vec; + } + + #[test] + fn building_test_builder_should_not_panic() { + let _ = RuntimeComponentsBuilder::for_tests().build(); // should not panic + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index d0a8e18b59..609375b210 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -4,31 +4,34 @@ */ use crate::box_error::BoxError; -use crate::client::interceptors::InterceptorRegistrar; +use crate::client::runtime_components::{ + RuntimeComponentsBuilder, EMPTY_RUNTIME_COMPONENTS_BUILDER, +}; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer}; +use std::borrow::Cow; use std::fmt::Debug; use std::sync::Arc; /// RuntimePlugin Trait /// -/// A RuntimePlugin is the unit of configuration for augmenting the SDK with new behavior +/// A RuntimePlugin is the unit of configuration for augmenting the SDK with new behavior. /// -/// Runtime plugins can set configuration and register interceptors. +/// Runtime plugins can register interceptors, set runtime components, and modify configuration. pub trait RuntimePlugin: Debug + Send + Sync { fn config(&self) -> Option { None } - fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { - let _ = interceptors; + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&EMPTY_RUNTIME_COMPONENTS_BUILDER) } } #[derive(Debug, Clone)] -struct SharedRuntimePlugin(Arc); +pub struct SharedRuntimePlugin(Arc); impl SharedRuntimePlugin { - fn new(plugin: impl RuntimePlugin + 'static) -> Self { + pub fn new(plugin: impl RuntimePlugin + 'static) -> Self { Self(Arc::new(plugin)) } } @@ -38,8 +41,8 @@ impl RuntimePlugin for SharedRuntimePlugin { self.0.config() } - fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { - self.0.interceptors(interceptors) + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + self.0.runtime_components() } } @@ -68,33 +71,66 @@ impl RuntimePlugins { pub fn apply_client_configuration( &self, cfg: &mut ConfigBag, - interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { + ) -> Result { tracing::trace!("applying client runtime plugins"); + let mut builder = RuntimeComponentsBuilder::new("apply_client_configuration"); for plugin in self.client_plugins.iter() { if let Some(layer) = plugin.config() { cfg.push_shared_layer(layer); } - plugin.interceptors(interceptors); + builder = builder.merge_from(&plugin.runtime_components()); } - - Ok(()) + Ok(builder) } pub fn apply_operation_configuration( &self, cfg: &mut ConfigBag, - interceptors: &mut InterceptorRegistrar, - ) -> Result<(), BoxError> { + ) -> Result { tracing::trace!("applying operation runtime plugins"); + let mut builder = RuntimeComponentsBuilder::new("apply_operation_configuration"); for plugin in self.operation_plugins.iter() { if let Some(layer) = plugin.config() { cfg.push_shared_layer(layer); } - plugin.interceptors(interceptors); + builder = builder.merge_from(&plugin.runtime_components()); } + Ok(builder) + } +} + +#[derive(Default, Debug)] +pub struct StaticRuntimePlugin { + config: Option, + runtime_components: Option, +} + +impl StaticRuntimePlugin { + pub fn new() -> Self { + Default::default() + } + + pub fn with_config(mut self, config: FrozenLayer) -> Self { + self.config = Some(config); + self + } + + pub fn with_runtime_components(mut self, runtime_components: RuntimeComponentsBuilder) -> Self { + self.runtime_components = Some(runtime_components); + self + } +} + +impl RuntimePlugin for StaticRuntimePlugin { + fn config(&self) -> Option { + self.config.clone() + } - Ok(()) + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + self.runtime_components + .as_ref() + .map(Cow::Borrowed) + .unwrap_or_else(|| RuntimePlugin::runtime_components(self)) } } diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index cb0d8f1ae8..31722c94d4 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -14,6 +14,9 @@ pub mod auth; /// By default, the orchestrator uses a connector provided by `hyper`. pub mod connectors; +/// Utility to simplify config building for config and config overrides. +pub mod config_override; + /// The client orchestrator implementation pub mod orchestrator; diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs index 5b62491bc9..1dda4dbe58 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs @@ -13,10 +13,9 @@ use aws_smithy_runtime_api::client::auth::{ AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, }; use aws_smithy_runtime_api::client::identity::http::{Login, Token}; -use aws_smithy_runtime_api::client::identity::{ - Identity, IdentityResolvers, SharedIdentityResolver, -}; +use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver}; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; +use aws_smithy_runtime_api::client::runtime_components::{GetIdentityResolver, RuntimeComponents}; use aws_smithy_types::base64::encode; use aws_smithy_types::config_bag::ConfigBag; use http::header::HeaderName; @@ -59,7 +58,7 @@ impl HttpAuthScheme for ApiKeyAuthScheme { fn identity_resolver( &self, - identity_resolvers: &IdentityResolvers, + identity_resolvers: &dyn GetIdentityResolver, ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -82,6 +81,7 @@ impl HttpRequestSigner for ApiKeySigner { request: &mut HttpRequest, identity: &Identity, _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { let api_key = identity @@ -129,7 +129,7 @@ impl HttpAuthScheme for BasicAuthScheme { fn identity_resolver( &self, - identity_resolvers: &IdentityResolvers, + identity_resolvers: &dyn GetIdentityResolver, ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -148,6 +148,7 @@ impl HttpRequestSigner for BasicAuthSigner { request: &mut HttpRequest, identity: &Identity, _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { let login = identity @@ -187,7 +188,7 @@ impl HttpAuthScheme for BearerAuthScheme { fn identity_resolver( &self, - identity_resolvers: &IdentityResolvers, + identity_resolvers: &dyn GetIdentityResolver, ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -206,6 +207,7 @@ impl HttpRequestSigner for BearerAuthSigner { request: &mut HttpRequest, identity: &Identity, _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { let token = identity @@ -243,7 +245,7 @@ impl HttpAuthScheme for DigestAuthScheme { fn identity_resolver( &self, - identity_resolvers: &IdentityResolvers, + identity_resolvers: &dyn GetIdentityResolver, ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -262,6 +264,7 @@ impl HttpRequestSigner for DigestAuthSigner { _request: &mut HttpRequest, _identity: &Identity, _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { unimplemented!( @@ -275,6 +278,7 @@ mod tests { use super::*; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::identity::http::Login; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; #[test] fn test_api_key_signing_headers() { @@ -283,6 +287,7 @@ mod tests { location: ApiKeyLocation::Header, name: "some-header-name".into(), }; + let runtime_components = RuntimeComponentsBuilder::for_tests().build().unwrap(); let config_bag = ConfigBag::base(); let identity = Identity::new(Token::new("some-token", None), None); let mut request = http::Request::builder() @@ -294,6 +299,7 @@ mod tests { &mut request, &identity, AuthSchemeEndpointConfig::empty(), + &runtime_components, &config_bag, ) .expect("success"); @@ -311,6 +317,7 @@ mod tests { location: ApiKeyLocation::Query, name: "some-query-name".into(), }; + let runtime_components = RuntimeComponentsBuilder::for_tests().build().unwrap(); let config_bag = ConfigBag::base(); let identity = Identity::new(Token::new("some-token", None), None); let mut request = http::Request::builder() @@ -322,6 +329,7 @@ mod tests { &mut request, &identity, AuthSchemeEndpointConfig::empty(), + &runtime_components, &config_bag, ) .expect("success"); @@ -335,6 +343,7 @@ mod tests { #[test] fn test_basic_auth() { let signer = BasicAuthSigner; + let runtime_components = RuntimeComponentsBuilder::for_tests().build().unwrap(); let config_bag = ConfigBag::base(); let identity = Identity::new(Login::new("Aladdin", "open sesame", None), None); let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); @@ -344,6 +353,7 @@ mod tests { &mut request, &identity, AuthSchemeEndpointConfig::empty(), + &runtime_components, &config_bag, ) .expect("success"); @@ -358,6 +368,7 @@ mod tests { let signer = BearerAuthSigner; let config_bag = ConfigBag::base(); + let runtime_components = RuntimeComponentsBuilder::for_tests().build().unwrap(); let identity = Identity::new(Token::new("some-token", None), None); let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); signer @@ -365,6 +376,7 @@ mod tests { &mut request, &identity, AuthSchemeEndpointConfig::empty(), + &runtime_components, &config_bag, ) .expect("success"); diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs index 6fa815046d..56f9633658 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs @@ -10,13 +10,14 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::{ AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, SharedHttpAuthScheme, }; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; -use aws_smithy_runtime_api::client::identity::{ - Identity, IdentityResolvers, SharedIdentityResolver, -}; +use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver}; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; +use aws_smithy_runtime_api::client::runtime_components::{ + GetIdentityResolver, RuntimeComponents, RuntimeComponentsBuilder, +}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; +use aws_smithy_types::config_bag::ConfigBag; +use std::borrow::Cow; pub const NO_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("no_auth"); @@ -26,7 +27,7 @@ pub const NO_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("no_auth"); /// a Smithy `@optionalAuth` trait. #[non_exhaustive] #[derive(Debug)] -pub struct NoAuthRuntimePlugin(FrozenLayer); +pub struct NoAuthRuntimePlugin(RuntimeComponentsBuilder); impl Default for NoAuthRuntimePlugin { fn default() -> Self { @@ -36,19 +37,20 @@ impl Default for NoAuthRuntimePlugin { impl NoAuthRuntimePlugin { pub fn new() -> Self { - let mut cfg = Layer::new("NoAuth"); - cfg.push_identity_resolver( - NO_AUTH_SCHEME_ID, - SharedIdentityResolver::new(NoAuthIdentityResolver::new()), - ); - cfg.push_http_auth_scheme(SharedHttpAuthScheme::new(NoAuthScheme::new())); - Self(cfg.freeze()) + Self( + RuntimeComponentsBuilder::new("NoAuthRuntimePlugin") + .with_identity_resolver( + NO_AUTH_SCHEME_ID, + SharedIdentityResolver::new(NoAuthIdentityResolver::new()), + ) + .with_http_auth_scheme(SharedHttpAuthScheme::new(NoAuthScheme::new())), + ) } } impl RuntimePlugin for NoAuthRuntimePlugin { - fn config(&self) -> Option { - Some(self.0.clone()) + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.0) } } @@ -72,6 +74,7 @@ impl HttpRequestSigner for NoAuthSigner { _request: &mut HttpRequest, _identity: &Identity, _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { Ok(()) @@ -85,7 +88,7 @@ impl HttpAuthScheme for NoAuthScheme { fn identity_resolver( &self, - identity_resolvers: &IdentityResolvers, + identity_resolvers: &dyn GetIdentityResolver, ) -> Option { identity_resolvers.identity_resolver(NO_AUTH_SCHEME_ID) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/config_override.rs b/rust-runtime/aws-smithy-runtime/src/client/config_override.rs new file mode 100644 index 0000000000..c89352e139 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/src/client/config_override.rs @@ -0,0 +1,254 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use aws_smithy_async::rt::sleep::SharedAsyncSleep; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; +use aws_smithy_types::config_bag::{FrozenLayer, Layer, Storable, Store, StoreReplace}; + +macro_rules! component { + ($typ:ty, $accessor:ident, $latest_accessor:ident) => { + pub fn $accessor(&self) -> Option<$typ> { + fallback_component!(self, $typ, $accessor) + } + + pub fn $latest_accessor(&self) -> Option<$typ> { + latest_component!(self, $typ, $accessor) + } + }; +} +macro_rules! fallback_component { + ($self:ident, $typ:ty, $accessor:ident) => { + match &$self.inner { + Inner::Initial(initial) => initial.components.$accessor(), + Inner::Override(overrid) => overrid + .components + .$accessor() + .or_else(|| overrid.initial_components.$accessor()), + } + }; +} +macro_rules! latest_component { + ($self:ident, $typ:ty, $accessor:ident) => { + match &$self.inner { + Inner::Initial(initial) => initial.components.$accessor(), + Inner::Override(overrid) => overrid.components.$accessor(), + } + }; +} + +struct Initial<'a> { + config: &'a mut Layer, + components: &'a mut RuntimeComponentsBuilder, +} + +struct Override<'a> { + initial_config: FrozenLayer, + initial_components: &'a RuntimeComponentsBuilder, + config: &'a mut Layer, + components: &'a mut RuntimeComponentsBuilder, +} + +enum Inner<'a> { + Initial(Initial<'a>), + Override(Override<'a>), +} + +/// Utility to simplify config building and config overrides. +/// +/// The resolver allows the same initialization logic to be reused +/// for both initial config and override config. +/// +/// This resolver can be initialized to one of two modes: +/// 1. _Initial mode_: The resolver is being used in a service `Config` builder's `build()` method, and thus, +/// there is no config override at this point. +/// 2. _Override mode_: The resolver is being used by the `ConfigOverrideRuntimePlugin`'s constructor and needs +/// to incorporate both the original config and the given config override for this operation. +/// +/// In all the methods on [`Resolver`], the term "latest" refers to the initial config when in _Initial mode_, +/// and to config override when in _Override mode_. +pub struct Resolver<'a> { + inner: Inner<'a>, +} + +impl<'a> Resolver<'a> { + /// Construct a new [`Resolver`] in _initial mode_. + pub fn initial(config: &'a mut Layer, components: &'a mut RuntimeComponentsBuilder) -> Self { + Self { + inner: Inner::Initial(Initial { config, components }), + } + } + + /// Construct a new [`Resolver`] in _override mode_. + pub fn overrid( + initial_config: FrozenLayer, + initial_components: &'a RuntimeComponentsBuilder, + config: &'a mut Layer, + components: &'a mut RuntimeComponentsBuilder, + ) -> Self { + Self { + inner: Inner::Override(Override { + initial_config, + initial_components, + config, + components, + }), + } + } + + /// Returns true if in _initial mode_. + pub fn is_initial(&self) -> bool { + matches!(self.inner, Inner::Initial(_)) + } + + /// Returns a mutable reference to the latest config. + pub fn config_mut(&mut self) -> &mut Layer { + match &mut self.inner { + Inner::Initial(initial) => initial.config, + Inner::Override(overrid) => overrid.config, + } + } + + /// Returns a mutable reference to the latest runtime components. + pub fn runtime_components_mut(&mut self) -> &mut RuntimeComponentsBuilder { + match &mut self.inner { + Inner::Initial(initial) => initial.components, + Inner::Override(overrid) => overrid.components, + } + } + + /// Returns true if the latest config has `T` set. + /// + /// The "latest" is initial for `Resolver::Initial`, and override for `Resolver::Override`. + pub fn is_latest_set(&self) -> bool + where + T: Storable>, + { + self.config().load::().is_some() + } + + /// Returns true if `T` is set anywhere. + pub fn is_set(&self) -> bool + where + T: Storable>, + { + match &self.inner { + Inner::Initial(initial) => initial.config.load::().is_some(), + Inner::Override(overrid) => { + overrid.initial_config.load::().is_some() || overrid.config.load::().is_some() + } + } + } + + /// Resolves the value `T` with fallback + pub fn resolve_config(&self) -> ::ReturnedType<'_> + where + T: Storable>, + { + let mut maybe_value = self.config().load::(); + if maybe_value.is_none() { + // Try to fallback + if let Inner::Override(overrid) = &self.inner { + maybe_value = overrid.initial_config.load::() + } + } + maybe_value + } + + // Add additional component methods as needed + component!(SharedAsyncSleep, sleep_impl, latest_sleep_impl); + + fn config(&self) -> &Layer { + match &self.inner { + Inner::Initial(initial) => initial.config, + Inner::Override(overrid) => overrid.config, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use aws_smithy_types::config_bag::CloneableLayer; + + #[derive(Clone, Debug)] + struct TestStorable(String); + impl Storable for TestStorable { + type Storer = StoreReplace; + } + + #[test] + fn initial_mode_config() { + let mut config = Layer::new("test"); + let mut components = RuntimeComponentsBuilder::new("test"); + + let mut resolver = Resolver::initial(&mut config, &mut components); + assert!(resolver.is_initial()); + assert!(!resolver.is_latest_set::()); + assert!(!resolver.is_set::()); + assert!(resolver.resolve_config::().is_none()); + + resolver.config_mut().store_put(TestStorable("test".into())); + assert!(resolver.is_latest_set::()); + assert!(resolver.is_set::()); + assert_eq!("test", resolver.resolve_config::().unwrap().0); + } + + #[test] + fn override_mode_config() { + let mut initial_config = CloneableLayer::new("initial"); + let initial_components = RuntimeComponentsBuilder::new("initial"); + let mut config = Layer::new("override"); + let mut components = RuntimeComponentsBuilder::new("override"); + + let resolver = Resolver::overrid( + initial_config.clone().freeze(), + &initial_components, + &mut config, + &mut components, + ); + assert!(!resolver.is_initial()); + assert!(!resolver.is_latest_set::()); + assert!(!resolver.is_set::()); + assert!(resolver.resolve_config::().is_none()); + + initial_config.store_put(TestStorable("test".into())); + let resolver = Resolver::overrid( + initial_config.clone().freeze(), + &initial_components, + &mut config, + &mut components, + ); + assert!(!resolver.is_latest_set::()); + assert!(resolver.is_set::()); + assert_eq!("test", resolver.resolve_config::().unwrap().0); + + initial_config.unset::(); + config.store_put(TestStorable("test".into())); + let resolver = Resolver::overrid( + initial_config.clone().freeze(), + &initial_components, + &mut config, + &mut components, + ); + assert!(resolver.is_latest_set::()); + assert!(resolver.is_set::()); + assert_eq!("test", resolver.resolve_config::().unwrap().0); + + initial_config.store_put(TestStorable("override me".into())); + config.store_put(TestStorable("override".into())); + let resolver = Resolver::overrid( + initial_config.freeze(), + &initial_components, + &mut config, + &mut components, + ); + assert!(resolver.is_latest_set::()); + assert!(resolver.is_set::()); + assert_eq!( + "override", + resolver.resolve_config::().unwrap().0 + ); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs index e2d08a60d5..6e3d2aadfb 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs @@ -5,12 +5,12 @@ use aws_smithy_http::connection::{CaptureSmithyConnection, ConnectionMetadata}; use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{ BeforeDeserializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, }; use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::retry::{ErrorKind, ReconnectMode, RetryConfig}; use std::fmt; @@ -43,6 +43,7 @@ impl Interceptor for ConnectionPoisoningInterceptor { fn modify_before_transmit( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let capture_smithy_connection = CaptureSmithyConnectionWrapper::new(); @@ -58,6 +59,7 @@ impl Interceptor for ConnectionPoisoningInterceptor { fn modify_before_deserialization( &self, context: &mut BeforeDeserializationInterceptorContextMut<'_>, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let reconnect_mode = cfg @@ -65,7 +67,9 @@ impl Interceptor for ConnectionPoisoningInterceptor { .map(RetryConfig::reconnect_mode) .unwrap_or(ReconnectMode::ReconnectOnTransientError); let captured_connection = cfg.load::().cloned(); - let retry_classifiers = cfg.retry_classifiers(); + let retry_classifiers = runtime_components + .retry_classifiers() + .ok_or("retry classifiers are required for connection poisoning to work")?; let error_is_transient = retry_classifiers .classify_retry(context.into_inner()) diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs index c289821e9b..396aeb6078 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs @@ -8,6 +8,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use std::error::Error as StdError; use std::fmt; @@ -41,6 +42,7 @@ where fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let mut request = HttpRequest::new(SdkBody::taken()); @@ -75,6 +77,7 @@ where fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let request = context.request_mut(); diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 552c78b04b..803310221f 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -9,22 +9,24 @@ use self::auth::orchestrate_auth; use crate::client::orchestrator::endpoints::orchestrate_endpoint; use crate::client::orchestrator::http::read_body; -use crate::client::timeout::{MaybeTimeout, ProvideMaybeTimeoutConfig, TimeoutKind}; +use crate::client::timeout::{MaybeTimeout, MaybeTimeoutConfig, TimeoutKind}; use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream::ByteStream; use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use aws_smithy_runtime_api::client::connectors::Connector; use aws_smithy_runtime_api::client::interceptors::context::{ Error, Input, InterceptorContext, Output, RewindResult, }; use aws_smithy_runtime_api::client::interceptors::Interceptors; use aws_smithy_runtime_api::client::orchestrator::{ - HttpResponse, LoadedRequestBody, OrchestratorError, RequestSerializer, + DynResponseDeserializer, HttpResponse, LoadedRequestBody, OrchestratorError, RequestSerializer, + ResponseDeserializer, SharedRequestSerializer, }; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; -use aws_smithy_runtime_api::client::retries::ShouldAttempt; +use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; use aws_smithy_types::config_bag::ConfigBag; use std::mem; @@ -62,6 +64,24 @@ macro_rules! continue_on_err { }; } +macro_rules! run_interceptors { + (continue_on_err: { $($interceptor:ident($ctx:ident, $rc:ident, $cfg:ident);)+ }) => { + $(run_interceptors!(continue_on_err: $interceptor($ctx, $rc, $cfg));)+ + }; + (continue_on_err: $interceptor:ident($ctx:ident, $rc:ident, $cfg:ident)) => { + continue_on_err!([$ctx] => run_interceptors!(__private $interceptor($ctx, $rc, $cfg))) + }; + (halt_on_err: { $($interceptor:ident($ctx:ident, $rc:ident, $cfg:ident);)+ }) => { + $(run_interceptors!(halt_on_err: $interceptor($ctx, $rc, $cfg));)+ + }; + (halt_on_err: $interceptor:ident($ctx:ident, $rc:ident, $cfg:ident)) => { + halt_on_err!([$ctx] => run_interceptors!(__private $interceptor($ctx, $rc, $cfg))) + }; + (__private $interceptor:ident($ctx:ident, $rc:ident, $cfg:ident)) => { + Interceptors::new($rc.interceptors()).$interceptor($ctx, $rc, $cfg) + }; +} + pub async fn invoke( service_name: &str, operation_name: &str, @@ -101,24 +121,25 @@ pub async fn invoke_with_stop_point( let mut cfg = ConfigBag::base(); let cfg = &mut cfg; - let mut interceptors = Interceptors::new(); let mut ctx = InterceptorContext::new(input); - if let Err(err) = apply_configuration(&mut ctx, cfg, &mut interceptors, runtime_plugins) { - return Err(SdkError::construction_failure(err)); - } - let operation_timeout_config = cfg.maybe_timeout_config(TimeoutKind::Operation); + let runtime_components = apply_configuration(&mut ctx, cfg, runtime_plugins) + .map_err(SdkError::construction_failure)?; + trace!(runtime_components = ?runtime_components); + + let operation_timeout_config = + MaybeTimeoutConfig::new(&runtime_components, cfg, TimeoutKind::Operation); trace!(operation_timeout_config = ?operation_timeout_config); async { // If running the pre-execution interceptors failed, then we skip running the op and run the // final interceptors instead. if !ctx.is_failed() { - try_op(&mut ctx, cfg, &interceptors, stop_point).await; + try_op(&mut ctx, cfg, &runtime_components, stop_point).await; } - finally_op(&mut ctx, cfg, &interceptors).await; + finally_op(&mut ctx, cfg, &runtime_components).await; Ok(ctx) } - .maybe_timeout_with_config(operation_timeout_config) + .maybe_timeout(operation_timeout_config) .await } .instrument(debug_span!("invoke", service = %service_name, operation = %operation_name)) @@ -132,42 +153,49 @@ pub async fn invoke_with_stop_point( fn apply_configuration( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, - interceptors: &mut Interceptors, runtime_plugins: &RuntimePlugins, -) -> Result<(), BoxError> { - runtime_plugins.apply_client_configuration(cfg, interceptors.client_interceptors_mut())?; - continue_on_err!([ctx] => interceptors.client_read_before_execution(ctx, cfg)); - - runtime_plugins - .apply_operation_configuration(cfg, interceptors.operation_interceptors_mut())?; - continue_on_err!([ctx] => interceptors.operation_read_before_execution(ctx, cfg)); - - Ok(()) +) -> Result { + let client_rc_builder = runtime_plugins.apply_client_configuration(cfg)?; + continue_on_err!([ctx] => Interceptors::new(client_rc_builder.interceptors()).read_before_execution(false, ctx, cfg)); + + let operation_rc_builder = runtime_plugins.apply_operation_configuration(cfg)?; + continue_on_err!([ctx] => Interceptors::new(operation_rc_builder.interceptors()).read_before_execution(true, ctx, cfg)); + + // The order below is important. Client interceptors must run before operation interceptors. + Ok(RuntimeComponents::builder() + .merge_from(&client_rc_builder) + .merge_from(&operation_rc_builder) + .build()?) } #[instrument(skip_all)] async fn try_op( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, - interceptors: &Interceptors, + runtime_components: &RuntimeComponents, stop_point: StopPoint, ) { // Before serialization - halt_on_err!([ctx] => interceptors.read_before_serialization(ctx, cfg)); - halt_on_err!([ctx] => interceptors.modify_before_serialization(ctx, cfg)); + run_interceptors!(halt_on_err: { + read_before_serialization(ctx, runtime_components, cfg); + modify_before_serialization(ctx, runtime_components, cfg); + }); // Serialization ctx.enter_serialization_phase(); { let _span = debug_span!("serialization").entered(); - let request_serializer = cfg.request_serializer(); + let request_serializer = cfg + .load::() + .expect("request serializer must be in the config bag") + .clone(); let input = ctx.take_input().expect("input set at this point"); let request = halt_on_err!([ctx] => request_serializer.serialize_input(input, cfg).map_err(OrchestratorError::other)); ctx.set_request(request); } // Load the request body into memory if configured to do so - if let LoadedRequestBody::Requested = cfg.loaded_request_body() { + if let Some(&LoadedRequestBody::Requested) = cfg.load::() { debug!("loading request body into memory"); let mut body = SdkBody::taken(); mem::swap(&mut body, ctx.request_mut().expect("set above").body_mut()); @@ -175,20 +203,21 @@ async fn try_op( *ctx.request_mut().as_mut().expect("set above").body_mut() = SdkBody::from(loaded_body.clone()); cfg.interceptor_state() - .set_loaded_request_body(LoadedRequestBody::Loaded(loaded_body)); + .store_put(LoadedRequestBody::Loaded(loaded_body)); } // Before transmit ctx.enter_before_transmit_phase(); - halt_on_err!([ctx] => interceptors.read_after_serialization(ctx, cfg)); - halt_on_err!([ctx] => interceptors.modify_before_retry_loop(ctx, cfg)); + run_interceptors!(halt_on_err: { + read_after_serialization(ctx, runtime_components, cfg); + modify_before_retry_loop(ctx, runtime_components, cfg); + }); - let retry_strategy = cfg.retry_strategy(); // If we got a retry strategy from the bag, ask it what to do. // Otherwise, assume we should attempt the initial request. - let should_attempt = retry_strategy - .map(|rs| rs.should_attempt_initial_request(cfg)) - .unwrap_or(Ok(ShouldAttempt::Yes)); + let should_attempt = runtime_components + .retry_strategy() + .should_attempt_initial_request(runtime_components, cfg); match should_attempt { // Yes, let's make a request Ok(ShouldAttempt::Yes) => debug!("retry strategy has OKed initial request"), @@ -200,7 +229,7 @@ async fn try_op( // No, we shouldn't make a request because... Err(err) => halt!([ctx] => OrchestratorError::other(err)), Ok(ShouldAttempt::YesAfterDelay(delay)) => { - let sleep_impl = halt_on_err!([ctx] => cfg.sleep_impl().ok_or(OrchestratorError::other( + let sleep_impl = halt_on_err!([ctx] => runtime_components.sleep_impl().ok_or_else(|| OrchestratorError::other( "the retry strategy requested a delay before sending the initial request, but no 'async sleep' implementation was set" ))); debug!("retry strategy has OKed initial request after a {delay:?} delay"); @@ -228,31 +257,28 @@ async fn try_op( debug!("delaying for {delay:?}"); sleep.await; } - let attempt_timeout_config = cfg.maybe_timeout_config(TimeoutKind::OperationAttempt); + let attempt_timeout_config = + MaybeTimeoutConfig::new(runtime_components, cfg, TimeoutKind::OperationAttempt); trace!(attempt_timeout_config = ?attempt_timeout_config); let maybe_timeout = async { debug!("beginning attempt #{i}"); - try_attempt(ctx, cfg, interceptors, stop_point).await; - finally_attempt(ctx, cfg, interceptors).await; + try_attempt(ctx, cfg, runtime_components, stop_point).await; + finally_attempt(ctx, cfg, runtime_components).await; Result::<_, SdkError>::Ok(()) } - .maybe_timeout_with_config(attempt_timeout_config) + .maybe_timeout(attempt_timeout_config) .await .map_err(|err| OrchestratorError::timeout(err.into_source().unwrap())); // We continue when encountering a timeout error. The retry classifier will decide what to do with it. continue_on_err!([ctx] => maybe_timeout); - let retry_strategy = cfg.retry_strategy(); - // If we got a retry strategy from the bag, ask it what to do. // If no strategy was set, we won't retry. - let should_attempt = match retry_strategy { - Some(retry_strategy) => halt_on_err!( - [ctx] => retry_strategy.should_attempt_retry(ctx, cfg).map_err(OrchestratorError::other) - ), - None => ShouldAttempt::No, - }; + let should_attempt = halt_on_err!([ctx] => runtime_components + .retry_strategy() + .should_attempt_retry(ctx, runtime_components, cfg) + .map_err(OrchestratorError::other)); match should_attempt { // Yes, let's retry the request ShouldAttempt::Yes => continue, @@ -262,7 +288,7 @@ async fn try_op( break; } ShouldAttempt::YesAfterDelay(delay) => { - let sleep_impl = halt_on_err!([ctx] => cfg.sleep_impl().ok_or(OrchestratorError::other( + let sleep_impl = halt_on_err!([ctx] => runtime_components.sleep_impl().ok_or_else(|| OrchestratorError::other( "the retry strategy requested a delay before sending the retry request, but no 'async sleep' implementation was set" ))); retry_delay = Some((delay, sleep_impl.sleep(delay))); @@ -276,21 +302,25 @@ async fn try_op( async fn try_attempt( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, - interceptors: &Interceptors, + runtime_components: &RuntimeComponents, stop_point: StopPoint, ) { - halt_on_err!([ctx] => interceptors.read_before_attempt(ctx, cfg)); + run_interceptors!(halt_on_err: read_before_attempt(ctx, runtime_components, cfg)); - halt_on_err!([ctx] => orchestrate_endpoint(ctx, cfg).await.map_err(OrchestratorError::other)); + halt_on_err!([ctx] => orchestrate_endpoint(ctx, runtime_components, cfg).await.map_err(OrchestratorError::other)); - halt_on_err!([ctx] => interceptors.modify_before_signing(ctx, cfg)); - halt_on_err!([ctx] => interceptors.read_before_signing(ctx, cfg)); + run_interceptors!(halt_on_err: { + modify_before_signing(ctx, runtime_components, cfg); + read_before_signing(ctx, runtime_components, cfg); + }); - halt_on_err!([ctx] => orchestrate_auth(ctx, cfg).await.map_err(OrchestratorError::other)); + halt_on_err!([ctx] => orchestrate_auth(ctx, runtime_components, cfg).await.map_err(OrchestratorError::other)); - halt_on_err!([ctx] => interceptors.read_after_signing(ctx, cfg)); - halt_on_err!([ctx] => interceptors.modify_before_transmit(ctx, cfg)); - halt_on_err!([ctx] => interceptors.read_before_transmit(ctx, cfg)); + run_interceptors!(halt_on_err: { + read_after_signing(ctx, runtime_components, cfg); + modify_before_transmit(ctx, runtime_components, cfg); + read_before_transmit(ctx, runtime_components, cfg); + }); // Return early if a stop point is set for before transmit if let StopPoint::BeforeTransmit = stop_point { @@ -304,7 +334,10 @@ async fn try_attempt( let response = halt_on_err!([ctx] => { let request = ctx.take_request().expect("set during serialization"); trace!(request = ?request, "transmitting request"); - cfg.connector().call(request).await.map_err(|err| { + let connector = halt_on_err!([ctx] => runtime_components.connector().ok_or_else(|| + OrchestratorError::other("a connector is required to send requests") + )); + connector.call(request).await.map_err(|err| { match err.downcast() { Ok(connector_error) => OrchestratorError::connector(*connector_error), Err(box_err) => OrchestratorError::other(box_err) @@ -315,14 +348,18 @@ async fn try_attempt( ctx.set_response(response); ctx.enter_before_deserialization_phase(); - halt_on_err!([ctx] => interceptors.read_after_transmit(ctx, cfg)); - halt_on_err!([ctx] => interceptors.modify_before_deserialization(ctx, cfg)); - halt_on_err!([ctx] => interceptors.read_before_deserialization(ctx, cfg)); + run_interceptors!(halt_on_err: { + read_after_transmit(ctx, runtime_components, cfg); + modify_before_deserialization(ctx, runtime_components, cfg); + read_before_deserialization(ctx, runtime_components, cfg); + }); ctx.enter_deserialization_phase(); let output_or_error = async { let response = ctx.response_mut().expect("set during transmit"); - let response_deserializer = cfg.response_deserializer(); + let response_deserializer = cfg + .load::() + .expect("a request deserializer must be in the config bag"); let maybe_deserialized = { let _span = debug_span!("deserialize_streaming").entered(); response_deserializer.deserialize_streaming(response) @@ -345,44 +382,48 @@ async fn try_attempt( ctx.set_output_or_error(output_or_error); ctx.enter_after_deserialization_phase(); - halt_on_err!([ctx] => interceptors.read_after_deserialization(ctx, cfg)); + run_interceptors!(halt_on_err: read_after_deserialization(ctx, runtime_components, cfg)); } #[instrument(skip_all)] async fn finally_attempt( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, - interceptors: &Interceptors, + runtime_components: &RuntimeComponents, ) { - continue_on_err!([ctx] => interceptors.modify_before_attempt_completion(ctx, cfg)); - continue_on_err!([ctx] => interceptors.read_after_attempt(ctx, cfg)); + run_interceptors!(continue_on_err: { + modify_before_attempt_completion(ctx, runtime_components, cfg); + read_after_attempt(ctx, runtime_components, cfg); + }); } #[instrument(skip_all)] async fn finally_op( ctx: &mut InterceptorContext, cfg: &mut ConfigBag, - interceptors: &Interceptors, + runtime_components: &RuntimeComponents, ) { - continue_on_err!([ctx] => interceptors.modify_before_completion(ctx, cfg)); - continue_on_err!([ctx] => interceptors.read_after_execution(ctx, cfg)); + run_interceptors!(continue_on_err: { + modify_before_completion(ctx, runtime_components, cfg); + read_after_execution(ctx, runtime_components, cfg); + }); } #[cfg(all(test, feature = "test-util"))] mod tests { use super::*; use crate::client::auth::no_auth::{NoAuthRuntimePlugin, NO_AUTH_SCHEME_ID}; - use crate::client::orchestrator::endpoints::{ - StaticUriEndpointResolver, StaticUriEndpointResolverParams, - }; + use crate::client::orchestrator::endpoints::StaticUriEndpointResolver; use crate::client::retries::strategy::NeverRetryStrategy; use crate::client::test_util::{ deserializer::CannedResponseDeserializer, serializer::CannedRequestSerializer, }; use ::http::{Request, Response, StatusCode}; use aws_smithy_runtime_api::client::auth::option_resolver::StaticAuthOptionResolver; - use aws_smithy_runtime_api::client::auth::{AuthOptionResolverParams, DynAuthOptionResolver}; - use aws_smithy_runtime_api::client::connectors::{Connector, DynConnector}; + use aws_smithy_runtime_api::client::auth::{ + AuthOptionResolverParams, SharedAuthOptionResolver, + }; + use aws_smithy_runtime_api::client::connectors::{Connector, SharedConnector}; use aws_smithy_runtime_api::client::interceptors::context::{ AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, @@ -390,17 +431,17 @@ mod tests { BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, }; - use aws_smithy_runtime_api::client::interceptors::{ - Interceptor, InterceptorRegistrar, SharedInterceptor, - }; + use aws_smithy_runtime_api::client::interceptors::{Interceptor, SharedInterceptor}; use aws_smithy_runtime_api::client::orchestrator::{ - BoxFuture, DynEndpointResolver, DynResponseDeserializer, Future, HttpRequest, - SharedRequestSerializer, + BoxFuture, DynResponseDeserializer, EndpointResolverParams, Future, HttpRequest, + SharedEndpointResolver, SharedRequestSerializer, }; - use aws_smithy_runtime_api::client::retries::DynRetryStrategy; + use aws_smithy_runtime_api::client::retries::SharedRetryStrategy; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_runtime_api::client::runtime_plugin::{RuntimePlugin, RuntimePlugins}; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; + use std::borrow::Cow; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use tracing_test::traced_test; @@ -442,36 +483,58 @@ mod tests { } #[derive(Debug)] - struct TestOperationRuntimePlugin; + struct TestOperationRuntimePlugin { + builder: RuntimeComponentsBuilder, + } + + impl TestOperationRuntimePlugin { + fn new() -> Self { + Self { + builder: RuntimeComponentsBuilder::new("TestOperationRuntimePlugin") + .with_retry_strategy(Some(SharedRetryStrategy::new(NeverRetryStrategy::new()))) + .with_endpoint_resolver(Some(SharedEndpointResolver::new( + StaticUriEndpointResolver::http_localhost(8080), + ))) + .with_connector(Some(SharedConnector::new(OkConnector::new()))) + .with_auth_option_resolver(Some(SharedAuthOptionResolver::new( + StaticAuthOptionResolver::new(vec![NO_AUTH_SCHEME_ID]), + ))), + } + } + } impl RuntimePlugin for TestOperationRuntimePlugin { fn config(&self) -> Option { - let mut cfg = Layer::new("test operation"); - cfg.set_request_serializer(SharedRequestSerializer::new(new_request_serializer())); - cfg.set_response_deserializer( - DynResponseDeserializer::new(new_response_deserializer()), - ); - cfg.set_retry_strategy(DynRetryStrategy::new(NeverRetryStrategy::new())); - cfg.set_endpoint_resolver(DynEndpointResolver::new( - StaticUriEndpointResolver::http_localhost(8080), - )); - cfg.set_endpoint_resolver_params(StaticUriEndpointResolverParams::new().into()); - cfg.set_connector(DynConnector::new(OkConnector::new())); - cfg.set_auth_option_resolver_params(AuthOptionResolverParams::new("idontcare")); - cfg.set_auth_option_resolver(DynAuthOptionResolver::new( - StaticAuthOptionResolver::new(vec![NO_AUTH_SCHEME_ID]), - )); - - Some(cfg.freeze()) + let mut layer = Layer::new("TestOperationRuntimePlugin"); + layer.store_put(AuthOptionResolverParams::new("idontcare")); + layer.store_put(EndpointResolverParams::new("dontcare")); + layer.store_put(SharedRequestSerializer::new(new_request_serializer())); + layer.store_put(DynResponseDeserializer::new(new_response_deserializer())); + Some(layer.freeze()) + } + + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.builder) } } macro_rules! interceptor_error_handling_test { + (read_before_execution, $ctx:ty, $expected:expr,) => { + interceptor_error_handling_test!(__private read_before_execution, $ctx, $expected,); + }; ($interceptor:ident, $ctx:ty, $expected:expr) => { + interceptor_error_handling_test!(__private $interceptor, $ctx, $expected, _rc: &RuntimeComponents,); + }; + (__private $interceptor:ident, $ctx:ty, $expected:expr, $($rc_arg:tt)*) => { #[derive(Debug)] struct FailingInterceptorA; impl Interceptor for FailingInterceptorA { - fn $interceptor(&self, _ctx: $ctx, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + fn $interceptor( + &self, + _ctx: $ctx, + $($rc_arg)* + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { tracing::debug!("FailingInterceptorA called!"); Err("FailingInterceptorA".into()) } @@ -480,7 +543,12 @@ mod tests { #[derive(Debug)] struct FailingInterceptorB; impl Interceptor for FailingInterceptorB { - fn $interceptor(&self, _ctx: $ctx, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + fn $interceptor( + &self, + _ctx: $ctx, + $($rc_arg)* + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { tracing::debug!("FailingInterceptorB called!"); Err("FailingInterceptorB".into()) } @@ -489,37 +557,53 @@ mod tests { #[derive(Debug)] struct FailingInterceptorC; impl Interceptor for FailingInterceptorC { - fn $interceptor(&self, _ctx: $ctx, _cfg: &mut ConfigBag) -> Result<(), BoxError> { + fn $interceptor( + &self, + _ctx: $ctx, + $($rc_arg)* + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { tracing::debug!("FailingInterceptorC called!"); Err("FailingInterceptorC".into()) } } #[derive(Debug)] - struct FailingInterceptorsClientRuntimePlugin; - + struct FailingInterceptorsClientRuntimePlugin(RuntimeComponentsBuilder); + impl FailingInterceptorsClientRuntimePlugin { + fn new() -> Self { + Self(RuntimeComponentsBuilder::new("test").with_interceptor(SharedInterceptor::new(FailingInterceptorA))) + } + } impl RuntimePlugin for FailingInterceptorsClientRuntimePlugin { - fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { - interceptors.register(SharedInterceptor::new(FailingInterceptorA)); + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.0) } } #[derive(Debug)] - struct FailingInterceptorsOperationRuntimePlugin; - + struct FailingInterceptorsOperationRuntimePlugin(RuntimeComponentsBuilder); + impl FailingInterceptorsOperationRuntimePlugin { + fn new() -> Self { + Self( + RuntimeComponentsBuilder::new("test") + .with_interceptor(SharedInterceptor::new(FailingInterceptorB)) + .with_interceptor(SharedInterceptor::new(FailingInterceptorC)) + ) + } + } impl RuntimePlugin for FailingInterceptorsOperationRuntimePlugin { - fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { - interceptors.register(SharedInterceptor::new(FailingInterceptorB)); - interceptors.register(SharedInterceptor::new(FailingInterceptorC)); + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.0) } } let input = TypeErasedBox::new(Box::new(())); let runtime_plugins = RuntimePlugins::new() - .with_client_plugin(FailingInterceptorsClientRuntimePlugin) - .with_operation_plugin(TestOperationRuntimePlugin) + .with_client_plugin(FailingInterceptorsClientRuntimePlugin::new()) + .with_operation_plugin(TestOperationRuntimePlugin::new()) .with_operation_plugin(NoAuthRuntimePlugin::new()) - .with_operation_plugin(FailingInterceptorsOperationRuntimePlugin); + .with_operation_plugin(FailingInterceptorsOperationRuntimePlugin::new()); let actual = invoke("test", "test", input, &runtime_plugins) .await .expect_err("should error"); @@ -539,7 +623,7 @@ mod tests { interceptor_error_handling_test!( read_before_execution, &BeforeSerializationInterceptorContextRef<'_>, - expected + expected, ); } @@ -742,13 +826,20 @@ mod tests { } macro_rules! interceptor_error_redirection_test { + (read_before_execution, $origin_ctx:ty, $destination_interceptor:ident, $destination_ctx:ty, $expected:expr) => { + interceptor_error_redirection_test!(__private read_before_execution, $origin_ctx, $destination_interceptor, $destination_ctx, $expected,); + }; ($origin_interceptor:ident, $origin_ctx:ty, $destination_interceptor:ident, $destination_ctx:ty, $expected:expr) => { + interceptor_error_redirection_test!(__private $origin_interceptor, $origin_ctx, $destination_interceptor, $destination_ctx, $expected, _rc: &RuntimeComponents,); + }; + (__private $origin_interceptor:ident, $origin_ctx:ty, $destination_interceptor:ident, $destination_ctx:ty, $expected:expr, $($rc_arg:tt)*) => { #[derive(Debug)] struct OriginInterceptor; impl Interceptor for OriginInterceptor { fn $origin_interceptor( &self, _ctx: $origin_ctx, + $($rc_arg)* _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { tracing::debug!("OriginInterceptor called!"); @@ -762,6 +853,7 @@ mod tests { fn $destination_interceptor( &self, _ctx: $destination_ctx, + _runtime_components: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { tracing::debug!("DestinationInterceptor called!"); @@ -770,20 +862,27 @@ mod tests { } #[derive(Debug)] - struct InterceptorsTestOperationRuntimePlugin; - + struct InterceptorsTestOperationRuntimePlugin(RuntimeComponentsBuilder); + impl InterceptorsTestOperationRuntimePlugin { + fn new() -> Self { + Self( + RuntimeComponentsBuilder::new("test") + .with_interceptor(SharedInterceptor::new(OriginInterceptor)) + .with_interceptor(SharedInterceptor::new(DestinationInterceptor)) + ) + } + } impl RuntimePlugin for InterceptorsTestOperationRuntimePlugin { - fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { - interceptors.register(SharedInterceptor::new(OriginInterceptor)); - interceptors.register(SharedInterceptor::new(DestinationInterceptor)); + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.0) } } let input = TypeErasedBox::new(Box::new(())); let runtime_plugins = RuntimePlugins::new() - .with_operation_plugin(TestOperationRuntimePlugin) + .with_operation_plugin(TestOperationRuntimePlugin::new()) .with_operation_plugin(NoAuthRuntimePlugin::new()) - .with_operation_plugin(InterceptorsTestOperationRuntimePlugin); + .with_operation_plugin(InterceptorsTestOperationRuntimePlugin::new()); let actual = invoke("test", "test", input, &runtime_plugins) .await .expect_err("should error"); @@ -1006,12 +1105,6 @@ mod tests { ); } - // #[tokio::test] - // #[traced_test] - // async fn test_read_after_attempt_error_causes_jump_to_modify_before_attempt_completion() { - // todo!("I'm confused by the behavior described in the spec") - // } - #[tokio::test] #[traced_test] async fn test_modify_before_completion_error_causes_jump_to_read_after_execution() { @@ -1029,7 +1122,7 @@ mod tests { async fn test_stop_points() { let runtime_plugins = || { RuntimePlugins::new() - .with_operation_plugin(TestOperationRuntimePlugin) + .with_operation_plugin(TestOperationRuntimePlugin::new()) .with_operation_plugin(NoAuthRuntimePlugin::new()) }; @@ -1076,6 +1169,7 @@ mod tests { fn modify_before_retry_loop( &self, _context: &mut BeforeTransmitInterceptorContextMut<'_>, + _rc: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { self.inner @@ -1087,6 +1181,7 @@ mod tests { fn modify_before_completion( &self, _context: &mut FinalizerInterceptorContextMut<'_>, + _rc: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { self.inner @@ -1098,6 +1193,7 @@ mod tests { fn read_after_execution( &self, _context: &FinalizerInterceptorContextRef<'_>, + _rc: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { self.inner @@ -1109,21 +1205,22 @@ mod tests { #[derive(Debug)] struct TestInterceptorRuntimePlugin { - interceptor: TestInterceptor, + builder: RuntimeComponentsBuilder, } impl RuntimePlugin for TestInterceptorRuntimePlugin { - fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { - interceptors.register(SharedInterceptor::new(self.interceptor.clone())); + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.builder) } } let interceptor = TestInterceptor::default(); let runtime_plugins = || { RuntimePlugins::new() - .with_operation_plugin(TestOperationRuntimePlugin) + .with_operation_plugin(TestOperationRuntimePlugin::new()) .with_operation_plugin(NoAuthRuntimePlugin::new()) .with_operation_plugin(TestInterceptorRuntimePlugin { - interceptor: interceptor.clone(), + builder: RuntimeComponentsBuilder::new("test") + .with_interceptor(SharedInterceptor::new(interceptor.clone())), }) }; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index ce64570870..48fd604392 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -5,11 +5,12 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::{ - AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, + AuthOptionResolver, AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, }; use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::identity::IdentityResolver; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::endpoint::Endpoint; use aws_smithy_types::Document; @@ -66,22 +67,22 @@ impl StdError for AuthOrchestrationError {} pub(super) async fn orchestrate_auth( ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &ConfigBag, ) -> Result<(), BoxError> { let params = cfg.auth_option_resolver_params(); - let auth_options = cfg.auth_option_resolver().resolve_auth_options(params)?; - let identity_resolvers = cfg.identity_resolvers(); + let auth_option_resolver = runtime_components.auth_option_resolver(); + let auth_options = auth_option_resolver.resolve_auth_options(params)?; trace!( auth_option_resolver_params = ?params, auth_options = ?auth_options, - identity_resolvers = ?identity_resolvers, "orchestrating auth", ); for &scheme_id in auth_options.as_ref() { - if let Some(auth_scheme) = cfg.http_auth_schemes().scheme(scheme_id) { - if let Some(identity_resolver) = auth_scheme.identity_resolver(&identity_resolvers) { + if let Some(auth_scheme) = runtime_components.http_auth_scheme(scheme_id) { + if let Some(identity_resolver) = auth_scheme.identity_resolver(runtime_components) { let request_signer = auth_scheme.request_signer(); trace!( auth_scheme = ?auth_scheme, @@ -106,6 +107,7 @@ pub(super) async fn orchestrate_auth( request, &identity, auth_scheme_endpoint_config, + runtime_components, cfg, )?; return Ok(()); @@ -145,21 +147,23 @@ fn extract_endpoint_auth_scheme_config( Ok(AuthSchemeEndpointConfig::new(Some(auth_scheme_config))) } -#[cfg(test)] +#[cfg(all(test, feature = "test-util"))] mod tests { use super::*; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::auth::option_resolver::StaticAuthOptionResolver; use aws_smithy_runtime_api::client::auth::{ - AuthOptionResolverParams, AuthSchemeId, DynAuthOptionResolver, HttpAuthScheme, - HttpRequestSigner, SharedHttpAuthScheme, + AuthOptionResolverParams, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, + SharedAuthOptionResolver, SharedHttpAuthScheme, }; - use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::identity::{ - Identity, IdentityResolver, IdentityResolvers, SharedIdentityResolver, + Identity, IdentityResolver, SharedIdentityResolver, }; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; + use aws_smithy_runtime_api::client::runtime_components::{ + GetIdentityResolver, RuntimeComponentsBuilder, + }; use aws_smithy_types::config_bag::Layer; use aws_smithy_types::type_erasure::TypedBox; use std::collections::HashMap; @@ -183,6 +187,7 @@ mod tests { request: &mut HttpRequest, _identity: &Identity, _auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + _runtime_components: &RuntimeComponents, _config_bag: &ConfigBag, ) -> Result<(), BoxError> { request @@ -205,7 +210,7 @@ mod tests { fn identity_resolver( &self, - identity_resolvers: &IdentityResolvers, + identity_resolvers: &dyn GetIdentityResolver, ) -> Option { identity_resolvers.identity_resolver(self.scheme_id()) } @@ -221,23 +226,28 @@ mod tests { let _ = ctx.take_input(); ctx.enter_before_transmit_phase(); - let mut layer = Layer::new("test"); - layer.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); - layer.set_auth_option_resolver(DynAuthOptionResolver::new(StaticAuthOptionResolver::new( - vec![TEST_SCHEME_ID], - ))); - layer.push_identity_resolver( - TEST_SCHEME_ID, - SharedIdentityResolver::new(TestIdentityResolver), - ); - layer.push_http_auth_scheme(SharedHttpAuthScheme::new(TestAuthScheme { - signer: TestSigner, - })); + let runtime_components = RuntimeComponentsBuilder::for_tests() + .with_auth_option_resolver(Some(SharedAuthOptionResolver::new( + StaticAuthOptionResolver::new(vec![TEST_SCHEME_ID]), + ))) + .with_identity_resolver( + TEST_SCHEME_ID, + SharedIdentityResolver::new(TestIdentityResolver), + ) + .with_http_auth_scheme(SharedHttpAuthScheme::new(TestAuthScheme { + signer: TestSigner, + })) + .build() + .unwrap(); + + let mut layer: Layer = Layer::new("test"); + layer.store_put(AuthOptionResolverParams::new("doesntmatter")); layer.store_put(Endpoint::builder().url("dontcare").build()); + let cfg = ConfigBag::of_layers(vec![layer]); - let mut cfg = ConfigBag::base(); - cfg.push_layer(layer); - orchestrate_auth(&mut ctx, &cfg).await.expect("success"); + orchestrate_auth(&mut ctx, &runtime_components, &cfg) + .await + .expect("success"); assert_eq!( "success!", @@ -267,26 +277,33 @@ mod tests { fn config_with_identity( scheme_id: AuthSchemeId, identity: impl IdentityResolver + 'static, - ) -> ConfigBag { + ) -> (RuntimeComponents, ConfigBag) { + let runtime_components = RuntimeComponentsBuilder::for_tests() + .with_http_auth_scheme(SharedHttpAuthScheme::new(BasicAuthScheme::new())) + .with_http_auth_scheme(SharedHttpAuthScheme::new(BearerAuthScheme::new())) + .with_auth_option_resolver(Some(SharedAuthOptionResolver::new( + StaticAuthOptionResolver::new(vec![ + HTTP_BASIC_AUTH_SCHEME_ID, + HTTP_BEARER_AUTH_SCHEME_ID, + ]), + ))) + .with_identity_resolver(scheme_id, SharedIdentityResolver::new(identity)) + .build() + .unwrap(); + let mut layer = Layer::new("test"); - layer.push_http_auth_scheme(SharedHttpAuthScheme::new(BasicAuthScheme::new())); - layer.push_http_auth_scheme(SharedHttpAuthScheme::new(BearerAuthScheme::new())); layer.store_put(Endpoint::builder().url("dontcare").build()); + layer.store_put(AuthOptionResolverParams::new("doesntmatter")); - layer.set_auth_option_resolver_params(AuthOptionResolverParams::new("doesntmatter")); - layer.set_auth_option_resolver(DynAuthOptionResolver::new( - StaticAuthOptionResolver::new(vec![ - HTTP_BASIC_AUTH_SCHEME_ID, - HTTP_BEARER_AUTH_SCHEME_ID, - ]), - )); - layer.push_identity_resolver(scheme_id, SharedIdentityResolver::new(identity)); - ConfigBag::of_layers(vec![layer]) + (runtime_components, ConfigBag::of_layers(vec![layer])) } // First, test the presence of a basic auth login and absence of a bearer token - let cfg = config_with_identity(HTTP_BASIC_AUTH_SCHEME_ID, Login::new("a", "b", None)); - orchestrate_auth(&mut ctx, &cfg).await.expect("success"); + let (runtime_components, cfg) = + config_with_identity(HTTP_BASIC_AUTH_SCHEME_ID, Login::new("a", "b", None)); + orchestrate_auth(&mut ctx, &runtime_components, &cfg) + .await + .expect("success"); assert_eq!( // "YTpi" == "a:b" in base64 "Basic YTpi", @@ -298,13 +315,16 @@ mod tests { ); // Next, test the presence of a bearer token and absence of basic auth - let cfg = config_with_identity(HTTP_BEARER_AUTH_SCHEME_ID, Token::new("t", None)); + let (runtime_components, cfg) = + config_with_identity(HTTP_BEARER_AUTH_SCHEME_ID, Token::new("t", None)); let mut ctx = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); ctx.enter_serialization_phase(); ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let _ = ctx.take_input(); ctx.enter_before_transmit_phase(); - orchestrate_auth(&mut ctx, &cfg).await.expect("success"); + orchestrate_auth(&mut ctx, &runtime_components, &cfg) + .await + .expect("success"); assert_eq!( "Bearer t", ctx.request() diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 394cde080b..1b22b3c5d9 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -14,6 +14,7 @@ use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::orchestrator::{ EndpointResolver, EndpointResolverParams, Future, HttpRequest, }; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::endpoint::Endpoint; use http::header::HeaderName; @@ -103,6 +104,7 @@ where pub(super) async fn orchestrate_endpoint( ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { trace!("orchestrating endpoint resolution"); @@ -111,8 +113,10 @@ pub(super) async fn orchestrate_endpoint( let endpoint_prefix = cfg.load::(); let request = ctx.request_mut().expect("set during serialization"); - let endpoint_resolver = cfg.endpoint_resolver(); - let endpoint = endpoint_resolver.resolve_endpoint(params).await?; + let endpoint = runtime_components + .endpoint_resolver() + .resolve_endpoint(params) + .await?; apply_endpoint(request, &endpoint, endpoint_prefix)?; // Make the endpoint config available to interceptors diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs index 7a41de659a..110f720a81 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs @@ -6,6 +6,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeDeserializationInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::date_time::Format; use aws_smithy_types::DateTime; @@ -68,6 +69,7 @@ impl Interceptor for ServiceClockSkewInterceptor { fn modify_before_deserialization( &self, ctx: &mut BeforeDeserializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let time_received = DateTime::from(SystemTime::now()); diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs index 69f36272ab..5453cad271 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs @@ -298,11 +298,8 @@ mod tests { use super::{cubic_throttle, ClientRateLimiter}; use crate::client::retries::client_rate_limiter::RequestReason; use approx::assert_relative_eq; - use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; + use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_async::test_util::instant_time_and_sleep; - use aws_smithy_async::time::SharedTimeSource; - use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; - use aws_smithy_types::config_bag::ConfigBag; use std::time::{Duration, SystemTime}; const ONE_SECOND: Duration = Duration::from_secs(1); @@ -325,12 +322,6 @@ mod tests { #[tokio::test] async fn throttling_is_enabled_once_throttling_error_is_received() { - let mut cfg = ConfigBag::base(); - let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); - cfg.interceptor_state() - .set_request_time(SharedTimeSource::new(time_source)); - cfg.interceptor_state() - .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl))); let rate_limiter = ClientRateLimiter::builder() .previous_time_bucket(0.0) .time_of_last_throttle(0.0) @@ -349,13 +340,6 @@ mod tests { #[tokio::test] async fn test_calculated_rate_with_successes() { - let mut cfg = ConfigBag::base(); - let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); - sleep_impl.sleep(Duration::from_secs(5)).await; - cfg.interceptor_state() - .set_request_time(SharedTimeSource::new(time_source)); - cfg.interceptor_state() - .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl.clone()))); let rate_limiter = ClientRateLimiter::builder() .time_of_last_throttle(5.0) .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) @@ -414,13 +398,6 @@ mod tests { #[tokio::test] async fn test_calculated_rate_with_throttles() { - let mut cfg = ConfigBag::base(); - let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); - sleep_impl.sleep(Duration::from_secs(5)).await; - cfg.interceptor_state() - .set_request_time(SharedTimeSource::new(time_source)); - cfg.interceptor_state() - .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl.clone()))); let rate_limiter = ClientRateLimiter::builder() .tokens_retrieved_per_second_at_time_of_last_throttle(10.0) .time_of_last_throttle(5.0) @@ -496,12 +473,7 @@ mod tests { #[tokio::test] async fn test_client_sending_rates() { - let mut cfg = ConfigBag::base(); - let (time_source, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); - cfg.interceptor_state() - .set_request_time(SharedTimeSource::new(time_source)); - cfg.interceptor_state() - .set_sleep_impl(Some(SharedAsyncSleep::new(sleep_impl.clone()))); + let (_, sleep_impl) = instant_time_and_sleep(SystemTime::UNIX_EPOCH); let rate_limiter = ClientRateLimiter::builder().build(); struct Attempt { diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs index 088e7bfd8c..1cdb94cae9 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -7,8 +7,9 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ - ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, ShouldAttempt, + ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt, }; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use std::time::Duration; @@ -34,13 +35,18 @@ impl FixedDelayRetryStrategy { } impl RetryStrategy for FixedDelayRetryStrategy { - fn should_attempt_initial_request(&self, _cfg: &ConfigBag) -> Result { + fn should_attempt_initial_request( + &self, + _runtime_components: &RuntimeComponents, + _cfg: &ConfigBag, + ) -> Result { Ok(ShouldAttempt::Yes) } fn should_attempt_retry( &self, ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &ConfigBag, ) -> Result { // Look a the result. If it's OK then we're done; No retry required. Otherwise, we need to inspect it @@ -65,8 +71,8 @@ impl RetryStrategy for FixedDelayRetryStrategy { return Ok(ShouldAttempt::No); } - let retry_classifiers = cfg - .load::() + let retry_classifiers = runtime_components + .retry_classifiers() .expect("a retry classifier is set"); let retry_reason = retry_classifiers.classify_retry(ctx); diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs index c06a745a27..b59b8fb7f1 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs @@ -6,6 +6,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; #[non_exhaustive] @@ -19,13 +20,18 @@ impl NeverRetryStrategy { } impl RetryStrategy for NeverRetryStrategy { - fn should_attempt_initial_request(&self, _cfg: &ConfigBag) -> Result { + fn should_attempt_initial_request( + &self, + _runtime_components: &RuntimeComponents, + _cfg: &ConfigBag, + ) -> Result { Ok(ShouldAttempt::Yes) } fn should_attempt_retry( &self, _context: &InterceptorContext, + _runtime_components: &RuntimeComponents, _cfg: &ConfigBag, ) -> Result { Ok(ShouldAttempt::No) diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index ecb29234f4..ba8d08aa66 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -9,12 +9,12 @@ use crate::client::retries::strategy::standard::ReleaseResult::{ }; use crate::client::retries::token_bucket::TokenBucket; use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt, }; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::retry::{ErrorKind, RetryConfig}; use std::sync::Mutex; @@ -96,6 +96,7 @@ impl StandardRetryStrategy { fn calculate_backoff( &self, + runtime_components: &RuntimeComponents, cfg: &ConfigBag, retry_reason: Option<&RetryReason>, ) -> Result { @@ -108,8 +109,12 @@ impl StandardRetryStrategy { match retry_reason { Some(RetryReason::Explicit(backoff)) => Ok(*backoff), Some(RetryReason::Error(kind)) => { - update_rate_limiter_if_exists(cfg, *kind == ErrorKind::ThrottlingError); - if let Some(delay) = check_rate_limiter_for_delay(cfg, *kind) { + update_rate_limiter_if_exists( + runtime_components, + cfg, + *kind == ErrorKind::ThrottlingError, + ); + if let Some(delay) = check_rate_limiter_for_delay(runtime_components, cfg, *kind) { let delay = delay.min(self.max_backoff); debug!("rate limiter has requested a {delay:?} delay before retrying"); Ok(delay) @@ -138,7 +143,7 @@ impl StandardRetryStrategy { } Some(_) => unreachable!("RetryReason is non-exhaustive"), None => { - update_rate_limiter_if_exists(cfg, false); + update_rate_limiter_if_exists(runtime_components, cfg, false); debug!( attempts = request_attempts, max_attempts = self.max_attempts, @@ -169,9 +174,13 @@ impl Default for StandardRetryStrategy { } impl RetryStrategy for StandardRetryStrategy { - fn should_attempt_initial_request(&self, cfg: &ConfigBag) -> Result { + fn should_attempt_initial_request( + &self, + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + ) -> Result { if let Some(crl) = cfg.load::() { - let seconds_since_unix_epoch = get_seconds_since_unix_epoch(cfg); + let seconds_since_unix_epoch = get_seconds_since_unix_epoch(runtime_components); if let Err(delay) = crl.acquire_permission_to_send_a_request( seconds_since_unix_epoch, RequestReason::InitialRequest, @@ -188,6 +197,7 @@ impl RetryStrategy for StandardRetryStrategy { fn should_attempt_retry( &self, ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, cfg: &ConfigBag, ) -> Result { // Look a the result. If it's OK then we're done; No retry required. Otherwise, we need to inspect it @@ -207,7 +217,7 @@ impl RetryStrategy for StandardRetryStrategy { tb.regenerate_a_token(); } } - update_rate_limiter_if_exists(cfg, false); + update_rate_limiter_if_exists(runtime_components, cfg, false); return Ok(ShouldAttempt::No); } @@ -218,7 +228,7 @@ impl RetryStrategy for StandardRetryStrategy { .expect("at least one request attempt is made before any retry is attempted") .attempts(); if request_attempts >= self.max_attempts { - update_rate_limiter_if_exists(cfg, false); + update_rate_limiter_if_exists(runtime_components, cfg, false); debug!( attempts = request_attempts, @@ -229,11 +239,13 @@ impl RetryStrategy for StandardRetryStrategy { } // Run the classifiers against the context to determine if we should retry - let retry_classifiers = cfg.retry_classifiers(); + let retry_classifiers = runtime_components + .retry_classifiers() + .ok_or("retry classifiers are required by the retry configuration")?; let retry_reason = retry_classifiers.classify_retry(ctx); // Calculate the appropriate backoff time. - let backoff = match self.calculate_backoff(cfg, retry_reason.as_ref()) { + let backoff = match self.calculate_backoff(runtime_components, cfg, retry_reason.as_ref()) { Ok(value) => value, // In some cases, backoff calculation will decide that we shouldn't retry at all. Err(value) => return Ok(value), @@ -248,23 +260,32 @@ impl RetryStrategy for StandardRetryStrategy { } } -fn update_rate_limiter_if_exists(cfg: &ConfigBag, is_throttling_error: bool) { +fn update_rate_limiter_if_exists( + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + is_throttling_error: bool, +) { if let Some(crl) = cfg.load::() { - let seconds_since_unix_epoch = get_seconds_since_unix_epoch(cfg); + let seconds_since_unix_epoch = get_seconds_since_unix_epoch(runtime_components); crl.update_rate_limiter(seconds_since_unix_epoch, is_throttling_error); } } -fn check_rate_limiter_for_delay(cfg: &ConfigBag, kind: ErrorKind) -> Option { +fn check_rate_limiter_for_delay( + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + kind: ErrorKind, +) -> Option { if let Some(crl) = cfg.load::() { let retry_reason = if kind == ErrorKind::ThrottlingError { RequestReason::RetryTimeout } else { RequestReason::Retry }; - if let Err(delay) = crl - .acquire_permission_to_send_a_request(get_seconds_since_unix_epoch(cfg), retry_reason) - { + if let Err(delay) = crl.acquire_permission_to_send_a_request( + get_seconds_since_unix_epoch(runtime_components), + retry_reason, + ) { return Some(delay); } } @@ -276,8 +297,10 @@ fn calculate_exponential_backoff(base: f64, initial_backoff: f64, retry_attempts base * initial_backoff * 2_u32.pow(retry_attempts) as f64 } -fn get_seconds_since_unix_epoch(cfg: &ConfigBag) -> f64 { - let request_time = cfg.request_time().unwrap(); +fn get_seconds_since_unix_epoch(runtime_components: &RuntimeComponents) -> f64 { + let request_time = runtime_components + .time_source() + .expect("time source required for retries"); request_time .now() .duration_since(SystemTime::UNIX_EPOCH) @@ -292,6 +315,7 @@ mod tests { use aws_smithy_runtime_api::client::retries::{ AlwaysRetry, ClassifyRetry, RetryClassifiers, RetryReason, RetryStrategy, }; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::config_bag::Layer; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; use aws_smithy_types::type_erasure::TypeErasedBox; @@ -305,11 +329,12 @@ mod tests { #[test] fn no_retry_necessary_for_ok_result() { let cfg = ConfigBag::base(); + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); let strategy = StandardRetryStrategy::default(); ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); let actual = strategy - .should_attempt_retry(&ctx, &cfg) + .should_attempt_retry(&ctx, &rc, &cfg) .expect("method is infallible for this use"); assert_eq!(ShouldAttempt::No, actual); } @@ -317,26 +342,29 @@ mod tests { fn set_up_cfg_and_context( error_kind: ErrorKind, current_request_attempts: u32, - ) -> (InterceptorContext, ConfigBag) { + ) -> (InterceptorContext, RuntimeComponents, ConfigBag) { let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); ctx.set_output_or_error(Err(OrchestratorError::other("doesn't matter"))); + let rc = RuntimeComponentsBuilder::for_tests() + .with_retry_classifiers(Some( + RetryClassifiers::new().with_classifier(AlwaysRetry(error_kind)), + )) + .build() + .unwrap(); let mut layer = Layer::new("test"); - layer.set_retry_classifiers( - RetryClassifiers::new().with_classifier(AlwaysRetry(error_kind)), - ); layer.store_put(RequestAttempts::new(current_request_attempts)); let cfg = ConfigBag::of_layers(vec![layer]); - (ctx, cfg) + (ctx, rc, cfg) } // Test that error kinds produce the correct "retry after X seconds" output. // All error kinds are handled in the same way for the standard strategy. fn test_should_retry_error_kind(error_kind: ErrorKind) { - let (ctx, cfg) = set_up_cfg_and_context(error_kind, 3); + let (ctx, rc, cfg) = set_up_cfg_and_context(error_kind, 3); let strategy = StandardRetryStrategy::default().with_base(|| 1.0); let actual = strategy - .should_attempt_retry(&ctx, &cfg) + .should_attempt_retry(&ctx, &rc, &cfg) .expect("method is infallible for this use"); assert_eq!(ShouldAttempt::YesAfterDelay(Duration::from_secs(4)), actual); } @@ -365,12 +393,12 @@ mod tests { fn dont_retry_when_out_of_attempts() { let current_attempts = 4; let max_attempts = current_attempts; - let (ctx, cfg) = set_up_cfg_and_context(ErrorKind::TransientError, current_attempts); + let (ctx, rc, cfg) = set_up_cfg_and_context(ErrorKind::TransientError, current_attempts); let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(max_attempts); let actual = strategy - .should_attempt_retry(&ctx, &cfg) + .should_attempt_retry(&ctx, &rc, &cfg) .expect("method is infallible for this use"); assert_eq!(ShouldAttempt::No, actual); } @@ -431,23 +459,28 @@ mod tests { } #[cfg(feature = "test-util")] - fn setup_test(retry_reasons: Vec) -> (ConfigBag, InterceptorContext) { - let mut cfg = ConfigBag::base(); - cfg.interceptor_state().set_retry_classifiers( - RetryClassifiers::new() - .with_classifier(PresetReasonRetryClassifier::new(retry_reasons)), - ); + fn setup_test( + retry_reasons: Vec, + ) -> (ConfigBag, RuntimeComponents, InterceptorContext) { + let rc = RuntimeComponentsBuilder::for_tests() + .with_retry_classifiers(Some( + RetryClassifiers::new() + .with_classifier(PresetReasonRetryClassifier::new(retry_reasons)), + )) + .build() + .unwrap(); + let cfg = ConfigBag::base(); let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); // This type doesn't matter b/c the classifier will just return whatever we tell it to. ctx.set_output_or_error(Err(OrchestratorError::other("doesn't matter"))); - (cfg, ctx) + (cfg, rc, ctx) } #[cfg(feature = "test-util")] #[test] fn eventual_success() { - let (mut cfg, mut ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let (mut cfg, rc, mut ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5); @@ -455,13 +488,13 @@ mod tests { let token_bucket = cfg.load::().unwrap().clone(); cfg.interceptor_state().store_put(RequestAttempts::new(1)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 495); cfg.interceptor_state().store_put(RequestAttempts::new(2)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(2)); assert_eq!(token_bucket.available_permits(), 490); @@ -469,7 +502,7 @@ mod tests { ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); cfg.interceptor_state().store_put(RequestAttempts::new(3)); - let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); assert_eq!(token_bucket.available_permits(), 495); } @@ -477,7 +510,7 @@ mod tests { #[cfg(feature = "test-util")] #[test] fn no_more_attempts() { - let (mut cfg, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let (mut cfg, rc, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(3); @@ -485,19 +518,19 @@ mod tests { let token_bucket = cfg.load::().unwrap().clone(); cfg.interceptor_state().store_put(RequestAttempts::new(1)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 495); cfg.interceptor_state().store_put(RequestAttempts::new(2)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(2)); assert_eq!(token_bucket.available_permits(), 490); cfg.interceptor_state().store_put(RequestAttempts::new(3)); - let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); assert_eq!(token_bucket.available_permits(), 490); } @@ -505,7 +538,7 @@ mod tests { #[cfg(feature = "test-util")] #[test] fn no_quota() { - let (mut cfg, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let (mut cfg, rc, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5); @@ -513,13 +546,13 @@ mod tests { let token_bucket = cfg.load::().unwrap().clone(); cfg.interceptor_state().store_put(RequestAttempts::new(1)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 0); cfg.interceptor_state().store_put(RequestAttempts::new(2)); - let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); assert_eq!(token_bucket.available_permits(), 0); } @@ -527,7 +560,7 @@ mod tests { #[cfg(feature = "test-util")] #[test] fn quota_replenishes_on_success() { - let (mut cfg, mut ctx) = setup_test(vec![ + let (mut cfg, rc, mut ctx) = setup_test(vec![ RetryReason::Error(ErrorKind::TransientError), RetryReason::Explicit(Duration::from_secs(1)), ]); @@ -538,13 +571,13 @@ mod tests { let token_bucket = cfg.load::().unwrap().clone(); cfg.interceptor_state().store_put(RequestAttempts::new(1)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 90); cfg.interceptor_state().store_put(RequestAttempts::new(2)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 90); @@ -552,7 +585,7 @@ mod tests { ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); cfg.interceptor_state().store_put(RequestAttempts::new(3)); - let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); assert_eq!(token_bucket.available_permits(), 100); @@ -562,7 +595,8 @@ mod tests { #[test] fn quota_replenishes_on_first_try_success() { const PERMIT_COUNT: usize = 20; - let (mut cfg, mut ctx) = setup_test(vec![RetryReason::Error(ErrorKind::TransientError)]); + let (mut cfg, rc, mut ctx) = + setup_test(vec![RetryReason::Error(ErrorKind::TransientError)]); let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(u32::MAX); @@ -581,7 +615,7 @@ mod tests { cfg.interceptor_state() .store_put(RequestAttempts::new(attempt)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); assert!(matches!(should_retry, ShouldAttempt::YesAfterDelay(_))); attempt += 1; } @@ -600,7 +634,7 @@ mod tests { cfg.interceptor_state() .store_put(RequestAttempts::new(attempt)); - let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); attempt += 1; } @@ -612,7 +646,7 @@ mod tests { #[cfg(feature = "test-util")] #[test] fn backoff_timing() { - let (mut cfg, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let (mut cfg, rc, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5); @@ -620,31 +654,31 @@ mod tests { let token_bucket = cfg.load::().unwrap().clone(); cfg.interceptor_state().store_put(RequestAttempts::new(1)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 495); cfg.interceptor_state().store_put(RequestAttempts::new(2)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(2)); assert_eq!(token_bucket.available_permits(), 490); cfg.interceptor_state().store_put(RequestAttempts::new(3)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(4)); assert_eq!(token_bucket.available_permits(), 485); cfg.interceptor_state().store_put(RequestAttempts::new(4)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(8)); assert_eq!(token_bucket.available_permits(), 480); cfg.interceptor_state().store_put(RequestAttempts::new(5)); - let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); assert_eq!(token_bucket.available_permits(), 480); } @@ -652,7 +686,7 @@ mod tests { #[cfg(feature = "test-util")] #[test] fn max_backoff_time() { - let (mut cfg, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); + let (mut cfg, rc, ctx) = setup_test(vec![RetryReason::Error(ErrorKind::ServerError)]); let strategy = StandardRetryStrategy::default() .with_base(|| 1.0) .with_max_attempts(5) @@ -662,31 +696,31 @@ mod tests { let token_bucket = cfg.load::().unwrap().clone(); cfg.interceptor_state().store_put(RequestAttempts::new(1)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 495); cfg.interceptor_state().store_put(RequestAttempts::new(2)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(2)); assert_eq!(token_bucket.available_permits(), 490); cfg.interceptor_state().store_put(RequestAttempts::new(3)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(3)); assert_eq!(token_bucket.available_permits(), 485); cfg.interceptor_state().store_put(RequestAttempts::new(4)); - let should_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let should_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); let dur = should_retry.expect_delay(); assert_eq!(dur, Duration::from_secs(3)); assert_eq!(token_bucket.available_permits(), 480); cfg.interceptor_state().store_put(RequestAttempts::new(5)); - let no_retry = strategy.should_attempt_retry(&ctx, &cfg).unwrap(); + let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); assert_eq!(no_retry, ShouldAttempt::No); assert_eq!(token_bucket.available_permits(), 480); } diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs index 4c2a34af11..957f04bab3 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs @@ -8,6 +8,7 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use std::fmt; @@ -34,6 +35,7 @@ where fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { (self.f)(context, cfg); @@ -41,34 +43,3 @@ where Ok(()) } } - -#[cfg(test)] -mod tests { - use super::*; - use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; - use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; - use aws_smithy_types::type_erasure::TypedBox; - use std::time::{Duration, UNIX_EPOCH}; - - #[test] - fn set_test_request_time() { - let mut cfg = ConfigBag::base(); - let mut ctx = InterceptorContext::new(TypedBox::new("anything").erase()); - ctx.enter_serialization_phase(); - ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); - let _ = ctx.take_input(); - ctx.enter_before_transmit_phase(); - let mut ctx = Into::into(&mut ctx); - let request_time = UNIX_EPOCH + Duration::from_secs(1624036048); - let interceptor = TestParamsSetterInterceptor::new( - move |_: &mut BeforeTransmitInterceptorContextMut<'_>, cfg: &mut ConfigBag| { - cfg.interceptor_state().set_request_time(request_time); - }, - ); - interceptor - .modify_before_signing(&mut ctx, &mut cfg) - .unwrap(); - assert_eq!(cfg.request_time().unwrap().now(), request_time); - } -} diff --git a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs index e1ba3aec63..c149878dda 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/timeout.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/timeout.rs @@ -6,8 +6,8 @@ use aws_smithy_async::future::timeout::Timeout; use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, Sleep}; use aws_smithy_client::SdkError; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::orchestrator::HttpResponse; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::timeout::TimeoutConfig; use pin_project_lite::pin_project; @@ -109,14 +109,14 @@ pub(super) struct MaybeTimeoutConfig { timeout_kind: TimeoutKind, } -pub(super) trait ProvideMaybeTimeoutConfig { - fn maybe_timeout_config(&self, timeout_kind: TimeoutKind) -> MaybeTimeoutConfig; -} - -impl ProvideMaybeTimeoutConfig for ConfigBag { - fn maybe_timeout_config(&self, timeout_kind: TimeoutKind) -> MaybeTimeoutConfig { - if let Some(timeout_config) = self.load::() { - let sleep_impl = self.sleep_impl(); +impl MaybeTimeoutConfig { + pub(super) fn new( + runtime_components: &RuntimeComponents, + cfg: &ConfigBag, + timeout_kind: TimeoutKind, + ) -> MaybeTimeoutConfig { + if let Some(timeout_config) = cfg.load::() { + let sleep_impl = runtime_components.sleep_impl(); let timeout = match (sleep_impl.as_ref(), timeout_kind) { (None, _) => None, (Some(_), TimeoutKind::Operation) => timeout_config.operation_timeout(), @@ -142,23 +142,14 @@ impl ProvideMaybeTimeoutConfig for ConfigBag { /// Trait to conveniently wrap a future with an optional timeout. pub(super) trait MaybeTimeout: Sized { /// Wraps a future in a timeout if one is set. - fn maybe_timeout_with_config( - self, - timeout_config: MaybeTimeoutConfig, - ) -> MaybeTimeoutFuture; - - /// Wraps a future in a timeout if one is set. - fn maybe_timeout(self, cfg: &ConfigBag, kind: TimeoutKind) -> MaybeTimeoutFuture; + fn maybe_timeout(self, timeout_config: MaybeTimeoutConfig) -> MaybeTimeoutFuture; } impl MaybeTimeout for T where T: Future, { - fn maybe_timeout_with_config( - self, - timeout_config: MaybeTimeoutConfig, - ) -> MaybeTimeoutFuture { + fn maybe_timeout(self, timeout_config: MaybeTimeoutConfig) -> MaybeTimeoutFuture { match timeout_config { MaybeTimeoutConfig { sleep_impl: Some(sleep_impl), @@ -172,22 +163,18 @@ where _ => MaybeTimeoutFuture::NoTimeout { future: self }, } } - - fn maybe_timeout(self, cfg: &ConfigBag, kind: TimeoutKind) -> MaybeTimeoutFuture { - self.maybe_timeout_with_config(cfg.maybe_timeout_config(kind)) - } } #[cfg(test)] mod tests { - use crate::client::timeout::{MaybeTimeout, TimeoutKind}; + use super::*; use aws_smithy_async::assert_elapsed; use aws_smithy_async::future::never::Never; use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep, TokioSleep}; use aws_smithy_http::result::SdkError; - use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::orchestrator::HttpResponse; - use aws_smithy_types::config_bag::{ConfigBag, Layer}; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; + use aws_smithy_types::config_bag::{CloneableLayer, ConfigBag}; use aws_smithy_types::timeout::TimeoutConfig; use std::time::Duration; @@ -203,14 +190,19 @@ mod tests { let now = tokio::time::Instant::now(); tokio::time::pause(); - let mut cfg = ConfigBag::base(); - let mut timeout_config = Layer::new("timeout"); + let runtime_components = RuntimeComponentsBuilder::for_tests() + .with_sleep_impl(Some(sleep_impl)) + .build() + .unwrap(); + + let mut timeout_config = CloneableLayer::new("timeout"); timeout_config.store_put(TimeoutConfig::builder().build()); - timeout_config.set_sleep_impl(Some(sleep_impl)); - cfg.push_layer(timeout_config); + let cfg = ConfigBag::of_layers(vec![timeout_config.into()]); + let maybe_timeout = + MaybeTimeoutConfig::new(&runtime_components, &cfg, TimeoutKind::Operation); underlying_future - .maybe_timeout(&cfg, TimeoutKind::Operation) + .maybe_timeout(maybe_timeout) .await .expect("success"); @@ -229,19 +221,21 @@ mod tests { let now = tokio::time::Instant::now(); tokio::time::pause(); - let mut cfg = ConfigBag::base(); - let mut timeout_config = Layer::new("timeout"); + let runtime_components = RuntimeComponentsBuilder::for_tests() + .with_sleep_impl(Some(sleep_impl)) + .build() + .unwrap(); + let mut timeout_config = CloneableLayer::new("timeout"); timeout_config.store_put( TimeoutConfig::builder() .operation_timeout(Duration::from_millis(250)) .build(), ); - timeout_config.set_sleep_impl(Some(sleep_impl)); - cfg.push_layer(timeout_config); + let cfg = ConfigBag::of_layers(vec![timeout_config.into()]); - let result = underlying_future - .maybe_timeout(&cfg, TimeoutKind::Operation) - .await; + let maybe_timeout = + MaybeTimeoutConfig::new(&runtime_components, &cfg, TimeoutKind::Operation); + let result = underlying_future.maybe_timeout(maybe_timeout).await; let err = result.expect_err("should have timed out"); assert_eq!(format!("{:?}", err), "TimeoutError(TimeoutError { source: MaybeTimeoutError { kind: Operation, duration: 250ms } })"); diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index f293dbea18..42adec399e 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -119,7 +119,7 @@ impl CloneableLayer { /// Removes `T` from this bag pub fn unset(&mut self) -> &mut Self { - self.0.unset::(); + self.put_directly_cloneable::>(Value::ExplicitlyUnset(type_name::())); self } @@ -851,6 +851,10 @@ mod test { let layer_1_cloned = layer_1.clone(); assert_eq!(expected_str, &layer_1_cloned.load::().unwrap().0); + // Should still be cloneable after unsetting a field + layer_1.unset::(); + assert!(layer_1.try_clone().unwrap().load::().is_none()); + #[derive(Clone, Debug)] struct Rope(String); impl Storable for Rope { diff --git a/rust-runtime/inlineable/src/client_http_checksum_required.rs b/rust-runtime/inlineable/src/client_http_checksum_required.rs index 636016a3fc..50fe4d18e1 100644 --- a/rust-runtime/inlineable/src/client_http_checksum_required.rs +++ b/rust-runtime/inlineable/src/client_http_checksum_required.rs @@ -5,20 +5,33 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; -use aws_smithy_runtime_api::client::interceptors::{ - Interceptor, InterceptorRegistrar, SharedInterceptor, +use aws_smithy_runtime_api::client::interceptors::{Interceptor, SharedInterceptor}; +use aws_smithy_runtime_api::client::runtime_components::{ + RuntimeComponents, RuntimeComponentsBuilder, }; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::base64; use aws_smithy_types::config_bag::ConfigBag; use http::header::HeaderName; +use std::borrow::Cow; #[derive(Debug)] -pub(crate) struct HttpChecksumRequiredRuntimePlugin; +pub(crate) struct HttpChecksumRequiredRuntimePlugin { + runtime_components: RuntimeComponentsBuilder, +} + +impl HttpChecksumRequiredRuntimePlugin { + pub(crate) fn new() -> Self { + Self { + runtime_components: RuntimeComponentsBuilder::new("HttpChecksumRequiredRuntimePlugin") + .with_interceptor(SharedInterceptor::new(HttpChecksumRequiredInterceptor)), + } + } +} impl RuntimePlugin for HttpChecksumRequiredRuntimePlugin { - fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { - interceptors.register(SharedInterceptor::new(HttpChecksumRequiredInterceptor)); + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.runtime_components) } } @@ -29,6 +42,7 @@ impl Interceptor for HttpChecksumRequiredInterceptor { fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, _cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let request = context.request_mut(); diff --git a/rust-runtime/inlineable/src/client_idempotency_token.rs b/rust-runtime/inlineable/src/client_idempotency_token.rs index cf4d2d7e53..5e46f6a01e 100644 --- a/rust-runtime/inlineable/src/client_idempotency_token.rs +++ b/rust-runtime/inlineable/src/client_idempotency_token.rs @@ -8,16 +8,18 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::{ BeforeSerializationInterceptorContextMut, Input, }; -use aws_smithy_runtime_api::client::interceptors::{ - Interceptor, InterceptorRegistrar, SharedInterceptor, +use aws_smithy_runtime_api::client::interceptors::{Interceptor, SharedInterceptor}; +use aws_smithy_runtime_api::client::runtime_components::{ + RuntimeComponents, RuntimeComponentsBuilder, }; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::ConfigBag; +use std::borrow::Cow; use std::fmt; #[derive(Debug)] pub(crate) struct IdempotencyTokenRuntimePlugin { - interceptor: SharedInterceptor, + runtime_components: RuntimeComponentsBuilder, } impl IdempotencyTokenRuntimePlugin { @@ -26,14 +28,17 @@ impl IdempotencyTokenRuntimePlugin { S: Fn(IdempotencyTokenProvider, &mut Input) + Send + Sync + 'static, { Self { - interceptor: SharedInterceptor::new(IdempotencyTokenInterceptor { set_token }), + runtime_components: RuntimeComponentsBuilder::new("IdempotencyTokenRuntimePlugin") + .with_interceptor(SharedInterceptor::new(IdempotencyTokenInterceptor { + set_token, + })), } } } impl RuntimePlugin for IdempotencyTokenRuntimePlugin { - fn interceptors(&self, interceptors: &mut InterceptorRegistrar) { - interceptors.register(self.interceptor.clone()); + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + Cow::Borrowed(&self.runtime_components) } } @@ -54,6 +59,7 @@ where fn modify_before_serialization( &self, context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let token_provider = cfg diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index a434b07761..0104e7f03d 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -31,6 +31,7 @@ services_that_pass_tests=(\ "polly"\ "qldbsession"\ "route53"\ + "s3"\ "s3control"\ "sso"\ "sts"\ From 3d1c34d3aa8a35b3d6943d489ef34954c8a680b3 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 12 Jul 2023 18:31:28 -0700 Subject: [PATCH 208/253] Fix panic from repeat clones of `TypeErasedBox` (#2842) This PR fixes a panic that would result from cloning a cloneable `TypeErasedBox` twice. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- rust-runtime/aws-smithy-types/src/config_bag.rs | 7 +++++++ .../aws-smithy-types/src/type_erasure.rs | 17 +++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index 42adec399e..cf17108f2c 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -855,6 +855,13 @@ mod test { layer_1.unset::(); assert!(layer_1.try_clone().unwrap().load::().is_none()); + // It is cloneable multiple times in succession + let _ = layer_1 + .try_clone() + .expect("clone 1") + .try_clone() + .expect("clone 2"); + #[derive(Clone, Debug)] struct Rope(String); impl Storable for Rope { diff --git a/rust-runtime/aws-smithy-types/src/type_erasure.rs b/rust-runtime/aws-smithy-types/src/type_erasure.rs index 2665585640..5f4cc08054 100644 --- a/rust-runtime/aws-smithy-types/src/type_erasure.rs +++ b/rust-runtime/aws-smithy-types/src/type_erasure.rs @@ -122,7 +122,13 @@ impl TypeErasedBox { impl fmt::Debug for TypeErasedBox { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("TypeErasedBox:")?; + f.write_str("TypeErasedBox[")?; + if self.clone.is_some() { + f.write_str("Clone")?; + } else { + f.write_str("!Clone")?; + } + f.write_str("]:")?; (self.debug)(&self.field, f) } } @@ -145,7 +151,7 @@ impl TypeErasedBox { fmt::Debug::fmt(value.downcast_ref::().expect("type-checked"), f) }; let clone = |value: &Box| { - TypeErasedBox::new(value.downcast_ref::().expect("typechecked").clone()) + TypeErasedBox::new_with_clone(value.downcast_ref::().expect("typechecked").clone()) }; Self { field: Box::new(value), @@ -292,8 +298,11 @@ mod tests { .0 = "3"; let bar_erased = bar.erase(); - assert_eq!("TypeErasedBox:Foo(\"3\")", format!("{foo_erased:?}")); - assert_eq!("TypeErasedBox:Bar(2)", format!("{bar_erased:?}")); + assert_eq!( + "TypeErasedBox[!Clone]:Foo(\"3\")", + format!("{foo_erased:?}") + ); + assert_eq!("TypeErasedBox[!Clone]:Bar(2)", format!("{bar_erased:?}")); let bar_erased = TypedBox::::assume_from(bar_erased).expect_err("it's not a Foo"); let mut bar = TypedBox::::assume_from(bar_erased).expect("it's a Bar"); From 26827f1acab8c061ec27dbfcc342648f93141668 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 13 Jul 2023 13:35:55 -0700 Subject: [PATCH 209/253] Don't allow `test-util` feature in compile time dependencies (#2843) During the orchestrator implementation, a bug was introduced that always enabled the `test-util` feature on `aws-smithy-runtime` in the generated crates. This led to several pieces of non-test code relying on test-only code, specifically around `SystemTime` implementing `TimeSource`. This PR fixes that issue, and also fixes another issue where the `RuntimeComponentsBuilder` was being initialized without a name for the service config. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-config/src/provider_config.rs | 3 +- aws/rust-runtime/aws-config/src/sts/util.rs | 3 +- .../AwsCustomizableOperationDecorator.kt | 69 +++++++++++++++---- .../smithy/rustsdk/AwsPresigningDecorator.kt | 4 +- .../rustsdk/IntegrationTestDependencies.kt | 6 +- aws/sdk/integration-tests/s3/Cargo.toml | 2 +- .../config/ServiceConfigGenerator.kt | 24 ++++++- .../customizations/HttpAuthDecoratorTest.kt | 2 +- .../MetadataCustomizationTest.kt | 2 +- ...onfigOverrideRuntimePluginGeneratorTest.kt | 4 +- .../client/FluentClientGeneratorTest.kt | 2 + codegen-core/build.gradle.kts | 3 +- .../codegen/core/rustlang/CargoDependency.kt | 21 ++++-- .../rust/codegen/core/rustlang/RustWriter.kt | 9 +++ .../codegen/core/smithy/CodegenDelegator.kt | 7 +- .../smithy/generators/CargoTomlGenerator.kt | 44 +++++++++--- .../smithy/rust/codegen/core/testutil/Rust.kt | 37 ++++++---- .../core/rustlang/CargoDependencyTest.kt | 48 +++++++++++++ .../core/smithy/CodegenDelegatorTest.kt | 23 ++++++- .../src/client/runtime_components.rs | 6 +- .../src/client/orchestrator.rs | 2 +- .../src/client/test_util.rs | 1 - .../src/client/test_util/interceptors.rs | 45 ------------ 23 files changed, 255 insertions(+), 112 deletions(-) create mode 100644 codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependencyTest.kt delete mode 100644 rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs diff --git a/aws/rust-runtime/aws-config/src/provider_config.rs b/aws/rust-runtime/aws-config/src/provider_config.rs index afa2989811..27bc23108d 100644 --- a/aws/rust-runtime/aws-config/src/provider_config.rs +++ b/aws/rust-runtime/aws-config/src/provider_config.rs @@ -91,6 +91,7 @@ impl ProviderConfig { /// Unlike [`ProviderConfig::empty`] where `env` and `fs` will use their non-mocked implementations, /// this method will use an empty mock environment and an empty mock file system. pub fn no_configuration() -> Self { + use aws_smithy_async::time::StaticTimeSource; use std::collections::HashMap; use std::time::UNIX_EPOCH; let fs = Fs::from_raw_map(HashMap::new()); @@ -100,7 +101,7 @@ impl ProviderConfig { profile_files: ProfileFiles::default(), env, fs, - time_source: SharedTimeSource::new(UNIX_EPOCH), + time_source: SharedTimeSource::new(StaticTimeSource::new(UNIX_EPOCH)), connector: HttpConnector::Prebuilt(None), sleep: None, region: None, diff --git a/aws/rust-runtime/aws-config/src/sts/util.rs b/aws/rust-runtime/aws-config/src/sts/util.rs index c4fd630aef..bc6151985d 100644 --- a/aws/rust-runtime/aws-config/src/sts/util.rs +++ b/aws/rust-runtime/aws-config/src/sts/util.rs @@ -7,7 +7,6 @@ use aws_credential_types::provider::{self, error::CredentialsError}; use aws_credential_types::Credentials as AwsCredentials; use aws_sdk_sts::types::Credentials as StsCredentials; -use aws_smithy_async::time::TimeSource; use std::convert::TryFrom; use std::time::{SystemTime, UNIX_EPOCH}; @@ -47,6 +46,6 @@ pub(crate) fn into_credentials( /// provide a name for the session, the provider will choose a name composed of a base + a timestamp, /// e.g. `profile-file-provider-123456789` pub(crate) fn default_session_name(base: &str, ts: SystemTime) -> String { - let now = ts.now().duration_since(UNIX_EPOCH).expect("post epoch"); + let now = ts.duration_since(UNIX_EPOCH).expect("post epoch"); format!("{}-{}", base, now.as_millis()) } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index 08d58134d4..6af6bfde0d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -5,6 +5,7 @@ package software.amazon.smithy.rustsdk +import software.amazon.smithy.rust.codegen.client.smithy.ClientRustModule import software.amazon.smithy.rust.codegen.client.smithy.generators.client.CustomizableOperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.client.CustomizableOperationSection import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency @@ -18,28 +19,70 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : CustomizableOperationCustomization() { private val codegenScope = arrayOf( *RuntimeType.preludeScope, - "AwsUserAgent" to AwsRuntimeType.awsHttp(runtimeConfig) - .resolve("user_agent::AwsUserAgent"), + "AwsUserAgent" to AwsRuntimeType.awsHttp(runtimeConfig).resolve("user_agent::AwsUserAgent"), "BeforeTransmitInterceptorContextMut" to RuntimeType.beforeTransmitInterceptorContextMut(runtimeConfig), "ConfigBag" to RuntimeType.configBag(runtimeConfig), "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "http" to CargoDependency.Http.toType(), "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), - "StaticRuntimePlugin" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::runtime_plugin::StaticRuntimePlugin"), "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(runtimeConfig), - "SharedTimeSource" to CargoDependency.smithyAsync(runtimeConfig).withFeature("test-util").toType() - .resolve("time::SharedTimeSource"), - "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::interceptors::SharedInterceptor"), - "TestParamsSetterInterceptor" to CargoDependency.smithyRuntime(runtimeConfig).withFeature("test-util") - .toType().resolve("client::test_util::interceptors::TestParamsSetterInterceptor"), + "SharedInterceptor" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::SharedInterceptor"), + "SharedTimeSource" to CargoDependency.smithyAsync(runtimeConfig).toType().resolve("time::SharedTimeSource"), + "StaticRuntimePlugin" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin::StaticRuntimePlugin"), + "StaticTimeSource" to CargoDependency.smithyAsync(runtimeConfig).toType().resolve("time::StaticTimeSource"), + "TestParamsSetterInterceptor" to testParamsSetterInterceptor(), ) + // TODO(enableNewSmithyRuntimeCleanup): Delete this once test helpers on `CustomizableOperation` have been removed + private fun testParamsSetterInterceptor(): RuntimeType = RuntimeType.forInlineFun("TestParamsSetterInterceptor", ClientRustModule.Client.customize) { + rustTemplate( + """ + mod test_params_setter_interceptor { + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; + use aws_smithy_runtime_api::client::interceptors::Interceptor; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; + use aws_smithy_types::config_bag::ConfigBag; + use std::fmt; + + pub(super) struct TestParamsSetterInterceptor { f: F } + + impl fmt::Debug for TestParamsSetterInterceptor { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "TestParamsSetterInterceptor") + } + } + + impl TestParamsSetterInterceptor { + pub fn new(f: F) -> Self { Self { f } } + } + + impl Interceptor for TestParamsSetterInterceptor + where + F: Fn(&mut BeforeTransmitInterceptorContextMut<'_>, &mut ConfigBag) + Send + Sync + 'static, + { + fn modify_before_signing( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + (self.f)(context, cfg); + Ok(()) + } + } + } + use test_params_setter_interceptor::TestParamsSetterInterceptor; + """, + *codegenScope, + ) + } + override fun section(section: CustomizableOperationSection): Writable = writable { if (section is CustomizableOperationSection.CustomizableOperationImpl) { if (section.isRuntimeModeOrchestrator) { + // TODO(enableNewSmithyRuntimeCleanup): Delete these utilities rustTemplate( """ ##[doc(hidden)] @@ -49,7 +92,7 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : #{StaticRuntimePlugin}::new() .with_runtime_components( #{RuntimeComponentsBuilder}::new("request_time_for_tests") - .with_time_source(Some(#{SharedTimeSource}::new(request_time))) + .with_time_source(Some(#{SharedTimeSource}::new(#{StaticTimeSource}::new(request_time)))) ) ) } @@ -92,7 +135,9 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : ##[doc(hidden)] // This is a temporary method for testing. NEVER use it in production pub fn request_time_for_tests(mut self, request_time: ::std::time::SystemTime) -> Self { - self.operation.properties_mut().insert(#{SharedTimeSource}::new(request_time)); + self.operation.properties_mut().insert( + #{SharedTimeSource}::new(#{StaticTimeSource}::new(request_time)) + ); self } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index ef59287b35..f236a08f49 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -216,10 +216,12 @@ class AwsInputPresignedMethod( """ // Change signature type to query params and wire up presigning config let mut props = request.properties_mut(); - props.insert(#{SharedTimeSource}::new(presigning_config.start_time())); + props.insert(#{SharedTimeSource}::new(#{StaticTimeSource}::new(presigning_config.start_time()))); """, "SharedTimeSource" to RuntimeType.smithyAsync(runtimeConfig) .resolve("time::SharedTimeSource"), + "StaticTimeSource" to RuntimeType.smithyAsync(runtimeConfig) + .resolve("time::StaticTimeSource"), ) withBlock("props.insert(", ");") { rustTemplate( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt index 8bdfe0cdc1..a091664c1a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt @@ -80,14 +80,14 @@ class IntegrationTestDependencies( override fun section(section: LibRsSection) = when (section) { is LibRsSection.Body -> testDependenciesOnly { if (hasTests) { - val smithyClient = CargoDependency.smithyClient(codegenContext.runtimeConfig) - .copy(features = setOf("test-util"), scope = DependencyScope.Dev) val smithyAsync = CargoDependency.smithyAsync(codegenContext.runtimeConfig) .copy(features = setOf("test-util"), scope = DependencyScope.Dev) + val smithyClient = CargoDependency.smithyClient(codegenContext.runtimeConfig) + .copy(features = setOf("test-util"), scope = DependencyScope.Dev) val smithyTypes = CargoDependency.smithyTypes(codegenContext.runtimeConfig) .copy(features = setOf("test-util"), scope = DependencyScope.Dev) - addDependency(smithyClient) addDependency(smithyAsync) + addDependency(smithyClient) addDependency(smithyTypes) addDependency(CargoDependency.smithyProtocolTestHelpers(codegenContext.runtimeConfig)) addDependency(SerdeJson) diff --git a/aws/sdk/integration-tests/s3/Cargo.toml b/aws/sdk/integration-tests/s3/Cargo.toml index 8a27b1cd2c..c504dba38d 100644 --- a/aws/sdk/integration-tests/s3/Cargo.toml +++ b/aws/sdk/integration-tests/s3/Cargo.toml @@ -17,7 +17,7 @@ aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", aws-http = { path = "../../build/aws-sdk/sdk/aws-http" } aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3" } aws-sdk-sts = { path = "../../build/aws-sdk/sdk/sts" } -aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["rt-tokio"] } +aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util", "rt-tokio"] } aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test" } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index f3cbe633af..8d93ae88fc 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -22,6 +22,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.docsOrFallback import software.amazon.smithy.rust.codegen.core.rustlang.raw import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate 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.RuntimeConfig @@ -374,9 +375,10 @@ class ServiceConfigGenerator( } writer.docs("Builder for creating a `Config`.") - writer.raw("#[derive(Clone, Default)]") - if (runtimeMode.defaultToOrchestrator) { - Attribute(Attribute.derive(RuntimeType.Debug)).render(writer) + if (runtimeMode.defaultToMiddleware) { + writer.raw("#[derive(Clone, Default)]") + } else { + writer.raw("#[derive(Clone, Debug)]") } writer.rustBlock("pub struct Builder") { if (runtimeMode.defaultToOrchestrator) { @@ -406,6 +408,22 @@ class ServiceConfigGenerator( """, ) } + } else { + // Custom implementation of Default to give the runtime components builder a name + writer.rustBlockTemplate("impl #{Default} for Builder", *codegenScope) { + writer.rustTemplate( + """ + fn default() -> Self { + Self { + config: #{Default}::default(), + runtime_components: #{RuntimeComponentsBuilder}::new("service config"), + runtime_plugins: #{Default}::default(), + } + } + """, + *codegenScope, + ) + } } writer.rustBlock("impl Builder") { diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt index 92f653bf1b..474ed7b43e 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt @@ -19,7 +19,7 @@ import software.amazon.smithy.rust.codegen.core.testutil.integrationTest class HttpAuthDecoratorTest { private fun codegenScope(runtimeConfig: RuntimeConfig): Array> = arrayOf( "TestConnection" to CargoDependency.smithyClient(runtimeConfig) - .withFeature("test-util").toType() + .toDevDependency().withFeature("test-util").toType() .resolve("test_connection::TestConnection"), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), ) diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt index 97d88c1680..0f087d50ad 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt @@ -52,7 +52,7 @@ class MetadataCustomizationTest { .resolve("client::runtime_components::RuntimeComponents"), ) rustCrate.testModule { - addDependency(CargoDependency.Tokio.withFeature("test-util").toDevDependency()) + addDependency(CargoDependency.Tokio.toDevDependency().withFeature("test-util")) tokioTest("test_extract_metadata_via_customizable_operation") { rustTemplate( """ diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt index 086a211915..40f82d2965 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt @@ -52,7 +52,7 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), ) rustCrate.testModule { - addDependency(CargoDependency.Tokio.withFeature("test-util").toDevDependency()) + addDependency(CargoDependency.Tokio.toDevDependency().withFeature("test-util")) tokioTest("test_operation_overrides_endpoint_resolver") { rustTemplate( """ @@ -97,7 +97,7 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), ) rustCrate.testModule { - addDependency(CargoDependency.Tokio.withFeature("test-util").toDevDependency()) + addDependency(CargoDependency.Tokio.toDevDependency().withFeature("test-util")) tokioTest("test_operation_overrides_http_connection") { rustTemplate( """ diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt index de6b6f175e..1e02493212 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGeneratorTest.kt @@ -62,6 +62,7 @@ class FluentClientGeneratorTest { } """, "TestConnection" to CargoDependency.smithyClient(codegenContext.runtimeConfig) + .toDevDependency() .withFeature("test-util").toType() .resolve("test_connection::TestConnection"), "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), @@ -108,6 +109,7 @@ class FluentClientGeneratorTest { } """, "TestConnection" to CargoDependency.smithyClient(codegenContext.runtimeConfig) + .toDevDependency() .withFeature("test-util").toType() .resolve("test_connection::TestConnection"), "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), diff --git a/codegen-core/build.gradle.kts b/codegen-core/build.gradle.kts index 393eb4dfa7..556088f8a0 100644 --- a/codegen-core/build.gradle.kts +++ b/codegen-core/build.gradle.kts @@ -112,12 +112,11 @@ if (isTestingEnabled.toBoolean()) { tasks.test { useJUnitPlatform() testLogging { - events("passed", "skipped", "failed") + events("failed") exceptionFormat = TestExceptionFormat.FULL showCauses = true showExceptions = true showStackTraces = true - showStandardStreams = true } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index b91020fe3f..8dad31b91c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -13,11 +13,11 @@ import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.dq import java.nio.file.Path -sealed class DependencyScope { - object Dev : DependencyScope() - object Compile : DependencyScope() - object CfgUnstable : DependencyScope() - object Build : DependencyScope() +enum class DependencyScope { + Build, + CfgUnstable, + Compile, + Dev, } sealed class DependencyLocation @@ -136,6 +136,8 @@ fun InlineDependency.toType() = RuntimeType(module.fullyQualifiedPath(), this) data class Feature(val name: String, val default: Boolean, val deps: List) +val DEV_ONLY_FEATURES = setOf("test-util") + /** * A dependency on an internal or external Cargo Crate */ @@ -150,6 +152,12 @@ data class CargoDependency( ) : RustDependency(name) { val key: Triple get() = Triple(name, location, scope) + init { + if (scope != DependencyScope.Dev && DEV_ONLY_FEATURES.any { features.contains(it) }) { + throw IllegalArgumentException("The `test-util` feature cannot be used outside of DependencyScope.Dev") + } + } + fun withFeature(feature: String): CargoDependency { return copy(features = features.toMutableSet().apply { add(feature) }) } @@ -205,7 +213,8 @@ data class CargoDependency( attribs.add("features = [${joinToString(",") { it.dq() }}]") } } - return "$name = { ${attribs.joinToString(",")} }" + attribs.add("scope = $scope") + return "$name = { ${attribs.joinToString(", ")} }" } fun toType(): RuntimeType { diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt index 9dece19f8b..dd17595c5f 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustWriter.kt @@ -498,6 +498,15 @@ class RustWriter private constructor( } } + fun toml(fileName: String, debugMode: Boolean = false): RustWriter = + RustWriter( + fileName, + namespace = "ignore", + commentCharacter = "#", + printWarning = false, + debugMode = debugMode, + ) + private fun rawWriter(fileName: String, debugMode: Boolean): RustWriter = RustWriter( fileName, diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegator.kt index 157a6f2539..5ae40c3574 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegator.kt @@ -12,6 +12,7 @@ import software.amazon.smithy.codegen.core.WriterDelegator import software.amazon.smithy.model.Model import software.amazon.smithy.model.shapes.Shape import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency +import software.amazon.smithy.rust.codegen.core.rustlang.DEV_ONLY_FEATURES import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope import software.amazon.smithy.rust.codegen.core.rustlang.Feature import software.amazon.smithy.rust.codegen.core.rustlang.InlineDependency @@ -298,7 +299,9 @@ internal fun List.mergeIdenticalTestDependencies(): List + dep.scope == DependencyScope.Dev && + DEV_ONLY_FEATURES.none { devOnly -> dep.features.contains(devOnly) } && + compileDeps.contains(dep.copy(scope = DependencyScope.Compile)) } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt index 86c76c521a..b5eca73acd 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/CargoTomlGenerator.kt @@ -43,12 +43,36 @@ typealias ManifestCustomizations = Map * Generates the crate manifest Cargo.toml file. */ class CargoTomlGenerator( - private val settings: CoreRustSettings, + private val moduleName: String, + private val moduleVersion: String, + private val moduleAuthors: List, + private val moduleDescription: String?, + private val moduleLicense: String?, + private val moduleRepository: String?, private val writer: RustWriter, - private val manifestCustomizations: ManifestCustomizations, - private val dependencies: List, - private val features: List, + private val manifestCustomizations: ManifestCustomizations = emptyMap(), + private val dependencies: List = emptyList(), + private val features: List = emptyList(), ) { + constructor( + settings: CoreRustSettings, + writer: RustWriter, + manifestCustomizations: ManifestCustomizations, + dependencies: List, + features: List, + ) : this( + settings.moduleName, + settings.moduleVersion, + settings.moduleAuthors, + settings.moduleDescription, + settings.license, + settings.moduleRepository, + writer, + manifestCustomizations, + dependencies, + features, + ) + fun render() { val cargoFeatures = features.map { it.name to it.deps }.toMutableList() if (features.isNotEmpty()) { @@ -57,13 +81,13 @@ class CargoTomlGenerator( val cargoToml = mapOf( "package" to listOfNotNull( - "name" to settings.moduleName, - "version" to settings.moduleVersion, - "authors" to settings.moduleAuthors, - settings.moduleDescription?.let { "description" to it }, + "name" to moduleName, + "version" to moduleVersion, + "authors" to moduleAuthors, + moduleDescription?.let { "description" to it }, "edition" to "2021", - "license" to settings.license, - "repository" to settings.moduleRepository, + "license" to moduleLicense, + "repository" to moduleRepository, "metadata" to listOfNotNull( "smithy" to listOfNotNull( "codegen-version" to Version.fullVersion(), diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt index 2bf386a519..8f8e6ff8b5 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt @@ -32,8 +32,10 @@ import software.amazon.smithy.rust.codegen.core.smithy.ModuleDocProvider import software.amazon.smithy.rust.codegen.core.smithy.RuntimeConfig import software.amazon.smithy.rust.codegen.core.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.RustSymbolProvider +import software.amazon.smithy.rust.codegen.core.smithy.generators.CargoTomlGenerator +import software.amazon.smithy.rust.codegen.core.smithy.mergeDependencyFeatures +import software.amazon.smithy.rust.codegen.core.smithy.mergeIdenticalTestDependencies import software.amazon.smithy.rust.codegen.core.util.CommandError -import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rust.codegen.core.util.orNullIfEmpty import software.amazon.smithy.rust.codegen.core.util.runCommand @@ -369,14 +371,19 @@ fun RustWriter.compileAndTest( clippy: Boolean = false, expectFailure: Boolean = false, ): String { - val deps = this.dependencies.map { RustDependency.fromSymbolDependency(it) }.filterIsInstance() + val deps = this.dependencies + .map { RustDependency.fromSymbolDependency(it) } + .filterIsInstance() + .distinct() + .mergeDependencyFeatures() + .mergeIdenticalTestDependencies() val module = if (this.namespace.contains("::")) { this.namespace.split("::")[1] } else { "lib" } val tempDir = this.toString() - .intoCrate(deps.toSet(), module = module, main = main, strict = clippy) + .intoCrate(deps, module = module, main = main, strict = clippy) val mainRs = tempDir.resolve("src/main.rs") val testModule = tempDir.resolve("src/$module.rs") try { @@ -398,23 +405,25 @@ fun RustWriter.compileAndTest( } private fun String.intoCrate( - deps: Set, + deps: List, module: String? = null, main: String = "", strict: Boolean = false, ): File { this.shouldParseAsRust() val tempDir = TestWorkspace.subproject() - val cargoToml = """ - [package] - name = ${tempDir.nameWithoutExtension.dq()} - version = "0.0.1" - authors = ["rcoh@amazon.com"] - edition = "2021" - - [dependencies] - ${deps.joinToString("\n") { it.toString() }} - """.trimIndent() + val cargoToml = RustWriter.toml("Cargo.toml").apply { + CargoTomlGenerator( + moduleName = tempDir.nameWithoutExtension, + moduleVersion = "0.0.1", + moduleAuthors = listOf("Testy McTesterson"), + moduleDescription = null, + moduleLicense = null, + moduleRepository = null, + writer = this, + dependencies = deps, + ).render() + }.toString() tempDir.resolve("Cargo.toml").writeText(cargoToml) tempDir.resolve("src").mkdirs() val mainRs = tempDir.resolve("src/main.rs") diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependencyTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependencyTest.kt new file mode 100644 index 0000000000..9d18c4725e --- /dev/null +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependencyTest.kt @@ -0,0 +1,48 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rust.codegen.core.rustlang + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows + +class CargoDependencyTest { + @Test + fun `it should not allow a dependency with test-util in non-dev scopes`() { + // OK + CargoDependency( + name = "test", + location = CratesIo("1.0"), + features = setOf("foo", "test-util", "bar"), + scope = DependencyScope.Dev, + ) + + // OK + CargoDependency( + name = "test", + location = CratesIo("1.0"), + features = setOf("foo", "bar"), + scope = DependencyScope.Dev, + ).toDevDependency().withFeature("test-util") + + assertThrows { + CargoDependency( + name = "test", + location = CratesIo("1.0"), + features = setOf("foo", "test-util", "bar"), + scope = DependencyScope.Compile, + ) + } + + assertThrows { + CargoDependency( + name = "test", + location = CratesIo("1.0"), + features = setOf("foo", "bar"), + scope = DependencyScope.Compile, + ).withFeature("test-util") + } + } +} diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegatorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegatorTest.kt index c9407372d7..6ed2fc5ed3 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegatorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/CodegenDelegatorTest.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rust.codegen.core.smithy import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.RepeatedTest import org.junit.jupiter.api.Test import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.CratesIo @@ -13,7 +14,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope.Compile import software.amazon.smithy.rust.codegen.core.rustlang.DependencyScope.Dev class CodegenDelegatorTest { - @Test + @RepeatedTest(10) // Test it several times since the shuffle adds in some randomness fun testMergeDependencyFeatures() { val merged = listOf( @@ -36,6 +37,22 @@ class CodegenDelegatorTest { ) } + @RepeatedTest(10) // Test it several times since the shuffle adds in some randomness + fun testMergeDependencyFeaturesDontMergeDevOnlyFeatures() { + val merged = listOf( + CargoDependency("A", CratesIo("1"), Compile, features = setOf("a")), + CargoDependency("A", CratesIo("1"), Compile, features = setOf("b")), + CargoDependency("A", CratesIo("1"), Dev, features = setOf("c")), + CargoDependency("A", CratesIo("1"), Dev, features = setOf("test-util")), + ).shuffled().mergeDependencyFeatures() + .sortedBy { it.scope } + + merged shouldBe setOf( + CargoDependency("A", CratesIo("1"), Compile, features = setOf("a", "b")), + CargoDependency("A", CratesIo("1"), Dev, features = setOf("c", "test-util")), + ) + } + @Test fun testMergeIdenticalFeatures() { val merged = listOf( @@ -43,11 +60,15 @@ class CodegenDelegatorTest { CargoDependency("A", CratesIo("1"), Dev), CargoDependency("B", CratesIo("1"), Compile), CargoDependency("B", CratesIo("1"), Dev, features = setOf("a", "b")), + CargoDependency("C", CratesIo("1"), Compile), + CargoDependency("C", CratesIo("1"), Dev, features = setOf("test-util")), ).mergeIdenticalTestDependencies() merged shouldBe setOf( CargoDependency("A", CratesIo("1"), Compile), CargoDependency("B", CratesIo("1"), Compile), CargoDependency("B", CratesIo("1"), Dev, features = setOf("a", "b")), + CargoDependency("C", CratesIo("1"), Compile), + CargoDependency("C", CratesIo("1"), Dev, features = setOf("test-util")), ) } } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs index 9b24628997..a00fe57671 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs @@ -134,7 +134,7 @@ macro_rules! declare_runtime_components { $($field_name: runtime_component_field_type!($outer_type $inner_type $($option)?),)+ } - #[derive(Clone, Debug, Default)] + #[derive(Clone, Debug)] pub struct $builder_name { builder_name: &'static str, $($field_name: $outer_type>,)+ @@ -200,8 +200,8 @@ declare_runtime_components! { impl RuntimeComponents { /// Returns a builder for runtime components. - pub fn builder() -> RuntimeComponentsBuilder { - Default::default() + pub fn builder(name: &'static str) -> RuntimeComponentsBuilder { + RuntimeComponentsBuilder::new(name) } /// Returns the auth option resolver. diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 803310221f..38b8852acb 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -162,7 +162,7 @@ fn apply_configuration( continue_on_err!([ctx] => Interceptors::new(operation_rc_builder.interceptors()).read_before_execution(true, ctx, cfg)); // The order below is important. Client interceptors must run before operation interceptors. - Ok(RuntimeComponents::builder() + Ok(RuntimeComponents::builder("merged orchestrator components") .merge_from(&client_rc_builder) .merge_from(&operation_rc_builder) .build()?) diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs index de7aef4ac5..30adb4f6cb 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs @@ -4,5 +4,4 @@ */ pub mod deserializer; -pub mod interceptors; pub mod serializer; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs deleted file mode 100644 index 957f04bab3..0000000000 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/interceptors.rs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -// TODO(enableNewSmithyRuntimeCleanup): Delete this file once test helpers on `CustomizableOperation` have been removed - -use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; -use aws_smithy_runtime_api::client::interceptors::Interceptor; -use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; -use aws_smithy_types::config_bag::ConfigBag; -use std::fmt; - -pub struct TestParamsSetterInterceptor { - f: F, -} - -impl fmt::Debug for TestParamsSetterInterceptor { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "TestParamsSetterInterceptor") - } -} - -impl TestParamsSetterInterceptor { - pub fn new(f: F) -> Self { - Self { f } - } -} - -impl Interceptor for TestParamsSetterInterceptor -where - F: Fn(&mut BeforeTransmitInterceptorContextMut<'_>, &mut ConfigBag) + Send + Sync + 'static, -{ - fn modify_before_signing( - &self, - context: &mut BeforeTransmitInterceptorContextMut<'_>, - _runtime_components: &RuntimeComponents, - cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { - (self.f)(context, cfg); - - Ok(()) - } -} From 299976659580932832318c562c228e366a3c8e24 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Fri, 14 Jul 2023 12:36:17 -0500 Subject: [PATCH 210/253] Remove third party types from public APIs (#2845) ## Motivation and Context Addresses item 1, 3, and 9 in #2413 ## Description This PR removes the third party types as follows: - removes `InvalidHeaderValue` from public API in `aws-http` - removes `SendError` from public API in `aws-smithy-async` - removes `xmlparser` from public API in `aws-smithy-xml` Those types were exposed to public APIs primarily due to the implementation of traits, e.g. `From` and `Iterator`. Those implementations are used internally (such as XML deserialization and converting an error type) so we should be able to hide them within crates. ## Testing - [x] Passed tests in CI ## Checklist - [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._ --------- Co-authored-by: ysaito1001 --- CHANGELOG.next.toml | 12 ++++ aws/rust-runtime/aws-http/external-types.toml | 3 - aws/rust-runtime/aws-http/src/user_agent.rs | 33 ++++++----- .../aws-smithy-async/external-types.toml | 3 - .../aws-smithy-async/src/future/rendezvous.rs | 35 +++++++++++- .../aws-smithy-xml/external-types.toml | 3 - rust-runtime/aws-smithy-xml/src/decode.rs | 55 ++++++++++--------- 7 files changed, 93 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index d603759359..7b44b6203d 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -628,3 +628,15 @@ message = "The naming `make_token` for fields and the API of `IdempotencyTokenPr references = ["smithy-rs#2783"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "ysaito1001" + +[[smithy-rs]] +message = "`aws_smithy_async::future::rendezvous::Sender::send` no longer exposes `tokio::sync::mpsc::error::SendError` for the error of its return type and instead exposes a new-type wrapper called `aws_smithy_async::future::rendezvous::error::SendError`. In addition, the `aws_smithy_xml` crate no longer exposes types from `xmlparser`." +references = ["smithy-rs#2845"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } +author = "ysaito1001" + +[[aws-sdk-rust]] +message = "The implementation `From` for `aws_http::user_agent::UserAgentStageError` has been removed." +references = ["smithy-rs#2845"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "ysaito1001" diff --git a/aws/rust-runtime/aws-http/external-types.toml b/aws/rust-runtime/aws-http/external-types.toml index 33a2ed14aa..d2e362f515 100644 --- a/aws/rust-runtime/aws-http/external-types.toml +++ b/aws/rust-runtime/aws-http/external-types.toml @@ -5,7 +5,4 @@ allowed_external_types = [ "aws_types::*", "bytes::bytes::Bytes", "http_body::Body", - - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Decide if the following should be exposed - "http::header::value::InvalidHeaderValue", ] diff --git a/aws/rust-runtime/aws-http/src/user_agent.rs b/aws/rust-runtime/aws-http/src/user_agent.rs index 59edb2a5c3..27abba7bba 100644 --- a/aws/rust-runtime/aws-http/src/user_agent.rs +++ b/aws/rust-runtime/aws-http/src/user_agent.rs @@ -550,6 +550,16 @@ pub struct UserAgentStageError { kind: UserAgentStageErrorKind, } +impl UserAgentStageError { + // `pub(crate)` method instead of implementing `From` so that we + // don't have to expose `InvalidHeaderValue` in public API. + pub(crate) fn from_invalid_header(value: InvalidHeaderValue) -> Self { + Self { + kind: UserAgentStageErrorKind::InvalidHeader(value), + } + } +} + impl Error for UserAgentStageError { fn source(&self) -> Option<&(dyn Error + 'static)> { use UserAgentStageErrorKind::*; @@ -578,14 +588,6 @@ impl From for UserAgentStageError { } } -impl From for UserAgentStageError { - fn from(value: InvalidHeaderValue) -> Self { - Self { - kind: UserAgentStageErrorKind::InvalidHeader(value), - } - } -} - #[allow(clippy::declare_interior_mutable_const)] // we will never mutate this const X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent"); @@ -601,11 +603,16 @@ impl MapRequest for UserAgentStage { let ua = conf .get::() .ok_or(UserAgentStageErrorKind::UserAgentMissing)?; - req.headers_mut() - .append(USER_AGENT, HeaderValue::try_from(ua.ua_header())?); - req.headers_mut() - .append(X_AMZ_USER_AGENT, HeaderValue::try_from(ua.aws_ua_header())?); - + req.headers_mut().append( + USER_AGENT, + HeaderValue::try_from(ua.ua_header()) + .map_err(UserAgentStageError::from_invalid_header)?, + ); + req.headers_mut().append( + X_AMZ_USER_AGENT, + HeaderValue::try_from(ua.aws_ua_header()) + .map_err(UserAgentStageError::from_invalid_header)?, + ); Ok(req) }) } diff --git a/rust-runtime/aws-smithy-async/external-types.toml b/rust-runtime/aws-smithy-async/external-types.toml index dee6e8bb30..424f7dc1db 100644 --- a/rust-runtime/aws-smithy-async/external-types.toml +++ b/rust-runtime/aws-smithy-async/external-types.toml @@ -5,7 +5,4 @@ allowed_external_types = [ # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Switch to AsyncIterator once standardized "futures_core::stream::Stream", - - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Don't expose `SendError` - "tokio::sync::mpsc::error::SendError", ] diff --git a/rust-runtime/aws-smithy-async/src/future/rendezvous.rs b/rust-runtime/aws-smithy-async/src/future/rendezvous.rs index b501efc85b..16456f123e 100644 --- a/rust-runtime/aws-smithy-async/src/future/rendezvous.rs +++ b/rust-runtime/aws-smithy-async/src/future/rendezvous.rs @@ -14,7 +14,6 @@ use std::sync::Arc; use std::task::{Context, Poll}; -use tokio::sync::mpsc::error::SendError; use tokio::sync::Semaphore; /// Create a new rendezvous channel @@ -38,6 +37,36 @@ pub fn channel() -> (Sender, Receiver) { ) } +/// Errors for rendezvous channel +pub mod error { + use std::fmt; + use tokio::sync::mpsc::error::SendError as TokioSendError; + + /// Error when [crate::future::rendezvous::Sender] fails to send a value to the associated `Receiver` + #[derive(Debug)] + pub struct SendError { + source: TokioSendError, + } + + impl SendError { + pub(crate) fn tokio_send_error(source: TokioSendError) -> Self { + Self { source } + } + } + + impl fmt::Display for SendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "failed to send value to the receiver") + } + } + + impl std::error::Error for SendError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.source) + } + } +} + #[derive(Debug)] /// Sender-half of a channel pub struct Sender { @@ -50,7 +79,7 @@ impl Sender { /// /// Unlike something like `tokio::sync::mpsc::Channel` where sending a value will be buffered until /// demand exists, a rendezvous sender will wait until matching demand exists before this function will return. - pub async fn send(&self, item: T) -> Result<(), SendError> { + pub async fn send(&self, item: T) -> Result<(), error::SendError> { let result = self.chan.send(item).await; // If this is an error, the rx half has been dropped. We will never get demand. if result.is_ok() { @@ -61,7 +90,7 @@ impl Sender { .expect("semaphore is never closed") .forget(); } - result + result.map_err(error::SendError::tokio_send_error) } } diff --git a/rust-runtime/aws-smithy-xml/external-types.toml b/rust-runtime/aws-smithy-xml/external-types.toml index b4b49878e5..ff30ccf5ad 100644 --- a/rust-runtime/aws-smithy-xml/external-types.toml +++ b/rust-runtime/aws-smithy-xml/external-types.toml @@ -1,5 +1,2 @@ allowed_external_types = [ - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Don't expose `xmlparser` at all - "xmlparser::Token", - "xmlparser::error::Error", ] diff --git a/rust-runtime/aws-smithy-xml/src/decode.rs b/rust-runtime/aws-smithy-xml/src/decode.rs index 522c5b12e3..a81c9dbe5f 100644 --- a/rust-runtime/aws-smithy-xml/src/decode.rs +++ b/rust-runtime/aws-smithy-xml/src/decode.rs @@ -28,14 +28,6 @@ pub struct XmlDecodeError { kind: XmlDecodeErrorKind, } -impl From for XmlDecodeError { - fn from(error: xmlparser::Error) -> Self { - Self { - kind: XmlDecodeErrorKind::InvalidXml(error), - } - } -} - impl Display for XmlDecodeError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match &self.kind { @@ -58,6 +50,12 @@ impl Error for XmlDecodeError { } impl XmlDecodeError { + pub(crate) fn invalid_xml(error: xmlparser::Error) -> Self { + Self { + kind: XmlDecodeErrorKind::InvalidXml(error), + } + } + pub(crate) fn invalid_escape(esc: impl Into) -> Self { Self { kind: XmlDecodeErrorKind::InvalidEscape { esc: esc.into() }, @@ -256,6 +254,11 @@ impl<'inp> Document<'inp> { } } +/// A new-type wrapper around `Token` to prevent the wrapped third party type from showing up in +/// public API +#[derive(Debug)] +pub struct XmlToken<'inp>(Token<'inp>); + /// Depth tracking iterator /// /// ```xml @@ -267,11 +270,11 @@ impl<'inp> Document<'inp> { /// <- endel depth 0 /// ``` impl<'inp> Iterator for Document<'inp> { - type Item = Result<(Token<'inp>, Depth), XmlDecodeError>; - fn next<'a>(&'a mut self) -> Option, Depth), XmlDecodeError>> { + type Item = Result<(XmlToken<'inp>, Depth), XmlDecodeError>; + fn next<'a>(&'a mut self) -> Option, Depth), XmlDecodeError>> { let tok = self.tokenizer.next()?; let tok = match tok { - Err(e) => return Some(Err(e.into())), + Err(e) => return Some(Err(XmlDecodeError::invalid_xml(e))), Ok(tok) => tok, }; // depth bookkeeping @@ -290,11 +293,11 @@ impl<'inp> Iterator for Document<'inp> { self.depth += 1; // We want the startel and endel to have the same depth, but after the opener, // the parser will be at depth 1. Return the previous depth: - return Some(Ok((t, self.depth - 1))); + return Some(Ok((XmlToken(t), self.depth - 1))); } _ => {} } - Some(Ok((tok, self.depth))) + Some(Ok((XmlToken(tok), self.depth))) } } @@ -351,7 +354,7 @@ impl<'inp> ScopedDecoder<'inp, '_> { } impl<'inp, 'a> Iterator for ScopedDecoder<'inp, 'a> { - type Item = Result<(Token<'inp>, Depth), XmlDecodeError>; + type Item = Result<(XmlToken<'inp>, Depth), XmlDecodeError>; fn next(&mut self) -> Option { if self.start_el.closed { @@ -365,7 +368,7 @@ impl<'inp, 'a> Iterator for ScopedDecoder<'inp, 'a> { other => return other, }; - match tok { + match tok.0 { Token::ElementEnd { end, .. } if self.start_el.end_el(end, depth) => { self.terminated = true; return None; @@ -378,23 +381,23 @@ impl<'inp, 'a> Iterator for ScopedDecoder<'inp, 'a> { /// Load the next start element out of a depth-tagged token iterator fn next_start_element<'a, 'inp>( - tokens: &'a mut impl Iterator, Depth), XmlDecodeError>>, + tokens: &'a mut impl Iterator, Depth), XmlDecodeError>>, ) -> Option> { let mut out = StartEl::new("", "", 0); loop { match tokens.next()? { - Ok((Token::ElementStart { local, prefix, .. }, depth)) => { + Ok((XmlToken(Token::ElementStart { local, prefix, .. }), depth)) => { out.name.local = local.as_str(); out.name.prefix = prefix.as_str(); out.depth = depth; } Ok(( - Token::Attribute { + XmlToken(Token::Attribute { prefix, local, value, .. - }, + }), _, )) => out.attributes.push(Attr { name: Name { @@ -404,17 +407,17 @@ fn next_start_element<'a, 'inp>( value: unescape(value.as_str()).ok()?, }), Ok(( - Token::ElementEnd { + XmlToken(Token::ElementEnd { end: ElementEnd::Open, .. - }, + }), _, )) => break, Ok(( - Token::ElementEnd { + XmlToken(Token::ElementEnd { end: ElementEnd::Empty, .. - }, + }), _, )) => { out.closed = true; @@ -431,13 +434,13 @@ fn next_start_element<'a, 'inp>( /// If the current position is not a data element (and is instead a ``) an error /// will be returned pub fn try_data<'a, 'inp>( - tokens: &'a mut impl Iterator, Depth), XmlDecodeError>>, + tokens: &'a mut impl Iterator, Depth), XmlDecodeError>>, ) -> Result, XmlDecodeError> { loop { match tokens.next().map(|opt| opt.map(|opt| opt.0)) { None => return Ok(Cow::Borrowed("")), - Some(Ok(Token::Text { text })) => return unescape(text.as_str()), - Some(Ok(e @ Token::ElementStart { .. })) => { + Some(Ok(XmlToken(Token::Text { text }))) => return unescape(text.as_str()), + Some(Ok(e @ XmlToken(Token::ElementStart { .. }))) => { return Err(XmlDecodeError::custom(format!( "looking for a data element, found: {:?}", e From 7d1d35c9f692770b7a5987dafacfa76b50d35773 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 14 Jul 2023 13:57:54 -0700 Subject: [PATCH 211/253] Fix Timestream in the orchestrator (#2846) This PR fixes Timestream in the orchestrator implementation. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- CHANGELOG.next.toml | 12 +-- .../aws-inlineable/src/endpoint_discovery.rs | 3 + .../timestream/TimestreamDecorator.kt | 83 +++++++++++-------- .../s3/tests/config_to_builder.rs | 18 ++++ .../timestreamquery/Cargo.toml | 5 +- .../timestreamquery/tests/endpoint_disco.rs | 28 ++++--- .../ConfigOverrideRuntimePluginGenerator.kt | 6 +- .../config/ServiceConfigGenerator.kt | 31 +++++-- .../aws-smithy-client/src/dvr/replay.rs | 2 +- .../src/client/config_override.rs | 21 +++-- .../src/client/orchestrator/endpoints.rs | 1 + .../check-aws-sdk-orchestrator-impl | 8 +- 12 files changed, 138 insertions(+), 80 deletions(-) create mode 100644 aws/sdk/integration-tests/s3/tests/config_to_builder.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 7b44b6203d..8043bf07b5 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -127,7 +127,7 @@ author = "rcoh" message = "Bump dependency on `lambda_http` by `aws-smithy-http-server` to 0.8.0. This version of `aws-smithy-http-server` is only guaranteed to be compatible with 0.8.0, or semver-compatible versions of 0.8.0 of the `lambda_http` crate. It will not work with versions prior to 0.8.0 _at runtime_, making requests to your smithy-rs service unroutable, so please make sure you're running your service in a compatible configuration" author = "david-perez" references = ["smithy-rs#2676", "smithy-rs#2685"] -meta = { "breaking" = true, "tada" = false, "bug" = false } +meta = { "breaking" = true, "tada" = false, "bug" = false, target = "server" } [[smithy-rs]] message = """Remove `PollError` from an operations `Service::Error`. @@ -141,9 +141,9 @@ meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } author = "hlbarber" [[aws-sdk-rust]] -message = "The SDK has added support for timestreamwrite and timestreamquery. Support for these services is considered experimental at this time. In order to use these services, you MUST call `.enable_endpoint_discovery()` on the `Client` after construction." +message = "The SDK has added support for timestreamwrite and timestreamquery. Support for these services is considered experimental at this time. In order to use these services, you MUST call `.with_endpoint_discovery_enabled()` on the `Client` after construction." meta = { "breaking" = false, "tada" = true, "bug" = false } -references = ["smithy-rs#2707", "aws-sdk-rust#114"] +references = ["smithy-rs#2707", "aws-sdk-rust#114", "smithy-rs#2846"] author = "rcoh" [[smithy-rs]] @@ -197,7 +197,7 @@ filter_by_operation_id(plugin, |id| id.absolute() != "namespace#name"); """ author = "82marbag" references = ["smithy-rs#2678"] -meta = { "breaking" = true, "tada" = false, "bug" = false } +meta = { "breaking" = true, "tada" = false, "bug" = false, target = "server" } [[smithy-rs]] message = "The occurrences of `Arc` have now been replaced with `SharedEndpointResolver` in public APIs." @@ -532,7 +532,7 @@ let scoped_plugin = Scoped::new::(plugin); """ references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779", "smithy-rs#2827"] -meta = { "breaking" = true, "tada" = true, "bug" = false } +meta = { "breaking" = true, "tada" = true, "bug" = false, target = "server" } author = "hlbarber" [[smithy-rs]] @@ -608,7 +608,7 @@ let plugin = plugin_from_operation_fn(map); ``` """ references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779"] -meta = { "breaking" = true, "tada" = false, "bug" = false } +meta = { "breaking" = true, "tada" = false, "bug" = false, target = "server" } author = "hlbarber" [[smithy-rs]] diff --git a/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs index d4ffc7d116..de870fecc9 100644 --- a/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs +++ b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs @@ -39,6 +39,7 @@ impl ReloadEndpoint { pub async fn reload_once(&self) { match (self.loader)().await { Ok((endpoint, expiry)) => { + tracing::debug!("caching resolved endpoint: {:?}", (&endpoint, &expiry)); *self.endpoint.lock().unwrap() = Some(ExpiringEndpoint { endpoint, expiry }) } Err(err) => *self.error.lock().unwrap() = Some(err), @@ -128,6 +129,7 @@ where sleep, time, }; + tracing::debug!("populating initial endpoint discovery cache"); reloader.reload_once().await; // if we didn't successfully get an endpoint, bail out so the client knows // configuration failed to work @@ -137,6 +139,7 @@ where impl EndpointCache { fn resolve_endpoint(&self) -> aws_smithy_http::endpoint::Result { + tracing::trace!("resolving endpoint from endpoint discovery cache"); self.endpoint .lock() .unwrap() diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt index 55a9e7f7bd..44c7767bd3 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt @@ -6,6 +6,7 @@ package software.amazon.smithy.rustsdk.customize.timestream 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.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Types import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency @@ -14,10 +15,10 @@ import software.amazon.smithy.rust.codegen.core.rustlang.Visibility import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.toType 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.smithy.RustCrate import software.amazon.smithy.rust.codegen.core.smithy.customize.AdHocCustomization import software.amazon.smithy.rust.codegen.core.smithy.customize.adhocCustomization +import software.amazon.smithy.rust.codegen.core.util.letIf import software.amazon.smithy.rustsdk.AwsCargoDependency import software.amazon.smithy.rustsdk.DocSection import software.amazon.smithy.rustsdk.InlineAwsDependency @@ -25,34 +26,43 @@ import software.amazon.smithy.rustsdk.InlineAwsDependency /** * This decorator does two things: * 1. Adds the `endpoint_discovery` inlineable - * 2. Adds a `enable_endpoint_discovery` method on client that returns a wrapped client with endpoint discovery enabled + * 2. Adds a `with_endpoint_discovery_enabled` method on client that returns a wrapped client with endpoint discovery enabled */ class TimestreamDecorator : ClientCodegenDecorator { override val name: String = "Timestream" override val order: Byte = -1 - override fun extraSections(codegenContext: ClientCodegenContext): List { - return listOf( - adhocCustomization { - addDependency(AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).toDevDependency()) - rustTemplate( - """ - let config = aws_config::load_from_env().await; - // You MUST call `enable_endpoint_discovery` to produce a working client for this service. - let ${it.clientName} = ${it.crateName}::Client::new(&config).enable_endpoint_discovery().await; - """.replaceIndent(it.indent), - ) - }, - ) - } + private fun applies(codegenContext: ClientCodegenContext): Boolean = + codegenContext.smithyRuntimeMode.defaultToOrchestrator + + override fun extraSections(codegenContext: ClientCodegenContext): List = + emptyList().letIf(applies(codegenContext)) { + listOf( + adhocCustomization { + addDependency(AwsCargoDependency.awsConfig(codegenContext.runtimeConfig).toDevDependency()) + rustTemplate( + """ + let config = aws_config::load_from_env().await; + // You MUST call `with_endpoint_discovery_enabled` to produce a working client for this service. + let ${it.clientName} = ${it.crateName}::Client::new(&config).with_endpoint_discovery_enabled().await; + """.replaceIndent(it.indent), + ) + }, + ) + } override fun extras(codegenContext: ClientCodegenContext, rustCrate: RustCrate) { + if (!applies(codegenContext)) { + return + } + val endpointDiscovery = InlineAwsDependency.forRustFile( "endpoint_discovery", Visibility.PUBLIC, CargoDependency.Tokio.copy(scope = DependencyScope.Compile, features = setOf("sync")), + CargoDependency.smithyAsync(codegenContext.runtimeConfig).toDevDependency().withFeature("test-util"), ) - rustCrate.lib { + rustCrate.withModule(ClientRustModule.client) { // helper function to resolve an endpoint given a base client rustTemplate( """ @@ -76,33 +86,40 @@ class TimestreamDecorator : ClientCodegenDecorator { /// Enable endpoint discovery for this client /// /// This method MUST be called to construct a working client. - pub async fn enable_endpoint_discovery(self) -> #{Result}<(Self, #{endpoint_discovery}::ReloadEndpoint), #{ResolveEndpointError}> { - let mut new_conf = self.conf().clone(); - let sleep = self.conf().sleep_impl().expect("sleep impl must be provided"); - let time = self.conf().time_source().expect("time source must be provided"); + pub async fn with_endpoint_discovery_enabled(self) -> #{Result}<(Self, #{endpoint_discovery}::ReloadEndpoint), #{ResolveEndpointError}> { + let handle = self.handle.clone(); + + // The original client without endpoint discover gets moved into the endpoint discovery + // resolver since calls to DescribeEndpoint without discovery need to be made. + let client_without_discovery = self; let (resolver, reloader) = #{endpoint_discovery}::create_cache( move || { - let client = self.clone(); + let client = client_without_discovery.clone(); async move { resolve_endpoint(&client).await } }, - sleep, - time - ) - .await?; - new_conf.endpoint_resolver = #{SharedEndpointResolver}::new(resolver); - Ok((Self::from_conf(new_conf), reloader)) + handle.conf.sleep_impl() + .expect("endpoint discovery requires the client config to have a sleep impl"), + handle.conf.time_source() + .expect("endpoint discovery requires the client config to have a time source"), + ).await?; + + let client_with_discovery = crate::Client::from_conf( + handle.conf.to_builder() + .endpoint_resolver(#{SharedEndpointResolver}::new(resolver)) + .build() + ); + Ok((client_with_discovery, reloader)) } } """, - "endpoint_discovery" to endpointDiscovery.toType(), - "SystemTime" to RuntimeType.std.resolve("time::SystemTime"), + *RuntimeType.preludeScope, + "Arc" to RuntimeType.Arc, "Duration" to RuntimeType.std.resolve("time::Duration"), "SharedEndpointResolver" to RuntimeType.smithyHttp(codegenContext.runtimeConfig) .resolve("endpoint::SharedEndpointResolver"), - "SystemTimeSource" to RuntimeType.smithyAsync(codegenContext.runtimeConfig) - .resolve("time::SystemTimeSource"), + "SystemTime" to RuntimeType.std.resolve("time::SystemTime"), + "endpoint_discovery" to endpointDiscovery.toType(), *Types(codegenContext.runtimeConfig).toArray(), - *preludeScope, ) } } diff --git a/aws/sdk/integration-tests/s3/tests/config_to_builder.rs b/aws/sdk/integration-tests/s3/tests/config_to_builder.rs new file mode 100644 index 0000000000..122b0418b6 --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/config_to_builder.rs @@ -0,0 +1,18 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(aws_sdk_orchestrator_mode)] +#[tokio::test] +async fn test_config_to_builder() { + use aws_sdk_s3::config::AppName; + + let config = aws_config::load_from_env().await; + let config = aws_sdk_s3::Config::new(&config); + // should not panic + let _ = config + .to_builder() + .app_name(AppName::new("SomeAppName").unwrap()) + .build(); +} diff --git a/aws/sdk/integration-tests/timestreamquery/Cargo.toml b/aws/sdk/integration-tests/timestreamquery/Cargo.toml index b81b618c88..99955e4764 100644 --- a/aws/sdk/integration-tests/timestreamquery/Cargo.toml +++ b/aws/sdk/integration-tests/timestreamquery/Cargo.toml @@ -13,8 +13,9 @@ 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" } -tokio = { version = "1.23.1", features = ["full", "test-util"] } -aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util"] } aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] } +aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", 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" } +tokio = { version = "1.23.1", features = ["full", "test-util"] } tracing-subscriber = "0.3.17" diff --git a/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs index d15244825f..447bdead21 100644 --- a/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs +++ b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs @@ -3,20 +3,22 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_credential_types::provider::SharedCredentialsProvider; -use aws_sdk_timestreamquery as query; -use aws_sdk_timestreamquery::config::Credentials; -use aws_smithy_async::rt::sleep::SharedAsyncSleep; -use aws_smithy_async::test_util::controlled_time_and_sleep; -use aws_smithy_async::time::{SharedTimeSource, TimeSource}; -use aws_smithy_client::dvr::{MediaType, ReplayingConnection}; -use aws_types::region::Region; -use aws_types::SdkConfig; -use std::time::{Duration, UNIX_EPOCH}; - +#[cfg(aws_sdk_orchestrator_mode)] #[tokio::test] async fn do_endpoint_discovery() { - tracing_subscriber::fmt::init(); + use aws_credential_types::provider::SharedCredentialsProvider; + use aws_sdk_timestreamquery as query; + use aws_sdk_timestreamquery::config::Credentials; + use aws_smithy_async::rt::sleep::SharedAsyncSleep; + use aws_smithy_async::test_util::controlled_time_and_sleep; + use aws_smithy_async::time::{SharedTimeSource, TimeSource}; + use aws_smithy_client::dvr::{MediaType, ReplayingConnection}; + use aws_types::region::Region; + use aws_types::SdkConfig; + use std::time::{Duration, UNIX_EPOCH}; + + let _logs = aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs(); + let conn = ReplayingConnection::from_file("tests/traffic.json").unwrap(); //let conn = aws_smithy_client::dvr::RecordingConnection::new(conn); let start = UNIX_EPOCH + Duration::from_secs(1234567890); @@ -32,7 +34,7 @@ async fn do_endpoint_discovery() { .idempotency_token_provider("0000-0000-0000") .build(); let (client, reloader) = query::Client::from_conf(conf) - .enable_endpoint_discovery() + .with_endpoint_discovery_enabled() .await .expect("initial setup of endpoint discovery failed"); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt index 5a37375e56..b199eaf5a5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt @@ -54,8 +54,7 @@ class ConfigOverrideRuntimePluginGenerator( initial_config: #{FrozenLayer}, initial_components: &#{RuntimeComponentsBuilder} ) -> Self { - let mut layer = #{Layer}::from(config_override.config) - .with_name("$moduleUseName::config::ConfigOverrideRuntimePlugin"); + let mut layer = config_override.config; let mut components = config_override.runtime_components; let mut resolver = #{Resolver}::overrid(initial_config, initial_components, &mut layer, &mut components); @@ -63,7 +62,8 @@ class ConfigOverrideRuntimePluginGenerator( let _ = resolver; Self { - config: layer.freeze(), + config: #{Layer}::from(layer) + .with_name("$moduleUseName::config::ConfigOverrideRuntimePlugin").freeze(), components, } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 8d93ae88fc..83f7c740b4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -336,7 +336,11 @@ class ServiceConfigGenerator( if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ + // Both `config` and `cloneable` are the same config, but the cloneable one + // is kept around so that it is possible to convert back into a builder. This can be + // optimized in the future. pub(crate) config: #{FrozenLayer}, + cloneable: #{CloneableLayer}, pub(crate) runtime_components: #{RuntimeComponentsBuilder}, pub(crate) runtime_plugins: #{Vec}<#{SharedRuntimePlugin}>, """, @@ -369,6 +373,20 @@ class ServiceConfigGenerator( pub fn builder() -> Builder { Builder::default() } """, ) + if (runtimeMode.defaultToOrchestrator) { + writer.rustTemplate( + """ + /// Converts this config back into a builder so that it can be tweaked. + pub fn to_builder(&self) -> Builder { + Builder { + config: self.cloneable.clone(), + runtime_components: self.runtime_components.clone(), + runtime_plugins: self.runtime_plugins.clone(), + } + } + """, + ) + } customizations.forEach { it.section(ServiceConfig.ConfigImpl)(this) } @@ -478,12 +496,7 @@ class ServiceConfigGenerator( rustBlock("pub fn build(mut self) -> Config") { rustTemplate( """ - // The builder is being turned into a service config. While doing so, we'd like to avoid - // requiring that items created and stored _during_ the build method be `Clone`, since they - // will soon be part of a `FrozenLayer` owned by the service config. So we will convert the - // current `CloneableLayer` into a `Layer` that does not impose the `Clone` requirement. - let mut layer = #{Layer}::from(self.config).with_name("$moduleUseName::config::Config"); - ##[allow(unused)] + let mut layer = self.config; let mut resolver = #{Resolver}::initial(&mut layer, &mut self.runtime_components); """, *codegenScope, @@ -495,12 +508,14 @@ class ServiceConfigGenerator( customizations.forEach { it.section(ServiceConfig.BuilderBuildExtras)(this) } - rust( + rustTemplate( """ - config: layer.freeze(), + config: #{Layer}::from(layer.clone()).with_name("$moduleUseName::config::Config").freeze(), + cloneable: layer, runtime_components: self.runtime_components, runtime_plugins: self.runtime_plugins, """, + *codegenScope, ) } } diff --git a/rust-runtime/aws-smithy-client/src/dvr/replay.rs b/rust-runtime/aws-smithy-client/src/dvr/replay.rs index 54065e5fc7..94d9123e0e 100644 --- a/rust-runtime/aws-smithy-client/src/dvr/replay.rs +++ b/rust-runtime/aws-smithy-client/src/dvr/replay.rs @@ -113,7 +113,7 @@ impl ReplayingConnection { ))? .take() .await; - aws_smithy_protocol_test::assert_uris_match(actual.uri(), expected.uri()); + aws_smithy_protocol_test::assert_uris_match(expected.uri(), actual.uri()); body_comparer(expected.body().as_ref(), actual.body().as_ref())?; let expected_headers = expected .headers() diff --git a/rust-runtime/aws-smithy-runtime/src/client/config_override.rs b/rust-runtime/aws-smithy-runtime/src/client/config_override.rs index c89352e139..c69770a974 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/config_override.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/config_override.rs @@ -5,7 +5,9 @@ use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; -use aws_smithy_types::config_bag::{FrozenLayer, Layer, Storable, Store, StoreReplace}; +use aws_smithy_types::config_bag::{ + CloneableLayer, FrozenLayer, Layer, Storable, Store, StoreReplace, +}; macro_rules! component { ($typ:ty, $accessor:ident, $latest_accessor:ident) => { @@ -39,14 +41,14 @@ macro_rules! latest_component { } struct Initial<'a> { - config: &'a mut Layer, + config: &'a mut CloneableLayer, components: &'a mut RuntimeComponentsBuilder, } struct Override<'a> { initial_config: FrozenLayer, initial_components: &'a RuntimeComponentsBuilder, - config: &'a mut Layer, + config: &'a mut CloneableLayer, components: &'a mut RuntimeComponentsBuilder, } @@ -74,7 +76,10 @@ pub struct Resolver<'a> { impl<'a> Resolver<'a> { /// Construct a new [`Resolver`] in _initial mode_. - pub fn initial(config: &'a mut Layer, components: &'a mut RuntimeComponentsBuilder) -> Self { + pub fn initial( + config: &'a mut CloneableLayer, + components: &'a mut RuntimeComponentsBuilder, + ) -> Self { Self { inner: Inner::Initial(Initial { config, components }), } @@ -84,7 +89,7 @@ impl<'a> Resolver<'a> { pub fn overrid( initial_config: FrozenLayer, initial_components: &'a RuntimeComponentsBuilder, - config: &'a mut Layer, + config: &'a mut CloneableLayer, components: &'a mut RuntimeComponentsBuilder, ) -> Self { Self { @@ -103,7 +108,7 @@ impl<'a> Resolver<'a> { } /// Returns a mutable reference to the latest config. - pub fn config_mut(&mut self) -> &mut Layer { + pub fn config_mut(&mut self) -> &mut CloneableLayer { match &mut self.inner { Inner::Initial(initial) => initial.config, Inner::Override(overrid) => overrid.config, @@ -180,7 +185,7 @@ mod tests { #[test] fn initial_mode_config() { - let mut config = Layer::new("test"); + let mut config = CloneableLayer::new("test"); let mut components = RuntimeComponentsBuilder::new("test"); let mut resolver = Resolver::initial(&mut config, &mut components); @@ -199,7 +204,7 @@ mod tests { fn override_mode_config() { let mut initial_config = CloneableLayer::new("initial"); let initial_components = RuntimeComponentsBuilder::new("initial"); - let mut config = Layer::new("override"); + let mut config = CloneableLayer::new("override"); let mut components = RuntimeComponentsBuilder::new("override"); let resolver = Resolver::overrid( diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 1b22b3c5d9..be6fc51bf0 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -117,6 +117,7 @@ pub(super) async fn orchestrate_endpoint( .endpoint_resolver() .resolve_endpoint(params) .await?; + tracing::debug!("will use endpoint {:?}", endpoint); apply_endpoint(request, &endpoint, endpoint_prefix)?; // Make the endpoint config available to interceptors diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl index 0104e7f03d..b86ebf3f0f 100755 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ b/tools/ci-scripts/check-aws-sdk-orchestrator-impl @@ -12,12 +12,6 @@ C_RESET='\033[0m' set -eu cd smithy-rs -# TODO(enableNewSmithyRuntimeLaunch): Move these into `services_that_compile` as more progress is made -services_that_dont_compile=(\ - "timestreamquery", - "timestreamwrite", -) - services_that_pass_tests=(\ "aws-config"\ "config"\ @@ -35,6 +29,8 @@ services_that_pass_tests=(\ "s3control"\ "sso"\ "sts"\ + "timestreamquery"\ + "timestreamwrite"\ "transcribestreaming"\ ) From 1ac59421a9f7fa84fec9fa8ff5a2fc83b9b6fb75 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Mon, 17 Jul 2023 09:58:48 -0500 Subject: [PATCH 212/253] Remove third party types from public APIs Part 2 (#2848) ## Motivation and Context Addresses item 8 in #2413 ## Description This PR removes the third party types as follows: - removes `SegmentedBuf` from public API in `aws-smithy-http` (used when the feature `event-stream` was enabled) ## Testing - [x] Passed tests in CI ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ --------- Co-authored-by: ysaito1001 --- CHANGELOG.next.toml | 6 ++++++ rust-runtime/aws-smithy-http/external-types.toml | 3 --- rust-runtime/aws-smithy-http/src/event_stream/receiver.rs | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 8043bf07b5..eb59a24af5 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -640,3 +640,9 @@ message = "The implementation `From` fo references = ["smithy-rs#2845"] meta = { "breaking" = true, "tada" = false, "bug" = false } author = "ysaito1001" + +[[smithy-rs]] +message = "The implementation `From` for `aws_smithy_http::event_stream::RawMessage` has been removed." +references = ["smithy-rs#2848"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } +author = "ysaito1001" diff --git a/rust-runtime/aws-smithy-http/external-types.toml b/rust-runtime/aws-smithy-http/external-types.toml index b06231e92f..a228978c9a 100644 --- a/rust-runtime/aws-smithy-http/external-types.toml +++ b/rust-runtime/aws-smithy-http/external-types.toml @@ -31,7 +31,4 @@ allowed_external_types = [ # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Once tooling permits it, only allow the following types in the `event-stream` feature "aws_smithy_eventstream::*", - - # TODO(https://github.com/awslabs/smithy-rs/issues/1193): Decide whether to expose this type or not - "bytes_utils::segmented::SegmentedBuf", ] diff --git a/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs b/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs index aa7f914223..3b38161f3b 100644 --- a/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs +++ b/rust-runtime/aws-smithy-http/src/event_stream/receiver.rs @@ -97,8 +97,8 @@ pub enum RawMessage { Invalid(Option), } -impl From<&mut SegmentedBuf> for RawMessage { - fn from(buf: &mut SegmentedBuf) -> Self { +impl RawMessage { + pub(crate) fn invalid(buf: &mut SegmentedBuf) -> Self { Self::Invalid(Some(buf.copy_to_bytes(buf.remaining()))) } } @@ -213,7 +213,7 @@ impl Receiver { ReceiverError { kind: ReceiverErrorKind::UnexpectedEndOfStream, }, - self.buffer.buffered().into(), + RawMessage::invalid(self.buffer.buffered()), )); } Ok(None) From d4c5064849145737bd1aeaebcd2b56e19e1fdfd5 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 17 Jul 2023 13:46:36 -0400 Subject: [PATCH 213/253] Rollback multi-rust version changes to the dockerfile (#2852) ## Motivation and Context solved by deploying a newer version of smithy-rs instead ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- tools/ci-build/Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/ci-build/Dockerfile b/tools/ci-build/Dockerfile index ef3cfbef56..3e04f15c7d 100644 --- a/tools/ci-build/Dockerfile +++ b/tools/ci-build/Dockerfile @@ -6,7 +6,6 @@ # This is the base Docker build image used by CI ARG base_image=public.ecr.aws/amazonlinux/amazonlinux:2 -ARG prev_rust_stable_version=1.67.1 ARG rust_stable_version=1.68.2 ARG rust_nightly_version=nightly-2023-05-31 @@ -26,7 +25,6 @@ RUN curl https://musl.libc.org/releases/musl-1.2.3.tar.gz -o musl-1.2.3.tar.gz \ FROM bare_base_image AS install_rust ARG rust_stable_version ARG rust_nightly_version -ARG prev_rust_stable_version ENV RUSTUP_HOME=/opt/rustup \ CARGO_HOME=/opt/cargo \ PATH=/opt/cargo/bin/:${PATH} \ @@ -62,7 +60,6 @@ RUN set -eux; \ rustup component add rustfmt; \ rustup component add clippy; \ rustup toolchain install ${rust_nightly_version} --component clippy; \ - rustup toolchain install ${prev_rust_stable_version} --component clippy; \ rustup target add x86_64-unknown-linux-musl; \ rustup target add wasm32-unknown-unknown; \ rustup target add wasm32-wasi; \ From fabef2bbc9201af697bc93750ff919a1989eb47f Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Tue, 18 Jul 2023 15:40:49 -0400 Subject: [PATCH 214/253] Set RUSTUP_TOOLCHAIN in Dockerfile (#2853) ## Motivation and Context During releases, if the toolchain version has changed, we'll try to invoke a Rust version that doesn't exist. This pins it to the stable version. ## Description Set the `RUSTUP_TOOLCHAIN` environment variable in the release/CI dockerfile ## Testing - ran a release with this image ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- tools/ci-build/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/ci-build/Dockerfile b/tools/ci-build/Dockerfile index 3e04f15c7d..d6ce780e65 100644 --- a/tools/ci-build/Dockerfile +++ b/tools/ci-build/Dockerfile @@ -207,4 +207,7 @@ ENV SMITHY_RS_DOCKER_BUILD_IMAGE=1 RUN pip3 install --no-cache-dir mypy==0.991 WORKDIR /home/build COPY sanity-test /home/build/sanity-test +# RUSTUP_TOOLCHAIN takes precedence over everything except `+` args. This will allow us to ignore the toolchain +# file during CI, avoiding issues during Rust version upgrades. +ENV RUSTUP_TOOLCHAIN=${rust_stable_version} RUN /home/build/sanity-test From 27df48fcc0fcf7a0c33700d9f99eff68565d9a48 Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Wed, 19 Jul 2023 10:02:49 -0500 Subject: [PATCH 215/253] Remove doc hidden from struct fields (#2854) ## Motivation and Context Removes `#[doc(hidden)]` from struct fields. ## Testing - [ ] Passed tests in CI ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ --------- Co-authored-by: ysaito1001 --- CHANGELOG.next.toml | 6 ++++++ .../codegen/core/smithy/SymbolMetadataProvider.kt | 15 +-------------- .../smithy/generators/StructureGeneratorTest.kt | 9 ++++----- 3 files changed, 11 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index eb59a24af5..7a753f4790 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -646,3 +646,9 @@ message = "The implementation `From` for ` references = ["smithy-rs#2848"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } author = "ysaito1001" + +[[smithy-rs]] +message = "Public fields in structs are no longer marked as `#[doc(hidden)]`, and they are now visible." +references = ["smithy-rs#2854"] +meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } +author = "ysaito1001" diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt index d78818601c..964f2046dd 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/SymbolMetadataProvider.kt @@ -20,7 +20,6 @@ import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.model.shapes.UnionShape import software.amazon.smithy.model.traits.EnumTrait import software.amazon.smithy.model.traits.SensitiveTrait -import software.amazon.smithy.model.traits.StreamingTrait import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.RustMetadata import software.amazon.smithy.rust.codegen.core.rustlang.Visibility @@ -99,19 +98,7 @@ class BaseSymbolMetadataProvider( override fun memberMeta(memberShape: MemberShape): RustMetadata = when (val container = model.expectShape(memberShape.container)) { - is StructureShape -> { - // TODO(https://github.com/awslabs/smithy-rs/issues/943): Once streaming accessors are usable, - // then also make streaming members `#[doc(hidden)]` - if (memberShape.getMemberTrait(model, StreamingTrait::class.java).isPresent) { - RustMetadata(visibility = Visibility.PUBLIC) - } else { - RustMetadata( - // At some point, visibility _may_ be made `PRIVATE`, so make these `#[doc(hidden)]` for now. - visibility = Visibility.PUBLIC, - additionalAttributes = listOf(Attribute.DocHidden), - ) - } - } + is StructureShape -> RustMetadata(visibility = Visibility.PUBLIC) is UnionShape, is CollectionShape, is MapShape -> RustMetadata(visibility = Visibility.PUBLIC) diff --git a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt index f765587300..77932ddfac 100644 --- a/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt +++ b/codegen-core/src/test/kotlin/software/amazon/smithy/rust/codegen/core/smithy/generators/StructureGeneratorTest.kt @@ -7,7 +7,6 @@ package software.amazon.smithy.rust.codegen.core.smithy.generators import io.kotest.matchers.string.shouldContainInOrder import io.kotest.matchers.string.shouldNotContain -import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import software.amazon.smithy.model.shapes.StructureShape import software.amazon.smithy.rust.codegen.core.rustlang.Attribute @@ -401,7 +400,7 @@ class StructureGeneratorTest { } @Test - fun `non-streaming fields are doc-hidden`() { + fun `fields are NOT doc-hidden`() { val model = """ namespace com.test structure MyStruct { @@ -415,9 +414,9 @@ class StructureGeneratorTest { val struct = model.lookup("com.test#MyStruct") val provider = testSymbolProvider(model, rustReservedWordConfig = rustReservedWordConfig) - RustWriter.forModule("test").let { - StructureGenerator(model, provider, it, struct, emptyList()).render() - assertEquals(6, it.toString().split("#[doc(hidden)]").size, "there should be 5 doc-hiddens") + RustWriter.forModule("test").let { writer -> + StructureGenerator(model, provider, writer, struct, emptyList()).render() + writer.toString().shouldNotContain("#[doc(hidden)]") } } From 5beee617f3f9363423d66b71c76c2be16ac7e574 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Wed, 19 Jul 2023 12:17:24 -0700 Subject: [PATCH 216/253] Improve local dev experience with `aws:sdk:assemble` build target (#2859) In local dev, if you have a terminal/shell open to `aws/sdk/build/aws-sdk/...` and run the `aws:sdk:assemble` target, that shell's current working directory will break and you'll have to change directory to somewhere safe and change back in order to do anything again (at least on MacOS). This PR makes the `deleteSdk` target that `aws:sdk:assemble` depends on only delete files instead of directories so that the current working directory doesn't get invalidated. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/sdk/build.gradle.kts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index d5225d66a9..23f7aed78c 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -453,7 +453,12 @@ tasks["test"].dependsOn("assemble") tasks["test"].finalizedBy(Cargo.CLIPPY.toString, Cargo.TEST.toString, Cargo.DOCS.toString) tasks.register("deleteSdk") { - delete = setOf(outputDir) + delete( + fileTree(outputDir) { + // Delete files but keep directories so that terminals don't get messed up in local development + include("**/*.*") + }, + ) } tasks["clean"].dependsOn("deleteSdk") tasks["clean"].doFirst { From f310668417b0904d59dadbec0360440d90c1ede5 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 20 Jul 2023 06:15:34 -0700 Subject: [PATCH 217/253] Stop rebuilding `sdk-lints` all the time (#2861) I noticed `sdk-lints` was getting recompiled just about every time I ran `git commit` as part of the pre-commit hooks. It looks like a compiler flag was added to the `ExecRustBuildTool` task type that isn't actually used by any tools. Removing it should eliminate compiler flag conflicts between the gradle targets and pre-commit. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- buildSrc/src/main/kotlin/RustBuildTool.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/RustBuildTool.kt b/buildSrc/src/main/kotlin/RustBuildTool.kt index b8a1a15fb1..1e67995648 100644 --- a/buildSrc/src/main/kotlin/RustBuildTool.kt +++ b/buildSrc/src/main/kotlin/RustBuildTool.kt @@ -28,7 +28,6 @@ private fun runCli( } } .copyTo(action) - action.environment("RUSTFLAGS", "--cfg aws_sdk_unstable") action.execute() } } From 25abe5a84203ab3cef0fa9e991b8ddbaf1f67b53 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 20 Jul 2023 08:49:53 -0700 Subject: [PATCH 218/253] Change the default runtime mode to orchestrator (#2847) This PR changes the default runtime mode to orchestrator for generated clients and the AWS SDK. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .github/workflows/ci.yml | 4 +- aws/rust-runtime/aws-config/Cargo.toml | 2 +- aws/rust-runtime/aws-config/src/connector.rs | 9 +- .../src/http_credential_provider.rs | 5 +- .../aws-config/src/imds/client.rs | 5 +- .../aws-config/src/imds/credentials.rs | 2 +- aws/rust-runtime/aws-config/src/lib.rs | 2 +- aws/rust-runtime/aws-config/src/sso.rs | 1 + aws/rust-runtime/aws-config/src/sts.rs | 5 +- .../aws-config/src/sts/assume_role.rs | 5 +- .../aws-credential-types/Cargo.toml | 2 +- aws/rust-runtime/aws-runtime/Cargo.toml | 3 +- .../aws-runtime/src/invocation_id.rs | 119 ++++-- aws/rust-runtime/aws-types/src/sdk_config.rs | 4 +- aws/sdk-adhoc-test/build.gradle.kts | 2 +- .../smithy/rustsdk/AwsPresigningDecorator.kt | 8 +- .../amazon/smithy/rustsdk/CredentialCaches.kt | 10 +- .../rustsdk/EndpointBuiltInsDecorator.kt | 2 +- .../rustsdk/IntegrationTestDependencies.kt | 4 +- .../smithy/rustsdk/SigV4SigningDecorator.kt | 2 +- .../amazon/smithy/rustsdk/TestUtil.kt | 2 +- aws/sdk/build.gradle.kts | 3 +- .../dynamodb/benches/deserialization_bench.rs | 2 +- .../dynamodb/benches/serialization_bench.rs | 2 +- .../retries-with-client-rate-limiting.rs | 2 +- .../kms/tests/retryable_errors.rs | 4 +- .../no-default-features/Cargo.toml | 1 + .../tests/client-construction.rs | 54 ++- aws/sdk/integration-tests/s3/Cargo.toml | 2 +- .../s3/tests/alternative-async-runtime.rs | 10 +- .../s3/tests/config-override.rs | 10 +- .../s3/tests/config_to_builder.rs | 2 +- .../integration-tests/s3/tests/endpoints.rs | 4 +- .../integration-tests/sts/tests/signing-it.rs | 4 +- .../timestreamquery/tests/endpoint_disco.rs | 2 +- aws/sdk/sdk-external-types.toml | 4 +- codegen-client-test/build.gradle.kts | 2 +- .../client/smithy/ClientRustSettings.kt | 13 +- .../HttpConnectorConfigDecorator.kt | 27 +- .../endpoint/EndpointConfigCustomization.kt | 39 ++ .../EndpointParamsInterceptorGenerator.kt | 3 +- .../smithy/generators/OperationGenerator.kt | 2 +- .../OperationRuntimePluginGenerator.kt | 8 +- .../ServiceRuntimePluginGenerator.kt | 4 +- .../protocol/ProtocolTestGenerator.kt | 56 +-- .../customizations/ApiKeyAuthDecoratorTest.kt | 9 +- .../HttpVersionListGeneratorTest.kt | 9 +- .../ResiliencyConfigCustomizationTest.kt | 8 + .../smithy/endpoint/EndpointsDecoratorTest.kt | 103 ++++- .../generators/EndpointTraitBindingsTest.kt | 140 ++++++- .../ProtocolTestGeneratorMiddlewareTest.kt | 363 ++++++++++++++++++ .../protocol/ProtocolTestGeneratorTest.kt | 244 ++++++------ .../protocols/AwsQueryCompatibleTest.kt | 173 ++++++++- .../codegen/core/rustlang/CargoDependency.kt | 2 +- .../rust/codegen/core/smithy/RuntimeType.kt | 2 + examples/pokemon-service-common/Cargo.toml | 4 + examples/pokemon-service-common/src/lib.rs | 22 +- .../tests/plugins_execution_order.rs | 21 +- .../pokemon-service-tls/tests/common/mod.rs | 27 +- examples/pokemon-service/tests/common/mod.rs | 22 +- .../pokemon-service-test/tests/helpers.rs | 38 +- rust-runtime/aws-smithy-client/Cargo.toml | 2 +- rust-runtime/aws-smithy-runtime/Cargo.toml | 2 +- .../src/client/orchestrator.rs | 3 +- rust-runtime/inlineable/Cargo.toml | 2 +- .../ci-scripts/check-aws-sdk-middleware-impl | 27 ++ .../check-aws-sdk-orchestrator-impl | 49 --- 67 files changed, 1315 insertions(+), 415 deletions(-) create mode 100644 codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorMiddlewareTest.kt create mode 100755 tools/ci-scripts/check-aws-sdk-middleware-impl delete mode 100755 tools/ci-scripts/check-aws-sdk-orchestrator-impl diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0cd2c8877b..9a1e8f274d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,8 +84,8 @@ jobs: test: - action: check-aws-sdk-adhoc-tests runner: ubuntu-latest - # TODO(enableNewSmithyRuntimeCleanup): Remove `check-aws-sdk-orchestrator-impl` when cleaning up middleware - - action: check-aws-sdk-orchestrator-impl + # TODO(enableNewSmithyRuntimeCleanup): Remove `check-aws-sdk-middleware-impl` when cleaning up middleware + - action: check-aws-sdk-middleware-impl runner: smithy_ubuntu-latest_8-core - action: check-client-codegen-integration-tests runner: smithy_ubuntu-latest_8-core diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index 54add78bb8..c19658d9c3 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -35,7 +35,7 @@ tokio = { version = "1.13.1", features = ["sync"] } tracing = { version = "0.1" } # implementation detail of IMDS credentials provider -fastrand = "1" +fastrand = "2.0.0" bytes = "1.1.0" http = "0.2.4" diff --git a/aws/rust-runtime/aws-config/src/connector.rs b/aws/rust-runtime/aws-config/src/connector.rs index c924a072bf..33c820261f 100644 --- a/aws/rust-runtime/aws-config/src/connector.rs +++ b/aws/rust-runtime/aws-config/src/connector.rs @@ -7,10 +7,13 @@ use aws_smithy_client::erase::DynConnector; -// unused when all crate features are disabled /// Unwrap an [`Option`](aws_smithy_client::erase::DynConnector), and panic with a helpful error message if it's `None` -pub(crate) fn expect_connector(connector: Option) -> DynConnector { - connector.expect("No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this.") +pub(crate) fn expect_connector(for_what: &str, connector: Option) -> DynConnector { + if let Some(conn) = connector { + conn + } else { + panic!("{for_what} require(s) a HTTP connector, but none was available. Enable the `rustls` crate feature or set a connector to fix this.") + } } #[cfg(feature = "client-hyper")] diff --git a/aws/rust-runtime/aws-config/src/http_credential_provider.rs b/aws/rust-runtime/aws-config/src/http_credential_provider.rs index e868ffdac4..2568cc435d 100644 --- a/aws/rust-runtime/aws-config/src/http_credential_provider.rs +++ b/aws/rust-runtime/aws-config/src/http_credential_provider.rs @@ -100,7 +100,10 @@ impl Builder { .read_timeout(DEFAULT_READ_TIMEOUT) .build() }); - let connector = expect_connector(provider_config.connector(&connector_settings)); + let connector = expect_connector( + "The HTTP credentials provider", + provider_config.connector(&connector_settings), + ); let mut client_builder = aws_smithy_client::Client::builder() .connector(connector) .middleware(Identity::new()); diff --git a/aws/rust-runtime/aws-config/src/imds/client.rs b/aws/rust-runtime/aws-config/src/imds/client.rs index e73c6454ac..cb77b0759d 100644 --- a/aws/rust-runtime/aws-config/src/imds/client.rs +++ b/aws/rust-runtime/aws-config/src/imds/client.rs @@ -430,7 +430,10 @@ impl Builder { .read_timeout(self.read_timeout.unwrap_or(DEFAULT_READ_TIMEOUT)) .build(); let connector_settings = ConnectorSettings::from_timeout_config(&timeout_config); - let connector = expect_connector(config.connector(&connector_settings)); + let connector = expect_connector( + "The IMDS credentials provider", + config.connector(&connector_settings), + ); let endpoint_source = self .endpoint .unwrap_or_else(|| EndpointSource::Env(config.clone())); diff --git a/aws/rust-runtime/aws-config/src/imds/credentials.rs b/aws/rust-runtime/aws-config/src/imds/credentials.rs index 9cdc47b7c9..c0c5707f24 100644 --- a/aws/rust-runtime/aws-config/src/imds/credentials.rs +++ b/aws/rust-runtime/aws-config/src/imds/credentials.rs @@ -201,7 +201,7 @@ impl ImdsCredentialsProvider { return expiration; } - let rng = fastrand::Rng::with_seed( + let mut rng = fastrand::Rng::with_seed( now.duration_since(SystemTime::UNIX_EPOCH) .expect("now should be after UNIX EPOCH") .as_secs(), diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index f0e4e3bdca..154659377f 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -764,7 +764,7 @@ mod loader { assert_eq!(Some(&app_name), conf.app_name()); } - #[cfg(aws_sdk_orchestrator_mode)] + #[cfg(all(not(aws_sdk_middleware_mode), feature = "rustls"))] #[tokio::test] async fn disable_default_credentials() { let config = from_env().no_credentials().load().await; diff --git a/aws/rust-runtime/aws-config/src/sso.rs b/aws/rust-runtime/aws-config/src/sso.rs index a4b0314b1d..592ed5b6bc 100644 --- a/aws/rust-runtime/aws-config/src/sso.rs +++ b/aws/rust-runtime/aws-config/src/sso.rs @@ -65,6 +65,7 @@ impl SsoCredentialsProvider { let mut sso_config = SsoConfig::builder() .http_connector(expect_connector( + "The SSO credentials provider", provider_config.connector(&Default::default()), )) .retry_config(RetryConfig::standard()); diff --git a/aws/rust-runtime/aws-config/src/sts.rs b/aws/rust-runtime/aws-config/src/sts.rs index aba7a24bfc..028409bfbe 100644 --- a/aws/rust-runtime/aws-config/src/sts.rs +++ b/aws/rust-runtime/aws-config/src/sts.rs @@ -18,7 +18,10 @@ use aws_smithy_types::retry::RetryConfig; impl crate::provider_config::ProviderConfig { pub(crate) fn sts_client_config(&self) -> StsConfigBuilder { let mut builder = aws_sdk_sts::Config::builder() - .http_connector(expect_connector(self.connector(&Default::default()))) + .http_connector(expect_connector( + "The STS features of aws-config", + self.connector(&Default::default()), + )) .retry_config(RetryConfig::standard()) .region(self.region()) .time_source(self.time_source()); diff --git a/aws/rust-runtime/aws-config/src/sts/assume_role.rs b/aws/rust-runtime/aws-config/src/sts/assume_role.rs index 6ae8f0a6ad..50a83b4b6d 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -213,7 +213,10 @@ impl AssumeRoleProviderBuilder { .credentials_provider(provider) .time_source(conf.time_source()) .region(self.region.clone()) - .http_connector(expect_connector(conf.connector(&Default::default()))); + .http_connector(expect_connector( + "The AssumeRole credentials provider", + conf.connector(&Default::default()), + )); config.set_sleep_impl(conf.sleep()); let session_name = self.session_name.unwrap_or_else(|| { diff --git a/aws/rust-runtime/aws-credential-types/Cargo.toml b/aws/rust-runtime/aws-credential-types/Cargo.toml index 9970072b9e..cbd1426fe2 100644 --- a/aws/rust-runtime/aws-credential-types/Cargo.toml +++ b/aws/rust-runtime/aws-credential-types/Cargo.toml @@ -14,7 +14,7 @@ test-util = [] [dependencies] aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } -fastrand = "1.4.0" +fastrand = "2.0.0" tokio = { version = "1.23.1", features = ["sync"] } tracing = "0.1" zeroize = "1" diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index aa6fdc1dde..8ffcade9fc 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -22,10 +22,11 @@ aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } aws-types = { path = "../aws-types" } +fastrand = "2.0.0" http = "0.2.3" percent-encoding = "2.1.0" tracing = "0.1" -uuid = { version = "1", features = ["v4", "fast-rng"] } +uuid = { version = "1" } [dev-dependencies] aws-credential-types = { path = "../aws-credential-types", features = ["test-util"] } diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 55cf9fd9e9..3d2a2854ba 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -9,9 +9,10 @@ use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use http::{HeaderName, HeaderValue}; use std::fmt::Debug; -use uuid::Uuid; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; +use fastrand::Rng; +use std::sync::Mutex; #[cfg(feature = "test-util")] pub use test_util::{NoInvocationIdGenerator, PredefinedInvocationIdGenerator}; @@ -46,10 +47,43 @@ impl Storable for DynInvocationIdGenerator { type Storer = StoreReplace; } +/// An invocation ID generator that uses random UUIDs for the invocation ID. +#[derive(Debug, Default)] +pub struct DefaultInvocationIdGenerator { + rng: Mutex, +} + +impl DefaultInvocationIdGenerator { + /// Creates a new [`DefaultInvocationIdGenerator`]. + pub fn new() -> Self { + Default::default() + } + + /// Creates a [`DefaultInvocationIdGenerator`] with the given seed. + pub fn with_seed(seed: u64) -> Self { + Self { + rng: Mutex::new(Rng::with_seed(seed)), + } + } +} + +impl InvocationIdGenerator for DefaultInvocationIdGenerator { + fn generate(&self) -> Result, BoxError> { + let mut rng = self.rng.lock().unwrap(); + let mut random_bytes = [0u8; 16]; + rng.fill(&mut random_bytes); + + let id = uuid::Builder::from_random_bytes(random_bytes).into_uuid(); + Ok(Some(InvocationId::new(id.to_string()))) + } +} + /// This interceptor generates a UUID and attaches it to all request attempts made as part of this operation. #[non_exhaustive] #[derive(Debug, Default)] -pub struct InvocationIdInterceptor {} +pub struct InvocationIdInterceptor { + default: DefaultInvocationIdGenerator, +} impl InvocationIdInterceptor { /// Creates a new `InvocationIdInterceptor` @@ -65,13 +99,13 @@ impl Interceptor for InvocationIdInterceptor { _runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let id = cfg + let gen = cfg .load::() - .map(|gen| gen.generate()) - .transpose()? - .flatten(); - cfg.interceptor_state() - .store_put::(id.unwrap_or_default()); + .map(|gen| gen as &dyn InvocationIdGenerator) + .unwrap_or(&self.default); + if let Some(id) = gen.generate()? { + cfg.interceptor_state().store_put::(id); + } Ok(()) } @@ -83,10 +117,9 @@ impl Interceptor for InvocationIdInterceptor { cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let headers = ctx.request_mut().headers_mut(); - let id = cfg - .load::() - .ok_or("Expected an InvocationId in the ConfigBag but none was present")?; - headers.append(AMZ_SDK_INVOCATION_ID, id.0.clone()); + if let Some(id) = cfg.load::() { + headers.append(AMZ_SDK_INVOCATION_ID, id.0.clone()); + } Ok(()) } } @@ -96,21 +129,15 @@ impl Interceptor for InvocationIdInterceptor { pub struct InvocationId(HeaderValue); impl InvocationId { - /// Create a new, random, invocation ID. - pub fn new() -> Self { - Self::default() - } -} - -/// Defaults to a random UUID. -impl Default for InvocationId { - fn default() -> Self { - let id = Uuid::new_v4(); - let id = id - .to_string() - .parse() - .expect("UUIDs always produce a valid header value"); - Self(id) + /// Create an invocation ID with the given value. + /// + /// # Panics + /// This constructor will panic if the given invocation ID is not a valid HTTP header value. + pub fn new(invocation_id: String) -> Self { + Self( + HeaderValue::try_from(invocation_id) + .expect("invocation ID must be a valid HTTP header value"), + ) } } @@ -181,14 +208,14 @@ mod test_util { #[cfg(test)] mod tests { - use crate::invocation_id::{InvocationId, InvocationIdInterceptor}; + use super::*; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::context::{ BeforeTransmitInterceptorContextMut, InterceptorContext, }; use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; - use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::type_erasure::TypeErasedBox; use http::HeaderValue; @@ -200,7 +227,7 @@ mod tests { } #[test] - fn test_id_is_generated_and_set() { + fn default_id_generator() { let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); ctx.enter_serialization_phase(); @@ -224,4 +251,36 @@ mod tests { // UUID should include 32 chars and 4 dashes assert_eq!(header.len(), 36); } + + #[cfg(feature = "test-util")] + #[test] + fn custom_id_generator() { + let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); + let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + ctx.enter_serialization_phase(); + ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); + let _ = ctx.take_input(); + ctx.enter_before_transmit_phase(); + + let mut cfg = ConfigBag::base(); + let mut layer = Layer::new("test"); + layer.store_put(DynInvocationIdGenerator::new( + PredefinedInvocationIdGenerator::new(vec![InvocationId::new( + "the-best-invocation-id".into(), + )]), + )); + cfg.push_layer(layer); + + let interceptor = InvocationIdInterceptor::new(); + let mut ctx = Into::into(&mut ctx); + interceptor + .modify_before_retry_loop(&mut ctx, &rc, &mut cfg) + .unwrap(); + interceptor + .modify_before_transmit(&mut ctx, &rc, &mut cfg) + .unwrap(); + + let header = expect_header(&ctx, "amz-sdk-invocation-id"); + assert_eq!("the-best-invocation-id", header); + } } diff --git a/aws/rust-runtime/aws-types/src/sdk_config.rs b/aws/rust-runtime/aws-types/src/sdk_config.rs index 4d0728a985..08e49469ba 100644 --- a/aws/rust-runtime/aws-types/src/sdk_config.rs +++ b/aws/rust-runtime/aws-types/src/sdk_config.rs @@ -117,7 +117,7 @@ impl Builder { self } - /// Set the endpoint url to use when making requests. + /// Set the endpoint URL to use when making requests. /// # Examples /// ``` /// use aws_types::SdkConfig; @@ -128,7 +128,7 @@ impl Builder { self } - /// Set the endpoint url to use when making requests. + /// Set the endpoint URL to use when making requests. pub fn set_endpoint_url(&mut self, endpoint_url: Option) -> &mut Self { self.endpoint_url = endpoint_url; self diff --git a/aws/sdk-adhoc-test/build.gradle.kts b/aws/sdk-adhoc-test/build.gradle.kts index 3a631a5bb2..2e09902e5d 100644 --- a/aws/sdk-adhoc-test/build.gradle.kts +++ b/aws/sdk-adhoc-test/build.gradle.kts @@ -37,7 +37,7 @@ dependencies { implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") } -fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "middleware" +fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "orchestrator" val allCodegenTests = listOf( CodegenTest( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index f236a08f49..de6da933e3 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -37,6 +37,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock 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.RuntimeType.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.smithy.contextName import software.amazon.smithy.rust.codegen.core.util.cloneOperation import software.amazon.smithy.rust.codegen.core.util.expectTrait @@ -359,17 +360,18 @@ class AwsPresignedFluentBuilderMethod( val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig) rustTemplate( """ - ##[derive(Debug)] + ##[derive(::std::fmt::Debug)] struct AlternatePresigningSerializerRuntimePlugin; impl #{RuntimePlugin} for AlternatePresigningSerializerRuntimePlugin { - fn config(&self) -> Option<#{FrozenLayer}> { + fn config(&self) -> #{Option}<#{FrozenLayer}> { use #{ConfigBagAccessors}; let mut cfg = #{Layer}::new("presigning_serializer"); cfg.set_request_serializer(#{SharedRequestSerializer}::new(#{AlternateSerializer})); - Some(cfg.freeze()) + #{Some}(cfg.freeze()) } } """, + *preludeScope, "AlternateSerializer" to alternateSerializer(operationShape), "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt index 41fad244ef..4f826ef35d 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt @@ -195,12 +195,8 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom rustTemplate( """ match ( - layer - .load::<#{CredentialsCache}>() - .cloned(), - layer - .load::<#{SharedCredentialsProvider}>() - .cloned(), + resolver.config_mut().load::<#{CredentialsCache}>().cloned(), + resolver.config_mut().load::<#{SharedCredentialsProvider}>().cloned(), ) { (#{None}, #{None}) => {} (#{None}, _) => { @@ -213,7 +209,7 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom #{Some}(credentials_cache), #{Some}(credentials_provider), ) => { - layer.store_put(credentials_cache.create_cache(credentials_provider)); + resolver.config_mut().store_put(credentials_cache.create_cache(credentials_provider)); } } """, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt index 3cc4783a17..1d79f10a6a 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt @@ -178,7 +178,7 @@ fun decoratorForBuiltIn( private val endpointUrlDocs = writable { rust( """ - /// Sets the endpoint url used to communicate with this service + /// Sets the endpoint URL used to communicate with this service /// Note: this is used in combination with other endpoint rules, e.g. an API that applies a host-label prefix /// will be prefixed onto this URL. To fully override the endpoint resolver, use diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt index a091664c1a..38c7d76a83 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt @@ -152,8 +152,8 @@ class S3TestDependencies(private val codegenContext: ClientCodegenContext) : Lib // TODO(enableNewSmithyRuntimeCleanup): These additional dependencies may not be needed anymore when removing this flag // depending on if the sra-test is kept around or not. if (codegenContext.smithyRuntimeMode.generateOrchestrator) { - addDependency(CargoDependency.smithyRuntime(codegenContext.runtimeConfig).toDevDependency()) - addDependency(CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig).toDevDependency()) + addDependency(smithyRuntime(codegenContext.runtimeConfig).toDevDependency()) + addDependency(smithyRuntimeApi(codegenContext.runtimeConfig).toDevDependency()) } } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt index 0549ecf525..a34c89f6e5 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt @@ -93,7 +93,7 @@ class SigV4SigningConfig( override fun section(section: ServiceConfig): Writable = writable { when (section) { ServiceConfig.ConfigImpl -> { - if (serviceHasEventStream) { + if (runtimeMode.generateMiddleware && serviceHasEventStream) { // enable the aws-sig-auth `sign-eventstream` feature addDependency(AwsRuntimeType.awsSigAuthEventStream(runtimeConfig).toSymbol()) } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt index 0dabd0033e..ca5deedba8 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt @@ -40,7 +40,7 @@ fun awsTestCodegenContext(model: Model? = null, settings: ClientRustSettings? = // TODO(enableNewSmithyRuntimeCleanup): Remove defaultToOrchestrator once the runtime switches to the orchestrator fun awsSdkIntegrationTest( model: Model, - defaultToOrchestrator: Boolean = false, + defaultToOrchestrator: Boolean = true, test: (ClientCodegenContext, RustCrate) -> Unit = { _, _ -> }, ) = clientIntegrationTest( diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index 23f7aed78c..1c975f58e4 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -61,7 +61,7 @@ val crateVersioner by lazy { aws.sdk.CrateVersioner.defaultFor(rootProject, prop fun getRustMSRV(): String = properties.get("rust.msrv") ?: throw Exception("Rust MSRV missing") fun getPreviousReleaseVersionManifestPath(): String? = properties.get("aws.sdk.previous.release.versions.manifest") -fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "middleware" +fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "orchestrator" fun loadServiceMembership(): Membership { val membershipOverride = properties.get("aws.services")?.let { parseMembership(it) } @@ -101,6 +101,7 @@ fun generateSmithyBuild(services: AwsServices): String { }, "codegen": { "includeFluentClient": false, + "includeEndpointUrlConfig": false, "renameErrors": false, "debugMode": $debugMode, "eventStreamAllowList": [$eventStreamAllowListMembers], diff --git a/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs b/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs index a29f74d72f..6b83b189d5 100644 --- a/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs +++ b/aws/sdk/integration-tests/dynamodb/benches/deserialization_bench.rs @@ -6,7 +6,7 @@ use criterion::{criterion_group, criterion_main, Criterion}; fn do_bench() { - #[cfg(not(aws_sdk_orchestrator_mode))] + #[cfg(aws_sdk_middleware_mode)] { use aws_sdk_dynamodb::operation::query::Query; use aws_smithy_http::response::ParseHttpResponse; diff --git a/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs b/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs index 909ed85a51..f103c07c00 100644 --- a/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs +++ b/aws/sdk/integration-tests/dynamodb/benches/serialization_bench.rs @@ -34,7 +34,7 @@ macro_rules! attr_obj { } fn do_bench(_config: &Config, _input: &PutItemInput) { - #[cfg(not(aws_sdk_orchestrator_mode))] + #[cfg(aws_sdk_middleware_mode)] { use futures_util::FutureExt; diff --git a/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs b/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs index 21ad0470bc..162c1fc50b 100644 --- a/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs +++ b/aws/sdk/integration-tests/dynamodb/tests/retries-with-client-rate-limiting.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#[cfg(aws_sdk_orchestrator_mode)] +#[cfg(not(aws_sdk_middleware_mode))] mod test { use aws_sdk_dynamodb::config::{Credentials, Region, SharedAsyncSleep}; use aws_sdk_dynamodb::{config::retry::RetryConfig, error::ProvideErrorMetadata}; diff --git a/aws/sdk/integration-tests/kms/tests/retryable_errors.rs b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs index e052dc28f2..3a275b460d 100644 --- a/aws/sdk/integration-tests/kms/tests/retryable_errors.rs +++ b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#[cfg(not(aws_sdk_orchestrator_mode))] +#[cfg(aws_sdk_middleware_mode)] mod middleware_mode_tests { use aws_http::retry::AwsResponseRetryClassifier; use aws_sdk_kms as kms; @@ -66,7 +66,7 @@ mod middleware_mode_tests { } } -#[cfg(aws_sdk_orchestrator_mode)] +#[cfg(not(aws_sdk_middleware_mode))] mod orchestrator_mode_tests { use aws_credential_types::Credentials; use aws_runtime::retries::classifier::AwsErrorCodeClassifier; diff --git a/aws/sdk/integration-tests/no-default-features/Cargo.toml b/aws/sdk/integration-tests/no-default-features/Cargo.toml index 545837f66c..8408ac6694 100644 --- a/aws/sdk/integration-tests/no-default-features/Cargo.toml +++ b/aws/sdk/integration-tests/no-default-features/Cargo.toml @@ -17,6 +17,7 @@ publish = false aws-config = { path = "../../build/aws-sdk/sdk/aws-config", default-features = false } aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3", default-features = false } aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async" } +aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } futures = "0.3.25" tokio = { version = "1.23.1", features = ["full", "test-util"] } tracing-subscriber = { version = "0.3.15", features = ["env-filter"] } diff --git a/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs b/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs index 0ee5c7a5c3..ada59bc6f4 100644 --- a/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs +++ b/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs @@ -3,6 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +use aws_sdk_s3::config::{Config, Credentials, SharedAsyncSleep, Sleep}; +use aws_sdk_s3::error::DisplayErrorContext; +use aws_smithy_async::rt::sleep::AsyncSleep; +use std::time::Duration; + // This will fail due to lack of a connector when constructing the SDK Config #[tokio::test] #[should_panic( @@ -13,23 +18,58 @@ async fn test_clients_from_sdk_config() { } // This will fail due to lack of a connector when constructing the service client +#[cfg(not(aws_sdk_middleware_mode))] +#[tokio::test] +async fn test_clients_from_service_config() { + use aws_sdk_s3::config::Region; + + #[derive(Clone, Debug)] + struct StubSleep; + impl AsyncSleep for StubSleep { + fn sleep(&self, _duration: Duration) -> Sleep { + Sleep::new(Box::pin(async { /* no-op */ })) + } + } + + let config = Config::builder() + .region(Region::new("us-east-1")) + .credentials_provider(Credentials::for_tests()) + .sleep_impl(SharedAsyncSleep::new(StubSleep)) + .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); + + let err = client + .list_buckets() + .send() + .await + .expect_err("it should fail to send a request because there is no connector"); + let msg = format!("{}", DisplayErrorContext(err)); + assert!( + msg.contains("No HTTP connector was available to send this request. Enable the `rustls` crate feature or set a connector to fix this."), + "expected '{msg}' to contain 'No HTTP connector was available to send this request. Enable the `rustls` crate feature or set a connector to fix this.'" + ); +} + +// TODO(enableNewSmithyRuntimeMode): Remove this test (covered above for orchestrator) +// +// This will fail due to lack of a connector when constructing the service client +#[cfg(aws_sdk_middleware_mode)] #[tokio::test] #[should_panic( expected = "No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this." )] -async fn test_clients_from_service_config() { +async fn test_clients_from_service_config_middleware() { #[derive(Clone, Debug)] struct StubSleep; - impl aws_smithy_async::rt::sleep::AsyncSleep for StubSleep { - fn sleep(&self, _duration: std::time::Duration) -> aws_sdk_s3::config::Sleep { + impl AsyncSleep for StubSleep { + fn sleep(&self, _duration: Duration) -> Sleep { todo!() } } - let config = aws_sdk_s3::Config::builder() - .sleep_impl(aws_smithy_async::rt::sleep::SharedAsyncSleep::new( - StubSleep {}, - )) + let config = Config::builder() + .sleep_impl(SharedAsyncSleep::new(StubSleep {})) .build(); // This will panic due to the lack of an HTTP connector aws_sdk_s3::Client::from_conf(config); diff --git a/aws/sdk/integration-tests/s3/Cargo.toml b/aws/sdk/integration-tests/s3/Cargo.toml index c504dba38d..010d73f659 100644 --- a/aws/sdk/integration-tests/s3/Cargo.toml +++ b/aws/sdk/integration-tests/s3/Cargo.toml @@ -26,7 +26,7 @@ aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } bytes = "1" bytes-utils = "0.1.2" -fastrand = "1.8.0" +fastrand = "2.0.0" futures-util = { version = "0.3.16", default-features = false } hdrhistogram = "7.5.2" http = "0.2.3" diff --git a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs index 8d1a604d95..5c1d8969cd 100644 --- a/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs +++ b/aws/sdk/integration-tests/s3/tests/alternative-async-runtime.rs @@ -20,7 +20,7 @@ use aws_smithy_types::timeout::TimeoutConfig; use std::fmt::Debug; use std::time::{Duration, Instant}; -#[cfg(aws_sdk_orchestrator_mode)] +#[cfg(not(aws_sdk_middleware_mode))] use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs; #[derive(Debug)] @@ -36,7 +36,7 @@ impl AsyncSleep for SmolSleep { #[test] fn test_smol_runtime_timeouts() { - #[cfg(aws_sdk_orchestrator_mode)] + #[cfg(not(aws_sdk_middleware_mode))] let _guard = capture_test_logs(); if let Err(err) = smol::block_on(async { timeout_test(SharedAsyncSleep::new(SmolSleep)).await }) @@ -48,7 +48,7 @@ fn test_smol_runtime_timeouts() { #[test] fn test_smol_runtime_retry() { - #[cfg(aws_sdk_orchestrator_mode)] + #[cfg(not(aws_sdk_middleware_mode))] let _guard = capture_test_logs(); if let Err(err) = smol::block_on(async { retry_test(SharedAsyncSleep::new(SmolSleep)).await }) { @@ -68,7 +68,7 @@ impl AsyncSleep for AsyncStdSleep { #[test] fn test_async_std_runtime_timeouts() { - #[cfg(aws_sdk_orchestrator_mode)] + #[cfg(not(aws_sdk_middleware_mode))] let _guard = capture_test_logs(); if let Err(err) = async_std::task::block_on(async { @@ -81,7 +81,7 @@ fn test_async_std_runtime_timeouts() { #[test] fn test_async_std_runtime_retry() { - #[cfg(aws_sdk_orchestrator_mode)] + #[cfg(not(aws_sdk_middleware_mode))] let _guard = capture_test_logs(); if let Err(err) = diff --git a/aws/sdk/integration-tests/s3/tests/config-override.rs b/aws/sdk/integration-tests/s3/tests/config-override.rs index b3d93be867..620b6405fa 100644 --- a/aws/sdk/integration-tests/s3/tests/config-override.rs +++ b/aws/sdk/integration-tests/s3/tests/config-override.rs @@ -9,7 +9,7 @@ use aws_sdk_s3::Client; use aws_smithy_client::test_connection::{capture_request, CaptureRequestReceiver}; use aws_types::SdkConfig; -// TODO(enableNewSmithyRuntimeCleanup): Remove this attribute once #[cfg(aws_sdk_orchestrator_mode)] +// TODO(enableNewSmithyRuntimeCleanup): Remove this attribute once #[cfg(aws_sdk_middleware_mode)] // has been removed #[allow(dead_code)] fn test_client() -> (CaptureRequestReceiver, Client) { @@ -23,7 +23,7 @@ fn test_client() -> (CaptureRequestReceiver, Client) { (captured_request, client) } -#[cfg(aws_sdk_orchestrator_mode)] +#[cfg(not(aws_sdk_middleware_mode))] #[tokio::test] async fn operation_overrides_force_path_style() { let (captured_request, client) = test_client(); @@ -42,7 +42,7 @@ async fn operation_overrides_force_path_style() { ); } -#[cfg(aws_sdk_orchestrator_mode)] +#[cfg(not(aws_sdk_middleware_mode))] #[tokio::test] async fn operation_overrides_fips() { let (captured_request, client) = test_client(); @@ -61,7 +61,7 @@ async fn operation_overrides_fips() { ); } -#[cfg(aws_sdk_orchestrator_mode)] +#[cfg(not(aws_sdk_middleware_mode))] #[tokio::test] async fn operation_overrides_dual_stack() { let (captured_request, client) = test_client(); @@ -84,7 +84,7 @@ async fn operation_overrides_dual_stack() { // accessed in ServiceRuntimePlugin::config. Currently, a credentials cache created for a single // operation invocation is not picked up by an identity resolver. /* -#[cfg(aws_sdk_orchestrator_mode)] +#[cfg(not(aws_sdk_middleware_mode))] #[tokio::test] async fn operation_overrides_credentials_provider() { let (captured_request, client) = test_client(); diff --git a/aws/sdk/integration-tests/s3/tests/config_to_builder.rs b/aws/sdk/integration-tests/s3/tests/config_to_builder.rs index 122b0418b6..af899ee2f6 100644 --- a/aws/sdk/integration-tests/s3/tests/config_to_builder.rs +++ b/aws/sdk/integration-tests/s3/tests/config_to_builder.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#[cfg(aws_sdk_orchestrator_mode)] +#[cfg(not(aws_sdk_middleware_mode))] #[tokio::test] async fn test_config_to_builder() { use aws_sdk_s3::config::AppName; diff --git a/aws/sdk/integration-tests/s3/tests/endpoints.rs b/aws/sdk/integration-tests/s3/tests/endpoints.rs index ec87eac2a3..0479f94bc5 100644 --- a/aws/sdk/integration-tests/s3/tests/endpoints.rs +++ b/aws/sdk/integration-tests/s3/tests/endpoints.rs @@ -62,7 +62,7 @@ async fn dual_stack() { ); } -#[cfg(aws_sdk_orchestrator_mode)] +#[cfg(not(aws_sdk_middleware_mode))] #[tokio::test] async fn multi_region_access_points() { let (_captured_request, client) = test_client(|b| b); @@ -84,7 +84,7 @@ async fn multi_region_access_points() { ); } -#[cfg(not(aws_sdk_orchestrator_mode))] +#[cfg(aws_sdk_middleware_mode)] #[tokio::test] async fn multi_region_access_points() { let (_captured_request, client) = test_client(|b| b); diff --git a/aws/sdk/integration-tests/sts/tests/signing-it.rs b/aws/sdk/integration-tests/sts/tests/signing-it.rs index 01727a53c9..74e82e0026 100644 --- a/aws/sdk/integration-tests/sts/tests/signing-it.rs +++ b/aws/sdk/integration-tests/sts/tests/signing-it.rs @@ -25,7 +25,7 @@ async fn assume_role_signed() { } // TODO(enableNewSmithyRuntimeCleanup): Delete the middleware version of this test -#[cfg(not(aws_sdk_orchestrator_mode))] +#[cfg(aws_sdk_middleware_mode)] #[tokio::test] async fn web_identity_unsigned() { let creds = Credentials::for_tests(); @@ -44,7 +44,7 @@ async fn web_identity_unsigned() { ); } -#[cfg(aws_sdk_orchestrator_mode)] +#[cfg(not(aws_sdk_middleware_mode))] #[tokio::test] async fn web_identity_unsigned() { let (server, request) = capture_request(None); diff --git a/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs index 447bdead21..16d4222515 100644 --- a/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs +++ b/aws/sdk/integration-tests/timestreamquery/tests/endpoint_disco.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#[cfg(aws_sdk_orchestrator_mode)] +#[cfg(not(aws_sdk_middleware_mode))] #[tokio::test] async fn do_endpoint_discovery() { use aws_credential_types::provider::SharedCredentialsProvider; diff --git a/aws/sdk/sdk-external-types.toml b/aws/sdk/sdk-external-types.toml index d2ab675425..b484544c27 100644 --- a/aws/sdk/sdk-external-types.toml +++ b/aws/sdk/sdk-external-types.toml @@ -3,11 +3,13 @@ allowed_external_types = [ "aws_credential_types::*", "aws_endpoint::*", "aws_http::*", - "aws_sig_auth::*", + "aws_runtime::*", "aws_smithy_async::*", "aws_smithy_client::*", "aws_smithy_http::*", "aws_smithy_http_tower::*", + "aws_smithy_runtime::*", + "aws_smithy_runtime_api::*", "aws_smithy_types::*", "aws_types::*", "http::header::map::HeaderMap", diff --git a/codegen-client-test/build.gradle.kts b/codegen-client-test/build.gradle.kts index 8ccf6f6ad4..ae84542a3e 100644 --- a/codegen-client-test/build.gradle.kts +++ b/codegen-client-test/build.gradle.kts @@ -15,7 +15,7 @@ plugins { val smithyVersion: String by project val defaultRustDocFlags: String by project val properties = PropertyRetriever(rootProject, project) -fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "middleware" +fun getSmithyRuntimeMode(): String = properties.get("smithy.runtime.mode") ?: "orchestrator" val pluginName = "rust-client-codegen" val workingDirUnderBuildDir = "smithyprojections/codegen-client-test/" diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt index f4d2fdeede..0fec857bec 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt @@ -125,6 +125,8 @@ data class ClientCodegenConfig( val eventStreamAllowList: Set = defaultEventStreamAllowList, // TODO(SmithyRuntime): Remove this once we commit to switch to aws-smithy-runtime and aws-smithy-runtime-api val enableNewSmithyRuntime: SmithyRuntimeMode = defaultEnableNewSmithyRuntime, + /** If true, adds `endpoint_url`/`set_endpoint_url` methods to the service config */ + val includeEndpointUrlConfig: Boolean = defaultIncludeEndpointUrlConfig, ) : CoreCodegenConfig( formatTimeoutSeconds, debugMode, ) { @@ -133,7 +135,8 @@ data class ClientCodegenConfig( private const val defaultIncludeFluentClient = true private const val defaultAddMessageToErrors = true private val defaultEventStreamAllowList: Set = emptySet() - private val defaultEnableNewSmithyRuntime = SmithyRuntimeMode.Middleware + private val defaultEnableNewSmithyRuntime = SmithyRuntimeMode.Orchestrator + private const val defaultIncludeEndpointUrlConfig = true fun fromCodegenConfigAndNode(coreCodegenConfig: CoreCodegenConfig, node: Optional) = if (node.isPresent) { @@ -144,11 +147,15 @@ data class ClientCodegenConfig( .map { array -> array.toList().mapNotNull { node -> node.asStringNode().orNull()?.value } } .orNull()?.toSet() ?: defaultEventStreamAllowList, renameExceptions = node.get().getBooleanMemberOrDefault("renameErrors", defaultRenameExceptions), - includeFluentClient = node.get().getBooleanMemberOrDefault("includeFluentClient", defaultIncludeFluentClient), - addMessageToErrors = node.get().getBooleanMemberOrDefault("addMessageToErrors", defaultAddMessageToErrors), + includeFluentClient = node.get() + .getBooleanMemberOrDefault("includeFluentClient", defaultIncludeFluentClient), + addMessageToErrors = node.get() + .getBooleanMemberOrDefault("addMessageToErrors", defaultAddMessageToErrors), enableNewSmithyRuntime = SmithyRuntimeMode.fromString( node.get().getStringMemberOrDefault("enableNewSmithyRuntime", "middleware"), ), + includeEndpointUrlConfig = node.get() + .getBooleanMemberOrDefault("includeEndpointUrlConfig", defaultIncludeEndpointUrlConfig), ) } else { ClientCodegenConfig( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index e2d71aeae7..b0ba6b1f24 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -41,7 +41,6 @@ private class HttpConnectorConfigCustomization( *preludeScope, "Connection" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::Connection"), "ConnectorSettings" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::ConnectorSettings"), - "default_connector" to RuntimeType.smithyClient(runtimeConfig).resolve("conns::default_connector"), "DynConnectorAdapter" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::connectors::adapter::DynConnectorAdapter"), "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), "Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"), @@ -50,6 +49,31 @@ private class HttpConnectorConfigCustomization( "TimeoutConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout::TimeoutConfig"), ) + private fun defaultConnectorFn(): RuntimeType = RuntimeType.forInlineFun("default_connector", ClientRustModule.config) { + rustTemplate( + """ + ##[cfg(feature = "rustls")] + fn default_connector( + connector_settings: &#{ConnectorSettings}, + sleep_impl: #{Option}<#{SharedAsyncSleep}>, + ) -> #{Option}<#{DynConnector}> { + #{default_connector}(connector_settings, sleep_impl) + } + + ##[cfg(not(feature = "rustls"))] + fn default_connector( + _connector_settings: &#{ConnectorSettings}, + _sleep_impl: #{Option}<#{SharedAsyncSleep}>, + ) -> #{Option}<#{DynConnector}> { + #{None} + } + """, + *codegenScope, + "default_connector" to RuntimeType.smithyClient(runtimeConfig).resolve("conns::default_connector"), + "DynConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("erase::DynConnector"), + ) + } + private fun setConnectorFn(): RuntimeType = RuntimeType.forInlineFun("set_connector", ClientRustModule.config) { rustTemplate( """ @@ -84,6 +108,7 @@ private class HttpConnectorConfigCustomization( } """, *codegenScope, + "default_connector" to defaultConnectorFn(), ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index 81b89d989d..2f81ef33f5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -31,6 +31,7 @@ internal class EndpointConfigCustomization( private val codegenScope = arrayOf( *preludeScope, "DefaultEndpointResolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::endpoints::DefaultEndpointResolver"), + "Endpoint" to RuntimeType.smithyHttp(runtimeConfig).resolve("endpoint::Endpoint"), "OldSharedEndpointResolver" to types.sharedEndpointResolver, "Params" to typesGenerator.paramsStruct(), "Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"), @@ -125,9 +126,47 @@ internal class EndpointConfigCustomization( } else { "" } + if (codegenContext.settings.codegenConfig.includeEndpointUrlConfig) { + rustTemplate( + """ + /// Set the endpoint URL to use when making requests. + /// + /// Note: setting an endpoint URL will replace any endpoint resolver that has been set. + /// + /// ## Panics + /// Panics if an invalid URL is given. + pub fn endpoint_url(mut self, endpoint_url: impl #{Into}<#{String}>) -> Self { + self.set_endpoint_url(#{Some}(endpoint_url.into())); + self + } + + /// Set the endpoint URL to use when making requests. + /// + /// Note: setting an endpoint URL will replace any endpoint resolver that has been set. + /// + /// ## Panics + /// Panics if an invalid URL is given. + pub fn set_endpoint_url(&mut self, endpoint_url: #{Option}<#{String}>) -> &mut Self { + ##[allow(deprecated)] + self.set_endpoint_resolver( + endpoint_url.map(|url| { + #{OldSharedEndpointResolver}::new( + #{Endpoint}::immutable(url).expect("invalid endpoint URL") + ) + }) + ); + self + } + """, + *codegenScope, + ) + } rustTemplate( """ /// Sets the endpoint resolver to use when making requests. + /// + /// Note: setting an endpoint resolver will replace any endpoint URL that has been set. + /// $defaultResolverDocs pub fn endpoint_resolver(mut self, endpoint_resolver: impl $resolverTrait + 'static) -> Self { self.set_endpoint_resolver(#{Some}(#{OldSharedEndpointResolver}::new(endpoint_resolver))); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index b9dca27732..1894dcd02a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -32,6 +32,7 @@ import software.amazon.smithy.rust.codegen.core.util.PANIC import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.inputShape import software.amazon.smithy.rust.codegen.core.util.orNull +import software.amazon.smithy.rust.codegen.core.util.toPascalCase class EndpointParamsInterceptorGenerator( private val codegenContext: ClientCodegenContext, @@ -122,7 +123,7 @@ class EndpointParamsInterceptorGenerator( idx.getClientContextParams(codegenContext.serviceShape).orNull()?.parameters?.forEach { (name, param) -> val setterName = EndpointParamsGenerator.setterName(name) val inner = ClientContextConfigCustomization.toSymbol(param.type, symbolProvider) - val newtype = configParamNewtype(name, inner, codegenContext.runtimeConfig) + val newtype = configParamNewtype(name.toPascalCase(), inner, codegenContext.runtimeConfig) rustTemplate( ".$setterName(cfg.#{load_from_service_config_layer})", "load_from_service_config_layer" to loadFromConfigBag(inner.name, newtype), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 39795ed503..0d93284e20 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -167,7 +167,7 @@ open class OperationGenerator( ) -> #{RuntimePlugins} { let mut runtime_plugins = client_runtime_plugins.with_operation_plugin(Self::new()); #{additional_runtime_plugins} - if let Some(config_override) = config_override { + if let #{Some}(config_override) = config_override { for plugin in config_override.runtime_plugins.iter().cloned() { runtime_plugins = runtime_plugins.with_operation_plugin(plugin); } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index a798a9753b..73bfe50780 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -67,15 +67,15 @@ class OperationRuntimePluginGenerator( let mut cfg = #{Layer}::new(${operationShape.id.name.dq()}); use #{ConfigBagAccessors} as _; - cfg.set_request_serializer(#{SharedRequestSerializer}::new(${operationStructName}RequestSerializer)); - cfg.set_response_deserializer(#{DynResponseDeserializer}::new(${operationStructName}ResponseDeserializer)); + cfg.store_put(#{SharedRequestSerializer}::new(${operationStructName}RequestSerializer)); + cfg.store_put(#{DynResponseDeserializer}::new(${operationStructName}ResponseDeserializer)); ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StaticAuthOptionResolverParams}::new())); #{additional_config} - Some(cfg.freeze()) + #{Some}(cfg.freeze()) } fn runtime_components(&self) -> #{Cow}<'_, #{RuntimeComponentsBuilder}> { @@ -85,7 +85,7 @@ class OperationRuntimePluginGenerator( #{Cow}::Owned( #{RuntimeComponentsBuilder}::new(${operationShape.id.name.dq()}) - .with_retry_classifiers(Some(retry_classifiers)) + .with_retry_classifiers(#{Some}(retry_classifiers)) #{auth_options} #{interceptors} ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index b5da3eaba9..ed68a0b306 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -101,7 +101,7 @@ class ServiceRuntimePluginGenerator( } writer.rustTemplate( """ - ##[derive(Debug)] + ##[derive(::std::fmt::Debug)] pub(crate) struct ServiceRuntimePlugin { config: #{Option}<#{FrozenLayer}>, runtime_components: #{RuntimeComponentsBuilder}, @@ -136,7 +136,7 @@ class ServiceRuntimePluginGenerator( """ let mut cfg = #{Layer}::new(${codegenContext.serviceShape.id.name.dq()}); #{additional_config} - Some(cfg.freeze()) + #{Some}(cfg.freeze()) """, *codegenScope, "additional_config" to additionalConfig, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index 22c64d36e0..f4d9a4c705 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -34,7 +34,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate import software.amazon.smithy.rust.codegen.core.rustlang.withBlock 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.generators.protocol.ProtocolSupport import software.amazon.smithy.rust.codegen.core.util.dq import software.amazon.smithy.rust.codegen.core.util.getTrait @@ -45,6 +44,7 @@ import software.amazon.smithy.rust.codegen.core.util.orNull import software.amazon.smithy.rust.codegen.core.util.outputShape import software.amazon.smithy.rust.codegen.core.util.toSnakeCase import java.util.logging.Logger +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType as RT data class ClientCreationParams( val codegenContext: ClientCodegenContext, @@ -81,9 +81,9 @@ class DefaultProtocolTestGenerator( """, "Client" to ClientRustModule.root.toType().resolve("Client"), "Builder" to ClientRustModule.client.toType().resolve("Builder"), - "SmithyEndpointStage" to RuntimeType.smithyHttp(codegenContext.runtimeConfig) + "SmithyEndpointStage" to RT.smithyHttp(codegenContext.runtimeConfig) .resolve("endpoint::middleware::SmithyEndpointStage"), - "MapRequestLayer" to RuntimeType.smithyHttpTower(codegenContext.runtimeConfig) + "MapRequestLayer" to RT.smithyHttpTower(codegenContext.runtimeConfig) .resolve("map_request::MapRequestLayer"), ) } else { @@ -100,6 +100,7 @@ class DefaultProtocolTestGenerator( } }, ) : ProtocolTestGenerator { + private val rc = codegenContext.runtimeConfig private val logger = Logger.getLogger(javaClass.name) private val inputShape = operationShape.inputShape(codegenContext.model) @@ -110,8 +111,8 @@ class DefaultProtocolTestGenerator( private val instantiator = ClientInstantiator(codegenContext) private val codegenScope = arrayOf( - "SmithyHttp" to RuntimeType.smithyHttp(codegenContext.runtimeConfig), - "AssertEq" to RuntimeType.PrettyAssertions.resolve("assert_eq!"), + "SmithyHttp" to RT.smithyHttp(rc), + "AssertEq" to RT.PrettyAssertions.resolve("assert_eq!"), ) sealed class TestCase { @@ -227,7 +228,7 @@ class DefaultProtocolTestGenerator( #{customParams} """, - "capture_request" to CargoDependency.smithyClient(codegenContext.runtimeConfig) + "capture_request" to CargoDependency.smithyClient(rc) .toDevDependency() .withFeature("test-util") .toType() @@ -334,7 +335,7 @@ class DefaultProtocolTestGenerator( writeInline("let expected_output =") instantiator.render(this, expectedShape, testCase.params) write(";") - write("let mut http_response = #T::new()", RuntimeType.HttpResponseBuilder) + write("let mut http_response = #T::new()", RT.HttpResponseBuilder) testCase.headers.forEach { (key, value) -> writeWithNoFormatting(".header(${key.dq()}, ${value.dq()})") } @@ -344,12 +345,12 @@ class DefaultProtocolTestGenerator( .body(#T::from(${testCase.body.orNull()?.dq()?.replace("#", "##") ?: "vec![]"})) .unwrap(); """, - RuntimeType.sdkBody(runtimeConfig = codegenContext.runtimeConfig), + RT.sdkBody(runtimeConfig = rc), ) if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { rust( "let mut op_response = #T::new(http_response);", - RuntimeType.operationModule(codegenContext.runtimeConfig).resolve("Response"), + RT.operationModule(rc).resolve("Response"), ) rustTemplate( """ @@ -363,14 +364,19 @@ class DefaultProtocolTestGenerator( }); """, "op" to operationSymbol, - "copy_from_slice" to RuntimeType.Bytes.resolve("copy_from_slice"), - "parse_http_response" to RuntimeType.parseHttpResponse(codegenContext.runtimeConfig), + "copy_from_slice" to RT.Bytes.resolve("copy_from_slice"), + "parse_http_response" to RT.parseHttpResponse(rc), ) } else { rustTemplate( """ use #{ResponseDeserializer}; - let de = #{OperationDeserializer}; + use #{RuntimePlugin}; + + let op = #{Operation}::new(); + let config = op.config().expect("the operation has config"); + let de = config.load::<#{DynResponseDeserializer}>().expect("the config must have a deserializer"); + let parsed = de.deserialize_streaming(&mut http_response); let parsed = parsed.unwrap_or_else(|| { let http_response = http_response.map(|body| { @@ -379,12 +385,12 @@ class DefaultProtocolTestGenerator( de.deserialize_nonstreaming(&http_response) }); """, - "OperationDeserializer" to codegenContext.symbolProvider.moduleForShape(operationShape).toType() - .resolve("${operationSymbol.name}ResponseDeserializer"), - "copy_from_slice" to RuntimeType.Bytes.resolve("copy_from_slice"), - "ResponseDeserializer" to CargoDependency.smithyRuntimeApi(codegenContext.runtimeConfig).toType() - .resolve("client::orchestrator::ResponseDeserializer"), - "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), + "copy_from_slice" to RT.Bytes.resolve("copy_from_slice"), + "DynResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::DynResponseDeserializer"), + "Operation" to codegenContext.symbolProvider.toSymbol(operationShape), + "ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::ResponseDeserializer"), + "RuntimePlugin" to RT.runtimePlugin(rc), + "SdkBody" to RT.sdkBody(rc), ) } if (expectedShape.hasTrait()) { @@ -432,9 +438,7 @@ class DefaultProtocolTestGenerator( } else { when (codegenContext.model.expectShape(member.target)) { is DoubleShape, is FloatShape -> { - addUseImports( - RuntimeType.protocolTest(codegenContext.runtimeConfig, "FloatEquals").toSymbol(), - ) + addUseImports(RT.protocolTest(rc, "FloatEquals").toSymbol()) rust( """ assert!(parsed.$memberName.float_equals(&expected_output.$memberName), @@ -470,8 +474,8 @@ class DefaultProtocolTestGenerator( "#T(&body, ${ rustWriter.escape(body).dq() }, #T::from(${(mediaType ?: "unknown").dq()}))", - RuntimeType.protocolTest(codegenContext.runtimeConfig, "validate_body"), - RuntimeType.protocolTest(codegenContext.runtimeConfig, "MediaType"), + RT.protocolTest(rc, "validate_body"), + RT.protocolTest(rc, "MediaType"), ) } } @@ -512,7 +516,7 @@ class DefaultProtocolTestGenerator( assertOk(rustWriter) { write( "#T($actualExpression, $variableName)", - RuntimeType.protocolTest(codegenContext.runtimeConfig, "validate_headers"), + RT.protocolTest(rc, "validate_headers"), ) } } @@ -566,7 +570,7 @@ class DefaultProtocolTestGenerator( assertOk(rustWriter) { write( "#T($actualExpression, $expectedVariableName)", - RuntimeType.protocolTest(codegenContext.runtimeConfig, checkFunction), + RT.protocolTest(rc, checkFunction), ) } } @@ -576,7 +580,7 @@ class DefaultProtocolTestGenerator( * for pretty printing protocol test helper results */ private fun assertOk(rustWriter: RustWriter, inner: Writable) { - rustWriter.write("#T(", RuntimeType.protocolTest(codegenContext.runtimeConfig, "assert_ok")) + rustWriter.write("#T(", RT.protocolTest(rc, "assert_ok")) inner(rustWriter) rustWriter.write(");") } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecoratorTest.kt index 1673ac299f..a6da0a29a3 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecoratorTest.kt @@ -6,14 +6,15 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest import software.amazon.smithy.rust.codegen.core.testutil.runWithWarnings +// TODO(enableNewSmithyRuntimeCleanup): Delete this test (replaced by HttpAuthDecoratorTest) internal class ApiKeyAuthDecoratorTest { private val modelQuery = """ namespace test @@ -46,7 +47,8 @@ internal class ApiKeyAuthDecoratorTest { val testDir = clientIntegrationTest( modelQuery, // just run integration tests - IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }), + TestCodegenSettings.middlewareModeTestParams + .copy(command = { "cargo test --test *".runWithWarnings(it) }), ) { clientCodegenContext, rustCrate -> rustCrate.integrationTest("api_key_present_in_property_bag") { val moduleName = clientCodegenContext.moduleUseName() @@ -136,7 +138,8 @@ internal class ApiKeyAuthDecoratorTest { val testDir = clientIntegrationTest( modelHeader, // just run integration tests - IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }), + TestCodegenSettings.middlewareModeTestParams + .copy(command = { "cargo test --test *".runWithWarnings(it) }), ) { clientCodegenContext, rustCrate -> rustCrate.integrationTest("api_key_auth_is_set_in_http_header") { val moduleName = clientCodegenContext.moduleUseName() diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt index 73633a603e..602e848261 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpVersionListGeneratorTest.kt @@ -6,10 +6,10 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.rust -import software.amazon.smithy.rust.codegen.core.testutil.IntegrationTestParams import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest @@ -18,6 +18,7 @@ import software.amazon.smithy.rust.codegen.core.testutil.integrationTest // ./gradlew codegen-client:test --tests software.amazon.smithy.rust.codegen.client.HttpVersionListGeneratorTest --info // ``` +// TODO(enableNewSmithyRuntimeCleanup): Delete this test (incomplete http version support wasn't ported to orchestrator) internal class HttpVersionListGeneratorTest { @Test fun `http version list integration test (no preferred version)`() { @@ -44,7 +45,7 @@ internal class HttpVersionListGeneratorTest { greeting: String } """.asSmithyModel() - clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("http_version_list") { Attribute.TokioTest.render(this) @@ -95,7 +96,7 @@ internal class HttpVersionListGeneratorTest { greeting: String } """.asSmithyModel() - clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("validate_http") { Attribute.TokioTest.render(this) @@ -161,7 +162,7 @@ internal class HttpVersionListGeneratorTest { clientIntegrationTest( model, - IntegrationTestParams(addModuleToEventStreamAllowList = true), + TestCodegenSettings.middlewareModeTestParams.copy(addModuleToEventStreamAllowList = true), ) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("validate_eventstream_http") { diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt index b84f204c24..b25e28c75e 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomizationTest.kt @@ -7,6 +7,8 @@ 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 @@ -40,6 +42,12 @@ internal class ResiliencyConfigCustomizationTest { val codegenContext = testClientCodegenContext(model, settings = project.clientRustSettings()) stubConfigProject(codegenContext, ResiliencyConfigCustomization(codegenContext), project) + project.withModule(ClientRustModule.config) { + ServiceRuntimePluginGenerator(codegenContext).render( + this, + listOf(ResiliencyServiceRuntimePluginCustomization(codegenContext)), + ) + } ResiliencyReExportCustomization(codegenContext).extras(project) project.compileAndTest() } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt index 62fff162aa..6d5bcaa05f 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt @@ -8,6 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.endpoint import io.kotest.assertions.throwables.shouldThrow import io.kotest.matchers.string.shouldContain import org.junit.jupiter.api.Test +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.core.rustlang.Attribute import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -120,12 +121,14 @@ class EndpointsDecoratorTest { } """.asSmithyModel() + // TODO(enableNewSmithyRuntimeCleanup): Delete this test (replaced by the second @Test below) @Test fun `set an endpoint in the property bag`() { val testDir = clientIntegrationTest( model, // Just run integration tests. - IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }), + TestCodegenSettings.middlewareModeTestParams + .copy(command = { "cargo test --test *".runWithWarnings(it) }), ) { clientCodegenContext, rustCrate -> rustCrate.integrationTest("endpoint_params_test") { val moduleName = clientCodegenContext.moduleUseName() @@ -167,4 +170,102 @@ class EndpointsDecoratorTest { failure.output shouldContain "https://failingtest.com" "cargo clippy".runWithWarnings(testDir) } + + @Test + fun `resolve endpoint`() { + val testDir = clientIntegrationTest( + model, + // Just run integration tests. + IntegrationTestParams(command = { "cargo test --test *".runWithWarnings(it) }), + ) { clientCodegenContext, rustCrate -> + rustCrate.integrationTest("endpoint_params_test") { + val moduleName = clientCodegenContext.moduleUseName() + Attribute.TokioTest.render(this) + rust( + """ + async fn endpoint_params_are_set() { + use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_client::never::NeverConnector; + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::orchestrator::EndpointResolverParams; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; + use aws_smithy_types::config_bag::ConfigBag; + use aws_smithy_types::endpoint::Endpoint; + use aws_smithy_types::timeout::TimeoutConfig; + use std::sync::atomic::AtomicBool; + use std::sync::atomic::Ordering; + use std::sync::Arc; + use std::time::Duration; + use $moduleName::{ + config::endpoint::Params, config::interceptors::BeforeTransmitInterceptorContextRef, + config::Interceptor, config::SharedAsyncSleep, Client, Config, + }; + + ##[derive(Clone, Debug, Default)] + struct TestInterceptor { + called: Arc, + } + impl Interceptor for TestInterceptor { + fn read_before_transmit( + &self, + _context: &BeforeTransmitInterceptorContextRef<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let params = cfg + .load::() + .expect("params set in config"); + let params: &Params = params.get().expect("correct type"); + assert_eq!( + params, + &Params::builder() + .bucket("bucket-name".to_string()) + .built_in_with_default("some-default") + .bool_built_in_with_default(true) + .a_bool_param(false) + .a_string_param("hello".to_string()) + .region("us-east-2".to_string()) + .build() + .unwrap() + ); + + let endpoint = cfg.load::().expect("endpoint set in config"); + assert_eq!(endpoint.url(), "https://www.us-east-2.example.com"); + + self.called.store(true, Ordering::Relaxed); + Ok(()) + } + } + + let interceptor = TestInterceptor::default(); + let config = Config::builder() + .http_connector(NeverConnector::new()) + .interceptor(interceptor.clone()) + .timeout_config( + TimeoutConfig::builder() + .operation_timeout(Duration::from_millis(30)) + .build(), + ) + .sleep_impl(SharedAsyncSleep::new(TokioSleep::new())) + .a_string_param("hello") + .a_bool_param(false) + .build(); + let client = Client::from_conf(config); + + let _ = dbg!(client.test_operation().bucket("bucket-name").send().await); + assert!( + interceptor.called.load(Ordering::Relaxed), + "the interceptor should have been called" + ); + } + """, + ) + } + } + // the model has an intentionally failing test—ensure it fails + val failure = shouldThrow { "cargo test".runWithWarnings(testDir) } + failure.output shouldContain "endpoint::test::test_1" + failure.output shouldContain "https://failingtest.com" + "cargo clippy".runWithWarnings(testDir) + } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt index 34225e8dfd..a94148d21c 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt @@ -12,13 +12,16 @@ import org.junit.jupiter.params.provider.ValueSource import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.EndpointTrait import software.amazon.smithy.rust.codegen.client.smithy.SmithyRuntimeMode +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest import software.amazon.smithy.rust.codegen.client.testutil.testSymbolProvider import software.amazon.smithy.rust.codegen.core.rustlang.Attribute +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.RustModule import software.amazon.smithy.rust.codegen.core.rustlang.implBlock import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock +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.generators.operationBuildError import software.amazon.smithy.rust.codegen.core.testutil.TestRuntimeConfig @@ -120,9 +123,10 @@ internal class EndpointTraitBindingsTest { project.compileAndTest() } + // TODO(enableNewSmithyRuntimeCleanup): Delete this test (replaced by the @Test below it) @ExperimentalPathApi @Test - fun `endpoint integration test`() { + fun `endpoint integration test middleware`() { val model = """ namespace com.example use aws.protocols#awsJson1_0 @@ -142,7 +146,7 @@ internal class EndpointTraitBindingsTest { greeting: String } """.asSmithyModel() - clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("test_endpoint_prefix") { Attribute.TokioTest.render(this) @@ -168,4 +172,136 @@ internal class EndpointTraitBindingsTest { } } } + + @ExperimentalPathApi + @Test + fun `endpoint integration test`() { + val model = """ + namespace com.example + use aws.protocols#awsJson1_0 + use smithy.rules#endpointRuleSet + + @awsJson1_0 + @aws.api#service(sdkId: "Test", endpointPrefix: "differentprefix") + @endpointRuleSet({ + "version": "1.0", + "rules": [{ + "conditions": [], + "type": "endpoint", + "endpoint": { + "url": "https://example.com", + "properties": {} + } + }], + "parameters": {} + }) + service TestService { + operations: [SayHello], + version: "1" + } + @endpoint(hostPrefix: "test123.{greeting}.") + operation SayHello { + input: SayHelloInput + } + structure SayHelloInput { + @required + @hostLabel + greeting: String + } + """.asSmithyModel() + clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + val moduleName = clientCodegenContext.moduleUseName() + rustCrate.integrationTest("test_endpoint_prefix") { + Attribute.TokioTest.render(this) + rustTemplate( + """ + async fn test_endpoint_prefix() { + use #{aws_smithy_client}::test_connection::capture_request; + use aws_smithy_http::body::SdkBody; + use aws_smithy_http::endpoint::EndpointPrefix; + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; + use aws_smithy_types::config_bag::ConfigBag; + use std::sync::atomic::{AtomicU32, Ordering}; + use std::sync::{Arc, Mutex}; + use $moduleName::{ + config::interceptors::BeforeTransmitInterceptorContextRef, + config::Interceptor, + error::DisplayErrorContext, + {Client, Config}, + }; + + ##[derive(Clone, Debug, Default)] + struct TestInterceptor { + called: Arc, + last_endpoint_prefix: Arc>>, + } + impl Interceptor for TestInterceptor { + fn read_before_transmit( + &self, + _context: &BeforeTransmitInterceptorContextRef<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + self.called.fetch_add(1, Ordering::Relaxed); + if let Some(prefix) = cfg.load::() { + self.last_endpoint_prefix + .lock() + .unwrap() + .replace(prefix.clone()); + } + Ok(()) + } + } + + let (conn, _r) = capture_request(Some( + http::Response::builder() + .status(200) + .body(SdkBody::from("")) + .unwrap(), + )); + let interceptor = TestInterceptor::default(); + let config = Config::builder() + .http_connector(conn) + .interceptor(interceptor.clone()) + .build(); + let client = Client::from_conf(config); + let err = dbg!(client.say_hello().greeting("hey there!").send().await) + .expect_err("the endpoint should be invalid since it has an exclamation mark in it"); + let err_fmt = format!("{}", DisplayErrorContext(err)); + assert!( + err_fmt.contains("endpoint prefix could not be built"), + "expected '{}' to contain 'endpoint prefix could not be built'", + err_fmt + ); + + assert!( + interceptor.called.load(Ordering::Relaxed) == 0, + "the interceptor should not have been called since endpoint resolution failed" + ); + + dbg!(client.say_hello().greeting("hello").send().await) + .expect("hello is a valid endpoint prefix"); + assert!( + interceptor.called.load(Ordering::Relaxed) == 1, + "the interceptor should have been called" + ); + assert_eq!( + "test123.hello.", + interceptor + .last_endpoint_prefix + .lock() + .unwrap() + .clone() + .unwrap() + .as_str() + ); + } + """, + "aws_smithy_client" to CargoDependency.smithyClient(clientCodegenContext.runtimeConfig) + .toDevDependency().withFeature("test-util").toType(), + ) + } + } + } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorMiddlewareTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorMiddlewareTest.kt new file mode 100644 index 0000000000..daea27a0b8 --- /dev/null +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorMiddlewareTest.kt @@ -0,0 +1,363 @@ +/* + * 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.protocol + +import io.kotest.matchers.string.shouldContain +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import software.amazon.smithy.aws.traits.protocols.RestJson1Trait +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.model.shapes.ShapeId +import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext +import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator +import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBoundProtocolTraitImplGenerator +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings +import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter +import software.amazon.smithy.rust.codegen.core.rustlang.escape +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.smithy.CodegenContext +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.AdditionalPayloadContext +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator +import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport +import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory +import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap +import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson +import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel +import software.amazon.smithy.rust.codegen.core.util.CommandError +import software.amazon.smithy.rust.codegen.core.util.dq +import software.amazon.smithy.rust.codegen.core.util.outputShape +import java.nio.file.Path + +private class TestProtocolPayloadGenerator(private val body: String) : ProtocolPayloadGenerator { + override fun payloadMetadata(operationShape: OperationShape, additionalPayloadContext: AdditionalPayloadContext) = + ProtocolPayloadGenerator.PayloadMetadata(takesOwnership = false) + + override fun generatePayload( + writer: RustWriter, + shapeName: String, + operationShape: OperationShape, + additionalPayloadContext: AdditionalPayloadContext, + ) { + writer.writeWithNoFormatting(body) + } +} + +private class TestProtocolTraitImplGenerator( + private val codegenContext: ClientCodegenContext, + private val correctResponse: String, +) : HttpBoundProtocolTraitImplGenerator(codegenContext, RestJson(codegenContext)) { + private val symbolProvider = codegenContext.symbolProvider + + override fun generateTraitImpls( + operationWriter: RustWriter, + operationShape: OperationShape, + customizations: List, + ) { + operationWriter.rustTemplate( + """ + impl #{parse_strict} for ${operationShape.id.name}{ + type Output = Result<#{Output}, #{Error}>; + fn parse(&self, _response: &#{Response}<#{Bytes}>) -> Self::Output { + ${operationWriter.escape(correctResponse)} + } + } + """, + "parse_strict" to RuntimeType.parseStrictResponse(codegenContext.runtimeConfig), + "Output" to symbolProvider.toSymbol(operationShape.outputShape(codegenContext.model)), + "Error" to symbolProvider.symbolForOperationError(operationShape), + "Response" to RuntimeType.HttpResponse, + "Bytes" to RuntimeType.Bytes, + ) + } +} + +private class TestProtocolMakeOperationGenerator( + codegenContext: CodegenContext, + protocol: Protocol, + body: String, + private val httpRequestBuilder: String, +) : MakeOperationGenerator( + codegenContext, + protocol, + TestProtocolPayloadGenerator(body), + public = true, + includeDefaultPayloadHeaders = true, +) { + override fun createHttpRequest(writer: RustWriter, operationShape: OperationShape) { + writer.rust("#T::new()", RuntimeType.HttpRequestBuilder) + writer.writeWithNoFormatting(httpRequestBuilder) + } +} + +// A stubbed test protocol to do enable testing intentionally broken protocols +private class TestProtocolGenerator( + codegenContext: ClientCodegenContext, + protocol: Protocol, + httpRequestBuilder: String, + body: String, + correctResponse: String, +) : OperationGenerator( + codegenContext, + protocol, + TestProtocolMakeOperationGenerator(codegenContext, protocol, body, httpRequestBuilder), + TestProtocolPayloadGenerator(body), + TestProtocolTraitImplGenerator(codegenContext, correctResponse), +) + +private class TestProtocolFactory( + private val httpRequestBuilder: String, + private val body: String, + private val correctResponse: String, +) : ProtocolGeneratorFactory { + override fun protocol(codegenContext: ClientCodegenContext): Protocol = RestJson(codegenContext) + + override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): OperationGenerator { + return TestProtocolGenerator( + codegenContext, + protocol(codegenContext), + httpRequestBuilder, + body, + correctResponse, + ) + } + + override fun support(): ProtocolSupport { + return ProtocolSupport( + requestSerialization = true, + requestBodySerialization = true, + responseDeserialization = true, + errorDeserialization = true, + requestDeserialization = false, + requestBodyDeserialization = false, + responseSerialization = false, + errorSerialization = false, + ) + } +} + +// TODO(enableNewSmithyRuntime): Delete this file/test (replaced by ProtocolTestGeneratorTest for the orchestrator) +class ProtocolTestGeneratorMiddlewareTest { + private val model = """ + namespace com.example + + use aws.protocols#restJson1 + use smithy.test#httpRequestTests + use smithy.test#httpResponseTests + + @restJson1 + service HelloService { + operations: [SayHello], + version: "1" + } + + @http(method: "POST", uri: "/") + @httpRequestTests([ + { + id: "say_hello", + protocol: restJson1, + params: { + "greeting": "Hi", + "name": "Teddy", + "query": "Hello there" + }, + method: "POST", + uri: "/", + queryParams: [ + "Hi=Hello%20there" + ], + forbidQueryParams: [ + "goodbye" + ], + requireQueryParams: ["required"], + headers: { + "X-Greeting": "Hi", + }, + body: "{\"name\": \"Teddy\"}", + bodyMediaType: "application/json" + } + ]) + @httpResponseTests([{ + id: "basic_response_test", + protocol: restJson1, + documentation: "Parses operations with empty JSON bodies", + body: "{\"value\": \"hey there!\"}", + params: {"value": "hey there!"}, + bodyMediaType: "application/json", + headers: {"Content-Type": "application/x-amz-json-1.1"}, + code: 200, + }]) + operation SayHello { + input: SayHelloInput, + output: SayHelloOutput, + errors: [BadRequest] + } + + structure SayHelloOutput { + value: String + } + + @error("client") + structure BadRequest { + message: String + } + + structure SayHelloInput { + @httpHeader("X-Greeting") + greeting: String, + + @httpQuery("Hi") + query: String, + + name: String + } + """.asSmithyModel() + private val correctBody = """{"name": "Teddy"}""" + + /** + * Creates a fake HTTP implementation for SayHello & generates the protocol test + * + * Returns the [Path] the service was generated at, suitable for running `cargo test` + */ + private fun testService( + httpRequestBuilder: String, + body: String = "${correctBody.dq()}.to_string()", + correctResponse: String = """Ok(crate::operation::say_hello::SayHelloOutput::builder().value("hey there!").build())""", + ): Path { + val codegenDecorator = object : ClientCodegenDecorator { + override val name: String = "mock" + override val order: Byte = 0 + override fun classpathDiscoverable(): Boolean = false + override fun protocols( + serviceId: ShapeId, + currentProtocols: ProtocolMap, + ): ProtocolMap = + // Intentionally replace the builtin implementation of RestJson1 with our fake protocol + mapOf(RestJson1Trait.ID to TestProtocolFactory(httpRequestBuilder, body, correctResponse)) + } + return clientIntegrationTest( + model, + TestCodegenSettings.middlewareModeTestParams, + additionalDecorators = listOf(codegenDecorator), + ) + } + + @Test + fun `passing e2e protocol request test`() { + testService( + """ + .uri("/?Hi=Hello%20there&required") + .header("X-Greeting", "Hi") + .method("POST") + """, + ) + } + + @Test + fun `test incorrect response parsing`() { + val err = assertThrows { + testService( + """ + .uri("/?Hi=Hello%20there&required") + .header("X-Greeting", "Hi") + .method("POST") + """, + correctResponse = "Ok(crate::operation::say_hello::SayHelloOutput::builder().build())", + ) + } + + err.message shouldContain "basic_response_test_response ... FAILED" + } + + @Test + fun `test invalid body`() { + val err = assertThrows { + testService( + """ + .uri("/?Hi=Hello%20there&required") + .header("X-Greeting", "Hi") + .method("POST") + """, + """"{}".to_string()""", + ) + } + + err.message shouldContain "say_hello_request ... FAILED" + err.message shouldContain "body did not match" + } + + @Test + fun `test invalid url parameter`() { + val err = assertThrows { + testService( + """ + .uri("/?Hi=INCORRECT&required") + .header("X-Greeting", "Hi") + .method("POST") + """, + ) + } + // Verify the test actually ran + err.message shouldContain "say_hello_request ... FAILED" + err.message shouldContain "missing query param" + } + + @Test + fun `test forbidden url parameter`() { + val err = assertThrows { + testService( + """ + .uri("/?goodbye&Hi=Hello%20there&required") + .header("X-Greeting", "Hi") + .method("POST") + """, + ) + } + // Verify the test actually ran + err.message shouldContain "say_hello_request ... FAILED" + err.message shouldContain "forbidden query param" + } + + @Test + fun `test required url parameter`() { + // Hard coded implementation for this 1 test + val err = assertThrows { + testService( + """ + .uri("/?Hi=Hello%20there") + .header("X-Greeting", "Hi") + .method("POST") + """, + ) + } + + // Verify the test actually ran + err.message shouldContain "say_hello_request ... FAILED" + err.message shouldContain "required query param missing" + } + + @Test + fun `invalid header`() { + val err = assertThrows { + testService( + """ + .uri("/?Hi=Hello%20there&required") + // should be "Hi" + .header("X-Greeting", "Hey") + .method("POST") + """, + ) + } + + err.message shouldContain "say_hello_request ... FAILED" + err.message shouldContain "invalid header value" + } +} diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt index 28ffe74561..1d1cddeca4 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt @@ -8,138 +8,112 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators.protocol import io.kotest.matchers.string.shouldContain import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -import software.amazon.smithy.aws.traits.protocols.RestJson1Trait import software.amazon.smithy.model.shapes.OperationShape -import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization -import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationGenerator -import software.amazon.smithy.rust.codegen.client.smithy.protocols.HttpBoundProtocolTraitImplGenerator +import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest -import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter -import software.amazon.smithy.rust.codegen.core.rustlang.escape -import software.amazon.smithy.rust.codegen.core.rustlang.rust +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.smithy.CodegenContext -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.AdditionalPayloadContext -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolPayloadGenerator -import software.amazon.smithy.rust.codegen.core.smithy.generators.protocol.ProtocolSupport -import software.amazon.smithy.rust.codegen.core.smithy.protocols.Protocol -import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolGeneratorFactory -import software.amazon.smithy.rust.codegen.core.smithy.protocols.ProtocolMap -import software.amazon.smithy.rust.codegen.core.smithy.protocols.RestJson +import software.amazon.smithy.rust.codegen.core.rustlang.writable +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.util.CommandError import software.amazon.smithy.rust.codegen.core.util.dq -import software.amazon.smithy.rust.codegen.core.util.outputShape import java.nio.file.Path - -private class TestProtocolPayloadGenerator(private val body: String) : ProtocolPayloadGenerator { - override fun payloadMetadata(operationShape: OperationShape, additionalPayloadContext: AdditionalPayloadContext) = - ProtocolPayloadGenerator.PayloadMetadata(takesOwnership = false) - - override fun generatePayload( - writer: RustWriter, - shapeName: String, - operationShape: OperationShape, - additionalPayloadContext: AdditionalPayloadContext, - ) { - writer.writeWithNoFormatting(body) - } -} - -private class TestProtocolTraitImplGenerator( - private val codegenContext: ClientCodegenContext, - private val correctResponse: String, -) : HttpBoundProtocolTraitImplGenerator(codegenContext, RestJson(codegenContext)) { - private val symbolProvider = codegenContext.symbolProvider - - override fun generateTraitImpls( - operationWriter: RustWriter, - operationShape: OperationShape, - customizations: List, - ) { - operationWriter.rustTemplate( - """ - impl #{parse_strict} for ${operationShape.id.name}{ - type Output = Result<#{Output}, #{Error}>; - fn parse(&self, _response: &#{Response}<#{Bytes}>) -> Self::Output { - ${operationWriter.escape(correctResponse)} - } +import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType as RT + +private class TestServiceRuntimePluginCustomization( + private val context: ClientCodegenContext, + private val fakeRequestBuilder: String, + private val fakeRequestBody: String, +) : ServiceRuntimePluginCustomization() { + override fun section(section: ServiceRuntimePluginSection): Writable = writable { + if (section is ServiceRuntimePluginSection.RegisterRuntimeComponents) { + val rc = context.runtimeConfig + section.registerInterceptor(rc, this) { + rustTemplate( + """ + { + ##[derive(::std::fmt::Debug)] + struct TestInterceptor; + impl #{Interceptor} for TestInterceptor { + fn modify_before_retry_loop( + &self, + context: &mut #{BeforeTransmitInterceptorContextMut}<'_>, + _rc: &#{RuntimeComponents}, + _cfg: &mut #{ConfigBag}, + ) -> #{Result}<(), #{BoxError}> { + // Replace the serialized request + let mut fake_req = ::http::Request::builder() + $fakeRequestBuilder + .body(#{SdkBody}::from($fakeRequestBody)) + .expect("valid request"); + ::std::mem::swap( + context.request_mut(), + &mut fake_req, + ); + Ok(()) + } + } + + TestInterceptor + } + """, + *preludeScope, + "BeforeTransmitInterceptorContextMut" to RT.beforeTransmitInterceptorContextMut(rc), + "BoxError" to RT.boxError(rc), + "ConfigBag" to RT.configBag(rc), + "Interceptor" to RT.interceptor(rc), + "RuntimeComponents" to RT.runtimeComponents(rc), + "SdkBody" to RT.sdkBody(rc), + ) } - """, - "parse_strict" to RuntimeType.parseStrictResponse(codegenContext.runtimeConfig), - "Output" to symbolProvider.toSymbol(operationShape.outputShape(codegenContext.model)), - "Error" to symbolProvider.symbolForOperationError(operationShape), - "Response" to RuntimeType.HttpResponse, - "Bytes" to RuntimeType.Bytes, - ) - } -} - -private class TestProtocolMakeOperationGenerator( - codegenContext: CodegenContext, - protocol: Protocol, - body: String, - private val httpRequestBuilder: String, -) : MakeOperationGenerator( - codegenContext, - protocol, - TestProtocolPayloadGenerator(body), - public = true, - includeDefaultPayloadHeaders = true, -) { - override fun createHttpRequest(writer: RustWriter, operationShape: OperationShape) { - writer.rust("#T::new()", RuntimeType.HttpRequestBuilder) - writer.writeWithNoFormatting(httpRequestBuilder) + } } } -// A stubbed test protocol to do enable testing intentionally broken protocols -private class TestProtocolGenerator( - codegenContext: ClientCodegenContext, - protocol: Protocol, - httpRequestBuilder: String, - body: String, - correctResponse: String, -) : OperationGenerator( - codegenContext, - protocol, - TestProtocolMakeOperationGenerator(codegenContext, protocol, body, httpRequestBuilder), - TestProtocolPayloadGenerator(body), - TestProtocolTraitImplGenerator(codegenContext, correctResponse), -) - -private class TestProtocolFactory( - private val httpRequestBuilder: String, - private val body: String, - private val correctResponse: String, -) : ProtocolGeneratorFactory { - override fun protocol(codegenContext: ClientCodegenContext): Protocol = RestJson(codegenContext) - - override fun buildProtocolGenerator(codegenContext: ClientCodegenContext): OperationGenerator { - return TestProtocolGenerator( - codegenContext, - protocol(codegenContext), - httpRequestBuilder, - body, - correctResponse, - ) - } - - override fun support(): ProtocolSupport { - return ProtocolSupport( - requestSerialization = true, - requestBodySerialization = true, - responseDeserialization = true, - errorDeserialization = true, - requestDeserialization = false, - requestBodyDeserialization = false, - responseSerialization = false, - errorSerialization = false, - ) +private class TestOperationCustomization( + private val context: ClientCodegenContext, + private val fakeOutput: String, +) : OperationCustomization() { + override fun section(section: OperationSection): Writable = writable { + val rc = context.runtimeConfig + if (section is OperationSection.AdditionalRuntimePluginConfig) { + rustTemplate( + """ + // Override the default response deserializer with our fake output + ##[derive(::std::fmt::Debug)] + struct TestDeser; + impl #{ResponseDeserializer} for TestDeser { + fn deserialize_nonstreaming( + &self, + _response: &#{HttpResponse}, + ) -> #{Result}<#{Output}, #{OrchestratorError}<#{Error}>> { + let fake_out: #{Result}< + crate::operation::say_hello::SayHelloOutput, + crate::operation::say_hello::SayHelloError, + > = $fakeOutput; + fake_out + .map(|o| #{TypedBox}::new(o).erase()) + .map_err(|e| #{OrchestratorError}::operation(#{TypedBox}::new(e).erase_error())) + } + } + cfg.store_put(#{DynResponseDeserializer}::new(TestDeser)); + """, + *preludeScope, + "DynResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::DynResponseDeserializer"), + "Error" to RT.smithyRuntimeApi(rc).resolve("client::interceptors::context::Error"), + "HttpResponse" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::HttpResponse"), + "OrchestratorError" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::OrchestratorError"), + "Output" to RT.smithyRuntimeApi(rc).resolve("client::interceptors::context::Output"), + "ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::ResponseDeserializer"), + "TypedBox" to RT.smithyTypes(rc).resolve("type_erasure::TypedBox"), + ) + } } } @@ -226,22 +200,34 @@ class ProtocolTestGeneratorTest { * Returns the [Path] the service was generated at, suitable for running `cargo test` */ private fun testService( - httpRequestBuilder: String, - body: String = "${correctBody.dq()}.to_string()", - correctResponse: String = """Ok(crate::operation::say_hello::SayHelloOutput::builder().value("hey there!").build())""", + fakeRequestBuilder: String, + fakeRequestBody: String = "${correctBody.dq()}.to_string()", + fakeOutput: String = """Ok(crate::operation::say_hello::SayHelloOutput::builder().value("hey there!").build())""", ): Path { val codegenDecorator = object : ClientCodegenDecorator { override val name: String = "mock" override val order: Byte = 0 override fun classpathDiscoverable(): Boolean = false - override fun protocols( - serviceId: ShapeId, - currentProtocols: ProtocolMap, - ): ProtocolMap = - // Intentionally replace the builtin implementation of RestJson1 with our fake protocol - mapOf(RestJson1Trait.ID to TestProtocolFactory(httpRequestBuilder, body, correctResponse)) + + override fun serviceRuntimePluginCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = baseCustomizations + TestServiceRuntimePluginCustomization( + codegenContext, + fakeRequestBuilder, + fakeRequestBody, + ) + + override fun operationCustomizations( + codegenContext: ClientCodegenContext, + operation: OperationShape, + baseCustomizations: List, + ): List = baseCustomizations + TestOperationCustomization(codegenContext, fakeOutput) } - return clientIntegrationTest(model, additionalDecorators = listOf(codegenDecorator)) + return clientIntegrationTest( + model, + additionalDecorators = listOf(codegenDecorator), + ) } @Test @@ -264,7 +250,7 @@ class ProtocolTestGeneratorTest { .header("X-Greeting", "Hi") .method("POST") """, - correctResponse = "Ok(crate::operation::say_hello::SayHelloOutput::builder().build())", + fakeOutput = "Ok(crate::operation::say_hello::SayHelloOutput::builder().build())", ) } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/AwsQueryCompatibleTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/AwsQueryCompatibleTest.kt index c5b2470dd9..e655777be7 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/AwsQueryCompatibleTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/protocols/AwsQueryCompatibleTest.kt @@ -6,10 +6,15 @@ package software.amazon.smithy.rust.codegen.client.smithy.protocols import org.junit.jupiter.api.Test +import software.amazon.smithy.model.shapes.OperationShape +import software.amazon.smithy.rust.codegen.client.testutil.TestCodegenSettings import software.amazon.smithy.rust.codegen.client.testutil.clientIntegrationTest +import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency 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.testutil.asSmithyModel import software.amazon.smithy.rust.codegen.core.testutil.integrationTest +import software.amazon.smithy.rust.codegen.core.util.lookup class AwsQueryCompatibleTest { @Test @@ -48,7 +53,168 @@ class AwsQueryCompatibleTest { } """.asSmithyModel() - clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + clientIntegrationTest(model) { context, rustCrate -> + val operation: OperationShape = context.model.lookup("test#SomeOperation") + rustCrate.withModule(context.symbolProvider.moduleForShape(operation)) { + rustTemplate( + """ + ##[cfg(test)] + ##[#{tokio}::test] + async fn should_parse_code_and_type_fields() { + use #{smithy_client}::test_connection::infallible_connection_fn; + use aws_smithy_http::body::SdkBody; + + let response = |_: http::Request| { + http::Response::builder() + .header( + "x-amzn-query-error", + http::HeaderValue::from_static("AWS.SimpleQueueService.NonExistentQueue;Sender"), + ) + .status(400) + .body( + SdkBody::from( + r##"{ + "__type": "com.amazonaws.sqs##QueueDoesNotExist", + "message": "Some user-visible message" + }"## + ) + ) + .unwrap() + }; + let client = crate::Client::from_conf( + crate::Config::builder() + .http_connector(infallible_connection_fn(response)) + .endpoint_url("http://localhost:1234") + .build() + ); + let error = dbg!(client.some_operation().send().await).err().unwrap().into_service_error(); + assert_eq!( + Some("AWS.SimpleQueueService.NonExistentQueue"), + error.meta().code(), + ); + assert_eq!(Some("Sender"), error.meta().extra("type")); + } + """, + "smithy_client" to CargoDependency.smithyClient(context.runtimeConfig) + .toDevDependency().withFeature("test-util").toType(), + "tokio" to CargoDependency.Tokio.toType(), + ) + } + } + } + + @Test + fun `aws-query-compatible json without aws query error should allow for retrieving error code from payload`() { + val model = """ + namespace test + use aws.protocols#awsJson1_0 + use aws.protocols#awsQueryCompatible + + @awsQueryCompatible + @awsJson1_0 + service TestService { + version: "2023-02-20", + operations: [SomeOperation] + } + + operation SomeOperation { + input: SomeOperationInputOutput, + output: SomeOperationInputOutput, + errors: [InvalidThingException], + } + + structure SomeOperationInputOutput { + a: String, + b: Integer + } + + @error("client") + structure InvalidThingException { + message: String + } + """.asSmithyModel() + + clientIntegrationTest(model) { context, rustCrate -> + val operation: OperationShape = context.model.lookup("test#SomeOperation") + rustCrate.withModule(context.symbolProvider.moduleForShape(operation)) { + rustTemplate( + """ + ##[cfg(test)] + ##[#{tokio}::test] + async fn should_parse_code_from_payload() { + use #{smithy_client}::test_connection::infallible_connection_fn; + use aws_smithy_http::body::SdkBody; + + let response = |_: http::Request| { + http::Response::builder() + .status(400) + .body( + SdkBody::from( + r##"{ + "__type": "com.amazonaws.sqs##QueueDoesNotExist", + "message": "Some user-visible message" + }"##, + ) + ) + .unwrap() + }; + let client = crate::Client::from_conf( + crate::Config::builder() + .http_connector(infallible_connection_fn(response)) + .endpoint_url("http://localhost:1234") + .build() + ); + let error = dbg!(client.some_operation().send().await).err().unwrap().into_service_error(); + assert_eq!(Some("QueueDoesNotExist"), error.meta().code()); + assert_eq!(None, error.meta().extra("type")); + } + """, + "smithy_client" to CargoDependency.smithyClient(context.runtimeConfig) + .toDevDependency().withFeature("test-util").toType(), + "tokio" to CargoDependency.Tokio.toType(), + ) + } + } + } + + // TODO(enableNewSmithyRuntimeCleanup): Delete this test (replaced above for orchestrator) + @Test + fun `middleware - aws-query-compatible json with aws query error should allow for retrieving error code and type from custom header`() { + val model = """ + namespace test + use aws.protocols#awsJson1_0 + use aws.protocols#awsQueryCompatible + use aws.protocols#awsQueryError + + @awsQueryCompatible + @awsJson1_0 + service TestService { + version: "2023-02-20", + operations: [SomeOperation] + } + + operation SomeOperation { + input: SomeOperationInputOutput, + output: SomeOperationInputOutput, + errors: [InvalidThingException], + } + + structure SomeOperationInputOutput { + a: String, + b: Integer + } + + @awsQueryError( + code: "InvalidThing", + httpResponseCode: 400, + ) + @error("client") + structure InvalidThingException { + message: String + } + """.asSmithyModel() + + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("should_parse_code_and_type_fields") { rust( @@ -87,8 +253,9 @@ class AwsQueryCompatibleTest { } } + // TODO(enableNewSmithyRuntimeCleanup): Delete this test (replaced above for orchestrator) @Test - fun `aws-query-compatible json without aws query error should allow for retrieving error code from payload`() { + fun `middleware - aws-query-compatible json without aws query error should allow for retrieving error code from payload`() { val model = """ namespace test use aws.protocols#awsJson1_0 @@ -118,7 +285,7 @@ class AwsQueryCompatibleTest { } """.asSmithyModel() - clientIntegrationTest(model) { clientCodegenContext, rustCrate -> + clientIntegrationTest(model, TestCodegenSettings.middlewareModeTestParams) { clientCodegenContext, rustCrate -> val moduleName = clientCodegenContext.moduleUseName() rustCrate.integrationTest("should_parse_code_from_payload") { rust( diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index 8dad31b91c..8b03d9656c 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -226,7 +226,7 @@ data class CargoDependency( val Url: CargoDependency = CargoDependency("url", CratesIo("2.3.1")) val Bytes: CargoDependency = CargoDependency("bytes", CratesIo("1.0.0")) val BytesUtils: CargoDependency = CargoDependency("bytes-utils", CratesIo("0.1.0")) - val FastRand: CargoDependency = CargoDependency("fastrand", CratesIo("1.8.0")) + val FastRand: CargoDependency = CargoDependency("fastrand", CratesIo("2.0.0")) val Hex: CargoDependency = CargoDependency("hex", CratesIo("0.4.3")) val Http: CargoDependency = CargoDependency("http", CratesIo("0.2.9")) val HttpBody: CargoDependency = CargoDependency("http-body", CratesIo("0.4.4")) diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index fbd4ed69dd..d3b7bd7420 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -338,6 +338,8 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag") fun configBagAccessors(runtimeConfig: RuntimeConfig): RuntimeType = smithyRuntimeApi(runtimeConfig).resolve("client::config_bag_accessors::ConfigBagAccessors") + fun runtimeComponents(runtimeConfig: RuntimeConfig) = + smithyRuntimeApi(runtimeConfig).resolve("client::runtime_components::RuntimeComponents") fun runtimeComponentsBuilder(runtimeConfig: RuntimeConfig) = smithyRuntimeApi(runtimeConfig).resolve("client::runtime_components::RuntimeComponentsBuilder") fun runtimePlugins(runtimeConfig: RuntimeConfig): RuntimeType = diff --git a/examples/pokemon-service-common/Cargo.toml b/examples/pokemon-service-common/Cargo.toml index d0012e178b..f2c86eee0e 100644 --- a/examples/pokemon-service-common/Cargo.toml +++ b/examples/pokemon-service-common/Cargo.toml @@ -21,3 +21,7 @@ aws-smithy-http = { path = "../../rust-runtime/aws-smithy-http" } aws-smithy-http-server = { path = "../../rust-runtime/aws-smithy-http-server" } pokemon-service-client = { path = "../pokemon-service-client" } pokemon-service-server-sdk = { path = "../pokemon-service-server-sdk" } + +[dev-dependencies] +aws-smithy-client = { path = "../../rust-runtime/aws-smithy-client", features = ["test-util"] } +aws-smithy-runtime = { path = "../../rust-runtime/aws-smithy-runtime", features = ["test-util"] } diff --git a/examples/pokemon-service-common/src/lib.rs b/examples/pokemon-service-common/src/lib.rs index d8618e0cce..cb5c4bd604 100644 --- a/examples/pokemon-service-common/src/lib.rs +++ b/examples/pokemon-service-common/src/lib.rs @@ -16,12 +16,9 @@ use std::{ use async_stream::stream; use aws_smithy_client::{conns, hyper_ext::Adapter}; -use aws_smithy_http::{body::SdkBody, byte_stream::ByteStream, operation::Request}; +use aws_smithy_http::{body::SdkBody, byte_stream::ByteStream}; use aws_smithy_http_server::Extension; -use http::{ - uri::{Authority, Scheme}, - Uri, -}; +use http::Uri; use pokemon_service_server_sdk::{ error, input, model, model::CapturingPayload, output, types::Blob, }; @@ -38,21 +35,6 @@ const PIKACHU_ITALIAN_FLAVOR_TEXT: &str = const PIKACHU_JAPANESE_FLAVOR_TEXT: &str = "ほっぺたの りょうがわに ちいさい でんきぶくろを もつ。ピンチのときに ほうでんする。"; -/// Rewrites the base URL of a request -pub fn rewrite_base_url( - scheme: Scheme, - authority: Authority, -) -> impl Fn(Request) -> Request + Clone { - move |mut req| { - let http_req = req.http_mut(); - let mut uri_parts = http_req.uri().clone().into_parts(); - uri_parts.authority = Some(authority.clone()); - uri_parts.scheme = Some(scheme.clone()); - *http_req.uri_mut() = Uri::from_parts(uri_parts).expect("failed to create uri from parts"); - req - } -} - /// Kills [`Child`] process when dropped. #[derive(Debug)] #[must_use] diff --git a/examples/pokemon-service-common/tests/plugins_execution_order.rs b/examples/pokemon-service-common/tests/plugins_execution_order.rs index 7a768cb005..fb5f0fb4d7 100644 --- a/examples/pokemon-service-common/tests/plugins_execution_order.rs +++ b/examples/pokemon-service-common/tests/plugins_execution_order.rs @@ -14,7 +14,8 @@ use aws_smithy_http::body::SdkBody; use aws_smithy_http_server::plugin::{HttpMarker, HttpPlugins, IdentityPlugin, Plugin}; use tower::{Layer, Service}; -use pokemon_service_client::{operation::do_nothing::DoNothingInput, Config}; +use aws_smithy_client::test_connection::capture_request; +use pokemon_service_client::{Client, Config}; use pokemon_service_common::do_nothing; trait OperationExt { @@ -43,13 +44,17 @@ async fn plugin_layers_are_executed_in_registration_order() { ) .do_nothing(do_nothing) .build_unchecked(); - let request = DoNothingInput::builder() - .build() - .unwrap() - .make_operation(&Config::builder().build()) - .await - .unwrap() - .into_http(); + + let request = { + let (conn, rcvr) = capture_request(None); + let config = Config::builder() + .http_connector(conn) + .endpoint_url("http://localhost:1234") + .build(); + Client::from_conf(config).do_nothing().send().await.unwrap(); + rcvr.expect_request() + }; + app.call(request).await.unwrap(); let output_guard = output.lock().unwrap(); diff --git a/examples/pokemon-service-tls/tests/common/mod.rs b/examples/pokemon-service-tls/tests/common/mod.rs index 5eb9d804ba..c56c5fe59f 100644 --- a/examples/pokemon-service-tls/tests/common/mod.rs +++ b/examples/pokemon-service-tls/tests/common/mod.rs @@ -3,18 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::{fs::File, io::BufReader, process::Command, str::FromStr, time::Duration}; +use std::{fs::File, io::BufReader, process::Command, time::Duration}; use assert_cmd::prelude::*; -use aws_smithy_client::{ - erase::{DynConnector, DynMiddleware}, - hyper_ext::Adapter, -}; -use hyper::http::uri::{Authority, Scheme}; +use aws_smithy_client::hyper_ext::Adapter; use tokio::time::sleep; -use pokemon_service_client::{Builder, Client, Config}; -use pokemon_service_common::{rewrite_base_url, ChildDrop}; +use pokemon_service_client::{Client, Config}; +use pokemon_service_common::ChildDrop; use pokemon_service_tls::{DEFAULT_DOMAIN, DEFAULT_PORT, DEFAULT_TEST_CERT}; pub async fn run_server() -> ChildDrop { @@ -28,7 +24,7 @@ pub async fn run_server() -> ChildDrop { // Returns a client that only talks through https and http2 connections. // It is useful in testing whether our server can talk to http2. -pub fn client_http2_only() -> Client> { +pub fn client_http2_only() -> Client { // Create custom cert store and add our test certificate to prevent unknown cert issues. let mut reader = BufReader::new(File::open(DEFAULT_TEST_CERT).expect("could not open certificate")); @@ -47,12 +43,9 @@ pub fn client_http2_only() -> Client> .enable_http2() .build(); - let authority = Authority::from_str(&format!("{DEFAULT_DOMAIN}:{DEFAULT_PORT}")) - .expect("could not parse authority"); - let raw_client = Builder::new() - .connector(DynConnector::new(Adapter::builder().build(connector))) - .middleware_fn(rewrite_base_url(Scheme::HTTPS, authority)) - .build_dyn(); - let config = Config::builder().build(); - Client::with_config(raw_client, config) + let config = Config::builder() + .http_connector(Adapter::builder().build(connector)) + .endpoint_url(format!("https://{DEFAULT_DOMAIN}:{DEFAULT_PORT}")) + .build(); + Client::from_conf(config) } diff --git a/examples/pokemon-service/tests/common/mod.rs b/examples/pokemon-service/tests/common/mod.rs index d10430bfbd..2d58f4a975 100644 --- a/examples/pokemon-service/tests/common/mod.rs +++ b/examples/pokemon-service/tests/common/mod.rs @@ -3,16 +3,14 @@ * SPDX-License-Identifier: Apache-2.0 */ -use std::{process::Command, str::FromStr, time::Duration}; +use std::{process::Command, time::Duration}; use assert_cmd::prelude::*; -use aws_smithy_client::erase::{DynConnector, DynMiddleware}; -use hyper::http::uri::{Authority, Scheme}; use tokio::time::sleep; use pokemon_service::{DEFAULT_ADDRESS, DEFAULT_PORT}; -use pokemon_service_client::{Builder, Client, Config}; -use pokemon_service_common::{rewrite_base_url, ChildDrop}; +use pokemon_service_client::{Client, Config}; +use pokemon_service_common::ChildDrop; pub async fn run_server() -> ChildDrop { let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); @@ -27,13 +25,9 @@ pub fn base_url() -> String { format!("http://{DEFAULT_ADDRESS}:{DEFAULT_PORT}") } -pub fn client() -> Client> { - let authority = Authority::from_str(&format!("{DEFAULT_ADDRESS}:{DEFAULT_PORT}")) - .expect("could not parse authority"); - let raw_client = Builder::new() - .rustls_connector(Default::default()) - .middleware_fn(rewrite_base_url(Scheme::HTTP, authority)) - .build_dyn(); - let config = Config::builder().build(); - Client::with_config(raw_client, config) +pub fn client() -> Client { + let config = Config::builder() + .endpoint_url(format!("http://{DEFAULT_ADDRESS}:{DEFAULT_PORT}")) + .build(); + Client::from_conf(config) } diff --git a/examples/python/pokemon-service-test/tests/helpers.rs b/examples/python/pokemon-service-test/tests/helpers.rs index 23050a3da3..708e17fe88 100644 --- a/examples/python/pokemon-service-test/tests/helpers.rs +++ b/examples/python/pokemon-service-test/tests/helpers.rs @@ -8,19 +8,15 @@ use std::io::BufReader; use std::process::Command; use std::time::Duration; -use aws_smithy_client::{erase::DynConnector, hyper_ext::Adapter}; -use aws_smithy_http::operation::Request; +use aws_smithy_client::hyper_ext::Adapter; use command_group::{CommandGroup, GroupChild}; -use pokemon_service_client::{Builder, Client, Config}; +use pokemon_service_client::{Client, Config}; use tokio::time; const TEST_KEY: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/testdata/localhost.key"); const TEST_CERT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/testdata/localhost.crt"); -pub type PokemonClient = Client< - aws_smithy_client::erase::DynConnector, - aws_smithy_client::erase::DynMiddleware, ->; +pub type PokemonClient = Client; enum PokemonServiceVariant { Http, @@ -94,12 +90,8 @@ impl Drop for PokemonService { #[allow(dead_code)] pub fn client() -> PokemonClient { let base_url = PokemonServiceVariant::Http.base_url(); - let raw_client = Builder::new() - .rustls_connector(Default::default()) - .middleware_fn(rewrite_base_url(base_url)) - .build_dyn(); - let config = Config::builder().build(); - Client::with_config(raw_client, config) + let config = Config::builder().endpoint_url(base_url).build(); + Client::from_conf(config) } #[allow(dead_code)] @@ -122,19 +114,9 @@ pub fn http2_client() -> PokemonClient { .build(); let base_url = PokemonServiceVariant::Http2.base_url(); - let raw_client = Builder::new() - .connector(DynConnector::new(Adapter::builder().build(connector))) - .middleware_fn(rewrite_base_url(base_url)) - .build_dyn(); - let config = Config::builder().build(); - Client::with_config(raw_client, config) -} - -fn rewrite_base_url(base_url: &'static str) -> impl Fn(Request) -> Request + Clone { - move |mut req| { - let http_req = req.http_mut(); - let uri = format!("{base_url}{}", http_req.uri().path()); - *http_req.uri_mut() = uri.parse().unwrap(); - req - } + let config = Config::builder() + .http_connector(Adapter::builder().build(connector)) + .endpoint_url(base_url) + .build(); + Client::from_conf(config) } diff --git a/rust-runtime/aws-smithy-client/Cargo.toml b/rust-runtime/aws-smithy-client/Cargo.toml index 8d6ac25eaf..826ce5eb3d 100644 --- a/rust-runtime/aws-smithy-client/Cargo.toml +++ b/rust-runtime/aws-smithy-client/Cargo.toml @@ -23,7 +23,7 @@ aws-smithy-http-tower = { path = "../aws-smithy-http-tower" } aws-smithy-protocol-test = { path = "../aws-smithy-protocol-test", optional = true } aws-smithy-types = { path = "../aws-smithy-types" } bytes = "1" -fastrand = "1.4.0" +fastrand = "2.0.0" http = "0.2.3" http-body = "0.4.4" hyper = { version = "0.14.26", features = ["client", "http2", "http1", "tcp"], optional = true } diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index b481d7ff99..8f5dc864ee 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -21,7 +21,7 @@ aws-smithy-protocol-test = { path = "../aws-smithy-protocol-test", optional = tr aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api" } aws-smithy-types = { path = "../aws-smithy-types" } bytes = "1" -fastrand = "1.4" +fastrand = "2.0.0" http = "0.2.8" http-body = "0.4.5" once_cell = "1.18.0" diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 38b8852acb..30aa1666cb 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -335,7 +335,8 @@ async fn try_attempt( let request = ctx.take_request().expect("set during serialization"); trace!(request = ?request, "transmitting request"); let connector = halt_on_err!([ctx] => runtime_components.connector().ok_or_else(|| - OrchestratorError::other("a connector is required to send requests") + OrchestratorError::other("No HTTP connector was available to send this request. \ + Enable the `rustls` crate feature or set a connector to fix this.") )); connector.call(request).await.map_err(|err| { match err.downcast() { diff --git a/rust-runtime/inlineable/Cargo.toml b/rust-runtime/inlineable/Cargo.toml index 5e7351291f..74bbcdebc0 100644 --- a/rust-runtime/inlineable/Cargo.toml +++ b/rust-runtime/inlineable/Cargo.toml @@ -26,7 +26,7 @@ aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api" } aws-smithy-types = { path = "../aws-smithy-types" } aws-smithy-xml = { path = "../aws-smithy-xml" } bytes = "1" -fastrand = "1" +fastrand = "2.0.0" futures-util = "0.3" http = "0.2.1" md-5 = "0.10.0" diff --git a/tools/ci-scripts/check-aws-sdk-middleware-impl b/tools/ci-scripts/check-aws-sdk-middleware-impl new file mode 100755 index 0000000000..ea6cfe85df --- /dev/null +++ b/tools/ci-scripts/check-aws-sdk-middleware-impl @@ -0,0 +1,27 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +# This script tests the SDK smoke test services against the middleware implementation + +C_YELLOW='\033[1;33m' +C_RESET='\033[0m' + +set -eu +cd smithy-rs + +./gradlew aws:sdk:assemble -Psmithy.runtime.mode=middleware + +cd aws/sdk/build/aws-sdk/sdk +for service in */; do + pushd "${service}" + echo -e "${C_YELLOW}# Running 'cargo test --all-features' on '${service}'${C_RESET}" + RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_middleware_mode" cargo test --all-features --all-targets --no-fail-fast + echo -e "${C_YELLOW}# Running 'cargo clippy --all-features' on '${service}'${C_RESET}" + RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_middleware_mode" cargo clippy --all-features + popd +done + +echo "SUCCESS" diff --git a/tools/ci-scripts/check-aws-sdk-orchestrator-impl b/tools/ci-scripts/check-aws-sdk-orchestrator-impl deleted file mode 100755 index b86ebf3f0f..0000000000 --- a/tools/ci-scripts/check-aws-sdk-orchestrator-impl +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -# -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 -# - -# This script tests the SDK smoke test services against the orchestrator implementation - -C_YELLOW='\033[1;33m' -C_RESET='\033[0m' - -set -eu -cd smithy-rs - -services_that_pass_tests=(\ - "aws-config"\ - "config"\ - "dynamodb"\ - "ec2"\ - "ecs"\ - "glacier"\ - "iam"\ - "kms"\ - "lambda"\ - "polly"\ - "qldbsession"\ - "route53"\ - "s3"\ - "s3control"\ - "sso"\ - "sts"\ - "timestreamquery"\ - "timestreamwrite"\ - "transcribestreaming"\ -) - -./gradlew aws:sdk:assemble -Psmithy.runtime.mode=orchestrator - -cd aws/sdk/build/aws-sdk/sdk -for service in "${services_that_pass_tests[@]}"; do - pushd "${service}" - echo -e "${C_YELLOW}# Running 'cargo test --all-features' on '${service}'${C_RESET}" - RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo test --all-features --all-targets --no-fail-fast - echo -e "${C_YELLOW}# Running 'cargo clippy --all-features' on '${service}'${C_RESET}" - RUSTFLAGS="${RUSTFLAGS:-} --cfg aws_sdk_orchestrator_mode" cargo clippy --all-features - popd -done - -echo "SUCCESS" From 255127a1149ecb471cc75e246287f17778561b2f Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 20 Jul 2023 11:13:34 -0700 Subject: [PATCH 219/253] Make the SDK invocation ID generator configurable (#2860) This PR makes it possible to configure the invocation ID generator in config. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-runtime/src/invocation_id.rs | 24 +++---- .../smithy/rustsdk/InvocationIdDecorator.kt | 71 +++++++++++++++++++ .../test/kotlin/SdkCodegenIntegrationTest.kt | 70 +++++++++--------- .../rustsdk/InvocationIdDecoratorTest.kt | 61 ++++++++++++++++ 4 files changed, 180 insertions(+), 46 deletions(-) create mode 100644 aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecoratorTest.kt diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 3d2a2854ba..16d3fc9811 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -6,13 +6,13 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use fastrand::Rng; use http::{HeaderName, HeaderValue}; use std::fmt::Debug; +use std::sync::{Arc, Mutex}; -use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; -use fastrand::Rng; -use std::sync::Mutex; #[cfg(feature = "test-util")] pub use test_util::{NoInvocationIdGenerator, PredefinedInvocationIdGenerator}; @@ -27,23 +27,23 @@ pub trait InvocationIdGenerator: Debug + Send + Sync { } /// Dynamic dispatch implementation of [`InvocationIdGenerator`] -#[derive(Debug)] -pub struct DynInvocationIdGenerator(Box); +#[derive(Clone, Debug)] +pub struct SharedInvocationIdGenerator(Arc); -impl DynInvocationIdGenerator { - /// Creates a new [`DynInvocationIdGenerator`]. +impl SharedInvocationIdGenerator { + /// Creates a new [`SharedInvocationIdGenerator`]. pub fn new(gen: impl InvocationIdGenerator + 'static) -> Self { - Self(Box::new(gen)) + Self(Arc::new(gen)) } } -impl InvocationIdGenerator for DynInvocationIdGenerator { +impl InvocationIdGenerator for SharedInvocationIdGenerator { fn generate(&self) -> Result, BoxError> { self.0.generate() } } -impl Storable for DynInvocationIdGenerator { +impl Storable for SharedInvocationIdGenerator { type Storer = StoreReplace; } @@ -100,7 +100,7 @@ impl Interceptor for InvocationIdInterceptor { cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let gen = cfg - .load::() + .load::() .map(|gen| gen as &dyn InvocationIdGenerator) .unwrap_or(&self.default); if let Some(id) = gen.generate()? { @@ -264,7 +264,7 @@ mod tests { let mut cfg = ConfigBag::base(); let mut layer = Layer::new("test"); - layer.store_put(DynInvocationIdGenerator::new( + layer.store_put(SharedInvocationIdGenerator::new( PredefinedInvocationIdGenerator::new(vec![InvocationId::new( "the-best-invocation-id".into(), )]), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt index 9b9fcccb49..1fdbfcb06f 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecorator.kt @@ -9,14 +9,19 @@ import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ConfigCustomization +import software.amazon.smithy.rust.codegen.client.smithy.generators.config.ServiceConfig import software.amazon.smithy.rust.codegen.core.rustlang.Writable +import software.amazon.smithy.rust.codegen.core.rustlang.docs 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.Companion.preludeScope import software.amazon.smithy.rust.codegen.core.util.letIf class InvocationIdDecorator : ClientCodegenDecorator { override val name: String get() = "InvocationIdDecorator" override val order: Byte get() = 0 + override fun serviceRuntimePluginCustomizations( codegenContext: ClientCodegenContext, baseCustomizations: List, @@ -24,6 +29,14 @@ class InvocationIdDecorator : ClientCodegenDecorator { baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { it + listOf(InvocationIdRuntimePluginCustomization(codegenContext)) } + + override fun configCustomizations( + codegenContext: ClientCodegenContext, + baseCustomizations: List, + ): List = + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + listOf(InvocationIdConfigCustomization(codegenContext)) + } } private class InvocationIdRuntimePluginCustomization( @@ -43,3 +56,61 @@ private class InvocationIdRuntimePluginCustomization( } } } + +const val GENERATOR_DOCS: String = + "The invocation ID generator generates ID values for the `amz-sdk-invocation-id` header. " + + "By default, this will be a random UUID. Overriding it may be useful in tests that " + + "examine the HTTP request and need to be deterministic." + +private class InvocationIdConfigCustomization( + codegenContext: ClientCodegenContext, +) : ConfigCustomization() { + private val awsRuntime = AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig) + private val codegenScope = arrayOf( + *preludeScope, + "InvocationIdGenerator" to awsRuntime.resolve("invocation_id::InvocationIdGenerator"), + "SharedInvocationIdGenerator" to awsRuntime.resolve("invocation_id::SharedInvocationIdGenerator"), + ) + + override fun section(section: ServiceConfig): Writable = writable { + when (section) { + is ServiceConfig.BuilderImpl -> { + docs("Overrides the default invocation ID generator.\n\n$GENERATOR_DOCS") + rustTemplate( + """ + pub fn invocation_id_generator(mut self, gen: impl #{InvocationIdGenerator} + 'static) -> Self { + self.set_invocation_id_generator(#{Some}(#{SharedInvocationIdGenerator}::new(gen))); + self + } + """, + *codegenScope, + ) + + docs("Overrides the default invocation ID generator.\n\n$GENERATOR_DOCS") + rustTemplate( + """ + pub fn set_invocation_id_generator(&mut self, gen: #{Option}<#{SharedInvocationIdGenerator}>) -> &mut Self { + self.config.store_or_unset(gen); + self + } + """, + *codegenScope, + ) + } + + is ServiceConfig.ConfigImpl -> { + docs("Returns the invocation ID generator if one was given in config.\n\n$GENERATOR_DOCS") + rustTemplate( + """ + pub fn invocation_id_generator(&self) -> #{Option}<#{SharedInvocationIdGenerator}> { + self.config.load::<#{SharedInvocationIdGenerator}>().cloned() + } + """, + *codegenScope, + ) + } + + else -> {} + } + } +} diff --git a/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt b/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt index fde2f32ae7..8ece4a5aa1 100644 --- a/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt @@ -8,41 +8,43 @@ import software.amazon.smithy.rust.codegen.core.testutil.asSmithyModel import software.amazon.smithy.rustsdk.awsSdkIntegrationTest class SdkCodegenIntegrationTest { - val model = """ - namespace test - - use aws.api#service - use aws.auth#sigv4 - use aws.protocols#restJson1 - use smithy.rules#endpointRuleSet - - @service(sdkId: "dontcare") - @restJson1 - @sigv4(name: "dontcare") - @auth([sigv4]) - @endpointRuleSet({ - "version": "1.0", - "rules": [{ "type": "endpoint", "conditions": [], "endpoint": { "url": "https://example.com" } }], - "parameters": { - "Region": { "required": false, "type": "String", "builtIn": "AWS::Region" }, + companion object { + val model = """ + namespace test + + use aws.api#service + use aws.auth#sigv4 + use aws.protocols#restJson1 + use smithy.rules#endpointRuleSet + + @service(sdkId: "dontcare") + @restJson1 + @sigv4(name: "dontcare") + @auth([sigv4]) + @endpointRuleSet({ + "version": "1.0", + "rules": [{ "type": "endpoint", "conditions": [], "endpoint": { "url": "https://example.com" } }], + "parameters": { + "Region": { "required": false, "type": "String", "builtIn": "AWS::Region" }, + } + }) + service TestService { + version: "2023-01-01", + operations: [SomeOperation] + } + + structure SomeOutput { + someAttribute: Long, + someVal: String } - }) - service TestService { - version: "2023-01-01", - operations: [SomeOperation] - } - - structure SomeOutput { - someAttribute: Long, - someVal: String - } - - @http(uri: "/SomeOperation", method: "GET") - @optionalAuth - operation SomeOperation { - output: SomeOutput - } - """.asSmithyModel() + + @http(uri: "/SomeOperation", method: "GET") + @optionalAuth + operation SomeOperation { + output: SomeOutput + } + """.asSmithyModel() + } @Test fun smokeTestSdkCodegen() { diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecoratorTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecoratorTest.kt new file mode 100644 index 0000000000..857139c9f9 --- /dev/null +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecoratorTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +package software.amazon.smithy.rustsdk + +import SdkCodegenIntegrationTest +import org.junit.jupiter.api.Test +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.integrationTest + +class InvocationIdDecoratorTest { + @Test + fun customInvocationIdGenerator() { + awsSdkIntegrationTest(SdkCodegenIntegrationTest.model) { context, rustCrate -> + val rc = context.runtimeConfig + val moduleName = context.moduleUseName() + rustCrate.integrationTest("custom_invocation_id") { + rustTemplate( + """ + ##[#{tokio}::test] + async fn custom_invocation_id() { + ##[derive(::std::fmt::Debug)] + struct TestIdGen; + impl #{InvocationIdGenerator} for TestIdGen { + fn generate(&self) -> #{Result}<#{Option}<#{InvocationId}>, #{BoxError}> { + #{Ok}(#{Some}(#{InvocationId}::new("custom".into()))) + } + } + + let (conn, rx) = #{capture_request}(None); + let config = $moduleName::Config::builder() + .http_connector(conn) + .invocation_id_generator(TestIdGen) + .build(); + assert!(config.invocation_id_generator().is_some()); + + let client = $moduleName::Client::from_conf(config); + + let _ = client.some_operation().send().await; + let request = rx.expect_request(); + assert_eq!("custom", request.headers().get("amz-sdk-invocation-id").unwrap()); + } + """, + *preludeScope, + "tokio" to CargoDependency.Tokio.toType(), + "InvocationIdGenerator" to AwsRuntimeType.awsRuntime(rc) + .resolve("invocation_id::InvocationIdGenerator"), + "InvocationId" to AwsRuntimeType.awsRuntime(rc) + .resolve("invocation_id::InvocationId"), + "BoxError" to RuntimeType.boxError(rc), + "capture_request" to RuntimeType.captureRequest(rc), + ) + } + } + } +} From 7f714db5328dcbf3d386ae9baf9d5af9d2cb7670 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 20 Jul 2023 12:09:51 -0700 Subject: [PATCH 220/253] Improve docs on config bag and type erasure (#2862) This PR improves the docs on the aws-smithy-types `config_bag` and `type_erasure` modules. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-smithy-types/src/config_bag.rs | 241 ++++++++++++++---- .../src/config_bag/storable.rs | 6 + rust-runtime/aws-smithy-types/src/lib.rs | 4 - .../aws-smithy-types/src/type_erasure.rs | 33 ++- 4 files changed, 230 insertions(+), 54 deletions(-) diff --git a/rust-runtime/aws-smithy-types/src/config_bag.rs b/rust-runtime/aws-smithy-types/src/config_bag.rs index cf17108f2c..f2bcc01d4c 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag.rs @@ -3,12 +3,133 @@ * SPDX-License-Identifier: Apache-2.0 */ -//! Layered Configuration Bag Structure +//! Layers and layered bags of configuration data. +//! +//! The [`ConfigBag`](crate::config_bag::ConfigBag) structure is used to store and pass around configuration for client operations. +//! Interacting with it may be required in order to write an `Interceptor` or `RuntimePlugin` to +//! customize a client. +//! +//! A `ConfigBag` is essentially a stack of several immutable and sharable layers, with a single _mutable_ layer at +//! the top of the stack that is called "interceptor state". The intent of this last mutable layer is to allow for +//! more performant mutation of config within the execution of an operation. +//! +//! There are three separate layer types to be aware of when using a `ConfigBag`: +//! 1. [`Layer`](crate::config_bag::Layer) - A mutable layer. This is usually only used for adding config +//! to the `ConfigBag`, but is also used for the interceptor state. +//! 2. [`CloneableLayer`](crate::config_bag::CloneableLayer) - Identical to `Layer`, except that it requires +//! `Clone` bounds on the items added to it so that it can be deep cloned. Can be converted to a `Layer` +//! while retaining the cloneability of its items such that the resulting layer could be cloned as long as +//! nothing else is added to it later. A `Layer` cannot be converted back into a `CloneableLayer`. +//! 3. [`FrozenLayer`](crate::config_bag::FrozenLayer) - Basically an [`Arc`](std::sync::Arc) wrapper around +//! a `Layer`. This wrapper is used to make the layer immutable, and to make it shareable between multiple +//! `ConfigBag` instances. The frozen layer can be converted back to a `Layer` if there is only a single reference to it. +//! +//! All of `Layer`, `CloneableLayer`, `FrozenLayer`, and `ConfigBag` are considered to be "bag" types. +//! That is, they store arbitrary types so long as they implement the [`Storable`](crate::config_bag::Storable) trait. +//! +//! A `Storable` requires a `Storer` to be configured, and the storer allows for storables to be stored +//! in two different modes: +//! 1. [`StoreReplace`](crate::config_bag::StoreReplace) - Only one value of this type is allowed in a bag, and +//! calling [`store_put()`](crate::config_bag::Layer::store_put) multiple times will replace the existing value +//! in the bag. Calling [`load::()`](crate::config_bag::Layer::load) returns exactly one value, if present. +//! 2. [`StoreAppend`](crate::config_bag::StoreAppend) - Multiple values of this type are allowed in a bag, and +//! calling [`store_append()`](crate::config_bag::Layer::store_append) will add an additional value of this type +//! to the bag. Calling [`load::()`](crate::config_bag::Layer::load) returns an iterator over multiple values. +//! +//! # Examples +//! +//! Creating a storable data type with `StoreReplace`: +//! +//! ```no_run +//! use aws_smithy_types::config_bag::{Storable, StoreReplace}; +//! +//! #[derive(Debug)] +//! struct SomeDataType { +//! some_data: String, +//! } +//! impl Storable for SomeDataType { +//! type Storer = StoreReplace; +//! } +//! ``` +//! +//! Creating a storable data type with `StoreAppend`: +//! +//! ```no_run +//! use aws_smithy_types::config_bag::{Storable, StoreAppend}; +//! +//! #[derive(Debug)] +//! struct SomeDataType { +//! some_data: String, +//! } +//! impl Storable for SomeDataType { +//! type Storer = StoreAppend; +//! } +//! ``` +//! +//! Storing a storable in a bag when it is configured for `StoreReplace`: +//! +//! ```no_run +//! # use aws_smithy_types::config_bag::{Storable, StoreReplace}; +//! # #[derive(Clone, Debug)] +//! # struct SomeDataType { some_data: String } +//! # impl Storable for SomeDataType { type Storer = StoreReplace; } +//! use aws_smithy_types::config_bag::{CloneableLayer, Layer}; +//! +//! let mut layer = Layer::new("example"); +//! layer.store_put(SomeDataType { some_data: "some data".to_string() }); +//! +//! // `store_put` can be called again to replace the original value: +//! layer.store_put(SomeDataType { some_data: "replacement".to_string() }); +//! +//! // Note: `SomeDataType` below must implement `Clone` to work with `CloneableLayer` +//! let mut cloneable = CloneableLayer::new("example"); +//! cloneable.store_put(SomeDataType { some_data: "some data".to_string() }); +//! ``` +//! +//! Storing a storable in a bag when it is configured for `StoreAppend`: +//! +//! ```no_run +//! # use aws_smithy_types::config_bag::{Storable, StoreAppend}; +//! # #[derive(Clone, Debug)] +//! # struct SomeDataType { some_data: String } +//! # impl Storable for SomeDataType { type Storer = StoreAppend; } +//! use aws_smithy_types::config_bag::{CloneableLayer, Layer}; +//! +//! let mut layer = Layer::new("example"); +//! layer.store_append(SomeDataType { some_data: "1".to_string() }); +//! layer.store_append(SomeDataType { some_data: "2".to_string() }); +//! ``` +//! +//! Loading a `StoreReplace` value from a bag: +//! +//! ```no_run +//! # use aws_smithy_types::config_bag::{Storable, StoreReplace}; +//! # #[derive(Clone, Debug)] +//! # struct SomeDataType { some_data: String } +//! # impl Storable for SomeDataType { type Storer = StoreReplace; } +//! # use aws_smithy_types::config_bag::Layer; +//! # let layer = Layer::new("example"); +//! let maybe_value: Option<&SomeDataType> = layer.load::(); +//! ``` +//! +//! Loading a `StoreAppend` value from a bag: +//! +//! ```no_run +//! # use aws_smithy_types::config_bag::{Storable, StoreAppend}; +//! # #[derive(Clone, Debug)] +//! # struct SomeDataType { some_data: String } +//! # impl Storable for SomeDataType { type Storer = StoreAppend; } +//! # use aws_smithy_types::config_bag::Layer; +//! # let layer = Layer::new("example"); +//! let values: Vec = layer.load::().cloned().collect(); +//! +//! // or iterate over them directly: +//! for value in layer.load::() { +//! # let _ = value; +//! // ... +//! } +//! ``` //! -//! [`config_bag::ConfigBag`] represents the layered configuration structure -//! with the following properties: -//! 1. A new layer of configuration may be applied onto an existing configuration structure without modifying it or taking ownership. -//! 2. No lifetime shenanigans to deal with mod storable; mod typeid_map; @@ -25,13 +146,20 @@ use std::sync::Arc; pub use storable::{AppendItemIter, Storable, Store, StoreAppend, StoreReplace}; -/// [`FrozenLayer`] is the "locked" form of [`Layer`]. +/// [`FrozenLayer`] is the immutable and shareable form of [`Layer`]. /// -/// [`ConfigBag`] contains a ordered collection of [`FrozenLayer`] +/// See the [module docs](crate::config_bag) for more documentation. #[derive(Clone, Debug)] #[must_use] pub struct FrozenLayer(Arc); +impl FrozenLayer { + /// Attempts to convert this bag directly into a [`Layer`] if no other references exist. + pub fn try_modify(self) -> Option { + Arc::try_unwrap(self.0).ok() + } +} + impl Deref for FrozenLayer { type Target = Layer; @@ -40,6 +168,12 @@ impl Deref for FrozenLayer { } } +impl From for FrozenLayer { + fn from(layer: Layer) -> Self { + FrozenLayer(Arc::new(layer)) + } +} + /// Private module to keep Value type while avoiding "private type in public latest" pub(crate) mod value { #[derive(Clone, Debug)] @@ -47,14 +181,14 @@ pub(crate) mod value { Set(T), ExplicitlyUnset(&'static str), } -} -use value::Value; -impl Default for Value { - fn default() -> Self { - Self::Set(Default::default()) + impl Default for Value { + fn default() -> Self { + Self::Set(Default::default()) + } } } +use value::Value; /// [`CloneableLayer`] allows itself to be cloned. This is useful when a type that implements /// `Clone` wishes to store a config layer. @@ -79,6 +213,8 @@ impl Default for Value { /// let mut layer = CloneableLayer::new("layer"); /// layer.store_put(MyNotCloneStruct); /// ``` +/// +/// See the [module docs](crate::config_bag) for more documentation. #[derive(Debug, Default)] pub struct CloneableLayer(Layer); @@ -113,6 +249,7 @@ impl CloneableLayer { Self(Layer::new(name)) } + /// Converts this layer into a frozen layer that can no longer be mutated. pub fn freeze(self) -> FrozenLayer { self.0.into() } @@ -191,6 +328,8 @@ impl CloneableLayer { } /// A named layer comprising a config bag +/// +/// See the [module docs](crate::config_bag) for more documentation. #[derive(Default)] pub struct Layer { name: Cow<'static, str>, @@ -236,10 +375,12 @@ impl Layer { self } - pub fn empty(&self) -> bool { + /// Returns true if this layer is empty. + pub fn is_empty(&self) -> bool { self.props.is_empty() } + /// Converts this layer into a frozen layer that can no longer be mutated. pub fn freeze(self) -> FrozenLayer { self.into() } @@ -253,6 +394,7 @@ impl Layer { } } + /// Changes the name of this layer. pub fn with_name(self, name: impl Into>) -> Self { Self { name: name.into(), @@ -373,19 +515,9 @@ impl Layer { } } -impl FrozenLayer { - /// Attempts to convert this bag directly into a [`ConfigBag`] if no other references exist - /// - /// This allows modifying the top layer of the bag. [`Self::add_layer`] may be - /// used to add a new layer to the bag. - pub fn try_modify(self) -> Option { - Arc::try_unwrap(self.0).ok() - } -} - -/// Layered Configuration Structure +/// Layered configuration structure /// -/// [`ConfigBag`] is the "unlocked" form of the bag. Only the top layer of the bag may be unlocked. +/// See the [module docs](crate::config_bag) for more documentation. #[must_use] pub struct ConfigBag { interceptor_state: Layer, @@ -408,10 +540,6 @@ impl Debug for ConfigBag { impl ConfigBag { /// Create a new config bag "base". - /// - /// Configuration may then be "layered" onto the base by calling - /// [`ConfigBag::store_put`], [`ConfigBag::store_or_unset`], [`ConfigBag::store_append`]. Layers - /// of configuration may then be "frozen" (made immutable) by calling [`ConfigBag::freeze`]. pub fn base() -> Self { ConfigBag { interceptor_state: Layer { @@ -422,7 +550,8 @@ impl ConfigBag { } } - pub fn of_layers(layers: Vec) -> Self { + /// Create a [`ConfigBag`] consisting of the given layers. + pub fn of_layers(layers: impl IntoIterator) -> Self { let mut bag = ConfigBag::base(); for layer in layers { bag.push_layer(layer); @@ -430,16 +559,19 @@ impl ConfigBag { bag } + /// Add the given layer to the config bag. pub fn push_layer(&mut self, layer: Layer) -> &mut Self { self.tail.push(layer.freeze()); self } + /// Add a frozen/shared layer to the config bag. pub fn push_shared_layer(&mut self, layer: FrozenLayer) -> &mut Self { self.tail.push(layer); self } + /// Return a reference to the mutable interceptor state. pub fn interceptor_state(&mut self) -> &mut Layer { &mut self.interceptor_state } @@ -516,20 +648,37 @@ impl ConfigBag { /// Add another layer to this configuration bag /// /// Hint: If you want to re-use this layer, call `freeze` first. + /// + /// # Examples /// ``` - /// /* - /// use aws_smithy_types::config_bag::{ConfigBag, Layer}; - /// let bag = ConfigBag::base(); - /// let first_layer = bag.with_fn("a", |b: &mut Layer| { b.put("a"); }); - /// let second_layer = first_layer.with_fn("other", |b: &mut Layer| { b.put(1i32); }); - /// // The number is only in the second layer - /// assert_eq!(first_layer.get::(), None); - /// assert_eq!(second_layer.get::(), Some(&1)); + /// use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; + /// + /// #[derive(Debug, Eq, PartialEq)] + /// struct ExampleStr(&'static str); + /// impl Storable for ExampleStr { + /// type Storer = StoreReplace; + /// } + /// + /// #[derive(Debug, Eq, PartialEq)] + /// struct ExampleInt(i32); + /// impl Storable for ExampleInt { + /// type Storer = StoreReplace; + /// } + /// + /// let mut bag = ConfigBag::base(); + /// bag = bag.with_fn("first", |layer: &mut Layer| { layer.store_put(ExampleStr("a")); }); + /// + /// // We can now load the example string out + /// assert_eq!(bag.load::(), Some(&ExampleStr("a"))); /// - /// // The string is in both layers - /// assert_eq!(first_layer.get::<&'static str>(), Some(&"a")); - /// assert_eq!(second_layer.get::<&'static str>(), Some(&"a")); - /// */ + /// // But there isn't a number stored in the bag yet + /// assert_eq!(bag.load::(), None); + /// + /// // Add a layer with an example int + /// bag = bag.with_fn("second", |layer: &mut Layer| { layer.store_put(ExampleInt(1)); }); + /// + /// // Now the example int can be retrieved + /// assert_eq!(bag.load::(), Some(&ExampleInt(1))); /// ``` pub fn with_fn( self, @@ -574,7 +723,7 @@ impl ConfigBag { } } -/// Iterator of items returned from config_bag +/// Iterator of items returned from [`ConfigBag`]. pub struct ItemIter<'a, T> { inner: BagIter<'a>, t: PhantomData, @@ -618,12 +767,6 @@ impl<'a> Iterator for BagIter<'a> { } } -impl From for FrozenLayer { - fn from(layer: Layer) -> Self { - FrozenLayer(Arc::new(layer)) - } -} - #[cfg(test)] mod test { use super::ConfigBag; diff --git a/rust-runtime/aws-smithy-types/src/config_bag/storable.rs b/rust-runtime/aws-smithy-types/src/config_bag/storable.rs index 33c0d2d434..3a70e422ea 100644 --- a/rust-runtime/aws-smithy-types/src/config_bag/storable.rs +++ b/rust-runtime/aws-smithy-types/src/config_bag/storable.rs @@ -22,6 +22,8 @@ pub trait Store: Sized + Send + Sync + 'static { } /// Store an item in the config bag by replacing the existing value +/// +/// See the [module docs](crate::config_bag) for more documentation. #[non_exhaustive] pub struct StoreReplace(PhantomData); @@ -32,6 +34,8 @@ impl Debug for StoreReplace { } /// Store an item in the config bag by effectively appending it to a list +/// +/// See the [module docs](crate::config_bag) for more documentation. #[non_exhaustive] pub struct StoreAppend(PhantomData); @@ -42,6 +46,8 @@ impl Debug for StoreAppend { } /// Trait that marks the implementing types as able to be stored in the config bag +/// +/// See the [module docs](crate::config_bag) for more documentation. pub trait Storable: Send + Sync + Debug + 'static { /// Specify how an item is stored in the config bag, e.g. [`StoreReplace`] and [`StoreAppend`] type Storer: Store; diff --git a/rust-runtime/aws-smithy-types/src/lib.rs b/rust-runtime/aws-smithy-types/src/lib.rs index 1d756d5802..76fb9010c3 100644 --- a/rust-runtime/aws-smithy-types/src/lib.rs +++ b/rust-runtime/aws-smithy-types/src/lib.rs @@ -14,8 +14,6 @@ unreachable_pub )] pub mod base64; -//TODO(enableNewSmithyRuntimeLaunch): Unhide this module when switching to the orchestrator -#[doc(hidden)] /// A typemap for storing configuration. pub mod config_bag; pub mod date_time; @@ -25,8 +23,6 @@ pub mod primitive; pub mod retry; pub mod timeout; -//TODO(enableNewSmithyRuntimeLaunch): Unhide this module when switching to the orchestrator -#[doc(hidden)] /// Utilities for type erasure. pub mod type_erasure; diff --git a/rust-runtime/aws-smithy-types/src/type_erasure.rs b/rust-runtime/aws-smithy-types/src/type_erasure.rs index 5f4cc08054..b4acdbb72d 100644 --- a/rust-runtime/aws-smithy-types/src/type_erasure.rs +++ b/rust-runtime/aws-smithy-types/src/type_erasure.rs @@ -98,7 +98,34 @@ impl DerefMut for TypedBox { } } -/// A new-type around `Box` +/// Abstraction over `Box` that provides `Debug` and optionally `Clone`. +/// +/// # Examples +/// +/// Creating a box: +/// ```no_run +/// use aws_smithy_types::type_erasure::{TypedBox, TypeErasedBox}; +/// +/// // Creating it directly: +/// let boxed = TypeErasedBox::new("some value"); +/// +/// // Creating it from a `TypedBox`: +/// let boxed = TypedBox::new("some value").erase(); +/// ``` +/// +/// Downcasting a box: +/// ```no_run +/// # use aws_smithy_types::type_erasure::{TypedBox, TypeErasedBox}; +/// # let boxed = TypedBox::new("some value".to_string()).erase(); +/// let value: Option<&String> = boxed.downcast_ref::(); +/// ``` +/// +/// Converting a box back into its value: +/// ``` +/// # use aws_smithy_types::type_erasure::{TypedBox, TypeErasedBox}; +/// let boxed = TypedBox::new("some value".to_string()).erase(); +/// let value: String = TypedBox::::assume_from(boxed).expect("it is a string").unwrap(); +/// ``` pub struct TypeErasedBox { field: Box, #[allow(clippy::type_complexity)] @@ -146,6 +173,7 @@ impl TypeErasedBox { } } + /// Create a new cloneable `TypeErasedBox` from the given `value`. pub fn new_with_clone(value: T) -> Self { let debug = |value: &Box, f: &mut fmt::Formatter<'_>| { fmt::Debug::fmt(value.downcast_ref::().expect("type-checked"), f) @@ -160,6 +188,9 @@ impl TypeErasedBox { } } + /// Attempts to clone this box. + /// + /// Note: this will only ever succeed if the box was created with [`TypeErasedBox::new_with_clone`]. pub fn try_clone(&self) -> Option { Some((self.clone.as_ref()?)(&self.field)) } From e91b393b5814cd5ceb6be5216d8b9554bcd4dca9 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 20 Jul 2023 16:30:24 -0400 Subject: [PATCH 221/253] Cleanup and add more tests for from/to nanos (#2655) ## Motivation and Context - Simplify no-op code in `as_nanos` - Add more proptests ## Testing Ran the tests ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- rust-runtime/aws-smithy-types/src/date_time/mod.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/rust-runtime/aws-smithy-types/src/date_time/mod.rs b/rust-runtime/aws-smithy-types/src/date_time/mod.rs index 8dcdc53067..b5e740cf5b 100644 --- a/rust-runtime/aws-smithy-types/src/date_time/mod.rs +++ b/rust-runtime/aws-smithy-types/src/date_time/mod.rs @@ -57,6 +57,9 @@ const NANOS_PER_SECOND_U32: u32 = 1_000_000_000; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub struct DateTime { pub(crate) seconds: i64, + /// Subsecond nanos always advances the wallclock time, even for times where seconds is negative + /// + /// Bigger subsecond nanos => later time pub(crate) subsecond_nanos: u32, } @@ -93,12 +96,7 @@ impl DateTime { /// Returns the number of nanoseconds since the Unix epoch that this `DateTime` represents. pub fn as_nanos(&self) -> i128 { let seconds = self.seconds as i128 * NANOS_PER_SECOND; - if seconds < 0 { - let adjusted_nanos = self.subsecond_nanos as i128 - NANOS_PER_SECOND; - seconds + NANOS_PER_SECOND + adjusted_nanos - } else { - seconds + self.subsecond_nanos as i128 - } + seconds + self.subsecond_nanos as i128 } /// Creates a `DateTime` from a number of seconds and a fractional second since the Unix epoch. From a57a719cfeb0313d6e61fc50a485dcd88c44cf19 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 20 Jul 2023 16:33:13 -0400 Subject: [PATCH 222/253] Add `sdk_ua_app_id` in addition to sdk-ua-app-id in profile file support (#2724) ## Motivation and Context The field name is being changed, but we need to maintain support for customers already using this feature ## Description ## Testing - existing tests ## Checklist - [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._ --- CHANGELOG.next.toml | 6 ++ .../src/default_provider/app_name.rs | 85 +++++++++++++++---- .../aws-config/src/environment/app_name.rs | 29 +------ .../aws-config/src/environment/mod.rs | 1 - .../aws-config/src/profile/app_name.rs | 81 ++---------------- 5 files changed, 84 insertions(+), 118 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 7a753f4790..9098765a55 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -652,3 +652,9 @@ message = "Public fields in structs are no longer marked as `#[doc(hidden)]`, an references = ["smithy-rs#2854"] meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } author = "ysaito1001" + +[[aws-sdk-rust]] +message = "The AppName property can now be set with `sdk_ua_app_id` in profile files. The old field, `sdk-ua-app-id`, is maintained for backwards compatibility." +references = ["smithy-rs#2724"] +meta = { "breaking" = false, "tada" = false, "bug" = false } +author = "rcoh" diff --git a/aws/rust-runtime/aws-config/src/default_provider/app_name.rs b/aws/rust-runtime/aws-config/src/default_provider/app_name.rs index f6fce3d730..7d91c6d363 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/app_name.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/app_name.rs @@ -3,16 +3,33 @@ * SPDX-License-Identifier: Apache-2.0 */ -use crate::environment::app_name::EnvironmentVariableAppNameProvider; -use crate::profile::app_name; use crate::provider_config::ProviderConfig; -use aws_types::app_name::AppName; +use crate::standard_property::{PropertyResolutionError, StandardProperty}; +use aws_smithy_types::error::display::DisplayErrorContext; +use aws_types::app_name::{AppName, InvalidAppName}; /// Default App Name Provider chain /// /// This provider will check the following sources in order: -/// 1. [Environment variables](EnvironmentVariableAppNameProvider) -/// 2. [Profile file](crate::profile::app_name::ProfileFileAppNameProvider) +/// 1. Environment variables: `AWS_SDK_UA_APP_ID` +/// 2. Profile files from the key `sdk_ua_app_id` +/// +#[doc = include_str!("../profile/location_of_profile_files.md")] +/// +/// # Examples +/// +/// **Loads "my-app" as the app name** +/// ```ini +/// [default] +/// sdk_ua_app_id = my-app +/// ``` +/// +/// **Loads "my-app" as the app name _if and only if_ the `AWS_PROFILE` environment variable +/// is set to `other`.** +/// ```ini +/// [profile other] +/// sdk_ua_app_id = my-app +/// ``` pub fn default_provider() -> Builder { Builder::default() } @@ -20,8 +37,7 @@ pub fn default_provider() -> Builder { /// Default provider builder for [`AppName`] #[derive(Debug, Default)] pub struct Builder { - env_provider: EnvironmentVariableAppNameProvider, - profile_file: app_name::Builder, + provider_config: ProviderConfig, } impl Builder { @@ -29,23 +45,43 @@ impl Builder { /// Configure the default chain /// /// Exposed for overriding the environment when unit-testing providers - pub fn configure(mut self, configuration: &ProviderConfig) -> Self { - self.env_provider = EnvironmentVariableAppNameProvider::new_with_env(configuration.env()); - self.profile_file = self.profile_file.configure(configuration); - self + pub fn configure(self, configuration: &ProviderConfig) -> Self { + Self { + provider_config: configuration.clone(), + } } /// Override the profile name used by this provider pub fn profile_name(mut self, name: &str) -> Self { - self.profile_file = self.profile_file.profile_name(name); + self.provider_config = self.provider_config.with_profile_name(name.to_string()); self } + async fn fallback_app_name( + &self, + ) -> Result, PropertyResolutionError> { + StandardProperty::new() + .profile("sdk-ua-app-id") + .validate(&self.provider_config, |name| AppName::new(name.to_string())) + .await + } + /// Build an [`AppName`] from the default chain pub async fn app_name(self) -> Option { - self.env_provider - .app_name() - .or(self.profile_file.build().app_name().await) + let standard = StandardProperty::new() + .env("AWS_SDK_UA_APP_ID") + .profile("sdk_ua_app_id") + .validate(&self.provider_config, |name| AppName::new(name.to_string())) + .await; + let with_fallback = match standard { + Ok(None) => self.fallback_app_name().await, + other => other, + }; + + with_fallback.map_err( + |err| tracing::warn!(err = %DisplayErrorContext(&err), "invalid value for App Name setting"), + ) + .unwrap_or(None) } } @@ -80,7 +116,7 @@ mod tests { // test that overriding profile_name on the root level is deprecated #[tokio::test] async fn profile_name_override() { - let fs = Fs::from_slice(&[("test_config", "[profile custom]\nsdk-ua-app-id = correct")]); + let fs = Fs::from_slice(&[("test_config", "[profile custom]\nsdk_ua_app_id = correct")]); let conf = crate::from_env() .configure( ProviderConfig::empty() @@ -101,6 +137,23 @@ mod tests { #[tokio::test] async fn load_from_profile() { + let fs = Fs::from_slice(&[("test_config", "[default]\nsdk_ua_app_id = correct")]); + let env = Env::from_slice(&[("AWS_CONFIG_FILE", "test_config")]); + let app_name = Builder::default() + .configure( + &ProviderConfig::empty() + .with_fs(fs) + .with_env(env) + .with_http_connector(no_traffic_connector()), + ) + .app_name() + .await; + + assert_eq!(Some(AppName::new("correct").unwrap()), app_name); + } + + #[tokio::test] + async fn load_from_profile_old_name() { let fs = Fs::from_slice(&[("test_config", "[default]\nsdk-ua-app-id = correct")]); let env = Env::from_slice(&[("AWS_CONFIG_FILE", "test_config")]); let app_name = Builder::default() diff --git a/aws/rust-runtime/aws-config/src/environment/app_name.rs b/aws/rust-runtime/aws-config/src/environment/app_name.rs index 79c3abb6ac..3abf0fdedb 100644 --- a/aws/rust-runtime/aws-config/src/environment/app_name.rs +++ b/aws/rust-runtime/aws-config/src/environment/app_name.rs @@ -9,10 +9,12 @@ use aws_types::os_shim_internal::Env; /// Load an app name from the `AWS_SDK_UA_APP_ID` environment variable. #[derive(Debug, Default)] +#[deprecated(note = "This is unused and will be removed in a future release.")] pub struct EnvironmentVariableAppNameProvider { env: Env, } +#[allow(deprecated)] impl EnvironmentVariableAppNameProvider { /// Create a new `EnvironmentVariableAppNameProvider` pub fn new() -> Self { @@ -42,30 +44,3 @@ impl EnvironmentVariableAppNameProvider { } } } - -#[cfg(test)] -mod tests { - use crate::environment::EnvironmentVariableAppNameProvider; - use aws_types::app_name::AppName; - use aws_types::os_shim_internal::Env; - use std::collections::HashMap; - - #[test] - fn env_var_not_set() { - let provider = EnvironmentVariableAppNameProvider::new_with_env(Env::from(HashMap::new())); - assert_eq!(None, provider.app_name()); - } - - #[test] - fn env_var_set() { - let provider = EnvironmentVariableAppNameProvider::new_with_env(Env::from( - vec![("AWS_SDK_UA_APP_ID".to_string(), "something".to_string())] - .into_iter() - .collect::>(), - )); - assert_eq!( - Some(AppName::new("something").unwrap()), - provider.app_name() - ); - } -} diff --git a/aws/rust-runtime/aws-config/src/environment/mod.rs b/aws/rust-runtime/aws-config/src/environment/mod.rs index 1191947287..dd2c7cba3b 100644 --- a/aws/rust-runtime/aws-config/src/environment/mod.rs +++ b/aws/rust-runtime/aws-config/src/environment/mod.rs @@ -8,7 +8,6 @@ /// Load app name from the environment pub mod app_name; -pub use app_name::EnvironmentVariableAppNameProvider; use std::error::Error; use std::fmt::{Display, Formatter}; diff --git a/aws/rust-runtime/aws-config/src/profile/app_name.rs b/aws/rust-runtime/aws-config/src/profile/app_name.rs index 80a5f192ca..715681dadf 100644 --- a/aws/rust-runtime/aws-config/src/profile/app_name.rs +++ b/aws/rust-runtime/aws-config/src/profile/app_name.rs @@ -33,10 +33,14 @@ use aws_types::app_name::AppName; /// /// This provider is part of the [default app name provider chain](crate::default_provider::app_name). #[derive(Debug, Default)] +#[deprecated( + note = "This is unused and is deprecated for backwards compatibility. It will be removed in a future release." +)] pub struct ProfileFileAppNameProvider { provider_config: ProviderConfig, } +#[allow(deprecated)] impl ProfileFileAppNameProvider { /// Create a new [ProfileFileAppNameProvider} /// @@ -67,12 +71,14 @@ impl ProfileFileAppNameProvider { /// Builder for [ProfileFileAppNameProvider] #[derive(Debug, Default)] +#[allow(deprecated)] pub struct Builder { config: Option, profile_override: Option, profile_files: Option, } +#[allow(deprecated)] impl Builder { /// Override the configuration for this provider pub fn configure(mut self, config: &ProviderConfig) -> Self { @@ -87,6 +93,7 @@ impl Builder { } /// Build a [ProfileFileAppNameProvider] from this builder + #[allow(deprecated)] pub fn build(self) -> ProfileFileAppNameProvider { let conf = self .config @@ -97,77 +104,3 @@ impl Builder { } } } - -#[cfg(test)] -mod tests { - use super::ProfileFileAppNameProvider; - use crate::provider_config::ProviderConfig; - use crate::test_case::no_traffic_connector; - use aws_sdk_sts::config::AppName; - use aws_types::os_shim_internal::{Env, Fs}; - use tracing_test::traced_test; - - fn provider_config(config_contents: &str) -> ProviderConfig { - let fs = Fs::from_slice(&[("test_config", config_contents)]); - let env = Env::from_slice(&[("AWS_CONFIG_FILE", "test_config")]); - ProviderConfig::empty() - .with_fs(fs) - .with_env(env) - .with_http_connector(no_traffic_connector()) - } - - fn default_provider(config_contents: &str) -> ProfileFileAppNameProvider { - ProfileFileAppNameProvider::builder() - .configure(&provider_config(config_contents)) - .build() - } - - #[tokio::test] - async fn no_app_name() { - assert_eq!(None, default_provider("[default]\n").app_name().await); - } - - #[tokio::test] - async fn app_name_default_profile() { - assert_eq!( - Some(AppName::new("test").unwrap()), - default_provider("[default]\nsdk-ua-app-id = test") - .app_name() - .await - ); - } - - #[tokio::test] - async fn app_name_other_profiles() { - let config = "\ - [default]\n\ - sdk-ua-app-id = test\n\ - \n\ - [profile other]\n\ - sdk-ua-app-id = bar\n - "; - assert_eq!( - Some(AppName::new("bar").unwrap()), - ProfileFileAppNameProvider::builder() - .profile_name("other") - .configure(&provider_config(config)) - .build() - .app_name() - .await - ); - } - - #[traced_test] - #[tokio::test] - async fn invalid_app_name() { - assert_eq!( - None, - default_provider("[default]\nsdk-ua-app-id = definitely invalid") - .app_name() - .await - ); - assert!(logs_contain( - "`sdk-ua-app-id` property `definitely invalid` was invalid" - )); - } -} From 7875278a2b1cd0be0b2fe72b3810f8d1d26abd60 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 20 Jul 2023 16:43:17 -0400 Subject: [PATCH 223/253] don't suppress unpublished examples (#2855) ## Motivation and Context The publish tool doesn't consider unpublished crates when computing the version tree for publish. This causes errors for examples that use shared crates. ## Description This adds support for considering unpublished crates when publishing. It allows unpublished crates to have unpublished dependencies but published crates can only have unpublished crates as dev dependencies. ## Testing - [x] build these exact changes into the docker image and test ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --- .../publisher/src/subcommand/fix_manifests.rs | 211 +++++++++++++----- .../src/subcommand/fix_manifests/validate.rs | 20 +- 2 files changed, 175 insertions(+), 56 deletions(-) diff --git a/tools/ci-build/publisher/src/subcommand/fix_manifests.rs b/tools/ci-build/publisher/src/subcommand/fix_manifests.rs index 910f5fd51c..2d34c1aba7 100644 --- a/tools/ci-build/publisher/src/subcommand/fix_manifests.rs +++ b/tools/ci-build/publisher/src/subcommand/fix_manifests.rs @@ -21,7 +21,7 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; use toml::value::Table; use toml::Value; -use tracing::info; +use tracing::{debug, info}; mod validate; @@ -72,6 +72,63 @@ struct Manifest { metadata: toml::Value, } +impl Manifest { + /// Returns the `publish` setting for a given crate + fn publish(&self) -> Result { + let value = self.metadata.get("package").and_then(|v| v.get("publish")); + match value { + None => Ok(true), + Some(value) => value + .as_bool() + .ok_or(anyhow::Error::msg("unexpected publish setting")), + } + } +} + +struct Versions(BTreeMap); +#[derive(Copy, Clone)] +enum FilterType { + AllCrates, + PublishedOnly, +} +struct VersionView<'a>(&'a Versions, FilterType); +impl VersionView<'_> { + fn get(&self, crate_name: &str) -> Option<&Version> { + let version = match (self.1, self.0 .0.get(crate_name)) { + (FilterType::AllCrates, version) => version, + (FilterType::PublishedOnly, v @ Some(VersionWithMetadata { publish: true, .. })) => v, + _ => None, + }; + version.map(|v| &v.version) + } + + fn all_crates(&self) -> Self { + VersionView(self.0, FilterType::AllCrates) + } +} + +impl Versions { + fn published(&self) -> VersionView { + VersionView(self, FilterType::PublishedOnly) + } + + fn published_crates(&self) -> impl Iterator + '_ { + self.0 + .iter() + .filter(|(_, v)| v.publish) + .map(|(k, v)| (k.as_str(), &v.version)) + } + + fn get(&self, crate_name: &str) -> Option<&Version> { + self.0.get(crate_name).map(|v| &v.version) + } +} + +struct VersionWithMetadata { + version: Version, + publish: bool, +} + async fn read_manifests(fs: Fs, manifest_paths: Vec) -> Result> { let mut result = Vec::new(); for path in manifest_paths { @@ -84,7 +141,7 @@ async fn read_manifests(fs: Fs, manifest_paths: Vec) -> Result Result> { +fn package_versions(manifests: &[Manifest]) -> Result { let mut versions = BTreeMap::new(); for manifest in manifests { // ignore workspace manifests @@ -92,15 +149,7 @@ fn package_versions(manifests: &[Manifest]) -> Result> Some(package) => package, None => continue, }; - // ignore non-publishable crates - if let Some(Value::Boolean(false)) = manifest - .metadata - .get("package") - .expect("checked above") - .get("publish") - { - continue; - } + let publish = manifest.publish()?; let name = package .get("name") .and_then(|name| name.as_str()) @@ -114,16 +163,12 @@ fn package_versions(manifests: &[Manifest]) -> Result> anyhow::Error::msg(format!("{:?} is missing a package version", manifest.path)) })?; let version = parse_version(&manifest.path, version)?; - versions.insert(name.into(), version); + versions.insert(name.into(), VersionWithMetadata { version, publish }); } - Ok(versions) + Ok(Versions(versions)) } -fn fix_dep_set( - versions: &BTreeMap, - key: &str, - metadata: &mut toml::Value, -) -> Result { +fn fix_dep_set(versions: &VersionView, key: &str, metadata: &mut Value) -> Result { let mut changed = 0; if let Some(dependencies) = metadata.as_table_mut().unwrap().get_mut(key) { if let Some(dependencies) = dependencies.as_table_mut() { @@ -143,11 +188,7 @@ fn fix_dep_set( Ok(changed) } -fn update_dep( - table: &mut Table, - dep_name: &str, - versions: &BTreeMap, -) -> Result { +fn update_dep(table: &mut Table, dep_name: &str, versions: &VersionView) -> Result { if !table.contains_key("path") { return Ok(0); } @@ -169,9 +210,10 @@ fn update_dep( } } -fn fix_dep_sets(versions: &BTreeMap, metadata: &mut toml::Value) -> Result { +fn fix_dep_sets(versions: &VersionView, metadata: &mut toml::Value) -> Result { let mut changed = fix_dep_set(versions, "dependencies", metadata)?; - changed += fix_dep_set(versions, "dev-dependencies", metadata)?; + // allow dev dependencies to be unpublished + changed += fix_dep_set(&versions.all_crates(), "dev-dependencies", metadata)?; changed += fix_dep_set(versions, "build-dependencies", metadata)?; Ok(changed) } @@ -202,45 +244,53 @@ fn conditionally_disallow_publish( // is not being run from CI, and disallow publish in that case. Also disallow // publishing of examples. if !is_github_actions || is_example { - if let Some(package) = metadata.as_table_mut().unwrap().get_mut("package") { - info!( - "Detected {}. Disallowing publish for {:?}.", - if is_example { "example" } else { "local build" }, - manifest_path, - ); - package - .as_table_mut() - .unwrap() - .insert("publish".into(), toml::Value::Boolean(false)); - return Ok(true); + if let Some(value) = set_publish_false(manifest_path, metadata, is_example) { + return Ok(value); } } Ok(false) } +fn set_publish_false(manifest_path: &Path, metadata: &mut Value, is_example: bool) -> Option { + if let Some(package) = metadata.as_table_mut().unwrap().get_mut("package") { + info!( + "Detected {}. Disallowing publish for {:?}.", + if is_example { "example" } else { "local build" }, + manifest_path, + ); + package + .as_table_mut() + .unwrap() + .insert("publish".into(), toml::Value::Boolean(false)); + return Some(true); + } + None +} + async fn fix_manifests( fs: Fs, - versions: &BTreeMap, + versions: &Versions, manifests: &mut Vec, mode: Mode, ) -> Result<()> { for manifest in manifests { let package_changed = conditionally_disallow_publish(&manifest.path, &mut manifest.metadata)?; - let dependencies_changed = fix_dep_sets(versions, &mut manifest.metadata)?; - if package_changed || dependencies_changed > 0 { + let num_deps_changed = fix_manifest(versions, manifest)?; + if package_changed || num_deps_changed > 0 { let contents = "# Code generated by software.amazon.smithy.rust.codegen.smithy-rs. DO NOT EDIT.\n" .to_string() + &toml::to_string(&manifest.metadata).with_context(|| { format!("failed to serialize to toml for {:?}", manifest.path) })?; + match mode { Mode::Execute => { fs.write_file(&manifest.path, contents.as_bytes()).await?; info!( "Changed {} dependencies in {:?}.", - dependencies_changed, manifest.path + num_deps_changed, manifest.path ); } Mode::Check => { @@ -255,10 +305,73 @@ async fn fix_manifests( Ok(()) } +fn fix_manifest(versions: &Versions, manifest: &mut Manifest) -> Result { + let mut view = versions.published(); + if !manifest.publish()? { + debug!(package = ?&manifest.path, "package has publishing disabled, allowing unpublished crates to be used"); + view = view.all_crates(); + } + fix_dep_sets(&view, &mut manifest.metadata) +} + #[cfg(test)] mod tests { use super::*; + fn make_versions<'a>(versions: impl Iterator) -> Versions { + let map = versions + .into_iter() + .map(|(name, version, publish)| { + let publish = *publish; + ( + name.to_string(), + VersionWithMetadata { + version: Version::parse(&version).unwrap(), + publish, + }, + ) + }) + .collect::>(); + + Versions(map) + } + + #[test] + fn unpublished_deps_cant_be_deps() { + let manifest = br#" + [package] + name = "test" + version = "1.2.0" + + [build-dependencies] + build_something = "1.3" + local_build_something = { path = "../local_build_something", version = "0.4.0-different" } + + [dev-dependencies] + dev_something = "1.1" + local_dev_something = { path = "../local_dev_something" } + + [dependencies] + something = "1.0" + local_something = { path = "../local_something" } + "#; + let metadata = toml::from_slice(manifest).unwrap(); + let mut manifest = Manifest { + path: "test".into(), + metadata, + }; + let versions = &[ + ("local_build_something", "0.2.0", true), + ("local_dev_something", "0.1.0", false), + ("local_something", "1.1.3", false), + ]; + let versions = make_versions(versions.iter()); + fix_manifest(&versions, &mut manifest).expect_err("depends on unpublished local something"); + set_publish_false(&manifest.path, &mut manifest.metadata, false).unwrap(); + fix_manifest(&versions, &mut manifest) + .expect("now it will work, the crate isn't published"); + } + #[test] fn test_fix_dep_sets() { let manifest = br#" @@ -283,16 +396,14 @@ mod tests { path: "test".into(), metadata, }; - let versions = vec![ - ("local_build_something", "0.2.0"), - ("local_dev_something", "0.1.0"), - ("local_something", "1.1.3"), - ] - .into_iter() - .map(|e| (e.0.to_string(), Version::parse(e.1).unwrap())) - .collect(); - - fix_dep_sets(&versions, &mut manifest.metadata).expect("success"); + let versions = &[ + ("local_build_something", "0.2.0", true), + ("local_dev_something", "0.1.0", false), + ("local_something", "1.1.3", true), + ]; + let versions = make_versions(versions.iter()); + + fix_dep_sets(&versions.published(), &mut manifest.metadata).expect("success"); let actual_deps = &manifest.metadata["dependencies"]; assert_eq!( diff --git a/tools/ci-build/publisher/src/subcommand/fix_manifests/validate.rs b/tools/ci-build/publisher/src/subcommand/fix_manifests/validate.rs index 53202468f3..40aa8f609a 100644 --- a/tools/ci-build/publisher/src/subcommand/fix_manifests/validate.rs +++ b/tools/ci-build/publisher/src/subcommand/fix_manifests/validate.rs @@ -5,10 +5,10 @@ use crate::fs::Fs; use crate::package::discover_and_validate_package_batches; +use crate::subcommand::fix_manifests::Versions; use anyhow::{anyhow, bail, Result}; use semver::Version; use smithy_rs_tool_common::package::PackageCategory; -use std::collections::BTreeMap; use std::path::Path; use tracing::info; @@ -17,7 +17,7 @@ use tracing::info; /// For now, this validates: /// - `aws-smithy-` prefixed versions match `aws-` (NOT `aws-sdk-`) prefixed versions pub(super) fn validate_before_fixes( - versions: &BTreeMap, + versions: &Versions, disable_version_number_validation: bool, ) -> Result<()> { // Later when we only generate independently versioned SDK crates, this flag can become permanent. @@ -30,7 +30,7 @@ pub(super) fn validate_before_fixes( .get("aws-smithy-types") .ok_or_else(|| anyhow!("`aws-smithy-types` crate missing"))?; - for (name, version) in versions { + for (name, version) in versions.published_crates() { let category = PackageCategory::from_package_name(name); if category == PackageCategory::SmithyRuntime || category == PackageCategory::AwsRuntime { confirm_version(name, expected_runtime_version, version)?; @@ -63,14 +63,22 @@ pub(super) async fn validate_after_fixes(location: &Path) -> Result<()> { #[cfg(test)] mod test { use super::*; + use crate::subcommand::fix_manifests::VersionWithMetadata; + use std::collections::BTreeMap; use std::str::FromStr; - fn versions(versions: &[(&'static str, &'static str)]) -> BTreeMap { + fn versions(versions: &[(&'static str, &'static str)]) -> Versions { let mut map = BTreeMap::new(); for (name, version) in versions { - map.insert((*name).into(), Version::from_str(version).unwrap()); + map.insert( + (*name).into(), + VersionWithMetadata { + version: Version::from_str(version).unwrap(), + publish: true, + }, + ); } - map + Versions(map) } #[track_caller] From 6aa585fa2d0add9d3ec910fa5fa9d1874e983d9e Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Thu, 20 Jul 2023 15:45:14 -0500 Subject: [PATCH 224/253] update generic clients to support user-configurable runtime plugins (#2864) _This PR also updates `pre-commit ktlint` runner. Now it won't spit out a bazillion debug logs when run_ ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .pre-commit-config.yaml | 2 +- aws/sdk/build.gradle.kts | 3 +- .../client/smithy/ClientCodegenContext.kt | 1 + .../client/smithy/ClientRustSettings.kt | 56 +++++++++---------- .../ClientRuntimeTypesReExportGenerator.kt | 11 ++++ .../config/ServiceConfigGenerator.kt | 53 ++++++++++-------- .../codegen/client/testutil/TestHelpers.kt | 3 + .../config/ServiceConfigGeneratorTest.kt | 27 ++++++++- .../rust/codegen/core/rustlang/RustType.kt | 1 + 9 files changed, 104 insertions(+), 53 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cce6e26327..a45d403a09 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: files: ^.*$ pass_filenames: false - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - rev: v2.6.0 + rev: v2.10.0 hooks: - id: pretty-format-kotlin args: [--autofix, --ktlint-version, 0.48.2] diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index 1c975f58e4..920cb7579e 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -105,7 +105,8 @@ fun generateSmithyBuild(services: AwsServices): String { "renameErrors": false, "debugMode": $debugMode, "eventStreamAllowList": [$eventStreamAllowListMembers], - "enableNewSmithyRuntime": "${getSmithyRuntimeMode()}" + "enableNewSmithyRuntime": "${getSmithyRuntimeMode()}", + "enableUserConfigurableRuntimePlugins": false }, "service": "${service.service}", "module": "$moduleName", diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt index 18db619823..7491b0e8e3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientCodegenContext.kt @@ -36,4 +36,5 @@ data class ClientCodegenContext( model, symbolProvider, moduleDocProvider, serviceShape, protocol, settings, CodegenTarget.CLIENT, ) { val smithyRuntimeMode: SmithyRuntimeMode get() = settings.codegenConfig.enableNewSmithyRuntime + val enableUserConfigurableRuntimePlugins: Boolean get() = settings.codegenConfig.enableUserConfigurableRuntimePlugins } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt index 0fec857bec..908c936be2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt @@ -74,28 +74,28 @@ data class ClientRustSettings( // TODO(enableNewSmithyRuntimeCleanup): Remove this mode after switching to the orchestrator enum class SmithyRuntimeMode { - Middleware, - BothDefaultMiddleware, - BothDefaultOrchestrator, - Orchestrator, + Middleware, BothDefaultMiddleware, BothDefaultOrchestrator, Orchestrator, ; val exclusivelyGenerateMiddleware: Boolean get() = generateMiddleware && !generateOrchestrator - val generateMiddleware: Boolean get() = when (this) { - Middleware, BothDefaultMiddleware, BothDefaultOrchestrator -> true - else -> false - } + val generateMiddleware: Boolean + get() = when (this) { + Middleware, BothDefaultMiddleware, BothDefaultOrchestrator -> true + else -> false + } - val generateOrchestrator: Boolean get() = when (this) { - Orchestrator, BothDefaultMiddleware, BothDefaultOrchestrator -> true - else -> false - } + val generateOrchestrator: Boolean + get() = when (this) { + Orchestrator, BothDefaultMiddleware, BothDefaultOrchestrator -> true + else -> false + } - val defaultToMiddleware: Boolean get() = when (this) { - Middleware, BothDefaultMiddleware -> true - else -> false - } + val defaultToMiddleware: Boolean + get() = when (this) { + Middleware, BothDefaultMiddleware -> true + else -> false + } val defaultToOrchestrator: Boolean get() = !defaultToMiddleware companion object { @@ -127,6 +127,7 @@ data class ClientCodegenConfig( val enableNewSmithyRuntime: SmithyRuntimeMode = defaultEnableNewSmithyRuntime, /** If true, adds `endpoint_url`/`set_endpoint_url` methods to the service config */ val includeEndpointUrlConfig: Boolean = defaultIncludeEndpointUrlConfig, + val enableUserConfigurableRuntimePlugins: Boolean = defaultEnableUserConfigurableRuntimePlugins, ) : CoreCodegenConfig( formatTimeoutSeconds, debugMode, ) { @@ -137,25 +138,24 @@ data class ClientCodegenConfig( private val defaultEventStreamAllowList: Set = emptySet() private val defaultEnableNewSmithyRuntime = SmithyRuntimeMode.Orchestrator private const val defaultIncludeEndpointUrlConfig = true + private const val defaultEnableUserConfigurableRuntimePlugins = true fun fromCodegenConfigAndNode(coreCodegenConfig: CoreCodegenConfig, node: Optional) = if (node.isPresent) { ClientCodegenConfig( formatTimeoutSeconds = coreCodegenConfig.formatTimeoutSeconds, debugMode = coreCodegenConfig.debugMode, - eventStreamAllowList = node.get().getArrayMember("eventStreamAllowList") - .map { array -> array.toList().mapNotNull { node -> node.asStringNode().orNull()?.value } } - .orNull()?.toSet() ?: defaultEventStreamAllowList, + eventStreamAllowList = node.get().getArrayMember("eventStreamAllowList").map { array -> + array.toList().mapNotNull { node -> + node.asStringNode().orNull()?.value + } + }.orNull()?.toSet() ?: defaultEventStreamAllowList, renameExceptions = node.get().getBooleanMemberOrDefault("renameErrors", defaultRenameExceptions), - includeFluentClient = node.get() - .getBooleanMemberOrDefault("includeFluentClient", defaultIncludeFluentClient), - addMessageToErrors = node.get() - .getBooleanMemberOrDefault("addMessageToErrors", defaultAddMessageToErrors), - enableNewSmithyRuntime = SmithyRuntimeMode.fromString( - node.get().getStringMemberOrDefault("enableNewSmithyRuntime", "middleware"), - ), - includeEndpointUrlConfig = node.get() - .getBooleanMemberOrDefault("includeEndpointUrlConfig", defaultIncludeEndpointUrlConfig), + includeFluentClient = node.get().getBooleanMemberOrDefault("includeFluentClient", defaultIncludeFluentClient), + addMessageToErrors = node.get().getBooleanMemberOrDefault("addMessageToErrors", defaultAddMessageToErrors), + enableNewSmithyRuntime = SmithyRuntimeMode.fromString(node.get().getStringMemberOrDefault("enableNewSmithyRuntime", "middleware")), + includeEndpointUrlConfig = node.get().getBooleanMemberOrDefault("includeEndpointUrlConfig", defaultIncludeEndpointUrlConfig), + enableUserConfigurableRuntimePlugins = node.get().getBooleanMemberOrDefault("userConfigurableRuntimePlugins", defaultEnableUserConfigurableRuntimePlugins), ) } else { ClientCodegenConfig( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt index 12fa3e264b..102400e511 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ClientRuntimeTypesReExportGenerator.kt @@ -34,6 +34,17 @@ class ClientRuntimeTypesReExportGenerator( "Interceptor" to RuntimeType.interceptor(rc), "SharedInterceptor" to RuntimeType.sharedInterceptor(rc), ) + + if (codegenContext.enableUserConfigurableRuntimePlugins) { + rustTemplate( + """ + pub use #{runtime_plugin}::{RuntimePlugin, SharedRuntimePlugin}; + pub use #{config_bag}::FrozenLayer; + """, + "runtime_plugin" to RuntimeType.smithyRuntimeApi(rc).resolve("client::runtime_plugin"), + "config_bag" to RuntimeType.smithyTypes(rc).resolve("config_bag"), + ) + } } rustCrate.withModule(ClientRustModule.endpoint(codegenContext)) { rustTemplate( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index 83f7c740b4..d732f4e1e2 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -19,7 +19,6 @@ import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.docs import software.amazon.smithy.rust.codegen.core.rustlang.docsOrFallback -import software.amazon.smithy.rust.codegen.core.rustlang.raw import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.rustBlock import software.amazon.smithy.rust.codegen.core.rustlang.rustBlockTemplate @@ -305,26 +304,29 @@ class ServiceConfigGenerator( } } - private val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig) + private val moduleUseName = codegenContext.moduleUseName() + private val runtimeMode = codegenContext.smithyRuntimeMode + private val runtimeConfig = codegenContext.runtimeConfig + private val enableUserConfigurableRuntimePlugins = codegenContext.enableUserConfigurableRuntimePlugins + private val smithyTypes = RuntimeType.smithyTypes(runtimeConfig) val codegenScope = arrayOf( *preludeScope, - "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), + "BoxError" to RuntimeType.boxError(runtimeConfig), "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), - "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), - "ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig), + "ConfigBag" to RuntimeType.configBag(runtimeConfig), + "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "Cow" to RuntimeType.Cow, "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "Layer" to smithyTypes.resolve("config_bag::Layer"), - "Resolver" to RuntimeType.smithyRuntime(codegenContext.runtimeConfig).resolve("client::config_override::Resolver"), - "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(codegenContext.runtimeConfig), - "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), - "SharedRuntimePlugin" to RuntimeType.sharedRuntimePlugin(codegenContext.runtimeConfig), + "Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"), + "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(runtimeConfig), + "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), + "SharedRuntimePlugin" to RuntimeType.sharedRuntimePlugin(runtimeConfig), + "runtime_plugin" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::runtime_plugin"), ) - private val moduleUseName = codegenContext.moduleUseName() - private val runtimeMode = codegenContext.smithyRuntimeMode fun render(writer: RustWriter) { - writer.docs("Service config.\n") + writer.docs("Configuration for a $moduleUseName service client.\n") customizations.forEach { it.section(ServiceConfig.ConfigStructAdditionalDocs)(writer) } @@ -394,9 +396,9 @@ class ServiceConfigGenerator( writer.docs("Builder for creating a `Config`.") if (runtimeMode.defaultToMiddleware) { - writer.raw("#[derive(Clone, Default)]") + Attribute(Attribute.derive(RuntimeType.Clone, RuntimeType.Default)).render(writer) } else { - writer.raw("#[derive(Clone, Debug)]") + Attribute(Attribute.derive(RuntimeType.Clone, RuntimeType.Debug)).render(writer) } writer.rustBlock("pub struct Builder") { if (runtimeMode.defaultToOrchestrator) { @@ -451,19 +453,25 @@ class ServiceConfigGenerator( it.section(ServiceConfig.BuilderImpl)(this) } - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { + val visibility = if (enableUserConfigurableRuntimePlugins) { "pub" } else { "pub(crate)" } + + docs("Adds a runtime plugin to the config.") + if (!enableUserConfigurableRuntimePlugins) { Attribute.AllowUnused.render(this) } rustTemplate( """ - /// Adds a runtime plugin to the config. - ##[allow(unused)] - pub(crate) fn runtime_plugin(mut self, plugin: impl #{RuntimePlugin} + 'static) -> Self { + $visibility fn runtime_plugin(mut self, plugin: impl #{RuntimePlugin} + 'static) -> Self { self.push_runtime_plugin(#{SharedRuntimePlugin}::new(plugin)); self } - - /// Adds a runtime plugin to the config. - ##[allow(unused)] - pub(crate) fn push_runtime_plugin(&mut self, plugin: #{SharedRuntimePlugin}) -> &mut Self { + """, + *codegenScope, + ) + docs("Adds a runtime plugin to the config.") + if (!enableUserConfigurableRuntimePlugins) { Attribute.AllowUnused.render(this) } + rustTemplate( + """ + $visibility fn push_runtime_plugin(&mut self, plugin: #{SharedRuntimePlugin}) -> &mut Self { self.runtime_plugins.push(plugin); self } @@ -528,6 +536,7 @@ class ServiceConfigGenerator( } } } + customizations.forEach { it.section(ServiceConfig.Extras)(writer) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt index 97464760e9..866cd7461f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestHelpers.kt @@ -94,6 +94,9 @@ fun testClientCodegenContext( fun ClientCodegenContext.withSmithyRuntimeMode(smithyRuntimeMode: SmithyRuntimeMode): ClientCodegenContext = copy(settings = settings.copy(codegenConfig = settings.codegenConfig.copy(enableNewSmithyRuntime = smithyRuntimeMode))) +fun ClientCodegenContext.withEnableUserConfigurableRuntimePlugins(enableUserConfigurableRuntimePlugins: Boolean): ClientCodegenContext = + copy(settings = settings.copy(codegenConfig = settings.codegenConfig.copy(enableUserConfigurableRuntimePlugins = enableUserConfigurableRuntimePlugins))) + fun TestWriterDelegator.clientRustSettings() = testClientRustSettings( service = ShapeId.from("fake#Fake"), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt index b41cff293c..9f8056931b 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt @@ -14,6 +14,7 @@ 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.SmithyRuntimeMode 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.testutil.withSmithyRuntimeMode import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -161,7 +162,9 @@ internal class ServiceConfigGeneratorTest { val model = "namespace empty".asSmithyModel() val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) - val codegenContext = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode) + val codegenContext = testClientCodegenContext(model) + .withSmithyRuntimeMode(smithyRuntimeMode) + .withEnableUserConfigurableRuntimePlugins(true) val sut = ServiceConfigGenerator(codegenContext, listOf(ServiceCustomizer(codegenContext))) val symbolProvider = codegenContext.symbolProvider val project = TestWorkspace.testProject(symbolProvider) @@ -176,6 +179,28 @@ internal class ServiceConfigGeneratorTest { 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 { + todo!("ExampleRuntimePlugin.config") + } + } + + let config = Config::builder() + .runtime_plugin(TestRuntimePlugin) + .build(); + assert_eq!(config.runtime_plugins.len(), 1); + """, + ) } else { unitTest( "set_config_fields", diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt index 3cb637b64b..798218ab96 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/RustType.kt @@ -509,6 +509,7 @@ class Attribute(val inner: Writable, val isDeriveHelper: Boolean = false) { val AllowNonSnakeCase = Attribute(allow("non_snake_case")) val AllowUnreachableCode = Attribute(allow("unreachable_code")) val AllowUnreachablePatterns = Attribute(allow("unreachable_patterns")) + val AllowUnused = Attribute(allow("unused")) val AllowUnusedImports = Attribute(allow("unused_imports")) val AllowUnusedMut = Attribute(allow("unused_mut")) val AllowUnusedVariables = Attribute(allow("unused_variables")) From d5674b8ad92fbffd9ffe31305428853668a2ed45 Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 21 Jul 2023 13:19:34 +0200 Subject: [PATCH 225/253] Adjust sSDK error message when using `@range` on floating point shapes (#2727) To point to a better issue https://github.com/awslabs/smithy-rs/issues/2007. Also let users know this is unlikely to ever get implemented. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../server/smithy/ValidateUnsupportedConstraints.kt | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt index 192facc2ba..4dfa97644e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/ValidateUnsupportedConstraints.kt @@ -61,10 +61,11 @@ private sealed class UnsupportedConstraintMessageKind { shape: Shape, constraintTrait: Trait, trackingIssue: String, + willSupport: Boolean = true, ) = buildMessage( "The ${shape.type} shape `${shape.id}` has the constraint trait `${constraintTrait.toShapeId()}` attached.", - willSupport = true, + willSupport, trackingIssue, ) @@ -104,7 +105,12 @@ private sealed class UnsupportedConstraintMessageKind { is UnsupportedRangeTraitOnShape -> LogMessage( level, - buildMessageShapeHasUnsupportedConstraintTrait(shape, rangeTrait, constraintTraitsUberIssue), + buildMessageShapeHasUnsupportedConstraintTrait( + shape, + rangeTrait, + willSupport = false, + trackingIssue = "https://github.com/awslabs/smithy-rs/issues/2007", + ), ) is UnsupportedUniqueItemsTraitOnShape -> LogMessage( @@ -250,7 +256,7 @@ fun validateUnsupportedConstraints( unsupportedConstraintOnNonErrorShapeReachableViaAnEventStreamSet + unsupportedConstraintErrorShapeReachableViaAnEventStreamSet // 3. Range trait used on unsupported shapes. - // TODO(https://github.com/awslabs/smithy-rs/issues/1401) + // TODO(https://github.com/awslabs/smithy-rs/issues/2007) val unsupportedRangeTraitOnShapeSet = walker .walkShapes(service) .asSequence() From e060135e0176677504c1d17dbbc5e6556aeee0ca Mon Sep 17 00:00:00 2001 From: david-perez Date: Fri, 21 Jul 2023 15:24:08 +0200 Subject: [PATCH 226/253] Move `alb_health_check` module out of the `plugin` module (#2865) The current module location is misleading, since you never want to run the layer as a plugin: plugins always run after routing, and the health check should be enacted before routing. Examples have been corrected, and a test has been added. ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --- CHANGELOG.next.toml | 6 +++ examples/pokemon-service/src/main.rs | 15 ++++---- examples/pokemon-service/tests/simple.rs | 28 ++++++++++++++ .../src/{plugin => layer}/alb_health_check.rs | 37 +++++++++++-------- .../aws-smithy-http-server/src/layer/mod.rs | 9 +++++ .../aws-smithy-http-server/src/lib.rs | 1 + .../src/plugin/either.rs | 2 + .../aws-smithy-http-server/src/plugin/mod.rs | 3 +- 8 files changed, 76 insertions(+), 25 deletions(-) rename rust-runtime/aws-smithy-http-server/src/{plugin => layer}/alb_health_check.rs (86%) create mode 100644 rust-runtime/aws-smithy-http-server/src/layer/mod.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 9098765a55..7b24ac49ef 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -658,3 +658,9 @@ message = "The AppName property can now be set with `sdk_ua_app_id` in profile f references = ["smithy-rs#2724"] meta = { "breaking" = false, "tada" = false, "bug" = false } author = "rcoh" + +[[smithy-rs]] +message = "The `alb_health_check` module has been moved out of the `plugin` module into a new `layer` module. ALB health checks should be enacted before routing, and plugins run after routing, so the module location was misleading. Examples have been corrected to reflect the intended application of the layer." +references = ["smithy-rs#2865"] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } +author = "david-perez" diff --git a/examples/pokemon-service/src/main.rs b/examples/pokemon-service/src/main.rs index b7566baa2e..227d778fc8 100644 --- a/examples/pokemon-service/src/main.rs +++ b/examples/pokemon-service/src/main.rs @@ -10,7 +10,8 @@ use std::{net::SocketAddr, sync::Arc}; use aws_smithy_http_server::{ extension::OperationExtensionExt, instrumentation::InstrumentExt, - plugin::{alb_health_check::AlbHealthCheckLayer, HttpPlugins, IdentityPlugin, Scoped}, + layer::alb_health_check::AlbHealthCheckLayer, + plugin::{HttpPlugins, IdentityPlugin, Scoped}, request::request_id::ServerRequestIdProviderLayer, AddExtensionLayer, }; @@ -61,11 +62,7 @@ pub async fn main() { // `Response::extensions`, or infer routing failure when it's missing. .insert_operation_extension() // Adds `tracing` spans and events to the request lifecycle. - .instrument() - // Handle `/ping` health check requests. - .layer(AlbHealthCheckLayer::from_handler("/ping", |_req| async { - StatusCode::OK - })); + .instrument(); let app = PokemonService::builder_with_plugins(plugins, IdentityPlugin) // Build a registry containing implementations to all the operations in the service. These @@ -84,7 +81,11 @@ pub async fn main() { let app = app // Setup shared state and middlewares. .layer(&AddExtensionLayer::new(Arc::new(State::default()))) - // Add request IDs + // Handle `/ping` health check requests. + .layer(&AlbHealthCheckLayer::from_handler("/ping", |_req| async { + StatusCode::OK + })) + // Add server request IDs. .layer(&ServerRequestIdProviderLayer::new()); // Using `into_make_service_with_connect_info`, rather than `into_make_service`, to adjoin the `SocketAddr` diff --git a/examples/pokemon-service/tests/simple.rs b/examples/pokemon-service/tests/simple.rs index af03bfee30..3a055da587 100644 --- a/examples/pokemon-service/tests/simple.rs +++ b/examples/pokemon-service/tests/simple.rs @@ -89,3 +89,31 @@ async fn simple_integration_test() { assert_eq!(result.status(), 200); } + +#[tokio::test] +#[serial] +async fn health_check() { + let _child = common::run_server().await; + + use pokemon_service::{DEFAULT_ADDRESS, DEFAULT_PORT}; + let url = format!("http://{DEFAULT_ADDRESS}:{DEFAULT_PORT}/ping"); + let uri = url.parse::().expect("invalid URL"); + + // Since the `/ping` route is not modeled in Smithy, we use a regular + // Hyper HTTP client to make a request to it. + let request = hyper::Request::builder() + .uri(uri) + .body(hyper::Body::empty()) + .expect("failed to build request"); + + let response = hyper::Client::new() + .request(request) + .await + .expect("failed to get response"); + + assert_eq!(response.status(), hyper::StatusCode::OK); + let body = hyper::body::to_bytes(response.into_body()) + .await + .expect("failed to read response body"); + assert!(body.is_empty()); +} diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs b/rust-runtime/aws-smithy-http-server/src/layer/alb_health_check.rs similarity index 86% rename from rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs rename to rust-runtime/aws-smithy-http-server/src/layer/alb_health_check.rs index 47b5460a3f..8c76bf1a9f 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/alb_health_check.rs +++ b/rust-runtime/aws-smithy-http-server/src/layer/alb_health_check.rs @@ -9,14 +9,17 @@ //! # Example //! //! ```no_run -//! # use aws_smithy_http_server::{body, plugin::{HttpPlugins, alb_health_check::AlbHealthCheckLayer}}; -//! # use hyper::{Body, Response, StatusCode}; -//! let plugins = HttpPlugins::new() -//! // Handle all `/ping` health check requests by returning a `200 OK`. -//! .layer(AlbHealthCheckLayer::from_handler("/ping", |_req| async { -//! StatusCode::OK -//! })); +//! use aws_smithy_http_server::layer::alb_health_check::AlbHealthCheckLayer; +//! use hyper::StatusCode; +//! use tower::Layer; //! +//! // Handle all `/ping` health check requests by returning a `200 OK`. +//! let ping_layer = AlbHealthCheckLayer::from_handler("/ping", |_req| async { +//! StatusCode::OK +//! }); +//! # async fn handle() { } +//! let app = tower::service_fn(handle); +//! let app = ping_layer.layer(app); //! ``` use std::borrow::Cow; @@ -31,8 +34,8 @@ use tower::{service_fn, util::Oneshot, Layer, Service, ServiceExt}; use crate::body::BoxBody; -use super::either::EitherProj; -use super::Either; +use crate::plugin::either::Either; +use crate::plugin::either::EitherProj; /// A [`tower::Layer`] used to apply [`AlbHealthCheckService`]. #[derive(Clone, Debug)] @@ -96,9 +99,7 @@ where H: Service, Response = StatusCode, Error = Infallible> + Clone, { type Response = S::Response; - type Error = S::Error; - type Future = AlbHealthCheckFuture; fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { @@ -133,7 +134,11 @@ pin_project! { } } -impl, Response = StatusCode>, S: Service>> AlbHealthCheckFuture { +impl AlbHealthCheckFuture +where + H: Service, Response = StatusCode>, + S: Service>, +{ fn handler_future(handler_future: Oneshot>) -> Self { Self { inner: Either::Left { value: handler_future }, @@ -147,10 +152,10 @@ impl, Response = StatusCode>, S: Service> } } -impl< - H: Service, Response = StatusCode, Error = Infallible>, - S: Service, Response = Response>, - > Future for AlbHealthCheckFuture +impl Future for AlbHealthCheckFuture +where + H: Service, Response = StatusCode, Error = Infallible>, + S: Service, Response = Response>, { type Output = Result; diff --git a/rust-runtime/aws-smithy-http-server/src/layer/mod.rs b/rust-runtime/aws-smithy-http-server/src/layer/mod.rs new file mode 100644 index 0000000000..fcbce76f44 --- /dev/null +++ b/rust-runtime/aws-smithy-http-server/src/layer/mod.rs @@ -0,0 +1,9 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! This module hosts [`Layer`](tower::Layer)s that are generally meant to be applied _around_ the +//! [`Router`](crate::routing::Router), so they are enacted before a request is routed. + +pub mod alb_health_check; diff --git a/rust-runtime/aws-smithy-http-server/src/lib.rs b/rust-runtime/aws-smithy-http-server/src/lib.rs index 7b32c50736..a7a78733b2 100644 --- a/rust-runtime/aws-smithy-http-server/src/lib.rs +++ b/rust-runtime/aws-smithy-http-server/src/lib.rs @@ -16,6 +16,7 @@ pub mod body; pub(crate) mod error; pub mod extension; pub mod instrumentation; +pub mod layer; pub mod operation; pub mod plugin; #[doc(hidden)] diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/either.rs b/rust-runtime/aws-smithy-http-server/src/plugin/either.rs index b2da9cc5f0..d0b6192ba0 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/either.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/either.rs @@ -15,6 +15,8 @@ use tower::{Layer, Service}; use super::Plugin; +// TODO(https://github.com/awslabs/smithy-rs/pull/2441#pullrequestreview-1331345692): Seems like +// this type should land in `tower-0.5`. pin_project! { /// Combine two different [`Futures`](std::future::Future)/[`Services`](tower::Service)/ /// [`Layers`](tower::Layer)/[`Plugins`](super::Plugin) into a single type. diff --git a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs index 5bbeda3eba..a5d5ec66ec 100644 --- a/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs +++ b/rust-runtime/aws-smithy-http-server/src/plugin/mod.rs @@ -194,9 +194,8 @@ //! impl ModelMarker for PrintPlugin { } //! ``` -pub mod alb_health_check; mod closure; -mod either; +pub(crate) mod either; mod filter; mod http_plugins; mod identity; From 666c474f4a4659ef53978ad090bf4b26cc12cebe Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Mon, 24 Jul 2023 08:28:49 -0700 Subject: [PATCH 227/253] Tidy up `aws-smithy-runtime-api` a bit (#2867) This PR makes some progress towards documenting the `aws-smithy-runtime-api` crate, as well as some additional work to make it more stable: - Places the `client` module behind a `client` feature so that it will be possible to add a `server` module/feature in the future. - Deletes `ConfigBagAccessors`. - Renames auth types to reflect changes in the internal spec. - Moves `RequestAttempts` into the `client::retries` module. - Moves several types that were in floating around in `client::orchestrator` to their own modules. - Renames `Connector` to `HttpConnector`. - Changes `DynResponseDeserializer` to `SharedResponseDeserializer` for consistency with serialization, and also so that it could be moved into runtime components or config in the future (since those need to implement `Clone`). - Updates the identity and auth design doc. - Updates READMEs and crate-level documentation. - Fixes most, but not all, the missing documentation in the crate. - Hides the builder macros. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../src/glacier_interceptors.rs | 3 +- aws/rust-runtime/aws-runtime/Cargo.toml | 4 +- .../aws-runtime/src/auth/sigv4.rs | 40 ++- .../aws-runtime/src/request_info.rs | 2 +- .../AwsCustomizableOperationDecorator.kt | 1 - .../smithy/rustsdk/AwsPresigningDecorator.kt | 6 +- .../smithy/rustsdk/SigV4AuthDecorator.kt | 16 +- .../customize/ServiceSpecificDecorator.kt | 8 +- .../customizations/HttpAuthDecorator.kt | 20 +- .../HttpConnectorConfigDecorator.kt | 18 +- .../smithy/customizations/NoAuthDecorator.kt | 8 +- .../customize/ClientCodegenDecorator.kt | 18 +- .../endpoint/EndpointConfigCustomization.kt | 2 +- .../EndpointParamsInterceptorGenerator.kt | 8 +- .../ConfigOverrideRuntimePluginGenerator.kt | 1 - .../smithy/generators/OperationGenerator.kt | 6 +- .../OperationRuntimePluginGenerator.kt | 42 ++- .../ServiceRuntimePluginGenerator.kt | 4 +- .../config/ServiceConfigGenerator.kt | 3 +- .../protocol/ProtocolTestGenerator.kt | 6 +- .../protocol/RequestSerializerGenerator.kt | 5 +- .../protocol/ResponseDeserializerGenerator.kt | 2 +- .../smithy/endpoint/EndpointsDecoratorTest.kt | 2 +- ...onfigOverrideRuntimePluginGeneratorTest.kt | 9 +- .../protocol/ProtocolTestGeneratorTest.kt | 6 +- .../codegen/core/rustlang/CargoDependency.kt | 2 + .../rust/codegen/core/smithy/RuntimeType.kt | 2 - design/src/client/identity_and_auth.md | 65 +++-- .../aws-smithy-runtime-api/Cargo.toml | 1 + rust-runtime/aws-smithy-runtime-api/README.md | 10 +- .../aws-smithy-runtime-api/src/client.rs | 21 +- .../aws-smithy-runtime-api/src/client/auth.rs | 134 ++++++--- .../src/client/auth/http.rs | 7 + .../src/client/auth/option_resolver.rs | 49 ---- .../src/client/auth/static_resolver.rs | 49 ++++ .../src/client/config_bag_accessors.rs | 161 ----------- .../src/client/connectors.rs | 20 +- .../src/client/endpoint.rs | 62 ++++ .../src/client/identity.rs | 27 +- .../src/client/interceptors.rs | 4 + .../src/client/interceptors/context.rs | 1 + .../client/interceptors/context/wrappers.rs | 12 + .../src/client/interceptors/error.rs | 1 + .../src/client/orchestrator.rs | 273 +++++++++++------- .../src/client/orchestrator/error.rs | 151 ---------- .../src/client/request_attempts.rs | 32 -- .../src/client/retries.rs | 69 ++++- .../src/client/runtime_components.rs | 133 +++++---- .../src/client/runtime_plugin.rs | 114 +++++--- .../src/client/ser_de.rs | 105 +++++++ .../aws-smithy-runtime-api/src/lib.rs | 19 +- .../aws-smithy-runtime-api/src/macros.rs | 3 + rust-runtime/aws-smithy-runtime/Cargo.toml | 1 + rust-runtime/aws-smithy-runtime/README.md | 2 - .../src/client/auth/http.rs | 42 +-- .../src/client/auth/no_auth.rs | 12 +- .../src/client/connectors.rs | 8 +- .../src/client/connectors/test_util.rs | 6 +- .../src/client/orchestrator.rs | 41 +-- .../src/client/orchestrator/auth.rs | 70 ++--- .../src/client/orchestrator/endpoints.rs | 10 +- .../client/retries/strategy/fixed_delay.rs | 3 +- .../src/client/retries/strategy/standard.rs | 3 +- .../src/client/test_util/deserializer.rs | 8 +- .../src/client/test_util/serializer.rs | 7 +- rust-runtime/aws-smithy-runtime/src/lib.rs | 1 + rust-runtime/inlineable/Cargo.toml | 2 +- 67 files changed, 1048 insertions(+), 935 deletions(-) delete mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/auth/static_resolver.rs delete mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/endpoint.rs delete mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs delete mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs create mode 100644 rust-runtime/aws-smithy-runtime-api/src/client/ser_de.rs diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs index b9a6165017..efd71582af 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -11,7 +11,6 @@ use aws_sigv4::http_request::SignableBody; use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream; use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{ BeforeSerializationInterceptorContextMut, BeforeTransmitInterceptorContextMut, }; @@ -126,7 +125,7 @@ impl Interceptor for GlacierTreeHashHeaderInterceptor { // Request the request body to be loaded into memory immediately after serialization // so that it can be checksummed before signing and transmit cfg.interceptor_state() - .set_loaded_request_body(LoadedRequestBody::Requested); + .store_put(LoadedRequestBody::Requested); Ok(()) } diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index 8ffcade9fc..9600d1f0a9 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -18,8 +18,8 @@ aws-sigv4 = { path = "../aws-sigv4" } aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-smithy-eventstream = { path = "../../../rust-runtime/aws-smithy-eventstream", optional = true } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } -aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } -aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } +aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime", features = ["client"] } +aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["client"] } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } aws-types = { path = "../aws-types" } fastrand = "2.0.0" diff --git a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs index 3a5a2e85ef..ac1eab1ea7 100644 --- a/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs +++ b/aws/rust-runtime/aws-runtime/src/auth/sigv4.rs @@ -10,7 +10,7 @@ use aws_sigv4::http_request::{ }; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::{ - AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, + AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, Signer, }; use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver}; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; @@ -79,18 +79,18 @@ impl StdError for SigV4SigningError { /// SigV4 auth scheme. #[derive(Debug, Default)] -pub struct SigV4HttpAuthScheme { - signer: SigV4HttpRequestSigner, +pub struct SigV4AuthScheme { + signer: SigV4Signer, } -impl SigV4HttpAuthScheme { - /// Creates a new `SigV4HttpAuthScheme`. +impl SigV4AuthScheme { + /// Creates a new `SigV4AuthScheme`. pub fn new() -> Self { Default::default() } } -impl HttpAuthScheme for SigV4HttpAuthScheme { +impl AuthScheme for SigV4AuthScheme { fn scheme_id(&self) -> AuthSchemeId { SCHEME_ID } @@ -102,7 +102,7 @@ impl HttpAuthScheme for SigV4HttpAuthScheme { identity_resolvers.identity_resolver(self.scheme_id()) } - fn request_signer(&self) -> &dyn HttpRequestSigner { + fn signer(&self) -> &dyn Signer { &self.signer } } @@ -174,11 +174,11 @@ impl Storable for SigV4OperationSigningConfig { type Storer = StoreReplace; } -/// SigV4 HTTP request signer. +/// SigV4 signer. #[derive(Debug, Default)] -pub struct SigV4HttpRequestSigner; +pub struct SigV4Signer; -impl SigV4HttpRequestSigner { +impl SigV4Signer { /// Creates a new signer instance. pub fn new() -> Self { Self @@ -291,7 +291,7 @@ impl SigV4HttpRequestSigner { endpoint_config: AuthSchemeEndpointConfig<'_>, ) -> Result { let (mut signing_region_override, mut signing_service_override) = (None, None); - if let Some(config) = endpoint_config.config().and_then(Document::as_object) { + if let Some(config) = endpoint_config.as_document().and_then(Document::as_object) { use SigV4SigningError::BadTypeInEndpointAuthSchemeConfig as UnexpectedType; signing_region_override = match config.get("signingRegion") { Some(Document::String(s)) => Some(SigningRegion::from(Region::new(s.clone()))), @@ -311,8 +311,8 @@ impl SigV4HttpRequestSigner { } } -impl HttpRequestSigner for SigV4HttpRequestSigner { - fn sign_request( +impl Signer for SigV4Signer { + fn sign_http_request( &self, request: &mut HttpRequest, identity: &Identity, @@ -550,15 +550,13 @@ mod tests { payload_override: None, }, }; - SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now) - .unwrap(); + SigV4Signer::signing_params(settings, &credentials, &operation_config, now).unwrap(); assert!(!logs_contain(EXPIRATION_WARNING)); let mut settings = SigningSettings::default(); settings.expires_in = Some(creds_expire_in + Duration::from_secs(10)); - SigV4HttpRequestSigner::signing_params(settings, &credentials, &operation_config, now) - .unwrap(); + SigV4Signer::signing_params(settings, &credentials, &operation_config, now).unwrap(); assert!(logs_contain(EXPIRATION_WARNING)); } @@ -583,11 +581,10 @@ mod tests { ); out }); - let config = AuthSchemeEndpointConfig::new(Some(&config)); + let config = AuthSchemeEndpointConfig::from(Some(&config)); let cfg = ConfigBag::of_layers(vec![layer]); - let result = - SigV4HttpRequestSigner::extract_operation_config(config, &cfg).expect("success"); + let result = SigV4Signer::extract_operation_config(config, &cfg).expect("success"); assert_eq!( result.region, @@ -611,8 +608,7 @@ mod tests { let cfg = ConfigBag::of_layers(vec![layer]); let config = AuthSchemeEndpointConfig::empty(); - let result = - SigV4HttpRequestSigner::extract_operation_config(config, &cfg).expect("success"); + let result = SigV4Signer::extract_operation_config(config, &cfg).expect("success"); assert_eq!( result.region, diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index 0fb9a929ee..f0abff3041 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -7,7 +7,7 @@ use aws_smithy_runtime::client::orchestrator::interceptors::ServiceClockSkew; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; -use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; +use aws_smithy_runtime_api::client::retries::RequestAttempts; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::date_time::Format; diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index 6af6bfde0d..17e8dd248b 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -22,7 +22,6 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : "AwsUserAgent" to AwsRuntimeType.awsHttp(runtimeConfig).resolve("user_agent::AwsUserAgent"), "BeforeTransmitInterceptorContextMut" to RuntimeType.beforeTransmitInterceptorContextMut(runtimeConfig), "ConfigBag" to RuntimeType.configBag(runtimeConfig), - "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "http" to CargoDependency.Http.toType(), "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(runtimeConfig), diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index de6da933e3..fc1833688c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -364,21 +364,19 @@ class AwsPresignedFluentBuilderMethod( struct AlternatePresigningSerializerRuntimePlugin; impl #{RuntimePlugin} for AlternatePresigningSerializerRuntimePlugin { fn config(&self) -> #{Option}<#{FrozenLayer}> { - use #{ConfigBagAccessors}; let mut cfg = #{Layer}::new("presigning_serializer"); - cfg.set_request_serializer(#{SharedRequestSerializer}::new(#{AlternateSerializer})); + cfg.store_put(#{SharedRequestSerializer}::new(#{AlternateSerializer})); #{Some}(cfg.freeze()) } } """, *preludeScope, "AlternateSerializer" to alternateSerializer(operationShape), - "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "Layer" to smithyTypes.resolve("config_bag::Layer"), "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), "SharedRequestSerializer" to RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) - .resolve("client::orchestrator::SharedRequestSerializer"), + .resolve("client::ser_de::SharedRequestSerializer"), ) } }, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt index 6163d78a2f..279626e508 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4AuthDecorator.kt @@ -10,7 +10,7 @@ import software.amazon.smithy.aws.traits.auth.UnsignedPayloadTrait import software.amazon.smithy.model.knowledge.ServiceIndex import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.OperationSection @@ -33,9 +33,9 @@ class SigV4AuthDecorator : ClientCodegenDecorator { override fun authOptions( codegenContext: ClientCodegenContext, operationShape: OperationShape, - baseAuthOptions: List, - ): List = baseAuthOptions.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { - it + AuthOption.StaticAuthOption(SigV4Trait.ID) { + baseAuthSchemeOptions: List, + ): List = baseAuthSchemeOptions.letIf(codegenContext.smithyRuntimeMode.generateOrchestrator) { + it + AuthSchemeOption.StaticAuthSchemeOption(SigV4Trait.ID) { rustTemplate( "#{scheme_id},", "scheme_id" to AwsRuntimeType.awsRuntime(codegenContext.runtimeConfig) @@ -69,10 +69,10 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext: val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) arrayOf( "SIGV4_SCHEME_ID" to awsRuntime.resolve("auth::sigv4::SCHEME_ID"), - "SigV4HttpAuthScheme" to awsRuntime.resolve("auth::sigv4::SigV4HttpAuthScheme"), + "SigV4AuthScheme" to awsRuntime.resolve("auth::sigv4::SigV4AuthScheme"), "SigningRegion" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("region::SigningRegion"), "SigningService" to AwsRuntimeType.awsTypes(runtimeConfig).resolve("SigningService"), - "SharedHttpAuthScheme" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::auth::SharedHttpAuthScheme"), + "SharedAuthScheme" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::auth::SharedAuthScheme"), ) } @@ -84,8 +84,8 @@ private class AuthServiceRuntimePluginCustomization(private val codegenContext: // enable the aws-runtime `sign-eventstream` feature addDependency(AwsCargoDependency.awsRuntime(runtimeConfig).withFeature("event-stream").toType().toSymbol()) } - section.registerHttpAuthScheme(this) { - rustTemplate("#{SharedHttpAuthScheme}::new(#{SigV4HttpAuthScheme}::new())", *codegenScope) + section.registerAuthScheme(this) { + rustTemplate("#{SharedAuthScheme}::new(#{SigV4AuthScheme}::new())", *codegenScope) } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt index 3f8a8f6d61..4850d299ef 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/ServiceSpecificDecorator.kt @@ -12,7 +12,7 @@ import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.model.shapes.ToShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.ClientRustSettings -import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientProtocolMap import software.amazon.smithy.rust.codegen.client.smithy.endpoint.EndpointCustomization @@ -63,9 +63,9 @@ class ServiceSpecificDecorator( override fun authOptions( codegenContext: ClientCodegenContext, operationShape: OperationShape, - baseAuthOptions: List, - ): List = baseAuthOptions.maybeApply(codegenContext.serviceShape) { - delegateTo.authOptions(codegenContext, operationShape, baseAuthOptions) + baseAuthSchemeOptions: List, + ): List = baseAuthSchemeOptions.maybeApply(codegenContext.serviceShape) { + delegateTo.authOptions(codegenContext, operationShape, baseAuthSchemeOptions) } override fun builderCustomizations( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt index ead864dc32..9477f1082a 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecorator.kt @@ -14,8 +14,8 @@ import software.amazon.smithy.model.traits.HttpBearerAuthTrait import software.amazon.smithy.model.traits.HttpDigestAuthTrait 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.customize.AuthOption -import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption.StaticAuthOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption.StaticAuthSchemeOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginCustomization import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRuntimePluginSection @@ -52,7 +52,7 @@ private fun codegenScope(runtimeConfig: RuntimeConfig): Array> "IdentityResolver" to smithyRuntimeApi.resolve("client::identity::IdentityResolver"), "Login" to smithyRuntimeApi.resolve("client::identity::http::Login"), "PropertyBag" to RuntimeType.smithyHttp(runtimeConfig).resolve("property_bag::PropertyBag"), - "SharedHttpAuthScheme" to smithyRuntimeApi.resolve("client::auth::SharedHttpAuthScheme"), + "SharedAuthScheme" to smithyRuntimeApi.resolve("client::auth::SharedAuthScheme"), "SharedIdentityResolver" to smithyRuntimeApi.resolve("client::identity::SharedIdentityResolver"), "Token" to smithyRuntimeApi.resolve("client::identity::http::Token"), ) @@ -89,16 +89,16 @@ class HttpAuthDecorator : ClientCodegenDecorator { override fun authOptions( codegenContext: ClientCodegenContext, operationShape: OperationShape, - baseAuthOptions: List, - ): List { + baseAuthSchemeOptions: List, + ): List { val serviceIndex = ServiceIndex.of(codegenContext.model) val authSchemes = serviceIndex.getEffectiveAuthSchemes(codegenContext.serviceShape, operationShape) val codegenScope = codegenScope(codegenContext.runtimeConfig) - val options = ArrayList() + val options = ArrayList() for (authScheme in authSchemes.keys) { fun addOption(schemeShapeId: ShapeId, name: String) { options.add( - StaticAuthOption( + StaticAuthSchemeOption( schemeShapeId, writable { rustTemplate("$name,", *codegenScope) @@ -114,7 +114,7 @@ class HttpAuthDecorator : ClientCodegenDecorator { else -> {} } } - return baseAuthOptions + options + return baseAuthSchemeOptions + options } override fun configCustomizations( @@ -164,8 +164,8 @@ private class HttpAuthServiceRuntimePluginCustomization( when (section) { is ServiceRuntimePluginSection.RegisterRuntimeComponents -> { fun registerAuthScheme(scheme: Writable) { - section.registerHttpAuthScheme(this) { - rustTemplate("#{SharedHttpAuthScheme}::new(#{Scheme})", *codegenScope, "Scheme" to scheme) + section.registerAuthScheme(this) { + rustTemplate("#{SharedAuthScheme}::new(#{Scheme})", *codegenScope, "Scheme" to scheme) } } fun registerNamedAuthScheme(name: String) { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index b0ba6b1f24..94e0f47daf 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -45,7 +45,7 @@ private class HttpConnectorConfigCustomization( "HttpConnector" to RuntimeType.smithyClient(runtimeConfig).resolve("http_connector::HttpConnector"), "Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"), "SharedAsyncSleep" to RuntimeType.smithyAsync(runtimeConfig).resolve("rt::sleep::SharedAsyncSleep"), - "SharedConnector" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::connectors::SharedConnector"), + "SharedHttpConnector" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::connectors::SharedHttpConnector"), "TimeoutConfig" to RuntimeType.smithyTypes(runtimeConfig).resolve("timeout::TimeoutConfig"), ) @@ -101,9 +101,9 @@ private class HttpConnectorConfigCustomization( http_connector .and_then(|c| c.connector(&connector_settings, sleep_impl.clone())) .or_else(|| #{default_connector}(&connector_settings, sleep_impl)) - .map(|c| #{SharedConnector}::new(#{DynConnectorAdapter}::new(c))); + .map(|c| #{SharedHttpConnector}::new(#{DynConnectorAdapter}::new(c))); - resolver.runtime_components_mut().set_connector(connector); + resolver.runtime_components_mut().set_http_connector(connector); } } """, @@ -124,15 +124,9 @@ private class HttpConnectorConfigCustomization( if (runtimeMode.defaultToOrchestrator) { rustTemplate( """ - // TODO(enableNewSmithyRuntimeCleanup): Remove this function - /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. - pub fn http_connector(&self) -> Option<&#{HttpConnector}> { - self.config.load::<#{HttpConnector}>() - } - - /// Return the [`SharedConnector`](#{SharedConnector}) to use when making requests, if any. - pub fn connector(&self) -> Option<#{SharedConnector}> { - self.runtime_components.connector() + /// Return the [`SharedHttpConnector`](#{SharedHttpConnector}) to use when making requests, if any. + pub fn http_connector(&self) -> Option<#{SharedHttpConnector}> { + self.runtime_components.http_connector() } """, *codegenScope, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt index 9cdcae78c5..38582fabcd 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/NoAuthDecorator.kt @@ -8,7 +8,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.customizations import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.shapes.ShapeId import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.core.rustlang.CargoDependency import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate @@ -28,9 +28,9 @@ class NoAuthDecorator : ClientCodegenDecorator { override fun authOptions( codegenContext: ClientCodegenContext, operationShape: OperationShape, - baseAuthOptions: List, - ): List = baseAuthOptions + - AuthOption.StaticAuthOption(noAuthSchemeShapeId) { + baseAuthSchemeOptions: List, + ): List = baseAuthSchemeOptions + + AuthSchemeOption.StaticAuthSchemeOption(noAuthSchemeShapeId) { rustTemplate( "#{NO_AUTH_SCHEME_ID},", "NO_AUTH_SCHEME_ID" to noAuthModule(codegenContext).resolve("NO_AUTH_SCHEME_ID"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt index 3dfd77976f..36a517bb18 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customize/ClientCodegenDecorator.kt @@ -26,14 +26,14 @@ import java.util.logging.Logger typealias ClientProtocolMap = ProtocolMap -sealed interface AuthOption { - /** Auth scheme for the `StaticAuthOptionResolver` */ - data class StaticAuthOption( +sealed interface AuthSchemeOption { + /** Auth scheme for the `StaticAuthSchemeOptionResolver` */ + data class StaticAuthSchemeOption( val schemeShapeId: ShapeId, val constructor: Writable, - ) : AuthOption + ) : AuthSchemeOption - class CustomResolver(/* unimplemented */) : AuthOption + class CustomResolver(/* unimplemented */) : AuthSchemeOption } /** @@ -47,8 +47,8 @@ interface ClientCodegenDecorator : CoreCodegenDecorator, - ): List = baseAuthOptions + baseAuthSchemeOptions: List, + ): List = baseAuthSchemeOptions fun configCustomizations( codegenContext: ClientCodegenContext, @@ -110,8 +110,8 @@ open class CombinedClientCodegenDecorator(decorators: List, - ): List = combineCustomizations(baseAuthOptions) { decorator, authOptions -> + baseAuthSchemeOptions: List, + ): List = combineCustomizations(baseAuthSchemeOptions) { decorator, authOptions -> decorator.authOptions(codegenContext, operationShape, authOptions) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index 2f81ef33f5..1cd523ad0f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -35,7 +35,7 @@ internal class EndpointConfigCustomization( "OldSharedEndpointResolver" to types.sharedEndpointResolver, "Params" to typesGenerator.paramsStruct(), "Resolver" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::config_override::Resolver"), - "SharedEndpointResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::SharedEndpointResolver"), + "SharedEndpointResolver" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::endpoint::SharedEndpointResolver"), "SmithyResolver" to types.resolveEndpoint, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 1894dcd02a..6b99410512 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -45,15 +45,12 @@ class EndpointParamsInterceptorGenerator( val runtimeApi = CargoDependency.smithyRuntimeApi(rc).toType() val interceptors = runtimeApi.resolve("client::interceptors") val orchestrator = runtimeApi.resolve("client::orchestrator") - val smithyTypes = CargoDependency.smithyTypes(rc).toType() arrayOf( *preludeScope, "BoxError" to RuntimeType.boxError(rc), "ConfigBag" to RuntimeType.configBag(rc), - "ConfigBagAccessors" to RuntimeType.smithyRuntimeApi(rc) - .resolve("client::config_bag_accessors::ConfigBagAccessors"), "ContextAttachedError" to interceptors.resolve("error::ContextAttachedError"), - "EndpointResolverParams" to orchestrator.resolve("EndpointResolverParams"), + "EndpointResolverParams" to runtimeApi.resolve("client::endpoint::EndpointResolverParams"), "HttpRequest" to orchestrator.resolve("HttpRequest"), "HttpResponse" to orchestrator.resolve("HttpResponse"), "Interceptor" to RuntimeType.interceptor(rc), @@ -82,7 +79,6 @@ class EndpointParamsInterceptorGenerator( context: &#{BeforeSerializationInterceptorContextRef}<'_, #{Input}, #{Output}, #{Error}>, cfg: &mut #{ConfigBag}, ) -> #{Result}<(), #{BoxError}> { - use #{ConfigBagAccessors}; let _input = context.input() .downcast_ref::<${operationInput.name}>() .ok_or("failed to downcast to ${operationInput.name}")?; @@ -93,7 +89,7 @@ class EndpointParamsInterceptorGenerator( #{param_setters} .build() .map_err(|err| #{ContextAttachedError}::new("endpoint params could not be built", err))?; - cfg.interceptor_state().set_endpoint_resolver_params(#{EndpointResolverParams}::new(params)); + cfg.interceptor_state().store_put(#{EndpointResolverParams}::new(params)); #{Ok}(()) } } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt index b199eaf5a5..0e595065f9 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGenerator.kt @@ -25,7 +25,6 @@ class ConfigOverrideRuntimePluginGenerator( *RuntimeType.preludeScope, "Cow" to RuntimeType.Cow, "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), - "ConfigBagAccessors" to runtimeApi.resolve("client::config_bag_accessors::ConfigBagAccessors"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "InterceptorRegistrar" to runtimeApi.resolve("client::interceptors::InterceptorRegistrar"), "Layer" to smithyTypes.resolve("config_bag::Layer"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 0d93284e20..90b970d1d1 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -7,7 +7,7 @@ package software.amazon.smithy.rust.codegen.client.smithy.generators import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext -import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption import software.amazon.smithy.rust.codegen.client.smithy.customize.ClientCodegenDecorator import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.EndpointParamsInterceptorGenerator import software.amazon.smithy.rust.codegen.client.smithy.generators.protocol.MakeOperationGenerator @@ -85,7 +85,7 @@ open class OperationGenerator( private fun renderOperationStruct( operationWriter: RustWriter, operationShape: OperationShape, - authOptions: List, + authSchemeOptions: List, operationCustomizations: List, ) { val operationName = symbolProvider.toSymbol(operationShape).name @@ -213,7 +213,7 @@ open class OperationGenerator( operationWriter, operationShape, operationName, - authOptions, + authSchemeOptions, operationCustomizations, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt index 73bfe50780..73d6756072 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationRuntimePluginGenerator.kt @@ -10,7 +10,7 @@ import software.amazon.smithy.model.shapes.OperationShape import software.amazon.smithy.model.traits.OptionalAuthTrait import software.amazon.smithy.rust.codegen.client.smithy.ClientCodegenContext import software.amazon.smithy.rust.codegen.client.smithy.customizations.noAuthSchemeShapeId -import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthOption +import software.amazon.smithy.rust.codegen.client.smithy.customize.AuthSchemeOption import software.amazon.smithy.rust.codegen.core.rustlang.RustWriter import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rustTemplate @@ -35,21 +35,20 @@ class OperationRuntimePluginGenerator( val smithyTypes = RuntimeType.smithyTypes(rc) arrayOf( *preludeScope, - "AuthOptionResolverParams" to runtimeApi.resolve("client::auth::AuthOptionResolverParams"), + "AuthSchemeOptionResolverParams" to runtimeApi.resolve("client::auth::AuthSchemeOptionResolverParams"), "BoxError" to RuntimeType.boxError(codegenContext.runtimeConfig), "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), - "ConfigBagAccessors" to RuntimeType.configBagAccessors(codegenContext.runtimeConfig), "Cow" to RuntimeType.Cow, - "SharedAuthOptionResolver" to runtimeApi.resolve("client::auth::SharedAuthOptionResolver"), - "DynResponseDeserializer" to runtimeApi.resolve("client::orchestrator::DynResponseDeserializer"), "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "Layer" to smithyTypes.resolve("config_bag::Layer"), "RetryClassifiers" to runtimeApi.resolve("client::retries::RetryClassifiers"), - "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(codegenContext.runtimeConfig), - "SharedRequestSerializer" to runtimeApi.resolve("client::orchestrator::SharedRequestSerializer"), - "StaticAuthOptionResolver" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolver"), - "StaticAuthOptionResolverParams" to runtimeApi.resolve("client::auth::option_resolver::StaticAuthOptionResolverParams"), + "RuntimePlugin" to RuntimeType.runtimePlugin(codegenContext.runtimeConfig), + "SharedAuthSchemeOptionResolver" to runtimeApi.resolve("client::auth::SharedAuthSchemeOptionResolver"), + "SharedRequestSerializer" to runtimeApi.resolve("client::ser_de::SharedRequestSerializer"), + "SharedResponseDeserializer" to runtimeApi.resolve("client::ser_de::SharedResponseDeserializer"), + "StaticAuthSchemeOptionResolver" to runtimeApi.resolve("client::auth::static_resolver::StaticAuthSchemeOptionResolver"), + "StaticAuthSchemeOptionResolverParams" to runtimeApi.resolve("client::auth::static_resolver::StaticAuthSchemeOptionResolverParams"), ) } @@ -57,7 +56,7 @@ class OperationRuntimePluginGenerator( writer: RustWriter, operationShape: OperationShape, operationStructName: String, - authOptions: List, + authSchemeOptions: List, customizations: List, ) { writer.rustTemplate( @@ -65,13 +64,12 @@ class OperationRuntimePluginGenerator( impl #{RuntimePlugin} for $operationStructName { fn config(&self) -> #{Option}<#{FrozenLayer}> { let mut cfg = #{Layer}::new(${operationShape.id.name.dq()}); - use #{ConfigBagAccessors} as _; cfg.store_put(#{SharedRequestSerializer}::new(${operationStructName}RequestSerializer)); - cfg.store_put(#{DynResponseDeserializer}::new(${operationStructName}ResponseDeserializer)); + cfg.store_put(#{SharedResponseDeserializer}::new(${operationStructName}ResponseDeserializer)); ${"" /* TODO(IdentityAndAuth): Resolve auth parameters from input for services that need this */} - cfg.set_auth_option_resolver_params(#{AuthOptionResolverParams}::new(#{StaticAuthOptionResolverParams}::new())); + cfg.store_put(#{AuthSchemeOptionResolverParams}::new(#{StaticAuthSchemeOptionResolverParams}::new())); #{additional_config} @@ -96,7 +94,7 @@ class OperationRuntimePluginGenerator( """, *codegenScope, *preludeScope, - "auth_options" to generateAuthOptions(operationShape, authOptions), + "auth_options" to generateAuthOptions(operationShape, authSchemeOptions), "additional_config" to writable { writeCustomizations( customizations, @@ -130,20 +128,20 @@ class OperationRuntimePluginGenerator( private fun generateAuthOptions( operationShape: OperationShape, - authOptions: List, + authSchemeOptions: List, ): Writable = writable { - if (authOptions.any { it is AuthOption.CustomResolver }) { - throw IllegalStateException("AuthOption.CustomResolver is unimplemented") + if (authSchemeOptions.any { it is AuthSchemeOption.CustomResolver }) { + throw IllegalStateException("AuthSchemeOption.CustomResolver is unimplemented") } else { - val authOptionsMap = authOptions.associate { - val option = it as AuthOption.StaticAuthOption + val authOptionsMap = authSchemeOptions.associate { + val option = it as AuthSchemeOption.StaticAuthSchemeOption option.schemeShapeId to option } withBlockTemplate( """ - .with_auth_option_resolver(#{Some}( - #{SharedAuthOptionResolver}::new( - #{StaticAuthOptionResolver}::new(vec![ + .with_auth_scheme_option_resolver(#{Some}( + #{SharedAuthSchemeOptionResolver}::new( + #{StaticAuthSchemeOptionResolver}::new(vec![ """, "]))))", *codegenScope, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt index ed68a0b306..86a2d4d63d 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ServiceRuntimePluginGenerator.kt @@ -50,10 +50,10 @@ sealed class ServiceRuntimePluginSection(name: String) : Section(name) { ) } - fun registerHttpAuthScheme(writer: RustWriter, authScheme: Writable) { + fun registerAuthScheme(writer: RustWriter, authScheme: Writable) { writer.rustTemplate( """ - runtime_components.push_http_auth_scheme(#{auth_scheme}); + runtime_components.push_auth_scheme(#{auth_scheme}); """, "auth_scheme" to authScheme, ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index d732f4e1e2..ba400c85bf 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -313,8 +313,7 @@ class ServiceConfigGenerator( *preludeScope, "BoxError" to RuntimeType.boxError(runtimeConfig), "CloneableLayer" to smithyTypes.resolve("config_bag::CloneableLayer"), - "ConfigBag" to RuntimeType.configBag(runtimeConfig), - "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), + "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), "Cow" to RuntimeType.Cow, "FrozenLayer" to smithyTypes.resolve("config_bag::FrozenLayer"), "Layer" to smithyTypes.resolve("config_bag::Layer"), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index f4d9a4c705..e3f61df3b0 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -375,7 +375,7 @@ class DefaultProtocolTestGenerator( let op = #{Operation}::new(); let config = op.config().expect("the operation has config"); - let de = config.load::<#{DynResponseDeserializer}>().expect("the config must have a deserializer"); + let de = config.load::<#{SharedResponseDeserializer}>().expect("the config must have a deserializer"); let parsed = de.deserialize_streaming(&mut http_response); let parsed = parsed.unwrap_or_else(|| { @@ -386,9 +386,9 @@ class DefaultProtocolTestGenerator( }); """, "copy_from_slice" to RT.Bytes.resolve("copy_from_slice"), - "DynResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::DynResponseDeserializer"), + "SharedResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::SharedResponseDeserializer"), "Operation" to codegenContext.symbolProvider.toSymbol(operationShape), - "ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::ResponseDeserializer"), + "ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::ResponseDeserializer"), "RuntimePlugin" to RT.runtimePlugin(rc), "SdkBody" to RT.sdkBody(rc), ) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt index fb21d70cd2..0a1385dcd3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt @@ -37,7 +37,6 @@ class RequestSerializerGenerator( private val codegenScope by lazy { val runtimeApi = RuntimeType.smithyRuntimeApi(codegenContext.runtimeConfig) val interceptorContext = runtimeApi.resolve("client::interceptors::context") - val orchestrator = runtimeApi.resolve("client::orchestrator") val smithyTypes = RuntimeType.smithyTypes(codegenContext.runtimeConfig) arrayOf( *preludeScope, @@ -46,11 +45,11 @@ class RequestSerializerGenerator( "ConfigBag" to RuntimeType.configBag(codegenContext.runtimeConfig), "header_util" to RuntimeType.smithyHttp(codegenContext.runtimeConfig).resolve("header"), "http" to RuntimeType.Http, - "HttpRequest" to orchestrator.resolve("HttpRequest"), + "HttpRequest" to runtimeApi.resolve("client::orchestrator::HttpRequest"), "HttpRequestBuilder" to RuntimeType.HttpRequestBuilder, "Input" to interceptorContext.resolve("Input"), "operation" to RuntimeType.operationModule(codegenContext.runtimeConfig), - "RequestSerializer" to orchestrator.resolve("RequestSerializer"), + "RequestSerializer" to runtimeApi.resolve("client::ser_de::RequestSerializer"), "SdkBody" to RuntimeType.sdkBody(codegenContext.runtimeConfig), "HeaderSerializationSettings" to RuntimeType.forInlineDependency( InlineDependency.serializationSettings( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index e08ea15f13..59d3956a9f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -44,7 +44,7 @@ class ResponseDeserializerGenerator( "Output" to interceptorContext.resolve("Output"), "OutputOrError" to interceptorContext.resolve("OutputOrError"), "OrchestratorError" to orchestrator.resolve("OrchestratorError"), - "ResponseDeserializer" to orchestrator.resolve("ResponseDeserializer"), + "ResponseDeserializer" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::ser_de::ResponseDeserializer"), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), "SdkError" to RuntimeType.sdkError(runtimeConfig), "TypedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypedBox"), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt index 6d5bcaa05f..0b64de45cf 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt @@ -187,7 +187,7 @@ class EndpointsDecoratorTest { use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_client::never::NeverConnector; use aws_smithy_runtime_api::box_error::BoxError; - use aws_smithy_runtime_api::client::orchestrator::EndpointResolverParams; + use aws_smithy_runtime_api::client::endpoint::EndpointResolverParams; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use aws_smithy_types::endpoint::Endpoint; diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt index 40f82d2965..bdbee67f3a 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt @@ -46,9 +46,8 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { val runtimeConfig = clientCodegenContext.runtimeConfig val codegenScope = arrayOf( *preludeScope, - "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "EndpointResolverParams" to RuntimeType.smithyRuntimeApi(runtimeConfig) - .resolve("client::orchestrator::EndpointResolverParams"), + .resolve("client::endpoint::EndpointResolverParams"), "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), ) rustCrate.testModule { @@ -57,7 +56,7 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { rustTemplate( """ use #{RuntimePlugin}; - use ::aws_smithy_runtime_api::client::orchestrator::EndpointResolver; + use ::aws_smithy_runtime_api::client::endpoint::EndpointResolver; let expected_url = "http://localhost:1234/"; let client_config = crate::config::Config::builder().build(); @@ -93,7 +92,6 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { val runtimeConfig = clientCodegenContext.runtimeConfig val codegenScope = arrayOf( *preludeScope, - "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), ) rustCrate.testModule { @@ -175,7 +173,6 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { "AlwaysRetry" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::retries::AlwaysRetry"), "ConfigBag" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag"), - "ConfigBagAccessors" to RuntimeType.configBagAccessors(runtimeConfig), "ErrorKind" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::ErrorKind"), "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), "Layer" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::Layer"), @@ -184,7 +181,7 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { "RetryConfig" to RuntimeType.smithyTypes(clientCodegenContext.runtimeConfig) .resolve("retry::RetryConfig"), "RequestAttempts" to smithyRuntimeApiTestUtil(runtimeConfig).toType() - .resolve("client::request_attempts::RequestAttempts"), + .resolve("client::retries::RequestAttempts"), "RetryClassifiers" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::retries::RetryClassifiers"), "RuntimeComponentsBuilder" to RuntimeType.runtimeComponentsBuilder(runtimeConfig), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt index 1d1cddeca4..99beb8d86b 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt @@ -102,15 +102,15 @@ private class TestOperationCustomization( .map_err(|e| #{OrchestratorError}::operation(#{TypedBox}::new(e).erase_error())) } } - cfg.store_put(#{DynResponseDeserializer}::new(TestDeser)); + cfg.store_put(#{SharedResponseDeserializer}::new(TestDeser)); """, *preludeScope, - "DynResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::DynResponseDeserializer"), + "SharedResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::SharedResponseDeserializer"), "Error" to RT.smithyRuntimeApi(rc).resolve("client::interceptors::context::Error"), "HttpResponse" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::HttpResponse"), "OrchestratorError" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::OrchestratorError"), "Output" to RT.smithyRuntimeApi(rc).resolve("client::interceptors::context::Output"), - "ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::ResponseDeserializer"), + "ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::ResponseDeserializer"), "TypedBox" to RT.smithyTypes(rc).resolve("type_erasure::TypedBox"), ) } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt index 8b03d9656c..73a5e9ebbe 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang/CargoDependency.kt @@ -297,7 +297,9 @@ data class CargoDependency( fun smithyQuery(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-query") fun smithyRuntime(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-runtime") + .withFeature("client") fun smithyRuntimeApi(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-runtime-api") + .withFeature("client") fun smithyRuntimeApiTestUtil(runtimeConfig: RuntimeConfig) = smithyRuntimeApi(runtimeConfig).toDevDependency().withFeature("test-util") fun smithyTypes(runtimeConfig: RuntimeConfig) = runtimeConfig.smithyRuntimeCrate("smithy-types") diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt index d3b7bd7420..e24edc7f86 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/RuntimeType.kt @@ -336,8 +336,6 @@ data class RuntimeType(val path: String, val dependency: RustDependency? = null) fun configBag(runtimeConfig: RuntimeConfig): RuntimeType = smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag") - fun configBagAccessors(runtimeConfig: RuntimeConfig): RuntimeType = - smithyRuntimeApi(runtimeConfig).resolve("client::config_bag_accessors::ConfigBagAccessors") fun runtimeComponents(runtimeConfig: RuntimeConfig) = smithyRuntimeApi(runtimeConfig).resolve("client::runtime_components::RuntimeComponents") fun runtimeComponentsBuilder(runtimeConfig: RuntimeConfig) = diff --git a/design/src/client/identity_and_auth.md b/design/src/client/identity_and_auth.md index 87aa8b9c66..d58efa1932 100644 --- a/design/src/client/identity_and_auth.md +++ b/design/src/client/identity_and_auth.md @@ -34,17 +34,17 @@ There are two stages to identity and auth: First, let's establish the aspects of auth that can be configured from the model at codegen time. - **Data** - - **AuthOptionResolverParams:** parameters required to resolve auth options. These parameters are allowed + - **AuthSchemeOptionResolverParams:** parameters required to resolve auth scheme options. These parameters are allowed to come from both the client config and the operation input structs. - - **HttpAuthSchemes:** a list of auth schemes that can be used to sign HTTP requests. This information + - **AuthSchemes:** a list of auth schemes that can be used to sign HTTP requests. This information comes directly from the service model. - **AuthSchemeProperties:** configuration from the auth scheme for the signer. - **IdentityResolvers:** list of available identity resolvers. - **Implementations** - **IdentityResolver:** resolves an identity for use in authentication. There can be multiple identity resolvers that need to be selected from. - - **HttpRequestSigner:** a signing implementation that signs a HTTP request. - - **AuthOptionResolver:** resolves a list of auth options for a given operation and its inputs. + - **Signer:** a signing implementation that signs a HTTP request. + - **AuthSchemeOptionResolver:** resolves a list of auth scheme options for a given operation and its inputs. As it is undocumented (at time of writing), this document assumes that the code generator creates one service-level runtime plugin, and an operation-level runtime plugin per operation, hence @@ -52,34 +52,34 @@ referred to as the service runtime plugin and operation runtime plugin. The code generator emits code to add identity resolvers and HTTP auth schemes to the config bag in the service runtime plugin. It then emits code to register an interceptor in the operation runtime -plugin that reads the operation input to generate the auth option resolver params (which also get added +plugin that reads the operation input to generate the auth scheme option resolver params (which also get added to the config bag). ### The execution stage At a high-level, the process of resolving an identity and signing a request looks as follows: -1. Retrieve the `AuthOptionResolverParams` from the config bag. The `AuthOptionResolverParams` allow client -config and operation inputs to play a role in which auth option is selected. -2. Retrieve the `AuthOptionResolver` from the config bag, and use it to resolve the auth options available -with the `AuthOptionResolverParams`. The returned auth options are in priority order. +1. Retrieve the `AuthSchemeOptionResolverParams` from the config bag. The `AuthSchemeOptionResolverParams` allow client +config and operation inputs to play a role in which auth scheme option is selected. +2. Retrieve the `AuthSchemeOptionResolver` from the config bag, and use it to resolve the auth scheme options available +with the `AuthSchemeOptionResolverParams`. The returned auth scheme options are in priority order. 3. Retrieve the `IdentityResolvers` list from the config bag. -4. For each auth option: - 1. Attempt to find an HTTP auth scheme for that auth option in the config bag (from the `HttpAuthSchemes` list). +4. For each auth scheme option: + 1. Attempt to find an HTTP auth scheme for that auth scheme option in the config bag (from the `AuthSchemes` list). 2. If an auth scheme is found: 1. Use the auth scheme to extract the correct identity resolver from the `IdentityResolvers` list. - 2. Retrieve the `HttpRequestSigner` implementation from the auth scheme. + 2. Retrieve the `Signer` implementation from the auth scheme. 3. Use the `IdentityResolver` to resolve the identity needed for signing. 4. Sign the request with the identity, and break out of the loop from step #4. -In general, it is assumed that if an HTTP auth scheme exists for an auth option, then an identity resolver -also exists for that auth option. Otherwise, the auth option was configured incorrectly during codegen. +In general, it is assumed that if an HTTP auth scheme exists for an auth scheme option, then an identity resolver +also exists for that auth scheme option. Otherwise, the auth option was configured incorrectly during codegen. How this looks in Rust ---------------------- The client will use trait objects and dynamic dispatch for the `IdentityResolver`, -`HttpRequestSigner`, and `AuthOptionResolver` implementations. Generics could potentially be used, +`Signer`, and `AuthSchemeOptionResolver` implementations. Generics could potentially be used, but the number of generic arguments and trait bounds in the orchestrator would balloon to unmaintainable levels if each configurable implementation in it was made generic. @@ -87,38 +87,37 @@ These traits look like this: ```rust,ignore #[derive(Clone, Debug)] -pub struct HttpAuthOption { +pub struct AuthSchemeId { scheme_id: &'static str, - properties: Arc, } -pub trait AuthOptionResolver: Send + Sync + Debug { - fn resolve_auth_options<'a>( +pub trait AuthSchemeOptionResolver: Send + Sync + Debug { + fn resolve_auth_scheme_options<'a>( &'a self, - params: &AuthOptionResolverParams, - ) -> Result, BoxError>; + params: &AuthSchemeOptionResolverParams, + ) -> Result, BoxError>; } pub trait IdentityResolver: Send + Sync + Debug { - // `identity_properties` come from `HttpAuthOption::properties` - fn resolve_identity(&self, identity_properties: &PropertyBag) -> BoxFallibleFut; + fn resolve_identity(&self, config: &ConfigBag) -> BoxFallibleFut; } -pub trait HttpRequestSigner: Send + Sync + Debug { +pub trait Signer: Send + Sync + Debug { /// Return a signed version of the given request using the given identity. /// /// If the provided identity is incompatible with this signer, an error must be returned. - fn sign_request( + fn sign_http_request( &self, request: &mut HttpRequest, identity: &Identity, - // `signing_properties` come from `HttpAuthOption::properties` - signing_properties: &PropertyBag, + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + runtime_components: &RuntimeComponents, + config_bag: &ConfigBag, ) -> Result<(), BoxError>; } ``` -`IdentityResolver` and `HttpRequestSigner` implementations are both given an `Identity`, but +`IdentityResolver` and `Signer` implementations are both given an `Identity`, but will need to understand what the concrete data type underlying that identity is. The `Identity` struct uses a `Arc` to represent the actual identity data so that generics are not needed in the traits: @@ -137,11 +136,13 @@ will use downcasting to access the identity data types they understand. For exam it might look like the following: ```rust,ignore -fn sign_request( +fn sign_http_request( &self, request: &mut HttpRequest, identity: &Identity, - signing_properties: &PropertyBag + auth_scheme_endpoint_config: AuthSchemeEndpointConfig<'_>, + runtime_components: &RuntimeComponents, + config_bag: &ConfigBag, ) -> Result<(), BoxError> { let aws_credentials = identity.data::() .ok_or_else(|| "The SigV4 signer requires AWS credentials")?; @@ -181,8 +182,8 @@ The `expiration` field is a special case that is allowed onto the `Identity` str cache implementations will always need to be aware of this piece of information, and having it as an `Option` still allows for non-expiring identities. -Ultimately, this design constrains `HttpRequestSigner` implementations to concrete types. There is no world -where an `HttpRequestSigner` can operate across multiple unknown identity data types via trait, and that +Ultimately, this design constrains `Signer` implementations to concrete types. There is no world +where an `Signer` can operate across multiple unknown identity data types via trait, and that should be OK since the signer implementation can always be wrapped with an implementation that is aware of the concrete type provided by the identity resolver, and can do any necessary conversions. diff --git a/rust-runtime/aws-smithy-runtime-api/Cargo.toml b/rust-runtime/aws-smithy-runtime-api/Cargo.toml index ec17790c1c..64b3c524c4 100644 --- a/rust-runtime/aws-smithy-runtime-api/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime-api/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://github.com/awslabs/smithy-rs" [features] default = [] +client = [] http-auth = ["dep:zeroize"] test-util = [] diff --git a/rust-runtime/aws-smithy-runtime-api/README.md b/rust-runtime/aws-smithy-runtime-api/README.md index 7eb00c40dc..de1aa0dd0a 100644 --- a/rust-runtime/aws-smithy-runtime-api/README.md +++ b/rust-runtime/aws-smithy-runtime-api/README.md @@ -1,8 +1,14 @@ # aws-smithy-runtime-api -**This crate is UNSTABLE! All internal and external interfaces are subject to change without notice.** +APIs needed to configure and customize the Smithy generated code. -Lightweight crate with traits and types necessary to configure runtime logic in the `aws-smithy-runtime` crate. +Most users will not need to use this crate directly as the most frequently used +APIs are re-exported in the generated clients. However, this crate will be useful +for anyone writing a library for others to use with their generated clients. + +If you're needing to depend on this and you're not writing a library for Smithy +generated clients, then please file an issue on [smithy-rs](https://github.com/awslabs/smithy-rs) +as we likely missed re-exporting one of the APIs. This crate is part of the [AWS SDK for Rust](https://awslabs.github.io/aws-sdk-rust/) and the [smithy-rs](https://github.com/awslabs/smithy-rs) code generator. In most cases, it should not be used directly. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client.rs b/rust-runtime/aws-smithy-runtime-api/src/client.rs index 106cc5087c..d67921332f 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client.rs @@ -3,35 +3,24 @@ * SPDX-License-Identifier: Apache-2.0 */ -pub mod runtime_components; - -/// Client orchestrator configuration accessors for the [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag). -pub mod config_bag_accessors; +pub mod endpoint; /// Smithy identity used by auth and signing. pub mod identity; -/// Smithy interceptors for smithy clients. -/// -/// Interceptors are lifecycle hooks that can read/modify requests and responses. pub mod interceptors; pub mod orchestrator; -/// Smithy code related to retry handling and token bucket. -/// -/// This code defines when and how failed requests should be retried. It also defines the behavior -/// used to limit the rate that requests are sent. pub mod retries; -/// Runtime plugin type definitions. +pub mod runtime_components; + pub mod runtime_plugin; -/// Smithy auth runtime plugins pub mod auth; -/// A type to track the number of requests sent by the orchestrator for a given operation. -pub mod request_attempts; - /// Smithy connectors and related code. pub mod connectors; + +pub mod ser_de; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index 5241f5187c..acd96739f6 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! APIs for request authentication. + use crate::box_error::BoxError; use crate::client::identity::{Identity, SharedIdentityResolver}; use crate::client::orchestrator::HttpRequest; @@ -14,12 +16,18 @@ use std::borrow::Cow; use std::fmt; use std::sync::Arc; +/// Auth schemes for the HTTP `Authorization` header. #[cfg(feature = "http-auth")] pub mod http; -pub mod option_resolver; +/// Static auth scheme option resolver. +pub mod static_resolver; /// New type around an auth scheme ID. +/// +/// Each auth scheme must have a unique string identifier associated with it, +/// which is used to refer to auth schemes by the auth scheme option resolver, and +/// also used to select an identity resolver to use. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct AuthSchemeId { scheme_id: &'static str, @@ -43,71 +51,116 @@ impl From<&'static str> for AuthSchemeId { } } +/// Parameters needed to resolve auth scheme options. +/// +/// Most generated clients will use the [`StaticAuthSchemeOptionResolver`](static_resolver::StaticAuthSchemeOptionResolver), +/// which doesn't require any parameters for resolution (and has its own empty params struct). +/// +/// However, more complex auth scheme resolvers may need modeled parameters in order to resolve +/// the auth scheme options. For those, this params struct holds a type erased box so that any +/// kind of parameters can be contained within, and type casted by the auth scheme option resolver +/// implementation. #[derive(Debug)] -pub struct AuthOptionResolverParams(TypeErasedBox); +pub struct AuthSchemeOptionResolverParams(TypeErasedBox); -impl AuthOptionResolverParams { +impl AuthSchemeOptionResolverParams { + /// Creates a new [`AuthSchemeOptionResolverParams`]. pub fn new(params: T) -> Self { Self(TypedBox::new(params).erase()) } + /// Returns the underlying parameters as the type `T` if they are that type. pub fn get(&self) -> Option<&T> { self.0.downcast_ref() } } -impl Storable for AuthOptionResolverParams { +impl Storable for AuthSchemeOptionResolverParams { type Storer = StoreReplace; } -pub trait AuthOptionResolver: Send + Sync + fmt::Debug { - fn resolve_auth_options( +/// Resolver for auth scheme options. +/// +/// The orchestrator needs to select an auth scheme to sign requests with, and potentially +/// from several different available auth schemes. Smithy models have a number of ways +/// to specify which operations can use which auth schemes under which conditions, as +/// documented in the [Smithy spec](https://smithy.io/2.0/spec/authentication-traits.html). +/// +/// The orchestrator uses the auth scheme option resolver runtime component to resolve +/// an ordered list of options that are available to choose from for a given request. +/// This resolver can be a simple static list, such as with the +/// [`StaticAuthSchemeOptionResolver`](static_resolver::StaticAuthSchemeOptionResolver), +/// or it can be a complex code generated resolver that incorporates parameters from both +/// the model and the resolved endpoint. +pub trait AuthSchemeOptionResolver: Send + Sync + fmt::Debug { + /// Returns a list of available auth scheme options to choose from. + fn resolve_auth_scheme_options( &self, - params: &AuthOptionResolverParams, + params: &AuthSchemeOptionResolverParams, ) -> Result, BoxError>; } +/// A shared auth scheme option resolver. #[derive(Clone, Debug)] -pub struct SharedAuthOptionResolver(Arc); +pub struct SharedAuthSchemeOptionResolver(Arc); -impl SharedAuthOptionResolver { - pub fn new(auth_option_resolver: impl AuthOptionResolver + 'static) -> Self { - Self(Arc::new(auth_option_resolver)) +impl SharedAuthSchemeOptionResolver { + /// Creates a new [`SharedAuthSchemeOptionResolver`]. + pub fn new(auth_scheme_option_resolver: impl AuthSchemeOptionResolver + 'static) -> Self { + Self(Arc::new(auth_scheme_option_resolver)) } } -impl AuthOptionResolver for SharedAuthOptionResolver { - fn resolve_auth_options( +impl AuthSchemeOptionResolver for SharedAuthSchemeOptionResolver { + fn resolve_auth_scheme_options( &self, - params: &AuthOptionResolverParams, + params: &AuthSchemeOptionResolverParams, ) -> Result, BoxError> { - (*self.0).resolve_auth_options(params) + (*self.0).resolve_auth_scheme_options(params) } } -pub trait HttpAuthScheme: Send + Sync + fmt::Debug { +/// An auth scheme. +/// +/// Auth schemes have unique identifiers (the `scheme_id`), +/// and provide an identity resolver and a signer. +pub trait AuthScheme: Send + Sync + fmt::Debug { + /// Returns the unique identifier associated with this auth scheme. + /// + /// This identifier is used to refer to this auth scheme from the + /// [`AuthSchemeOptionResolver`], and is also associated with + /// identity resolvers in the config. fn scheme_id(&self) -> AuthSchemeId; + /// Returns the identity resolver that can resolve an identity for this scheme, if one is available. + /// + /// The [`AuthScheme`] doesn't actually own an identity resolver. Rather, identity resolvers + /// are configured as runtime components. The auth scheme merely chooses a compatible identity + /// resolver from the runtime components via the [`GetIdentityResolver`] trait. The trait is + /// given rather than the full set of runtime components to prevent complex resolution logic + /// involving multiple components from taking place in this function, since that's not the + /// intended use of this design. fn identity_resolver( &self, identity_resolvers: &dyn GetIdentityResolver, ) -> Option; - fn request_signer(&self) -> &dyn HttpRequestSigner; + /// Returns the signing implementation for this auth scheme. + fn signer(&self) -> &dyn Signer; } -/// Container for a shared HTTP auth scheme implementation. +/// Container for a shared auth scheme implementation. #[derive(Clone, Debug)] -pub struct SharedHttpAuthScheme(Arc); +pub struct SharedAuthScheme(Arc); -impl SharedHttpAuthScheme { - /// Creates a new [`SharedHttpAuthScheme`] from the given auth scheme. - pub fn new(auth_scheme: impl HttpAuthScheme + 'static) -> Self { +impl SharedAuthScheme { + /// Creates a new [`SharedAuthScheme`] from the given auth scheme. + pub fn new(auth_scheme: impl AuthScheme + 'static) -> Self { Self(Arc::new(auth_scheme)) } } -impl HttpAuthScheme for SharedHttpAuthScheme { +impl AuthScheme for SharedAuthScheme { fn scheme_id(&self) -> AuthSchemeId { self.0.scheme_id() } @@ -119,16 +172,17 @@ impl HttpAuthScheme for SharedHttpAuthScheme { self.0.identity_resolver(identity_resolvers) } - fn request_signer(&self) -> &dyn HttpRequestSigner { - self.0.request_signer() + fn signer(&self) -> &dyn Signer { + self.0.signer() } } -pub trait HttpRequestSigner: Send + Sync + fmt::Debug { - /// Return a signed version of the given request using the given identity. +/// Signing implementation for an auth scheme. +pub trait Signer: Send + Sync + fmt::Debug { + /// Sign the given request with the given identity, components, and config. /// /// If the provided identity is incompatible with this signer, an error must be returned. - fn sign_request( + fn sign_http_request( &self, request: &mut HttpRequest, identity: &Identity, @@ -140,23 +194,33 @@ pub trait HttpRequestSigner: Send + Sync + fmt::Debug { /// Endpoint configuration for the selected auth scheme. /// +/// The configuration held by this struct originates from the endpoint rule set in the service model. +/// /// This struct gets added to the request state by the auth orchestrator. #[non_exhaustive] #[derive(Clone, Debug)] pub struct AuthSchemeEndpointConfig<'a>(Option<&'a Document>); impl<'a> AuthSchemeEndpointConfig<'a> { - /// Creates a new [`AuthSchemeEndpointConfig`]. - pub fn new(config: Option<&'a Document>) -> Self { - Self(config) - } - - /// Creates an empty AuthSchemeEndpointConfig. + /// Creates an empty [`AuthSchemeEndpointConfig`]. pub fn empty() -> Self { Self(None) } - pub fn config(&self) -> Option<&'a Document> { + /// Returns the endpoint configuration as a [`Document`]. + pub fn as_document(&self) -> Option<&'a Document> { self.0 } } + +impl<'a> From> for AuthSchemeEndpointConfig<'a> { + fn from(value: Option<&'a Document>) -> Self { + Self(value) + } +} + +impl<'a> From<&'a Document> for AuthSchemeEndpointConfig<'a> { + fn from(value: &'a Document) -> Self { + Self(Some(value)) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs index a4583c6e0a..bb6ee4cc49 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth/http.rs @@ -5,7 +5,14 @@ use crate::client::auth::AuthSchemeId; +/// Auth scheme ID for HTTP API key based authentication. pub const HTTP_API_KEY_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-api-key-auth"); + +/// Auth scheme ID for HTTP Basic Auth. pub const HTTP_BASIC_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-basic-auth"); + +/// Auth scheme ID for HTTP Bearer Auth. pub const HTTP_BEARER_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-bearer-auth"); + +/// Auth scheme ID for HTTP Digest Auth. pub const HTTP_DIGEST_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("http-digest-auth"); diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs deleted file mode 100644 index bf973bf6a4..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth/option_resolver.rs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use crate::box_error::BoxError; -use crate::client::auth::{AuthOptionResolver, AuthOptionResolverParams, AuthSchemeId}; -use std::borrow::Cow; - -/// New-type around a `Vec` that implements `AuthOptionResolver`. -/// -/// This is useful for clients that don't require `AuthOptionResolverParams` to resolve auth options. -#[derive(Debug)] -pub struct StaticAuthOptionResolver { - auth_options: Vec, -} - -impl StaticAuthOptionResolver { - /// Creates a new instance of `StaticAuthOptionResolver`. - pub fn new(auth_options: Vec) -> Self { - Self { auth_options } - } -} - -impl AuthOptionResolver for StaticAuthOptionResolver { - fn resolve_auth_options( - &self, - _params: &AuthOptionResolverParams, - ) -> Result, BoxError> { - Ok(Cow::Borrowed(&self.auth_options)) - } -} - -/// Empty params to be used with [`StaticAuthOptionResolver`]. -#[derive(Debug)] -pub struct StaticAuthOptionResolverParams; - -impl StaticAuthOptionResolverParams { - /// Creates a new `StaticAuthOptionResolverParams`. - pub fn new() -> Self { - Self - } -} - -impl From for AuthOptionResolverParams { - fn from(params: StaticAuthOptionResolverParams) -> Self { - AuthOptionResolverParams::new(params) - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth/static_resolver.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth/static_resolver.rs new file mode 100644 index 0000000000..23781e2545 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth/static_resolver.rs @@ -0,0 +1,49 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +use crate::box_error::BoxError; +use crate::client::auth::{AuthSchemeId, AuthSchemeOptionResolver, AuthSchemeOptionResolverParams}; +use std::borrow::Cow; + +/// New-type around a `Vec` that implements `AuthSchemeOptionResolver`. +#[derive(Debug)] +pub struct StaticAuthSchemeOptionResolver { + auth_scheme_options: Vec, +} + +impl StaticAuthSchemeOptionResolver { + /// Creates a new instance of `StaticAuthSchemeOptionResolver`. + pub fn new(auth_scheme_options: Vec) -> Self { + Self { + auth_scheme_options, + } + } +} + +impl AuthSchemeOptionResolver for StaticAuthSchemeOptionResolver { + fn resolve_auth_scheme_options( + &self, + _params: &AuthSchemeOptionResolverParams, + ) -> Result, BoxError> { + Ok(Cow::Borrowed(&self.auth_scheme_options)) + } +} + +/// Empty params to be used with [`StaticAuthSchemeOptionResolver`]. +#[derive(Debug)] +pub struct StaticAuthSchemeOptionResolverParams; + +impl StaticAuthSchemeOptionResolverParams { + /// Creates a new `StaticAuthSchemeOptionResolverParams`. + pub fn new() -> Self { + Self + } +} + +impl From for AuthSchemeOptionResolverParams { + fn from(params: StaticAuthSchemeOptionResolverParams) -> Self { + AuthSchemeOptionResolverParams::new(params) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs deleted file mode 100644 index 5f338cd93f..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/config_bag_accessors.rs +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use crate::client::auth::AuthOptionResolverParams; -use crate::client::orchestrator::{ - DynResponseDeserializer, EndpointResolverParams, LoadedRequestBody, ResponseDeserializer, - SharedRequestSerializer, NOT_NEEDED, -}; -use aws_smithy_types::config_bag::{CloneableLayer, ConfigBag, FrozenLayer, Layer}; - -// Place traits in a private module so that they can be used in the public API without being a part of the public API. -mod internal { - use aws_smithy_types::config_bag::{ - CloneableLayer, ConfigBag, FrozenLayer, Layer, Storable, Store, StoreAppend, StoreReplace, - }; - use std::fmt::Debug; - - pub trait Settable { - fn unset(&mut self); - - fn store_put(&mut self, value: T) - where - T: Storable>; - - fn store_append(&mut self, item: T) - where - T: Storable>; - } - - impl Settable for Layer { - fn unset(&mut self) { - Layer::unset::(self); - } - - fn store_put(&mut self, value: T) - where - T: Storable>, - { - Layer::store_put(self, value); - } - - fn store_append(&mut self, item: T) - where - T: Storable>, - { - Layer::store_append(self, item); - } - } - - pub trait Gettable { - fn load(&self) -> ::ReturnedType<'_>; - } - - impl Gettable for ConfigBag { - fn load(&self) -> ::ReturnedType<'_> { - ConfigBag::load::(self) - } - } - - impl Gettable for CloneableLayer { - fn load(&self) -> ::ReturnedType<'_> { - Layer::load::(self) - } - } - - impl Gettable for Layer { - fn load(&self) -> ::ReturnedType<'_> { - Layer::load::(self) - } - } - - impl Gettable for FrozenLayer { - fn load(&self) -> ::ReturnedType<'_> { - Layer::load::(self) - } - } -} - -use internal::{Gettable, Settable}; - -pub trait ConfigBagAccessors { - fn auth_option_resolver_params(&self) -> &AuthOptionResolverParams - where - Self: Gettable, - { - self.load::() - .expect("auth option resolver params must be set") - } - fn set_auth_option_resolver_params( - &mut self, - auth_option_resolver_params: AuthOptionResolverParams, - ) where - Self: Settable, - { - self.store_put::(auth_option_resolver_params); - } - - fn endpoint_resolver_params(&self) -> &EndpointResolverParams - where - Self: Gettable, - { - self.load::() - .expect("endpoint resolver params must be set") - } - - fn set_endpoint_resolver_params(&mut self, endpoint_resolver_params: EndpointResolverParams) - where - Self: Settable, - { - self.store_put::(endpoint_resolver_params); - } - - fn request_serializer(&self) -> SharedRequestSerializer - where - Self: Gettable, - { - self.load::() - .expect("missing request serializer") - .clone() - } - fn set_request_serializer(&mut self, request_serializer: SharedRequestSerializer) - where - Self: Settable, - { - self.store_put::(request_serializer); - } - - fn response_deserializer(&self) -> &dyn ResponseDeserializer - where - Self: Gettable, - { - self.load::() - .expect("missing response deserializer") - } - fn set_response_deserializer(&mut self, response_deserializer: DynResponseDeserializer) - where - Self: Settable, - { - self.store_put::(response_deserializer); - } - - fn loaded_request_body(&self) -> &LoadedRequestBody - where - Self: Gettable, - { - self.load::().unwrap_or(&NOT_NEEDED) - } - fn set_loaded_request_body(&mut self, loaded_request_body: LoadedRequestBody) - where - Self: Settable, - { - self.store_put::(loaded_request_body); - } -} - -impl ConfigBagAccessors for ConfigBag {} -impl ConfigBagAccessors for FrozenLayer {} -impl ConfigBagAccessors for CloneableLayer {} -impl ConfigBagAccessors for Layer {} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs index a7d10a2dae..79629b9af1 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/connectors.rs @@ -7,20 +7,30 @@ use crate::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse}; use std::fmt; use std::sync::Arc; -pub trait Connector: Send + Sync + fmt::Debug { +/// Trait with a `call` function that asynchronously converts a request into a response. +/// +/// Ordinarily, a connector would use an underlying HTTP library such as [hyper](https://crates.io/crates/hyper), +/// and any associated HTTPS implementation alongside it to service requests. +/// +/// However, it can also be useful to create fake connectors implementing this trait +/// for testing. +pub trait HttpConnector: Send + Sync + fmt::Debug { + /// Asynchronously converts a request into a response. fn call(&self, request: HttpRequest) -> BoxFuture; } +/// A shared [`HttpConnector`] implementation. #[derive(Clone, Debug)] -pub struct SharedConnector(Arc); +pub struct SharedHttpConnector(Arc); -impl SharedConnector { - pub fn new(connection: impl Connector + 'static) -> Self { +impl SharedHttpConnector { + /// Returns a new [`SharedHttpConnector`]. + pub fn new(connection: impl HttpConnector + 'static) -> Self { Self(Arc::new(connection)) } } -impl Connector for SharedConnector { +impl HttpConnector for SharedHttpConnector { fn call(&self, request: HttpRequest) -> BoxFuture { (*self.0).call(request) } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/endpoint.rs b/rust-runtime/aws-smithy-runtime-api/src/client/endpoint.rs new file mode 100644 index 0000000000..654d7affb5 --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/endpoint.rs @@ -0,0 +1,62 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! APIs needed to configure endpoint resolution for clients. + +use crate::client::orchestrator::Future; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; +use aws_smithy_types::endpoint::Endpoint; +use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; +use std::fmt; +use std::sync::Arc; + +/// Parameters originating from the Smithy endpoint ruleset required for endpoint resolution. +/// +/// The actual endpoint parameters are code generated from the Smithy model, and thus, +/// are not known to the runtime crates. Hence, this struct is really a new-type around +/// a [`TypeErasedBox`] that holds the actual concrete parameters in it. +#[derive(Debug)] +pub struct EndpointResolverParams(TypeErasedBox); + +impl EndpointResolverParams { + /// Creates a new [`EndpointResolverParams`] from a concrete parameters instance. + pub fn new(params: T) -> Self { + Self(TypedBox::new(params).erase()) + } + + /// Attempts to downcast the underlying concrete parameters to `T` and return it as a reference. + pub fn get(&self) -> Option<&T> { + self.0.downcast_ref() + } +} + +impl Storable for EndpointResolverParams { + type Storer = StoreReplace; +} + +/// Configurable endpoint resolver implementation. +pub trait EndpointResolver: Send + Sync + fmt::Debug { + /// Asynchronously resolves an endpoint to use from the given endpoint parameters. + fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future; +} + +/// Shared endpoint resolver. +/// +/// This is a simple shared ownership wrapper type for the [`EndpointResolver`] trait. +#[derive(Clone, Debug)] +pub struct SharedEndpointResolver(Arc); + +impl SharedEndpointResolver { + /// Creates a new [`SharedEndpointResolver`]. + pub fn new(endpoint_resolver: impl EndpointResolver + 'static) -> Self { + Self(Arc::new(endpoint_resolver)) + } +} + +impl EndpointResolver for SharedEndpointResolver { + fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future { + self.0.resolve_endpoint(params) + } +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index 60a110fbb9..eec7c4e32c 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -15,8 +15,19 @@ use std::time::SystemTime; #[cfg(feature = "http-auth")] pub mod http; -/// Resolves an identity for a request. +/// Resolver for identities. +/// +/// Every [`AuthScheme`](crate::client::auth::AuthScheme) has one or more compatible +/// identity resolvers, which are selected from runtime components by the auth scheme +/// implementation itself. +/// +/// The identity resolver must return a [`Future`] with the resolved identity, or an error +/// if resolution failed. There is no optionality for identity resolvers. The identity either +/// resolves successfully, or it fails. The orchestrator will choose exactly one auth scheme +/// to use, and thus, its chosen identity resolver is the only identity resolver that runs. +/// There is no fallback to other auth schemes in the absense of an identity. pub trait IdentityResolver: Send + Sync + Debug { + /// Asynchronously resolves an identity for a request using the given config. fn resolve_identity(&self, config_bag: &ConfigBag) -> Future; } @@ -67,6 +78,17 @@ impl ConfiguredIdentityResolver { } } +/// An identity that can be used for authentication. +/// +/// The [`Identity`] is a container for any arbitrary identity data that may be used +/// by a [`Signer`](crate::client::auth::Signer) implementation. Under the hood, it +/// has an `Arc`, and it is the responsibility of the signer to downcast +/// to the appropriate data type using the `data()` function. +/// +/// The `Identity` also holds an optional expiration time, which may duplicate +/// an expiration time on the identity data. This is because an `Arc` +/// can't be downcast to any arbitrary trait, and expiring identities are +/// common enough to be built-in. #[derive(Clone)] pub struct Identity { data: Arc, @@ -76,6 +98,7 @@ pub struct Identity { } impl Identity { + /// Creates a new identity with the given data and expiration time. pub fn new(data: T, expiration: Option) -> Self where T: Any + Debug + Send + Sync, @@ -87,10 +110,12 @@ impl Identity { } } + /// Returns the raw identity data. pub fn data(&self) -> Option<&T> { self.data.downcast_ref() } + /// Returns the expiration time for this identity, if any. pub fn expiration(&self) -> Option<&SystemTime> { self.expiration.as_ref() } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index 5bf1ade4b1..a755a34e44 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -3,6 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Interceptors for clients. +//! +//! Interceptors are operation lifecycle hooks that can read/modify requests and responses. + use crate::box_error::BoxError; use crate::client::interceptors::context::{ AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index a33b62db63..b97915dcbb 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -36,6 +36,7 @@ use std::fmt::Debug; use std::{fmt, mem}; use tracing::{debug, error, trace}; +// TODO(enableNewSmithyRuntimeLaunch): New-type `Input`/`Output`/`Error` pub type Input = TypeErasedBox; pub type Output = TypeErasedBox; pub type Error = TypeErasedError; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs index 7a79c13427..64ebdbe08a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs @@ -193,18 +193,22 @@ impl<'a, I, O, E: Debug> From<&'a InterceptorContext> } impl<'a, I, O, E: Debug> FinalizerInterceptorContextRef<'a, I, O, E> { + /// Returns the operation input. pub fn input(&self) -> Option<&I> { self.inner.input.as_ref() } + /// Returns the serialized request. pub fn request(&self) -> Option<&Request> { self.inner.request.as_ref() } + /// Returns the raw response. pub fn response(&self) -> Option<&Response> { self.inner.response.as_ref() } + /// Returns the deserialized operation output or error. pub fn output_or_error(&self) -> Option>> { self.inner.output_or_error.as_ref().map(|o| o.as_ref()) } @@ -223,34 +227,42 @@ impl<'a, I, O, E: Debug> From<&'a mut InterceptorContext> } impl<'a, I, O, E: Debug> FinalizerInterceptorContextMut<'a, I, O, E> { + /// Returns the operation input. pub fn input(&self) -> Option<&I> { self.inner.input.as_ref() } + /// Returns the serialized request. pub fn request(&self) -> Option<&Request> { self.inner.request.as_ref() } + /// Returns the raw response. pub fn response(&self) -> Option<&Response> { self.inner.response.as_ref() } + /// Returns the deserialized operation output or error. pub fn output_or_error(&self) -> Option>> { self.inner.output_or_error.as_ref().map(|o| o.as_ref()) } + /// Mutably returns the operation input. pub fn input_mut(&mut self) -> Option<&mut I> { self.inner.input.as_mut() } + /// Mutably returns the serialized request. pub fn request_mut(&mut self) -> Option<&mut Request> { self.inner.request.as_mut() } + /// Mutably returns the raw response. pub fn response_mut(&mut self) -> Option<&mut Response> { self.inner.response.as_mut() } + /// Mutably returns the deserialized operation output or error. pub fn output_or_error_mut(&mut self) -> Option<&mut Result>> { self.inner.output_or_error.as_mut() } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs index f01dace42d..11ffbd60c9 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs @@ -181,6 +181,7 @@ pub struct ContextAttachedError { } impl ContextAttachedError { + /// Creates a new `ContextAttachedError` with the given `context` and `source`. pub fn new(context: impl Into, source: impl Into) -> Self { Self { context: context.into(), diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index ed68be978d..cadeccf0d0 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -3,154 +3,211 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Client request orchestration. +//! +//! The orchestrator handles the full request/response lifecycle including: +//! - Request serialization +//! - Endpoint resolution +//! - Identity resolution +//! - Signing +//! - Request transmission with retry and timeouts +//! - Response deserialization +//! +//! There are several hook points in the orchestration where [interceptors](crate::client::interceptors) +//! can read and modify the input, request, response, or output/error. + use crate::box_error::BoxError; -use crate::client::interceptors::context::{Error, Input, Output}; +use crate::client::interceptors::context::phase::Phase; +use crate::client::interceptors::InterceptorError; use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_http::body::SdkBody; -use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; -use aws_smithy_types::endpoint::Endpoint; -use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; +use aws_smithy_http::result::{ConnectorError, SdkError}; +use aws_smithy_types::config_bag::{Storable, StoreReplace}; +use aws_smithy_types::type_erasure::TypeErasedError; use bytes::Bytes; -use std::fmt; +use std::fmt::Debug; use std::future::Future as StdFuture; use std::pin::Pin; -use std::sync::Arc; - -/// Errors that can occur while running the orchestrator. -mod error; - -pub use error::OrchestratorError; +/// Type alias for the HTTP request type that the orchestrator uses. pub type HttpRequest = http::Request; + +/// Type alias for the HTTP response type that the orchestrator uses. pub type HttpResponse = http::Response; + +/// Type alias for boxed futures that are returned from several traits since async trait functions are not stable yet (as of 2023-07-21). +/// +/// See [the Rust blog](https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html) for +/// more information on async functions in traits. pub type BoxFuture = Pin> + Send>>; -pub type Future = NowOrLater, BoxFuture>; -pub trait RequestSerializer: Send + Sync + fmt::Debug { - fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result; -} +/// Type alias for futures that are returned from several traits since async trait functions are not stable yet (as of 2023-07-21). +/// +/// See [the Rust blog](https://blog.rust-lang.org/inside-rust/2023/05/03/stabilizing-async-fn-in-trait.html) for +/// more information on async functions in traits. +pub type Future = NowOrLater, BoxFuture>; +/// Informs the orchestrator on whether or not the request body needs to be loaded into memory before transmit. +/// +/// This enum gets placed into the `ConfigBag` to change the orchestrator behavior. +/// Immediately after serialization (before the `read_after_serialization` interceptor hook), +/// if it was set to `Requested` in the config bag, it will be replaced back into the config bag as +/// `Loaded` with the request body contents for use in later interceptors. +/// +/// This all happens before the attempt loop, so the loaded request body will remain available +/// for interceptors that run in any subsequent retry attempts. +#[non_exhaustive] #[derive(Clone, Debug)] -pub struct SharedRequestSerializer(Arc); - -impl SharedRequestSerializer { - pub fn new(serializer: impl RequestSerializer + 'static) -> Self { - Self(Arc::new(serializer)) - } -} - -impl RequestSerializer for SharedRequestSerializer { - fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result { - self.0.serialize_input(input, cfg) - } +pub enum LoadedRequestBody { + /// Don't attempt to load the request body into memory. + NotNeeded, + /// Attempt to load the request body into memory. + Requested, + /// The request body is already loaded. + Loaded(Bytes), } -impl Storable for SharedRequestSerializer { +impl Storable for LoadedRequestBody { type Storer = StoreReplace; } -pub trait ResponseDeserializer: Send + Sync + fmt::Debug { - fn deserialize_streaming( - &self, - response: &mut HttpResponse, - ) -> Option>> { - let _ = response; - None - } - - fn deserialize_nonstreaming( - &self, - response: &HttpResponse, - ) -> Result>; -} - +// TODO(enableNewSmithyRuntimeLaunch): Make OrchestratorError adhere to the errors RFC +/// Errors that can occur while running the orchestrator. #[derive(Debug)] -pub struct DynResponseDeserializer(Box); - -impl DynResponseDeserializer { - pub fn new(serializer: impl ResponseDeserializer + 'static) -> Self { - Self(Box::new(serializer)) - } +#[non_exhaustive] +pub enum OrchestratorError { + /// An error occurred within an interceptor. + Interceptor { err: InterceptorError }, + /// An error returned by a service. + Operation { err: E }, + /// An error that occurs when a request times out. + Timeout { err: BoxError }, + /// An error that occurs when request dispatch fails. + Connector { err: ConnectorError }, + /// An error that occurs when a response can't be deserialized. + Response { err: BoxError }, + /// A general orchestrator error. + Other { err: BoxError }, } -impl ResponseDeserializer for DynResponseDeserializer { - fn deserialize_nonstreaming( - &self, - response: &HttpResponse, - ) -> Result> { - self.0.deserialize_nonstreaming(response) +impl OrchestratorError { + /// Create a new `OrchestratorError` from a [`BoxError`]. + pub fn other(err: impl Into>) -> Self { + let err = err.into(); + Self::Other { err } } - fn deserialize_streaming( - &self, - response: &mut HttpResponse, - ) -> Option>> { - self.0.deserialize_streaming(response) + /// Create a new `OrchestratorError` from an error received from a service. + pub fn operation(err: E) -> Self { + Self::Operation { err } } -} -impl Storable for DynResponseDeserializer { - type Storer = StoreReplace; -} + /// Create a new `OrchestratorError::Interceptor` from an [`InterceptorError`]. + pub fn interceptor(err: InterceptorError) -> Self { + Self::Interceptor { err } + } -#[derive(Debug)] -pub struct EndpointResolverParams(TypeErasedBox); + /// Create a new `OrchestratorError::Timeout` from a [`BoxError`]. + pub fn timeout(err: BoxError) -> Self { + Self::Timeout { err } + } -impl EndpointResolverParams { - pub fn new(params: T) -> Self { - Self(TypedBox::new(params).erase()) + /// Create a new `OrchestratorError::Response` from a [`BoxError`]. + pub fn response(err: BoxError) -> Self { + Self::Response { err } } - pub fn get(&self) -> Option<&T> { - self.0.downcast_ref() + /// Create a new `OrchestratorError::Connector` from a [`ConnectorError`]. + pub fn connector(err: ConnectorError) -> Self { + Self::Connector { err } } -} -impl Storable for EndpointResolverParams { - type Storer = StoreReplace; -} + /// Convert the `OrchestratorError` into `Some` operation specific error if it is one. Otherwise, + /// return `None`. + pub fn as_operation_error(&self) -> Option<&E> { + match self { + Self::Operation { err } => Some(err), + _ => None, + } + } -pub trait EndpointResolver: Send + Sync + fmt::Debug { - fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future; + /// Convert the `OrchestratorError` into an [`SdkError`]. + pub(crate) fn into_sdk_error( + self, + phase: &Phase, + response: Option, + ) -> SdkError { + match self { + Self::Interceptor { err } => { + use Phase::*; + match phase { + BeforeSerialization | Serialization => SdkError::construction_failure(err), + BeforeTransmit | Transmit => match response { + Some(response) => SdkError::response_error(err, response), + None => SdkError::dispatch_failure(ConnectorError::other(err.into(), None)), + }, + BeforeDeserialization | Deserialization | AfterDeserialization => { + SdkError::response_error(err, response.expect("phase has a response")) + } + } + } + Self::Operation { err } => { + debug_assert!(phase.is_after_deserialization(), "operation errors are a result of successfully receiving and parsing a response from the server. Therefore, we must be in the 'After Deserialization' phase."); + SdkError::service_error(err, response.expect("phase has a response")) + } + Self::Connector { err } => SdkError::dispatch_failure(err), + Self::Timeout { err } => SdkError::timeout_error(err), + Self::Response { err } => SdkError::response_error(err, response.unwrap()), + Self::Other { err } => { + use Phase::*; + match phase { + BeforeSerialization | Serialization => SdkError::construction_failure(err), + BeforeTransmit | Transmit => convert_dispatch_error(err, response), + BeforeDeserialization | Deserialization | AfterDeserialization => { + SdkError::response_error(err, response.expect("phase has a response")) + } + } + } + } + } } -#[derive(Clone, Debug)] -pub struct SharedEndpointResolver(Arc); - -impl SharedEndpointResolver { - pub fn new(endpoint_resolver: impl EndpointResolver + 'static) -> Self { - Self(Arc::new(endpoint_resolver)) +fn convert_dispatch_error( + err: BoxError, + response: Option, +) -> SdkError { + let err = match err.downcast::() { + Ok(connector_error) => { + return SdkError::dispatch_failure(*connector_error); + } + Err(e) => e, + }; + match response { + Some(response) => SdkError::response_error(err, response), + None => SdkError::dispatch_failure(ConnectorError::other(err, None)), } } -impl EndpointResolver for SharedEndpointResolver { - fn resolve_endpoint(&self, params: &EndpointResolverParams) -> Future { - self.0.resolve_endpoint(params) +impl From for OrchestratorError +where + E: Debug + std::error::Error + 'static, +{ + fn from(err: InterceptorError) -> Self { + Self::interceptor(err) } } -/// Informs the orchestrator on whether or not the request body needs to be loaded into memory before transmit. -/// -/// This enum gets placed into the `ConfigBag` to change the orchestrator behavior. -/// Immediately after serialization (before the `read_after_serialization` interceptor hook), -/// if it was set to `Requested` in the config bag, it will be replaced back into the config bag as -/// `Loaded` with the request body contents for use in later interceptors. -/// -/// This all happens before the attempt loop, so the loaded request body will remain available -/// for interceptors that run in any subsequent retry attempts. -#[non_exhaustive] -#[derive(Clone, Debug)] -pub enum LoadedRequestBody { - /// Don't attempt to load the request body into memory. - NotNeeded, - /// Attempt to load the request body into memory. - Requested, - /// The request body is already loaded. - Loaded(Bytes), +impl From for OrchestratorError { + fn from(err: TypeErasedError) -> Self { + Self::operation(err) + } } -impl Storable for LoadedRequestBody { - type Storer = StoreReplace; +impl From for OrchestratorError +where + E: Debug + std::error::Error + 'static, +{ + fn from(err: aws_smithy_http::byte_stream::error::Error) -> Self { + Self::other(err) + } } - -pub(crate) const NOT_NEEDED: LoadedRequestBody = LoadedRequestBody::NotNeeded; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs deleted file mode 100644 index fa2aa7361a..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator/error.rs +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use super::BoxError; -use crate::client::interceptors::context::phase::Phase; -use crate::client::interceptors::InterceptorError; -use crate::client::orchestrator::HttpResponse; -use aws_smithy_http::result::{ConnectorError, SdkError}; -use aws_smithy_types::type_erasure::TypeErasedError; -use std::fmt::Debug; - -#[derive(Debug)] -#[non_exhaustive] -pub enum OrchestratorError { - /// An error occurred within an interceptor. - Interceptor { err: InterceptorError }, - /// An error returned by a service. - Operation { err: E }, - /// An error that occurs when a request times out. - Timeout { err: BoxError }, - /// An error that occurs when request dispatch fails. - Connector { err: ConnectorError }, - /// An error that occurs when a response can't be deserialized. - Response { err: BoxError }, - /// A general orchestrator error. - Other { err: BoxError }, -} - -impl OrchestratorError { - /// Create a new `OrchestratorError` from a [`BoxError`]. - pub fn other(err: impl Into>) -> Self { - let err = err.into(); - Self::Other { err } - } - - /// Create a new `OrchestratorError` from an error received from a service. - pub fn operation(err: E) -> Self { - Self::Operation { err } - } - - /// Create a new `OrchestratorError::Interceptor` from an [`InterceptorError`]. - pub fn interceptor(err: InterceptorError) -> Self { - Self::Interceptor { err } - } - - /// Create a new `OrchestratorError::Timeout` from a [`BoxError`]. - pub fn timeout(err: BoxError) -> Self { - Self::Timeout { err } - } - - /// Create a new `OrchestratorError::Response` from a [`BoxError`]. - pub fn response(err: BoxError) -> Self { - Self::Response { err } - } - - /// Create a new `OrchestratorError::Connector` from a [`ConnectorError`]. - pub fn connector(err: ConnectorError) -> Self { - Self::Connector { err } - } - - /// Convert the `OrchestratorError` into `Some` operation specific error if it is one. Otherwise, - /// return `None`. - pub fn as_operation_error(&self) -> Option<&E> { - match self { - Self::Operation { err } => Some(err), - _ => None, - } - } - - /// Convert the `OrchestratorError` into an [`SdkError`]. - pub(crate) fn into_sdk_error( - self, - phase: &Phase, - response: Option, - ) -> SdkError { - match self { - Self::Interceptor { err } => { - use Phase::*; - match phase { - BeforeSerialization | Serialization => SdkError::construction_failure(err), - BeforeTransmit | Transmit => match response { - Some(response) => SdkError::response_error(err, response), - None => SdkError::dispatch_failure(ConnectorError::other(err.into(), None)), - }, - BeforeDeserialization | Deserialization | AfterDeserialization => { - SdkError::response_error(err, response.expect("phase has a response")) - } - } - } - Self::Operation { err } => { - debug_assert!(phase.is_after_deserialization(), "operation errors are a result of successfully receiving and parsing a response from the server. Therefore, we must be in the 'After Deserialization' phase."); - SdkError::service_error(err, response.expect("phase has a response")) - } - Self::Connector { err } => SdkError::dispatch_failure(err), - Self::Timeout { err } => SdkError::timeout_error(err), - Self::Response { err } => SdkError::response_error(err, response.unwrap()), - Self::Other { err } => { - use Phase::*; - match phase { - BeforeSerialization | Serialization => SdkError::construction_failure(err), - BeforeTransmit | Transmit => convert_dispatch_error(err, response), - BeforeDeserialization | Deserialization | AfterDeserialization => { - SdkError::response_error(err, response.expect("phase has a response")) - } - } - } - } - } -} - -fn convert_dispatch_error( - err: BoxError, - response: Option, -) -> SdkError { - let err = match err.downcast::() { - Ok(connector_error) => { - return SdkError::dispatch_failure(*connector_error); - } - Err(e) => e, - }; - match response { - Some(response) => SdkError::response_error(err, response), - None => SdkError::dispatch_failure(ConnectorError::other(err, None)), - } -} - -impl From for OrchestratorError -where - E: Debug + std::error::Error + 'static, -{ - fn from(err: InterceptorError) -> Self { - Self::interceptor(err) - } -} - -impl From for OrchestratorError { - fn from(err: TypeErasedError) -> Self { - Self::operation(err) - } -} - -impl From for OrchestratorError -where - E: Debug + std::error::Error + 'static, -{ - fn from(err: aws_smithy_http::byte_stream::error::Error) -> Self { - Self::other(err) - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs b/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs deleted file mode 100644 index 440019c660..0000000000 --- a/rust-runtime/aws-smithy-runtime-api/src/client/request_attempts.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_smithy_types::config_bag::{Storable, StoreReplace}; - -#[derive(Debug, Clone, Copy)] -pub struct RequestAttempts { - attempts: u32, -} - -impl RequestAttempts { - #[cfg(any(feature = "test-util", test))] - pub fn new(attempts: u32) -> Self { - Self { attempts } - } - - pub fn attempts(&self) -> u32 { - self.attempts - } -} - -impl From for RequestAttempts { - fn from(attempts: u32) -> Self { - Self { attempts } - } -} - -impl Storable for RequestAttempts { - type Storer = StoreReplace; -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs index 37aa72d9ce..0703e87d87 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/retries.rs @@ -3,8 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Retry handling and token bucket. +//! +//! This code defines when and how failed requests should be retried. It also defines the behavior +//! used to limit the rate that requests are sent. + use crate::client::interceptors::context::InterceptorContext; -use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use std::fmt::Debug; use std::time::Duration; use tracing::trace; @@ -14,13 +19,17 @@ pub use aws_smithy_types::retry::ErrorKind; #[derive(Debug, Clone, PartialEq, Eq)] /// An answer to the question "should I make a request attempt?" pub enum ShouldAttempt { + /// Yes, an attempt should be made Yes, + /// No, no attempt should be made No, + /// Yes, an attempt should be made, but only after the given amount of time has passed YesAfterDelay(Duration), } #[cfg(feature = "test-util")] impl ShouldAttempt { + /// Returns the delay duration if this is a `YesAfterDelay` variant. pub fn expect_delay(self) -> Duration { match self { ShouldAttempt::YesAfterDelay(delay) => delay, @@ -29,13 +38,26 @@ impl ShouldAttempt { } } +/// Decider for whether or not to attempt a request, and when. +/// +/// The orchestrator consults the retry strategy every time before making a request. +/// This includes the initial request, and any retry attempts thereafter. The +/// orchestrator will retry indefinitely (until success) if the retry strategy +/// always returns `ShouldAttempt::Yes` from `should_attempt_retry`. pub trait RetryStrategy: Send + Sync + Debug { + /// Decides if the initial attempt should be made. fn should_attempt_initial_request( &self, runtime_components: &RuntimeComponents, cfg: &ConfigBag, ) -> Result; + /// Decides if a retry should be done. + /// + /// The previous attempt's output or error are provided in the + /// [`InterceptorContext`] when this is called. + /// + /// `ShouldAttempt::YesAfterDelay` can be used to add a backoff time. fn should_attempt_retry( &self, context: &InterceptorContext, @@ -44,10 +66,12 @@ pub trait RetryStrategy: Send + Sync + Debug { ) -> Result; } +/// A shared retry strategy. #[derive(Clone, Debug)] pub struct SharedRetryStrategy(Arc); impl SharedRetryStrategy { + /// Creates a new [`SharedRetryStrategy`] from a retry strategy. pub fn new(retry_strategy: impl RetryStrategy + 'static) -> Self { Self(Arc::new(retry_strategy)) } @@ -74,10 +98,13 @@ impl RetryStrategy for SharedRetryStrategy { } } +/// Classification result from [`ClassifyRetry`]. #[non_exhaustive] #[derive(Clone, Eq, PartialEq, Debug)] pub enum RetryReason { + /// There was an unexpected error, and this is the kind of error so that it can be properly retried. Error(ErrorKind), + /// The server explicitly told us to back off by this amount of time. Explicit(Duration), } @@ -91,12 +118,14 @@ pub trait ClassifyRetry: Send + Sync + Debug { fn name(&self) -> &'static str; } +/// Classifies an error into a [`RetryReason`]. #[derive(Clone, Debug)] pub struct RetryClassifiers { inner: Vec>, } impl RetryClassifiers { + /// Creates a new [`RetryClassifiers`]. pub fn new() -> Self { Self { // It's always expected that at least one classifier will be defined, @@ -105,6 +134,7 @@ impl RetryClassifiers { } } + /// Adds a classifier to this collection. pub fn with_classifier(mut self, retry_classifier: impl ClassifyRetry + 'static) -> Self { self.inner.push(Arc::new(retry_classifier)); self @@ -138,6 +168,43 @@ impl ClassifyRetry for RetryClassifiers { } } +/// A type to track the number of requests sent by the orchestrator for a given operation. +/// +/// `RequestAttempts` is added to the `ConfigBag` by the orchestrator, +/// and holds the current attempt number. +#[derive(Debug, Clone, Copy)] +pub struct RequestAttempts { + attempts: u32, +} + +impl RequestAttempts { + /// Creates a new [`RequestAttempts`] with the given number of attempts. + pub fn new(attempts: u32) -> Self { + Self { attempts } + } + + /// Returns the number of attempts. + pub fn attempts(&self) -> u32 { + self.attempts + } +} + +impl From for RequestAttempts { + fn from(attempts: u32) -> Self { + Self::new(attempts) + } +} + +impl From for u32 { + fn from(value: RequestAttempts) -> Self { + value.attempts() + } +} + +impl Storable for RequestAttempts { + type Storer = StoreReplace; +} + #[cfg(feature = "test-util")] mod test_util { use super::{ClassifyRetry, ErrorKind, RetryReason}; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs index a00fe57671..520c164e74 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_components.rs @@ -3,13 +3,21 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Runtime components used to make a request and handle a response. +//! +//! Runtime components are trait implementations that are _always_ used by the orchestrator. +//! There are other trait implementations that can be configured for a client, but if they +//! aren't directly and always used by the orchestrator, then they are placed in the +//! [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag) instead of in +//! [`RuntimeComponents`](RuntimeComponents). + use crate::client::auth::{ - AuthSchemeId, HttpAuthScheme, SharedAuthOptionResolver, SharedHttpAuthScheme, + AuthScheme, AuthSchemeId, SharedAuthScheme, SharedAuthSchemeOptionResolver, }; -use crate::client::connectors::SharedConnector; +use crate::client::connectors::SharedHttpConnector; +use crate::client::endpoint::SharedEndpointResolver; use crate::client::identity::{ConfiguredIdentityResolver, SharedIdentityResolver}; use crate::client::interceptors::SharedInterceptor; -use crate::client::orchestrator::SharedEndpointResolver; use crate::client::retries::{RetryClassifiers, SharedRetryStrategy}; use aws_smithy_async::rt::sleep::SharedAsyncSleep; use aws_smithy_async::time::SharedTimeSource; @@ -134,6 +142,7 @@ macro_rules! declare_runtime_components { $($field_name: runtime_component_field_type!($outer_type $inner_type $($option)?),)+ } + /// Builder for [`RuntimeComponents`]. #[derive(Clone, Debug)] pub struct $builder_name { builder_name: &'static str, @@ -171,16 +180,16 @@ macro_rules! declare_runtime_components { declare_runtime_components! { fields for RuntimeComponents and RuntimeComponentsBuilder { #[required] - auth_option_resolver: Option, + auth_scheme_option_resolver: Option, // A connector is not required since a client could technically only be used for presigning - connector: Option, + http_connector: Option, #[required] endpoint_resolver: Option, #[atLeastOneRequired] - http_auth_schemes: Vec, + auth_schemes: Vec, #[atLeastOneRequired] identity_resolvers: Vec, @@ -204,14 +213,14 @@ impl RuntimeComponents { RuntimeComponentsBuilder::new(name) } - /// Returns the auth option resolver. - pub fn auth_option_resolver(&self) -> SharedAuthOptionResolver { - self.auth_option_resolver.value.clone() + /// Returns the auth scheme option resolver. + pub fn auth_scheme_option_resolver(&self) -> SharedAuthSchemeOptionResolver { + self.auth_scheme_option_resolver.value.clone() } /// Returns the connector. - pub fn connector(&self) -> Option { - self.connector.as_ref().map(|s| s.value.clone()) + pub fn http_connector(&self) -> Option { + self.http_connector.as_ref().map(|s| s.value.clone()) } /// Returns the endpoint resolver. @@ -220,8 +229,8 @@ impl RuntimeComponents { } /// Returns the requested auth scheme if it is set. - pub fn http_auth_scheme(&self, scheme_id: AuthSchemeId) -> Option { - self.http_auth_schemes + pub fn auth_scheme(&self, scheme_id: AuthSchemeId) -> Option { + self.auth_schemes .iter() .find(|s| s.value.scheme_id() == scheme_id) .map(|s| s.value.clone()) @@ -254,44 +263,46 @@ impl RuntimeComponents { } impl RuntimeComponentsBuilder { - /// Returns the auth option resolver. - pub fn auth_option_resolver(&self) -> Option { - self.auth_option_resolver.as_ref().map(|s| s.value.clone()) + /// Returns the auth scheme option resolver. + pub fn auth_scheme_option_resolver(&self) -> Option { + self.auth_scheme_option_resolver + .as_ref() + .map(|s| s.value.clone()) } - /// Sets the auth option resolver. - pub fn set_auth_option_resolver( + /// Sets the auth scheme option resolver. + pub fn set_auth_scheme_option_resolver( &mut self, - auth_option_resolver: Option, + auth_scheme_option_resolver: Option, ) -> &mut Self { - self.auth_option_resolver = - auth_option_resolver.map(|r| Tracked::new(self.builder_name, r)); + self.auth_scheme_option_resolver = + auth_scheme_option_resolver.map(|r| Tracked::new(self.builder_name, r)); self } - /// Sets the auth option resolver. - pub fn with_auth_option_resolver( + /// Sets the auth scheme option resolver. + pub fn with_auth_scheme_option_resolver( mut self, - auth_option_resolver: Option, + auth_scheme_option_resolver: Option, ) -> Self { - self.set_auth_option_resolver(auth_option_resolver); + self.set_auth_scheme_option_resolver(auth_scheme_option_resolver); self } - /// Returns the connector. - pub fn connector(&self) -> Option { - self.connector.as_ref().map(|s| s.value.clone()) + /// Returns the HTTP connector. + pub fn http_connector(&self) -> Option { + self.http_connector.as_ref().map(|s| s.value.clone()) } - /// Sets the connector. - pub fn set_connector(&mut self, connector: Option) -> &mut Self { - self.connector = connector.map(|c| Tracked::new(self.builder_name, c)); + /// Sets the HTTP connector. + pub fn set_http_connector(&mut self, connector: Option) -> &mut Self { + self.http_connector = connector.map(|c| Tracked::new(self.builder_name, c)); self } - /// Sets the connector. - pub fn with_connector(mut self, connector: Option) -> Self { - self.set_connector(connector); + /// Sets the HTTP connector. + pub fn with_http_connector(mut self, connector: Option) -> Self { + self.set_http_connector(connector); self } @@ -318,21 +329,21 @@ impl RuntimeComponentsBuilder { self } - /// Returns the HTTP auth schemes. - pub fn http_auth_schemes(&self) -> impl Iterator + '_ { - self.http_auth_schemes.iter().map(|s| s.value.clone()) + /// Returns the auth schemes. + pub fn auth_schemes(&self) -> impl Iterator + '_ { + self.auth_schemes.iter().map(|s| s.value.clone()) } - /// Adds a HTTP auth scheme. - pub fn push_http_auth_scheme(&mut self, auth_scheme: SharedHttpAuthScheme) -> &mut Self { - self.http_auth_schemes + /// Adds an auth scheme. + pub fn push_auth_scheme(&mut self, auth_scheme: SharedAuthScheme) -> &mut Self { + self.auth_schemes .push(Tracked::new(self.builder_name, auth_scheme)); self } - /// Adds a HTTP auth scheme. - pub fn with_http_auth_scheme(mut self, auth_scheme: SharedHttpAuthScheme) -> Self { - self.push_http_auth_scheme(auth_scheme); + /// Adds an auth scheme. + pub fn with_auth_scheme(mut self, auth_scheme: SharedAuthScheme) -> Self { + self.push_auth_scheme(auth_scheme); self } @@ -499,11 +510,12 @@ impl RuntimeComponentsBuilder { /// Creates a runtime components builder with all the required components filled in with fake (panicking) implementations. #[cfg(feature = "test-util")] pub fn for_tests() -> Self { - use crate::client::auth::AuthOptionResolver; - use crate::client::connectors::Connector; + use crate::client::auth::AuthSchemeOptionResolver; + use crate::client::connectors::HttpConnector; + use crate::client::endpoint::{EndpointResolver, EndpointResolverParams}; use crate::client::identity::Identity; use crate::client::identity::IdentityResolver; - use crate::client::orchestrator::{EndpointResolver, EndpointResolverParams, Future}; + use crate::client::orchestrator::Future; use crate::client::retries::RetryStrategy; use aws_smithy_async::rt::sleep::AsyncSleep; use aws_smithy_async::time::TimeSource; @@ -511,20 +523,20 @@ impl RuntimeComponentsBuilder { use aws_smithy_types::endpoint::Endpoint; #[derive(Debug)] - struct FakeAuthOptionResolver; - impl AuthOptionResolver for FakeAuthOptionResolver { - fn resolve_auth_options( + struct FakeAuthSchemeOptionResolver; + impl AuthSchemeOptionResolver for FakeAuthSchemeOptionResolver { + fn resolve_auth_scheme_options( &self, - _: &crate::client::auth::AuthOptionResolverParams, + _: &crate::client::auth::AuthSchemeOptionResolverParams, ) -> Result, crate::box_error::BoxError> { - unreachable!("fake auth option resolver must be overridden for this test") + unreachable!("fake auth scheme option resolver must be overridden for this test") } } #[derive(Debug)] struct FakeConnector; - impl Connector for FakeConnector { + impl HttpConnector for FakeConnector { fn call( &self, _: crate::client::orchestrator::HttpRequest, @@ -543,8 +555,8 @@ impl RuntimeComponentsBuilder { } #[derive(Debug)] - struct FakeHttpAuthScheme; - impl HttpAuthScheme for FakeHttpAuthScheme { + struct FakeAuthScheme; + impl AuthScheme for FakeAuthScheme { fn scheme_id(&self) -> AuthSchemeId { AuthSchemeId::new("fake") } @@ -556,7 +568,7 @@ impl RuntimeComponentsBuilder { None } - fn request_signer(&self) -> &dyn crate::client::auth::HttpRequestSigner { + fn signer(&self) -> &dyn crate::client::auth::Signer { unreachable!("fake http auth scheme must be overridden for this test") } } @@ -609,18 +621,19 @@ impl RuntimeComponentsBuilder { } Self::new("aws_smithy_runtime_api::client::runtime_components::RuntimeComponentBuilder::for_tests") - .with_auth_option_resolver(Some(SharedAuthOptionResolver::new(FakeAuthOptionResolver))) - .with_connector(Some(SharedConnector::new(FakeConnector))) + .with_auth_scheme(SharedAuthScheme::new(FakeAuthScheme)) + .with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new(FakeAuthSchemeOptionResolver))) .with_endpoint_resolver(Some(SharedEndpointResolver::new(FakeEndpointResolver))) - .with_http_auth_scheme(SharedHttpAuthScheme::new(FakeHttpAuthScheme)) + .with_http_connector(Some(SharedHttpConnector::new(FakeConnector))) .with_identity_resolver(AuthSchemeId::new("fake"), SharedIdentityResolver::new(FakeIdentityResolver)) .with_retry_classifiers(Some(RetryClassifiers::new())) .with_retry_strategy(Some(SharedRetryStrategy::new(FakeRetryStrategy))) - .with_time_source(Some(SharedTimeSource::new(FakeTimeSource))) .with_sleep_impl(Some(SharedAsyncSleep::new(FakeSleep))) + .with_time_source(Some(SharedTimeSource::new(FakeTimeSource))) } } +/// An error that occurs when building runtime components. #[derive(Debug)] pub struct BuildError(&'static str); @@ -634,7 +647,7 @@ impl fmt::Display for BuildError { /// A trait for retrieving a shared identity resolver. /// -/// This trait exists so that [`HttpAuthScheme::identity_resolver`](crate::client::auth::HttpAuthScheme::identity_resolver) +/// This trait exists so that [`AuthScheme::identity_resolver`](crate::client::auth::AuthScheme::identity_resolver) /// can have access to configured identity resolvers without having access to all the runtime components. pub trait GetIdentityResolver: Send + Sync { /// Returns the requested identity resolver if it is set. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs index 609375b210..abc687ca66 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/runtime_plugin.rs @@ -3,6 +3,21 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Runtime plugin type definitions. +//! +//! Runtime plugins are used to extend the runtime with custom behavior. +//! This can include: +//! - Registering interceptors +//! - Registering auth schemes +//! - Adding entries to the [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag) for orchestration +//! - Setting runtime components +//! +//! Runtime plugins are divided into service/operation "levels", with service runtime plugins +//! executing before operation runtime plugins. Runtime plugins configured in a service +//! config will always be at the service level, while runtime plugins added during +//! operation customization will be at the operation level. Custom runtime plugins will +//! always run after the default runtime plugins within their level. + use crate::box_error::BoxError; use crate::client::runtime_components::{ RuntimeComponentsBuilder, EMPTY_RUNTIME_COMPONENTS_BUILDER, @@ -12,25 +27,44 @@ use std::borrow::Cow; use std::fmt::Debug; use std::sync::Arc; -/// RuntimePlugin Trait +/// Runtime plugin trait /// -/// A RuntimePlugin is the unit of configuration for augmenting the SDK with new behavior. +/// A `RuntimePlugin` is the unit of configuration for augmenting the SDK with new behavior. /// /// Runtime plugins can register interceptors, set runtime components, and modify configuration. pub trait RuntimePlugin: Debug + Send + Sync { + /// Optionally returns additional config that should be added to the [`ConfigBag`](aws_smithy_types::config_bag::ConfigBag). + /// + /// As a best practice, a frozen layer should be stored on the runtime plugin instance as + /// a member, and then cloned upon return since that clone is cheap. Constructing a new + /// [`Layer`](aws_smithy_types::config_bag::Layer) and freezing it will require a lot of allocations. fn config(&self) -> Option { None } + /// Returns a [`RuntimeComponentsBuilder`](RuntimeComponentsBuilder) to incorporate into the final runtime components. + /// + /// The order of runtime plugins determines which runtime components "win". Components set by later runtime plugins will + /// override those set by earlier runtime plugins. + /// + /// If no runtime component changes are desired, just return an empty builder. + /// + /// This method returns a [`Cow`] for flexibility. Some implementers may want to store the components builder + /// as a member and return a reference to it, while others may need to create the builder every call. If possible, + /// returning a reference is preferred for performance. fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { Cow::Borrowed(&EMPTY_RUNTIME_COMPONENTS_BUILDER) } } +/// Shared runtime plugin +/// +/// Allows for multiple places to share ownership of one runtime plugin. #[derive(Debug, Clone)] pub struct SharedRuntimePlugin(Arc); impl SharedRuntimePlugin { + /// Returns a new [`SharedRuntimePlugin`]. pub fn new(plugin: impl RuntimePlugin + 'static) -> Self { Self(Arc::new(plugin)) } @@ -46,6 +80,47 @@ impl RuntimePlugin for SharedRuntimePlugin { } } +/// Runtime plugin that simply returns the config and components given at construction time. +#[derive(Default, Debug)] +pub struct StaticRuntimePlugin { + config: Option, + runtime_components: Option, +} + +impl StaticRuntimePlugin { + /// Returns a new [`StaticRuntimePlugin`]. + pub fn new() -> Self { + Default::default() + } + + /// Changes the config. + pub fn with_config(mut self, config: FrozenLayer) -> Self { + self.config = Some(config); + self + } + + /// Changes the runtime components. + pub fn with_runtime_components(mut self, runtime_components: RuntimeComponentsBuilder) -> Self { + self.runtime_components = Some(runtime_components); + self + } +} + +impl RuntimePlugin for StaticRuntimePlugin { + fn config(&self) -> Option { + self.config.clone() + } + + fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { + self.runtime_components + .as_ref() + .map(Cow::Borrowed) + .unwrap_or_else(|| RuntimePlugin::runtime_components(self)) + } +} + +/// Used internally in the orchestrator implementation and in the generated code. Not intended to be used elsewhere. +#[doc(hidden)] #[derive(Default, Clone, Debug)] pub struct RuntimePlugins { client_plugins: Vec, @@ -99,41 +174,6 @@ impl RuntimePlugins { } } -#[derive(Default, Debug)] -pub struct StaticRuntimePlugin { - config: Option, - runtime_components: Option, -} - -impl StaticRuntimePlugin { - pub fn new() -> Self { - Default::default() - } - - pub fn with_config(mut self, config: FrozenLayer) -> Self { - self.config = Some(config); - self - } - - pub fn with_runtime_components(mut self, runtime_components: RuntimeComponentsBuilder) -> Self { - self.runtime_components = Some(runtime_components); - self - } -} - -impl RuntimePlugin for StaticRuntimePlugin { - fn config(&self) -> Option { - self.config.clone() - } - - fn runtime_components(&self) -> Cow<'_, RuntimeComponentsBuilder> { - self.runtime_components - .as_ref() - .map(Cow::Borrowed) - .unwrap_or_else(|| RuntimePlugin::runtime_components(self)) - } -} - #[cfg(test)] mod tests { use super::{RuntimePlugin, RuntimePlugins}; diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/ser_de.rs b/rust-runtime/aws-smithy-runtime-api/src/client/ser_de.rs new file mode 100644 index 0000000000..5a3a77988e --- /dev/null +++ b/rust-runtime/aws-smithy-runtime-api/src/client/ser_de.rs @@ -0,0 +1,105 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Serialization/deserialization for the orchestrator. + +use crate::box_error::BoxError; +use crate::client::interceptors::context::{Error, Input, Output}; +use crate::client::orchestrator::{HttpRequest, HttpResponse, OrchestratorError}; +use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; +use std::fmt; +use std::sync::Arc; + +/// Serialization implementation that converts an [`Input`] into an [`HttpRequest`]. +pub trait RequestSerializer: Send + Sync + fmt::Debug { + /// Serializes the input into an HTTP request. + /// + /// The type of the [`Input`] must be known ahead of time by the request serializer + /// implementation, and must be downcasted to get access to the information necessary + /// for serialization. + /// + /// The request serializer is generally added to the [`ConfigBag`] by the operation's + /// code generated runtime plugin, which is aware of the correct input/output/error types. + fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result; +} + +/// A shared request serializer. +/// +/// This is a simple shared ownership wrapper type for the [`RequestSerializer`] trait. +#[derive(Clone, Debug)] +pub struct SharedRequestSerializer(Arc); + +impl SharedRequestSerializer { + /// Creates a new shared request serializer. + pub fn new(serializer: impl RequestSerializer + 'static) -> Self { + Self(Arc::new(serializer)) + } +} + +impl RequestSerializer for SharedRequestSerializer { + fn serialize_input(&self, input: Input, cfg: &mut ConfigBag) -> Result { + self.0.serialize_input(input, cfg) + } +} + +impl Storable for SharedRequestSerializer { + type Storer = StoreReplace; +} + +/// Deserialization implementation that converts an [`HttpResponse`] into an [`Output`] or [`Error`]. +pub trait ResponseDeserializer: Send + Sync + fmt::Debug { + /// For streaming requests, deserializes the response headers. + /// + /// The orchestrator will call `deserialize_streaming` first, and if it returns `None`, + /// then it will continue onto `deserialize_nonstreaming`. This method should only be + /// implemented for streaming requests where the streaming response body needs to be a part + /// of the deserialized output. + fn deserialize_streaming( + &self, + response: &mut HttpResponse, + ) -> Option>> { + let _ = response; + None + } + + /// Deserialize the entire response including its body into an output or error. + fn deserialize_nonstreaming( + &self, + response: &HttpResponse, + ) -> Result>; +} + +/// Shared response deserializer. +/// +/// This is a simple shared ownership wrapper type for the [`ResponseDeserializer`] trait. +#[derive(Debug)] +pub struct SharedResponseDeserializer(Arc); + +impl SharedResponseDeserializer { + /// Creates a new [`SharedResponseDeserializer`]. + pub fn new(serializer: impl ResponseDeserializer + 'static) -> Self { + Self(Arc::new(serializer)) + } +} + +impl ResponseDeserializer for SharedResponseDeserializer { + fn deserialize_nonstreaming( + &self, + response: &HttpResponse, + ) -> Result> { + self.0.deserialize_nonstreaming(response) + } + + fn deserialize_streaming( + &self, + response: &mut HttpResponse, + ) -> Option>> { + self.0.deserialize_streaming(response) + } +} + +impl Storable for SharedResponseDeserializer { + type Storer = StoreReplace; +} diff --git a/rust-runtime/aws-smithy-runtime-api/src/lib.rs b/rust-runtime/aws-smithy-runtime-api/src/lib.rs index 8cc3db6309..3f95c9304a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/lib.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/lib.rs @@ -4,6 +4,7 @@ */ #![warn( + // TODO(enableNewSmithyRuntimeLaunch): Add in remaining missing docs // missing_docs, rustdoc::missing_crate_level_docs, unreachable_pub, @@ -11,12 +12,26 @@ )] #![allow(clippy::new_without_default)] -//! Basic types for the new smithy client orchestrator. +//! APIs needed to configure and customize the Smithy generated code. +//! +//! Most users will not need to use this crate directly as the most frequently used +//! APIs are re-exported in the generated clients. However, this crate will be useful +//! for anyone writing a library for others to use with their generated clients. +//! +//! If you're needing to depend on this and you're not writing a library for Smithy +//! generated clients, then please file an issue on [smithy-rs](https://github.com/awslabs/smithy-rs) +//! as we likely missed re-exporting one of the APIs. +//! +//! All client-specific code is in the [`client`](crate::client) root level module +//! to leave room for smithy-rs server APIs in the future. /// A boxed error that is `Send` and `Sync`. pub mod box_error; -/// Smithy runtime for client orchestration. +/// APIs for client orchestration. +#[cfg(feature = "client")] pub mod client; +/// Internal builder macros. Not intended to be used outside of the aws-smithy-runtime crates. +#[doc(hidden)] pub mod macros; diff --git a/rust-runtime/aws-smithy-runtime-api/src/macros.rs b/rust-runtime/aws-smithy-runtime-api/src/macros.rs index 09efd0a74c..759d1b8452 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/macros.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/macros.rs @@ -111,6 +111,7 @@ /// } /// } /// ``` +#[doc(hidden)] #[macro_export] macro_rules! builder { ($($tt:tt)+) => { @@ -128,6 +129,7 @@ macro_rules! builder { /// Define a new builder struct, its fields, and their docs. This macro is intended to be called /// by the `builder!` macro and should not be called directly. +#[doc(hidden)] #[macro_export] macro_rules! builder_struct { ($($_setter_name:ident, $field_name:ident, $ty:ty, $doc:literal $(,)?)+) => { @@ -143,6 +145,7 @@ macro_rules! builder_struct { /// Define setter methods for a builder struct. Must be called from within an `impl` block. This /// macro is intended to be called by the `builder!` macro and should not be called directly. +#[doc(hidden)] #[macro_export] macro_rules! builder_methods { ($fn_name:ident, $arg_name:ident, $ty:ty, $doc:literal, $($tail:tt)+) => { diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index 8f5dc864ee..a59e20abdd 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -10,6 +10,7 @@ repository = "https://github.com/awslabs/smithy-rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] +client = ["aws-smithy-runtime-api/client"] http-auth = ["aws-smithy-runtime-api/http-auth"] test-util = ["dep:aws-smithy-protocol-test", "dep:tracing-subscriber"] diff --git a/rust-runtime/aws-smithy-runtime/README.md b/rust-runtime/aws-smithy-runtime/README.md index f283643f8c..667b2267ff 100644 --- a/rust-runtime/aws-smithy-runtime/README.md +++ b/rust-runtime/aws-smithy-runtime/README.md @@ -1,7 +1,5 @@ # aws-smithy-runtime -**This crate is UNSTABLE! All internal and external interfaces are subject to change without notice.** - Runtime support logic and types for smithy-rs generated code. diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs index 1dda4dbe58..8593f0d9a5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs @@ -10,7 +10,7 @@ use aws_smithy_runtime_api::client::auth::http::{ HTTP_DIGEST_AUTH_SCHEME_ID, }; use aws_smithy_runtime_api::client::auth::{ - AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, + AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, Signer, }; use aws_smithy_runtime_api::client::identity::http::{Login, Token}; use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver}; @@ -51,7 +51,7 @@ impl ApiKeyAuthScheme { } } -impl HttpAuthScheme for ApiKeyAuthScheme { +impl AuthScheme for ApiKeyAuthScheme { fn scheme_id(&self) -> AuthSchemeId { HTTP_API_KEY_AUTH_SCHEME_ID } @@ -63,7 +63,7 @@ impl HttpAuthScheme for ApiKeyAuthScheme { identity_resolvers.identity_resolver(self.scheme_id()) } - fn request_signer(&self) -> &dyn HttpRequestSigner { + fn signer(&self) -> &dyn Signer { &self.signer } } @@ -75,8 +75,8 @@ struct ApiKeySigner { name: String, } -impl HttpRequestSigner for ApiKeySigner { - fn sign_request( +impl Signer for ApiKeySigner { + fn sign_http_request( &self, request: &mut HttpRequest, identity: &Identity, @@ -122,7 +122,7 @@ impl BasicAuthScheme { } } -impl HttpAuthScheme for BasicAuthScheme { +impl AuthScheme for BasicAuthScheme { fn scheme_id(&self) -> AuthSchemeId { HTTP_BASIC_AUTH_SCHEME_ID } @@ -134,7 +134,7 @@ impl HttpAuthScheme for BasicAuthScheme { identity_resolvers.identity_resolver(self.scheme_id()) } - fn request_signer(&self) -> &dyn HttpRequestSigner { + fn signer(&self) -> &dyn Signer { &self.signer } } @@ -142,8 +142,8 @@ impl HttpAuthScheme for BasicAuthScheme { #[derive(Debug, Default)] struct BasicAuthSigner; -impl HttpRequestSigner for BasicAuthSigner { - fn sign_request( +impl Signer for BasicAuthSigner { + fn sign_http_request( &self, request: &mut HttpRequest, identity: &Identity, @@ -181,7 +181,7 @@ impl BearerAuthScheme { } } -impl HttpAuthScheme for BearerAuthScheme { +impl AuthScheme for BearerAuthScheme { fn scheme_id(&self) -> AuthSchemeId { HTTP_BEARER_AUTH_SCHEME_ID } @@ -193,7 +193,7 @@ impl HttpAuthScheme for BearerAuthScheme { identity_resolvers.identity_resolver(self.scheme_id()) } - fn request_signer(&self) -> &dyn HttpRequestSigner { + fn signer(&self) -> &dyn Signer { &self.signer } } @@ -201,8 +201,8 @@ impl HttpAuthScheme for BearerAuthScheme { #[derive(Debug, Default)] struct BearerAuthSigner; -impl HttpRequestSigner for BearerAuthSigner { - fn sign_request( +impl Signer for BearerAuthSigner { + fn sign_http_request( &self, request: &mut HttpRequest, identity: &Identity, @@ -238,7 +238,7 @@ impl DigestAuthScheme { } } -impl HttpAuthScheme for DigestAuthScheme { +impl AuthScheme for DigestAuthScheme { fn scheme_id(&self) -> AuthSchemeId { HTTP_DIGEST_AUTH_SCHEME_ID } @@ -250,7 +250,7 @@ impl HttpAuthScheme for DigestAuthScheme { identity_resolvers.identity_resolver(self.scheme_id()) } - fn request_signer(&self) -> &dyn HttpRequestSigner { + fn signer(&self) -> &dyn Signer { &self.signer } } @@ -258,8 +258,8 @@ impl HttpAuthScheme for DigestAuthScheme { #[derive(Debug, Default)] struct DigestAuthSigner; -impl HttpRequestSigner for DigestAuthSigner { - fn sign_request( +impl Signer for DigestAuthSigner { + fn sign_http_request( &self, _request: &mut HttpRequest, _identity: &Identity, @@ -295,7 +295,7 @@ mod tests { .body(SdkBody::empty()) .unwrap(); signer - .sign_request( + .sign_http_request( &mut request, &identity, AuthSchemeEndpointConfig::empty(), @@ -325,7 +325,7 @@ mod tests { .body(SdkBody::empty()) .unwrap(); signer - .sign_request( + .sign_http_request( &mut request, &identity, AuthSchemeEndpointConfig::empty(), @@ -349,7 +349,7 @@ mod tests { let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); signer - .sign_request( + .sign_http_request( &mut request, &identity, AuthSchemeEndpointConfig::empty(), @@ -372,7 +372,7 @@ mod tests { let identity = Identity::new(Token::new("some-token", None), None); let mut request = http::Request::builder().body(SdkBody::empty()).unwrap(); signer - .sign_request( + .sign_http_request( &mut request, &identity, AuthSchemeEndpointConfig::empty(), diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs index 56f9633658..115f78b8e8 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs @@ -8,7 +8,7 @@ use crate::client::identity::no_auth::NoAuthIdentityResolver; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::{ - AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, SharedHttpAuthScheme, + AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, SharedAuthScheme, Signer, }; use aws_smithy_runtime_api::client::identity::{Identity, SharedIdentityResolver}; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; @@ -43,7 +43,7 @@ impl NoAuthRuntimePlugin { NO_AUTH_SCHEME_ID, SharedIdentityResolver::new(NoAuthIdentityResolver::new()), ) - .with_http_auth_scheme(SharedHttpAuthScheme::new(NoAuthScheme::new())), + .with_auth_scheme(SharedAuthScheme::new(NoAuthScheme::new())), ) } } @@ -68,8 +68,8 @@ impl NoAuthScheme { #[derive(Debug, Default)] struct NoAuthSigner; -impl HttpRequestSigner for NoAuthSigner { - fn sign_request( +impl Signer for NoAuthSigner { + fn sign_http_request( &self, _request: &mut HttpRequest, _identity: &Identity, @@ -81,7 +81,7 @@ impl HttpRequestSigner for NoAuthSigner { } } -impl HttpAuthScheme for NoAuthScheme { +impl AuthScheme for NoAuthScheme { fn scheme_id(&self) -> AuthSchemeId { NO_AUTH_SCHEME_ID } @@ -93,7 +93,7 @@ impl HttpAuthScheme for NoAuthScheme { identity_resolvers.identity_resolver(NO_AUTH_SCHEME_ID) } - fn request_signer(&self) -> &dyn HttpRequestSigner { + fn signer(&self) -> &dyn Signer { &self.signer } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/connectors.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors.rs index 60e9a77096..445425680b 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connectors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors.rs @@ -7,9 +7,13 @@ pub mod connection_poisoning; #[cfg(feature = "test-util")] pub mod test_util; +// TODO(enableNewSmithyRuntimeCleanup): Delete this module +/// Unstable API for interfacing the old middleware connectors with the newer orchestrator connectors. +/// +/// Important: This module and its contents will be removed in the next release. pub mod adapter { use aws_smithy_client::erase::DynConnector; - use aws_smithy_runtime_api::client::connectors::Connector; + use aws_smithy_runtime_api::client::connectors::HttpConnector; use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse}; use std::sync::{Arc, Mutex}; @@ -27,7 +31,7 @@ pub mod adapter { } } - impl Connector for DynConnectorAdapter { + impl HttpConnector for DynConnectorAdapter { fn call(&self, request: HttpRequest) -> BoxFuture { let future = self.dyn_connector.lock().unwrap().call_lite(request); future diff --git a/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs index 5a9b3bfdfd..457b09888d 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs @@ -9,7 +9,7 @@ use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; use aws_smithy_protocol_test::{assert_ok, validate_body, MediaType}; -use aws_smithy_runtime_api::client::connectors::Connector; +use aws_smithy_runtime_api::client::connectors::HttpConnector; use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse}; use http::header::{HeaderName, CONTENT_TYPE}; use std::fmt::Debug; @@ -181,7 +181,7 @@ impl ValidateRequest { } } -/// TestConnection for use as a [`Connector`]. +/// TestConnection for use as a [`HttpConnector`]. /// /// A basic test connection. It will: /// - Respond to requests with a preloaded series of responses @@ -222,7 +222,7 @@ impl TestConnection { } } -impl Connector for TestConnection { +impl HttpConnector for TestConnection { fn call(&self, request: HttpRequest) -> BoxFuture { let (res, simulated_latency) = if let Some(event) = self.data.lock().unwrap().pop() { self.requests.lock().unwrap().push(ValidateRequest { diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 30aa1666cb..15c3b0791c 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -15,19 +15,20 @@ use aws_smithy_http::body::SdkBody; use aws_smithy_http::byte_stream::ByteStream; use aws_smithy_http::result::SdkError; use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::connectors::Connector; +use aws_smithy_runtime_api::client::connectors::HttpConnector; use aws_smithy_runtime_api::client::interceptors::context::{ Error, Input, InterceptorContext, Output, RewindResult, }; use aws_smithy_runtime_api::client::interceptors::Interceptors; use aws_smithy_runtime_api::client::orchestrator::{ - DynResponseDeserializer, HttpResponse, LoadedRequestBody, OrchestratorError, RequestSerializer, - ResponseDeserializer, SharedRequestSerializer, + HttpResponse, LoadedRequestBody, OrchestratorError, }; -use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; -use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; +use aws_smithy_runtime_api::client::retries::{RequestAttempts, RetryStrategy, ShouldAttempt}; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugins; +use aws_smithy_runtime_api::client::ser_de::{ + RequestSerializer, ResponseDeserializer, SharedRequestSerializer, SharedResponseDeserializer, +}; use aws_smithy_types::config_bag::ConfigBag; use std::mem; use tracing::{debug, debug_span, instrument, trace, Instrument}; @@ -334,7 +335,7 @@ async fn try_attempt( let response = halt_on_err!([ctx] => { let request = ctx.take_request().expect("set during serialization"); trace!(request = ?request, "transmitting request"); - let connector = halt_on_err!([ctx] => runtime_components.connector().ok_or_else(|| + let connector = halt_on_err!([ctx] => runtime_components.http_connector().ok_or_else(|| OrchestratorError::other("No HTTP connector was available to send this request. \ Enable the `rustls` crate feature or set a connector to fix this.") )); @@ -359,7 +360,7 @@ async fn try_attempt( let output_or_error = async { let response = ctx.response_mut().expect("set during transmit"); let response_deserializer = cfg - .load::() + .load::() .expect("a request deserializer must be in the config bag"); let maybe_deserialized = { let _span = debug_span!("deserialize_streaming").entered(); @@ -420,11 +421,14 @@ mod tests { deserializer::CannedResponseDeserializer, serializer::CannedRequestSerializer, }; use ::http::{Request, Response, StatusCode}; - use aws_smithy_runtime_api::client::auth::option_resolver::StaticAuthOptionResolver; + use aws_smithy_runtime_api::client::auth::static_resolver::StaticAuthSchemeOptionResolver; use aws_smithy_runtime_api::client::auth::{ - AuthOptionResolverParams, SharedAuthOptionResolver, + AuthSchemeOptionResolverParams, SharedAuthSchemeOptionResolver, + }; + use aws_smithy_runtime_api::client::connectors::{HttpConnector, SharedHttpConnector}; + use aws_smithy_runtime_api::client::endpoint::{ + EndpointResolverParams, SharedEndpointResolver, }; - use aws_smithy_runtime_api::client::connectors::{Connector, SharedConnector}; use aws_smithy_runtime_api::client::interceptors::context::{ AfterDeserializationInterceptorContextRef, BeforeDeserializationInterceptorContextMut, BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, @@ -433,10 +437,7 @@ mod tests { FinalizerInterceptorContextRef, }; use aws_smithy_runtime_api::client::interceptors::{Interceptor, SharedInterceptor}; - use aws_smithy_runtime_api::client::orchestrator::{ - BoxFuture, DynResponseDeserializer, EndpointResolverParams, Future, HttpRequest, - SharedEndpointResolver, SharedRequestSerializer, - }; + use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, Future, HttpRequest}; use aws_smithy_runtime_api::client::retries::SharedRetryStrategy; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_runtime_api::client::runtime_plugin::{RuntimePlugin, RuntimePlugins}; @@ -474,7 +475,7 @@ mod tests { } } - impl Connector for OkConnector { + impl HttpConnector for OkConnector { fn call(&self, _request: HttpRequest) -> BoxFuture { Box::pin(Future::ready(Ok(::http::Response::builder() .status(200) @@ -496,9 +497,9 @@ mod tests { .with_endpoint_resolver(Some(SharedEndpointResolver::new( StaticUriEndpointResolver::http_localhost(8080), ))) - .with_connector(Some(SharedConnector::new(OkConnector::new()))) - .with_auth_option_resolver(Some(SharedAuthOptionResolver::new( - StaticAuthOptionResolver::new(vec![NO_AUTH_SCHEME_ID]), + .with_http_connector(Some(SharedHttpConnector::new(OkConnector::new()))) + .with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new( + StaticAuthSchemeOptionResolver::new(vec![NO_AUTH_SCHEME_ID]), ))), } } @@ -507,10 +508,10 @@ mod tests { impl RuntimePlugin for TestOperationRuntimePlugin { fn config(&self) -> Option { let mut layer = Layer::new("TestOperationRuntimePlugin"); - layer.store_put(AuthOptionResolverParams::new("idontcare")); + layer.store_put(AuthSchemeOptionResolverParams::new("idontcare")); layer.store_put(EndpointResolverParams::new("dontcare")); layer.store_put(SharedRequestSerializer::new(new_request_serializer())); - layer.store_put(DynResponseDeserializer::new(new_response_deserializer())); + layer.store_put(SharedResponseDeserializer::new(new_response_deserializer())); Some(layer.freeze()) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index 48fd604392..f300750b2d 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -5,9 +5,9 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::{ - AuthOptionResolver, AuthSchemeEndpointConfig, AuthSchemeId, HttpAuthScheme, + AuthScheme, AuthSchemeEndpointConfig, AuthSchemeId, AuthSchemeOptionResolver, + AuthSchemeOptionResolverParams, }; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::identity::IdentityResolver; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; @@ -49,7 +49,7 @@ impl fmt::Display for AuthOrchestrationError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::NoMatchingAuthScheme => f.write_str( - "no auth scheme matched auth options. This is a bug. Please file an issue.", + "no auth scheme matched auth scheme options. This is a bug. Please file an issue.", ), Self::BadAuthSchemeEndpointConfig(message) => f.write_str(message), Self::AuthSchemeEndpointConfigMismatch(supported_schemes) => { @@ -70,24 +70,26 @@ pub(super) async fn orchestrate_auth( runtime_components: &RuntimeComponents, cfg: &ConfigBag, ) -> Result<(), BoxError> { - let params = cfg.auth_option_resolver_params(); - let auth_option_resolver = runtime_components.auth_option_resolver(); - let auth_options = auth_option_resolver.resolve_auth_options(params)?; + let params = cfg + .load::() + .expect("auth scheme option resolver params must be set"); + let option_resolver = runtime_components.auth_scheme_option_resolver(); + let options = option_resolver.resolve_auth_scheme_options(params)?; trace!( - auth_option_resolver_params = ?params, - auth_options = ?auth_options, + auth_scheme_option_resolver_params = ?params, + auth_scheme_options = ?options, "orchestrating auth", ); - for &scheme_id in auth_options.as_ref() { - if let Some(auth_scheme) = runtime_components.http_auth_scheme(scheme_id) { + for &scheme_id in options.as_ref() { + if let Some(auth_scheme) = runtime_components.auth_scheme(scheme_id) { if let Some(identity_resolver) = auth_scheme.identity_resolver(runtime_components) { - let request_signer = auth_scheme.request_signer(); + let signer = auth_scheme.signer(); trace!( auth_scheme = ?auth_scheme, identity_resolver = ?identity_resolver, - request_signer = ?request_signer, + signer = ?signer, "resolved auth scheme, identity resolver, and signing implementation" ); @@ -103,7 +105,7 @@ pub(super) async fn orchestrate_auth( trace!("signing request"); let request = ctx.request_mut().expect("set during serialization"); - request_signer.sign_request( + signer.sign_http_request( request, &identity, auth_scheme_endpoint_config, @@ -125,7 +127,7 @@ fn extract_endpoint_auth_scheme_config( let auth_schemes = match endpoint.properties().get("authSchemes") { Some(Document::Array(schemes)) => schemes, // no auth schemes: - None => return Ok(AuthSchemeEndpointConfig::new(None)), + None => return Ok(AuthSchemeEndpointConfig::from(None)), _other => { return Err(AuthOrchestrationError::BadAuthSchemeEndpointConfig( "expected an array for `authSchemes` in endpoint config".into(), @@ -144,17 +146,17 @@ fn extract_endpoint_auth_scheme_config( .ok_or_else(|| { AuthOrchestrationError::auth_scheme_endpoint_config_mismatch(auth_schemes.iter()) })?; - Ok(AuthSchemeEndpointConfig::new(Some(auth_scheme_config))) + Ok(AuthSchemeEndpointConfig::from(Some(auth_scheme_config))) } #[cfg(all(test, feature = "test-util"))] mod tests { use super::*; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::auth::option_resolver::StaticAuthOptionResolver; + use aws_smithy_runtime_api::client::auth::static_resolver::StaticAuthSchemeOptionResolver; use aws_smithy_runtime_api::client::auth::{ - AuthOptionResolverParams, AuthSchemeId, HttpAuthScheme, HttpRequestSigner, - SharedAuthOptionResolver, SharedHttpAuthScheme, + AuthScheme, AuthSchemeId, AuthSchemeOptionResolverParams, SharedAuthScheme, + SharedAuthSchemeOptionResolver, Signer, }; use aws_smithy_runtime_api::client::identity::{ Identity, IdentityResolver, SharedIdentityResolver, @@ -181,8 +183,8 @@ mod tests { #[derive(Debug)] struct TestSigner; - impl HttpRequestSigner for TestSigner { - fn sign_request( + impl Signer for TestSigner { + fn sign_http_request( &self, request: &mut HttpRequest, _identity: &Identity, @@ -203,7 +205,7 @@ mod tests { struct TestAuthScheme { signer: TestSigner, } - impl HttpAuthScheme for TestAuthScheme { + impl AuthScheme for TestAuthScheme { fn scheme_id(&self) -> AuthSchemeId { TEST_SCHEME_ID } @@ -215,7 +217,7 @@ mod tests { identity_resolvers.identity_resolver(self.scheme_id()) } - fn request_signer(&self) -> &dyn HttpRequestSigner { + fn signer(&self) -> &dyn Signer { &self.signer } } @@ -227,21 +229,19 @@ mod tests { ctx.enter_before_transmit_phase(); let runtime_components = RuntimeComponentsBuilder::for_tests() - .with_auth_option_resolver(Some(SharedAuthOptionResolver::new( - StaticAuthOptionResolver::new(vec![TEST_SCHEME_ID]), + .with_auth_scheme(SharedAuthScheme::new(TestAuthScheme { signer: TestSigner })) + .with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new( + StaticAuthSchemeOptionResolver::new(vec![TEST_SCHEME_ID]), ))) .with_identity_resolver( TEST_SCHEME_ID, SharedIdentityResolver::new(TestIdentityResolver), ) - .with_http_auth_scheme(SharedHttpAuthScheme::new(TestAuthScheme { - signer: TestSigner, - })) .build() .unwrap(); let mut layer: Layer = Layer::new("test"); - layer.store_put(AuthOptionResolverParams::new("doesntmatter")); + layer.store_put(AuthSchemeOptionResolverParams::new("doesntmatter")); layer.store_put(Endpoint::builder().url("dontcare").build()); let cfg = ConfigBag::of_layers(vec![layer]); @@ -279,10 +279,10 @@ mod tests { identity: impl IdentityResolver + 'static, ) -> (RuntimeComponents, ConfigBag) { let runtime_components = RuntimeComponentsBuilder::for_tests() - .with_http_auth_scheme(SharedHttpAuthScheme::new(BasicAuthScheme::new())) - .with_http_auth_scheme(SharedHttpAuthScheme::new(BearerAuthScheme::new())) - .with_auth_option_resolver(Some(SharedAuthOptionResolver::new( - StaticAuthOptionResolver::new(vec![ + .with_auth_scheme(SharedAuthScheme::new(BasicAuthScheme::new())) + .with_auth_scheme(SharedAuthScheme::new(BearerAuthScheme::new())) + .with_auth_scheme_option_resolver(Some(SharedAuthSchemeOptionResolver::new( + StaticAuthSchemeOptionResolver::new(vec![ HTTP_BASIC_AUTH_SCHEME_ID, HTTP_BEARER_AUTH_SCHEME_ID, ]), @@ -293,7 +293,7 @@ mod tests { let mut layer = Layer::new("test"); layer.store_put(Endpoint::builder().url("dontcare").build()); - layer.store_put(AuthOptionResolverParams::new("doesntmatter")); + layer.store_put(AuthSchemeOptionResolverParams::new("doesntmatter")); (runtime_components, ConfigBag::of_layers(vec![layer])) } @@ -343,7 +343,7 @@ mod tests { .build(); let config = extract_endpoint_auth_scheme_config(&endpoint, "test-scheme-id".into()) .expect("success"); - assert!(config.config().is_none()); + assert!(config.as_document().is_none()); } #[test] @@ -412,7 +412,7 @@ mod tests { assert_eq!( "magic string value", config - .config() + .as_document() .expect("config is set") .as_object() .expect("it's an object") diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index be6fc51bf0..4f170ec641 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -9,11 +9,9 @@ use aws_smithy_http::endpoint::{ SharedEndpointResolver, }; use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; +use aws_smithy_runtime_api::client::endpoint::{EndpointResolver, EndpointResolverParams}; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::{ - EndpointResolver, EndpointResolverParams, Future, HttpRequest, -}; +use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::endpoint::Endpoint; @@ -109,7 +107,9 @@ pub(super) async fn orchestrate_endpoint( ) -> Result<(), BoxError> { trace!("orchestrating endpoint resolution"); - let params = cfg.endpoint_resolver_params(); + let params = cfg + .load::() + .expect("endpoint resolver params must be set"); let endpoint_prefix = cfg.load::(); let request = ctx.request_mut().expect("set during serialization"); diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs index 1cdb94cae9..c6315512a3 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -5,9 +5,8 @@ use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; -use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ - ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt, + ClassifyRetry, RequestAttempts, RetryReason, RetryStrategy, ShouldAttempt, }; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index ba8d08aa66..160fe71ca6 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -10,9 +10,8 @@ use crate::client::retries::strategy::standard::ReleaseResult::{ use crate::client::retries::token_bucket::TokenBucket; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; -use aws_smithy_runtime_api::client::request_attempts::RequestAttempts; use aws_smithy_runtime_api::client::retries::{ - ClassifyRetry, RetryReason, RetryStrategy, ShouldAttempt, + ClassifyRetry, RequestAttempts, RetryReason, RetryStrategy, ShouldAttempt, }; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs index 0a4b0ee860..4b1d43ae79 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs @@ -3,12 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::{Error, Output}; -use aws_smithy_runtime_api::client::orchestrator::{ - DynResponseDeserializer, HttpResponse, OrchestratorError, ResponseDeserializer, -}; +use aws_smithy_runtime_api::client::orchestrator::{HttpResponse, OrchestratorError}; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::client::ser_de::{ResponseDeserializer, SharedResponseDeserializer}; use aws_smithy_types::config_bag::{FrozenLayer, Layer}; use std::sync::Mutex; @@ -46,7 +44,7 @@ impl ResponseDeserializer for CannedResponseDeserializer { impl RuntimePlugin for CannedResponseDeserializer { fn config(&self) -> Option { let mut cfg = Layer::new("CannedResponse"); - cfg.set_response_deserializer(DynResponseDeserializer::new(Self { + cfg.store_put(SharedResponseDeserializer::new(Self { inner: Mutex::new(self.take()), })); Some(cfg.freeze()) diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs index 4f5bff24bd..ec3eb2dafd 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -4,11 +4,10 @@ */ use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::config_bag_accessors::ConfigBagAccessors; use aws_smithy_runtime_api::client::interceptors::context::Input; -use aws_smithy_runtime_api::client::orchestrator::SharedRequestSerializer; -use aws_smithy_runtime_api::client::orchestrator::{HttpRequest, RequestSerializer}; +use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; +use aws_smithy_runtime_api::client::ser_de::{RequestSerializer, SharedRequestSerializer}; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; use std::sync::Mutex; @@ -52,7 +51,7 @@ impl RequestSerializer for CannedRequestSerializer { impl RuntimePlugin for CannedRequestSerializer { fn config(&self) -> Option { let mut cfg = Layer::new("CannedRequest"); - cfg.set_request_serializer(SharedRequestSerializer::new(Self { + cfg.store_put(SharedRequestSerializer::new(Self { inner: Mutex::new(self.take()), })); Some(cfg.freeze()) diff --git a/rust-runtime/aws-smithy-runtime/src/lib.rs b/rust-runtime/aws-smithy-runtime/src/lib.rs index cd4364a9b3..3afb476bf9 100644 --- a/rust-runtime/aws-smithy-runtime/src/lib.rs +++ b/rust-runtime/aws-smithy-runtime/src/lib.rs @@ -19,6 +19,7 @@ )] /// Runtime support logic for generated clients. +#[cfg(feature = "client")] pub mod client; pub mod static_partition_map; diff --git a/rust-runtime/inlineable/Cargo.toml b/rust-runtime/inlineable/Cargo.toml index 74bbcdebc0..6d0c099429 100644 --- a/rust-runtime/inlineable/Cargo.toml +++ b/rust-runtime/inlineable/Cargo.toml @@ -22,7 +22,7 @@ async-trait = "0.1" aws-smithy-http = { path = "../aws-smithy-http" } aws-smithy-http-server = { path = "../aws-smithy-http-server" } aws-smithy-json = { path = "../aws-smithy-json" } -aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api" } +aws-smithy-runtime-api = { path = "../aws-smithy-runtime-api", features = ["client"] } aws-smithy-types = { path = "../aws-smithy-types" } aws-smithy-xml = { path = "../aws-smithy-xml" } bytes = "1" From a5465b79a5e864673f0f9d6606b29d9e6ab50e44 Mon Sep 17 00:00:00 2001 From: david-perez Date: Tue, 25 Jul 2023 14:03:21 +0200 Subject: [PATCH 228/253] Render only shape name in server error responses (#2866) This essentially reverts the behavior introduced in #1982. The rationale for that change: > [...] since some existing clients rely on it to deserialize the error shape > and fail if only the shape name is present no longer holds. Since serializing only the shape name is a SHOULD in the spec, it's best that we honor it. ## Checklist - [x] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or 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._ --- CHANGELOG.next.toml | 22 +++++++++++++++++++ .../rest-json-extras.smithy | 8 ++----- .../codegen/core/smithy/protocols/RestJson.kt | 17 +++++++++----- .../protocol/ServerProtocolTestGenerator.kt | 16 +------------- .../server/smithy/protocols/ServerAwsJson.kt | 8 +++++++ 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 7b24ac49ef..741a454094 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -664,3 +664,25 @@ message = "The `alb_health_check` module has been moved out of the `plugin` modu references = ["smithy-rs#2865"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } author = "david-perez" + +[[smithy-rs]] +message = """ +[RestJson1](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restjson1-protocol.html#operation-error-serialization) server SDKs now serialize only the [shape name](https://smithy.io/2.0/spec/model.html#shape-id) in operation error responses. Previously (from versions 0.52.0 to 0.55.4), the full shape ID was rendered. +Example server error response by a smithy-rs server version 0.52.0 until 0.55.4: +``` +HTTP/1.1 400 Bad Request +content-type: application/json +x-amzn-errortype: com.example.service#InvalidRequestException +... +``` +Example server error response now: +``` +HTTP/1.1 400 Bad Request +content-type: application/json +x-amzn-errortype: InvalidRequestException +... +``` +""" +references = ["smithy-rs#2866"] +meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "server" } +author = "david-perez" diff --git a/codegen-core/common-test-models/rest-json-extras.smithy b/codegen-core/common-test-models/rest-json-extras.smithy index b9946baa81..ff92f36c6b 100644 --- a/codegen-core/common-test-models/rest-json-extras.smithy +++ b/codegen-core/common-test-models/rest-json-extras.smithy @@ -81,16 +81,12 @@ service RestJsonExtras { appliesTo: "client", }, { - documentation: """ - Upper case error modeled lower case. - Servers render the full shape ID (including namespace), since some - existing clients rely on it to deserialize the error shape and fail - if only the shape name is present.""", + documentation: "Upper case error modeled lower case.", id: "ServiceLevelErrorServer", protocol: "aws.protocols#restJson1", code: 500, body: "{}", - headers: { "X-Amzn-Errortype": "aws.protocoltests.restjson#ExtraError" }, + headers: { "X-Amzn-Errortype": "ExtraError" }, params: {}, appliesTo: "server", } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt index 9a3d12a993..ba526d0c87 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy/protocols/RestJson.kt @@ -80,14 +80,19 @@ open class RestJson(val codegenContext: CodegenContext) : Protocol { * RestJson1 implementations can denote errors in responses in several ways. * New server-side protocol implementations MUST use a header field named `X-Amzn-Errortype`. * - * Note that the spec says that implementations SHOULD strip the error shape ID's namespace. - * However, our server implementation renders the full shape ID (including namespace), since some - * existing clients rely on it to deserialize the error shape and fail if only the shape name is present. - * This is compliant with the spec, see https://github.com/awslabs/smithy/pull/1493. - * See https://github.com/awslabs/smithy/issues/1494 too. + * Note that the spec says that implementations SHOULD strip the error shape ID's namespace + * (see https://smithy.io/2.0/aws/protocols/aws-restjson1-protocol.html#operation-error-serialization): + * + * > The value of this component SHOULD contain only the shape name of the error's Shape ID. + * + * But it's a SHOULD; we could strip the namespace if we wanted to. In fact, we did so in smithy-rs versions + * 0.52.0 to 0.55.4; see: + * - https://github.com/awslabs/smithy-rs/pull/1982 + * - https://github.com/awslabs/smithy/pull/1493 + * - https://github.com/awslabs/smithy/issues/1494 */ override fun additionalErrorResponseHeaders(errorShape: StructureShape): List> = - listOf("x-amzn-errortype" to errorShape.id.toString()) + listOf("x-amzn-errortype" to errorShape.id.name) override fun structuredDataParser(): StructuredDataParserGenerator = JsonParserGenerator(codegenContext, httpBindingResolver, ::restJsonFieldName) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index 7288de8942..74978699ca 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -921,15 +921,6 @@ class ServerProtocolTestGenerator( ).asObjectNode().get(), ).build() - private fun fixRestJsonInvalidGreetingError(testCase: HttpResponseTestCase): HttpResponseTestCase = - testCase.toBuilder().putHeader("X-Amzn-Errortype", "aws.protocoltests.restjson#InvalidGreeting").build() - - private fun fixRestJsonEmptyComplexErrorWithNoMessage(testCase: HttpResponseTestCase): HttpResponseTestCase = - testCase.toBuilder().putHeader("X-Amzn-Errortype", "aws.protocoltests.restjson#ComplexError").build() - - private fun fixRestJsonComplexErrorWithNoMessage(testCase: HttpResponseTestCase): HttpResponseTestCase = - testCase.toBuilder().putHeader("X-Amzn-Errortype", "aws.protocoltests.restjson#ComplexError").build() - // TODO(https://github.com/awslabs/smithy/issues/1506) private fun fixRestJsonMalformedPatternReDOSString(testCase: HttpMalformedRequestTestCase): HttpMalformedRequestTestCase { val brokenResponse = testCase.response @@ -962,12 +953,7 @@ class ServerProtocolTestGenerator( ) private val BrokenResponseTests: Map, KFunction1> = - // TODO(https://github.com/awslabs/smithy/issues/1494) - mapOf( - Pair(RestJson, "RestJsonInvalidGreetingError") to ::fixRestJsonInvalidGreetingError, - Pair(RestJson, "RestJsonEmptyComplexErrorWithNoMessage") to ::fixRestJsonEmptyComplexErrorWithNoMessage, - Pair(RestJson, "RestJsonComplexErrorWithNoMessage") to ::fixRestJsonComplexErrorWithNoMessage, - ) + mapOf() private val BrokenMalformedRequestTests: Map, KFunction1> = // TODO(https://github.com/awslabs/smithy/issues/1506) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt index 920813e34d..549ca88b1e 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/protocols/ServerAwsJson.kt @@ -68,6 +68,14 @@ class ServerAwsJsonFactory( /** * AwsJson requires errors to be serialized in server responses with an additional `__type` field. This * customization writes the right field depending on the version of the AwsJson protocol. + * + * From the specs: + * - https://smithy.io/2.0/aws/protocols/aws-json-1_0-protocol.html#operation-error-serialization + * - https://smithy.io/2.0/aws/protocols/aws-json-1_1-protocol.html#operation-error-serialization + * + * > Error responses in the protocol are serialized identically to standard responses with one additional + * > component to distinguish which error is contained. New server-side protocol implementations SHOULD use a body + * > field named __type */ class ServerAwsJsonError(private val awsJsonVersion: AwsJsonVersion) : JsonSerializerCustomization() { override fun section(section: JsonSerializerSection): Writable = when (section) { From 2922f561b0d67c5b908e2dabfb2a8a563f41a3ef Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 25 Jul 2023 12:59:17 -0700 Subject: [PATCH 229/253] More tidying up of `aws-smithy-runtime-api` (#2869) This PR finishes documenting `aws-smithy-runtime-api` and also: - Adds `Send + Sync` bounds to `Interceptor`. - Moves `Interceptors` into `aws-smithy-runtime`. - Expands the more complicated macros in the interceptor context wrappers. - Makes the `OrchestratorError` an informative error according to [RFC-22](https://github.com/awslabs/smithy-rs/blob/main/design/src/rfcs/rfc0022_error_context_and_compatibility.md). - Renames `ConnectorError::is_other` to `as_other` and adds an equivalent `is_other` that returns a boolean. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../src/http_request_checksum.rs | 2 +- .../src/http_response_checksum.rs | 2 +- .../src/route53_resource_id_preprocessor.rs | 2 +- .../InterceptorConfigCustomization.kt | 2 +- .../client/CustomizableOperationGenerator.kt | 2 +- rust-runtime/aws-smithy-http/src/result.rs | 16 +- rust-runtime/aws-smithy-http/src/retry.rs | 2 +- .../src/client/interceptors.rs | 545 ++++-------------- .../src/client/interceptors/context.rs | 91 +-- .../client/interceptors/context/wrappers.rs | 344 ++++++----- .../src/client/orchestrator.rs | 153 +++-- .../aws-smithy-runtime-api/src/lib.rs | 3 +- .../client/connectors/connection_poisoning.rs | 2 +- .../src/client/interceptors.rs | 303 +++++++++- .../src/client/orchestrator.rs | 7 +- .../src/client/retries/classifier.rs | 18 +- 16 files changed, 793 insertions(+), 701 deletions(-) diff --git a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs index b97b2d2bd0..702fbe56c2 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs @@ -77,7 +77,7 @@ impl RequestChecksumInterceptor { impl Interceptor for RequestChecksumInterceptor where - AP: Fn(&Input) -> Result, BoxError>, + AP: Fn(&Input) -> Result, BoxError> + Send + Sync, { fn read_before_serialization( &self, diff --git a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs index 9a54d00191..2a98830d8e 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs @@ -54,7 +54,7 @@ impl ResponseChecksumInterceptor { impl Interceptor for ResponseChecksumInterceptor where - VE: Fn(&Input) -> bool, + VE: Fn(&Input) -> bool + Send + Sync, { fn read_before_serialization( &self, diff --git a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs index d8fa3f4c97..00f158317f 100644 --- a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs +++ b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs @@ -69,7 +69,7 @@ where impl Interceptor for Route53ResourceIdInterceptor where - G: for<'a> Fn(&'a mut T) -> &'a mut Option, + G: for<'a> Fn(&'a mut T) -> &'a mut Option + Send + Sync, T: fmt::Debug + Send + Sync + 'static, { fn modify_before_serialization( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt index 2dfec5159e..9bb9e16ef3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/InterceptorConfigCustomization.kt @@ -91,7 +91,7 @@ class InterceptorConfigCustomization(codegenContext: ClientCodegenContext) : Con /// ## } /// ## } /// ``` - pub fn interceptor(mut self, interceptor: impl #{Interceptor} + Send + Sync + 'static) -> Self { + pub fn interceptor(mut self, interceptor: impl #{Interceptor} + 'static) -> Self { self.push_interceptor(#{SharedInterceptor}::new(interceptor)); self } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index 633a50985a..4046d29e7f 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -199,7 +199,7 @@ class CustomizableOperationGenerator( /// `map_request`, and `mutate_request` (the last two are implemented via interceptors under the hood). /// The order in which those user-specified operation interceptors are invoked should not be relied upon /// as it is an implementation detail. - pub fn interceptor(mut self, interceptor: impl #{Interceptor} + #{Send} + #{Sync} + 'static) -> Self { + pub fn interceptor(mut self, interceptor: impl #{Interceptor} + 'static) -> Self { self.interceptors.push(#{SharedInterceptor}::new(interceptor)); self } diff --git a/rust-runtime/aws-smithy-http/src/result.rs b/rust-runtime/aws-smithy-http/src/result.rs index bd6d2f1f3b..8d60c8d2ed 100644 --- a/rust-runtime/aws-smithy-http/src/result.rs +++ b/rust-runtime/aws-smithy-http/src/result.rs @@ -232,11 +232,16 @@ impl DispatchFailure { self.source.is_user() } - /// Returns the optional error kind associated with an unclassified error - pub fn is_other(&self) -> Option { + /// Returns true if the error is an unclassified error. + pub fn is_other(&self) -> bool { self.source.is_other() } + /// Returns the optional error kind associated with an unclassified error + pub fn as_other(&self) -> Option { + self.source.as_other() + } + /// Returns the inner error if it is a connector error pub fn as_connector_error(&self) -> Option<&ConnectorError> { Some(&self.source) @@ -633,8 +638,13 @@ impl ConnectorError { matches!(self.kind, ConnectorErrorKind::User) } + /// Returns true if the error is an unclassified error. + pub fn is_other(&self) -> bool { + matches!(self.kind, ConnectorErrorKind::Other(..)) + } + /// Returns the optional error kind associated with an unclassified error - pub fn is_other(&self) -> Option { + pub fn as_other(&self) -> Option { match &self.kind { ConnectorErrorKind::Other(ek) => *ek, _ => None, diff --git a/rust-runtime/aws-smithy-http/src/retry.rs b/rust-runtime/aws-smithy-http/src/retry.rs index 30c8654926..eaf7d0e093 100644 --- a/rust-runtime/aws-smithy-http/src/retry.rs +++ b/rust-runtime/aws-smithy-http/src/retry.rs @@ -45,7 +45,7 @@ impl DefaultResponseRetryClassifier { Err(SdkError::DispatchFailure(err)) => { if err.is_timeout() || err.is_io() { Err(RetryKind::Error(ErrorKind::TransientError)) - } else if let Some(ek) = err.is_other() { + } else if let Some(ek) = err.as_other() { Err(RetryKind::Error(ek)) } else { Err(RetryKind::UnretryableFailure) diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index a755a34e44..91d7225262 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -13,12 +13,10 @@ use crate::client::interceptors::context::{ BeforeDeserializationInterceptorContextRef, BeforeSerializationInterceptorContextMut, BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, BeforeTransmitInterceptorContextRef, FinalizerInterceptorContextMut, - FinalizerInterceptorContextRef, InterceptorContext, + FinalizerInterceptorContextRef, }; use crate::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; -use aws_smithy_types::error::display::DisplayErrorContext; -use context::{Error, Input, Output}; use std::fmt; use std::marker::PhantomData; use std::ops::Deref; @@ -38,9 +36,7 @@ macro_rules! interceptor_trait_fn { runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let _ctx = context; - let _rc = runtime_components; - let _cfg = cfg; + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); Ok(()) } }; @@ -52,9 +48,7 @@ macro_rules! interceptor_trait_fn { runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let _ctx = context; - let _rc = runtime_components; - let _cfg = cfg; + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); Ok(()) } }; @@ -70,7 +64,7 @@ macro_rules! interceptor_trait_fn { /// of the SDK ’s request execution pipeline. Hooks are either "read" hooks, which make it possible /// to read in-flight request or response messages, or "read/write" hooks, which make it possible /// to modify in-flight request or output messages. -pub trait Interceptor: fmt::Debug { +pub trait Interceptor: fmt::Debug + Send + Sync { /// A hook called at the start of an execution, before the SDK /// does anything else. /// @@ -78,14 +72,14 @@ pub trait Interceptor: fmt::Debug { /// between invocation of this hook and `after_execution` is very close /// to full duration of the execution. /// - /// **Available Information:** The [InterceptorContext::input()] is - /// **ALWAYS** available. Other information **WILL NOT** be available. + /// **Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input) + /// is **ALWAYS** available. Other information **WILL NOT** be available. /// /// **Error Behavior:** Errors raised by this hook will be stored /// until all interceptors have had their `before_execution` invoked. /// Other hooks will then be skipped and execution will jump to /// `modify_before_completion` with the raised error as the - /// [InterceptorContext::output_or_error()]. If multiple + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). If multiple /// `before_execution` methods raise errors, the latest /// will be used and earlier ones will be logged and dropped. fn read_before_execution( @@ -93,8 +87,7 @@ pub trait Interceptor: fmt::Debug { context: &BeforeSerializationInterceptorContextRef<'_>, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let _ctx = context; - let _cfg = cfg; + let (_ctx, _cfg) = (context, cfg); Ok(()) } @@ -110,14 +103,14 @@ pub trait Interceptor: fmt::Debug { **When:** This will **ALWAYS** be called once per execution, except when a failure occurs earlier in the request pipeline. - **Available Information:** The [InterceptorContext::input()] is + **Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input) is **ALWAYS** available. This request may have been modified by earlier `modify_before_serialization` hooks, and may be modified further by later hooks. Other information **WILL NOT** be available. **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_completion` with the raised - error as the [InterceptorContext::output_or_error()]. + error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). **Return Constraints:** The input message returned by this hook MUST be the same type of input message passed into this hook. @@ -138,12 +131,12 @@ pub trait Interceptor: fmt::Debug { duration between invocation of this hook and `after_serialization` is very close to the amount of time spent marshalling the request. - **Available Information:** The [InterceptorContext::input()] is + **Available Information:** The [`InterceptorContext::input`](context::InterceptorContext::input) is **ALWAYS** available. Other information **WILL NOT** be available. **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_completion` with the raised - error as the [InterceptorContext::output_or_error()]. + error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); @@ -151,21 +144,20 @@ pub trait Interceptor: fmt::Debug { read_after_serialization, BeforeTransmitInterceptorContextRef, " - /// A hook called after the input message is marshalled into - /// a transport message. - /// - /// **When:** This will **ALWAYS** be called once per execution, except when a - /// failure occurs earlier in the request pipeline. The duration - /// between invocation of this hook and `before_serialization` is very - /// close to the amount of time spent marshalling the request. - /// - /// **Available Information:** The [InterceptorContext::input()] - /// and [InterceptorContext::request()] are **ALWAYS** available. - /// Other information **WILL NOT** be available. - /// - /// **Error Behavior:** If errors are raised by this hook, - /// execution will jump to `modify_before_completion` with the raised - /// error as the [InterceptorContext::output_or_error()]. + A hook called after the input message is marshalled into + a transport message. + + **When:** This will **ALWAYS** be called once per execution, except when a + failure occurs earlier in the request pipeline. The duration + between invocation of this hook and `before_serialization` is very + close to the amount of time spent marshalling the request. + + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. Other information **WILL NOT** be available. + + **Error Behavior:** If errors are raised by this hook, + execution will jump to `modify_before_completion` with the raised + error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); @@ -177,13 +169,12 @@ pub trait Interceptor: fmt::Debug { has the ability to modify and return a new transport request message of the same type, except when a failure occurs earlier in the request pipeline. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. - Other information **WILL NOT** be available. + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. Other information **WILL NOT** be available. **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_completion` with the raised - error as the [InterceptorContext::output_or_error()]. + error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). **Return Constraints:** The transport request message returned by this hook MUST be the same type of request message passed into this hook @@ -202,9 +193,8 @@ pub trait Interceptor: fmt::Debug { failure occurs earlier in the request pipeline. This method will be called multiple times in the event of retries. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. - Other information **WILL NOT** be available. In the event of retries, + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). @@ -212,7 +202,7 @@ pub trait Interceptor: fmt::Debug { until all interceptors have had their `before_attempt` invoked. Other hooks will then be skipped and execution will jump to `modify_before_attempt_completion` with the raised error as the - [InterceptorContext::output_or_error()]. If multiple + [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). If multiple `before_attempt` methods raise errors, the latest will be used and earlier ones will be logged and dropped. " @@ -230,18 +220,16 @@ pub trait Interceptor: fmt::Debug { failure occurs earlier in the request pipeline. This method may be called multiple times in the event of retries. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. - The `http::Request` may have been modified by earlier + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. The `http::Request` may have been modified by earlier `modify_before_signing` hooks, and may be modified further by later hooks. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made - in previous attempts - (e.g. by request signers or other interceptors). + in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). **Return Constraints:** The transport request message returned by this hook MUST be the same type of request message passed into this hook @@ -262,15 +250,14 @@ pub trait Interceptor: fmt::Debug { invocation of this hook and `after_signing` is very close to the amount of time spent signing the request. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); @@ -286,15 +273,14 @@ pub trait Interceptor: fmt::Debug { invocation of this hook and `before_signing` is very close to the amount of time spent signing the request. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); @@ -302,26 +288,25 @@ pub trait Interceptor: fmt::Debug { mut modify_before_transmit, BeforeTransmitInterceptorContextMut, " - /// A hook called before the transport request message is sent to the - /// service. This method has the ability to modify and return - /// a new transport request message of the same type. - /// - /// **When:** This will **ALWAYS** be called once per attempt, except when a - /// failure occurs earlier in the request pipeline. This method may be - /// called multiple times in the event of retries. - /// - /// **Available Information:** The [InterceptorContext::input()] - /// and [InterceptorContext::request()] are **ALWAYS** available. - /// The `http::Request` may have been modified by earlier - /// `modify_before_transmit` hooks, and may be modified further by later - /// hooks. Other information **WILL NOT** be available. - /// In the event of retries, the `InterceptorContext` will not include - /// changes made in previous attempts (e.g. by request signers or + A hook called before the transport request message is sent to the + service. This method has the ability to modify and return + a new transport request message of the same type. + + **When:** This will **ALWAYS** be called once per attempt, except when a + failure occurs earlier in the request pipeline. This method may be + called multiple times in the event of retries. + + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. The `http::Request` may have been modified by earlier + `modify_before_transmit` hooks, and may be modified further by later + hooks. Other information **WILL NOT** be available. + In the event of retries, the `InterceptorContext` will not include + changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). **Return Constraints:** The transport request message returned by this hook MUST be the same type of request message passed into this hook @@ -345,16 +330,15 @@ pub trait Interceptor: fmt::Debug { Depending on the protocol, the duration may not include the time spent reading the response data. - **Available Information:** The [InterceptorContext::input()] - and [InterceptorContext::request()] are **ALWAYS** available. - Other information **WILL NOT** be available. In the event of retries, + **Available Information:** The [`InterceptorContext::request`](context::InterceptorContext::request) + is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); @@ -373,16 +357,14 @@ pub trait Interceptor: fmt::Debug { Depending on the protocol, the duration may not include the time spent reading the response data. - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()] and - [InterceptorContext::response()] are **ALWAYS** available. - Other information **WILL NOT** be available. In the event of retries, + **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response) + is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); @@ -398,10 +380,8 @@ pub trait Interceptor: fmt::Debug { failure occurs earlier in the request pipeline. This method may be called multiple times in the event of retries. - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()] and - [InterceptorContext::response()] are **ALWAYS** available. - The transmit_response may have been modified by earlier + **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response) + is **ALWAYS** available. The transmit_response may have been modified by earlier `modify_before_deserialization` hooks, and may be modified further by later hooks. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in @@ -410,7 +390,7 @@ pub trait Interceptor: fmt::Debug { **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with the raised error as the - [InterceptorContext::output_or_error()]. + [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). **Return Constraints:** The transport response message returned by this hook MUST be the same type of response message passed into @@ -432,16 +412,14 @@ pub trait Interceptor: fmt::Debug { Depending on the protocol and operation, the duration may include the time spent downloading the response data. - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()] and - [InterceptorContext::response()] are **ALWAYS** available. - Other information **WILL NOT** be available. In the event of retries, + **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response) + is **ALWAYS** available. Other information **WILL NOT** be available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` - with the raised error as the [InterceptorContext::output_or_error()]. + with the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); @@ -459,16 +437,14 @@ pub trait Interceptor: fmt::Debug { the duration may include the time spent downloading the response data. - **Available Information:** The [InterceptorContext::input()], - [InterceptorContext::request()], - [InterceptorContext::response()] and - [InterceptorContext::output_or_error()] are **ALWAYS** available. In the event - of retries, the `InterceptorContext` will not include changes made + **Available Information:** The [`InterceptorContext::response`](context::InterceptorContext::response) + and [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) + are **ALWAYS** available. In the event of retries, the `InterceptorContext` will not include changes made in previous attempts (e.g. by request signers or other interceptors). **Error Behavior:** If errors are raised by this hook, execution will jump to `modify_before_attempt_completion` with - the raised error as the [InterceptorContext::output_or_error()]. + the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). " ); @@ -480,16 +456,19 @@ pub trait Interceptor: fmt::Debug { /// failure occurs before `before_attempt`. This method may /// be called multiple times in the event of retries. /// - /// **Available Information:** The [InterceptorContext::input()], - /// [InterceptorContext::request()], - /// [InterceptorContext::response()] and - /// [InterceptorContext::output_or_error()] are **ALWAYS** available. In the event - /// of retries, the `InterceptorContext` will not include changes made + /// **Available Information:** + /// The [`InterceptorContext::input`](context::InterceptorContext::input), + /// [`InterceptorContext::request`](context::InterceptorContext::request), + /// [`InterceptorContext::response`](context::InterceptorContext::response), or + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available. + /// If the operation succeeded, the `output` will be available. Otherwise, any of the other + /// pieces of information may be available depending on where in the operation lifecycle it failed. + /// In the event of retries, the `InterceptorContext` will not include changes made /// in previous attempts (e.g. by request signers or other interceptors). /// /// **Error Behavior:** If errors are raised by this /// hook, execution will jump to `after_attempt` with - /// the raised error as the [InterceptorContext::output_or_error()]. + /// the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). /// /// **Return Constraints:** Any output message returned by this /// hook MUST match the operation being invoked. Any error type can be @@ -500,9 +479,7 @@ pub trait Interceptor: fmt::Debug { runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let _ctx = context; - let _rc = runtime_components; - let _cfg = cfg; + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); Ok(()) } @@ -511,14 +488,15 @@ pub trait Interceptor: fmt::Debug { /// **When:** This will **ALWAYS** be called once per attempt, as long as /// `before_attempt` has been executed. /// - /// **Available Information:** The [InterceptorContext::input()], - /// [InterceptorContext::request()] and - /// [InterceptorContext::output_or_error()] are **ALWAYS** available. - /// The [InterceptorContext::response()] is available if a - /// response was received by the service for this attempt. - /// In the event of retries, the `InterceptorContext` will not include - /// changes made in previous attempts (e.g. by request signers or other - /// interceptors). + /// **Available Information:** + /// The [`InterceptorContext::input`](context::InterceptorContext::input), + /// [`InterceptorContext::request`](context::InterceptorContext::request), + /// [`InterceptorContext::response`](context::InterceptorContext::response), or + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available. + /// If the operation succeeded, the `output` will be available. Otherwise, any of the other + /// pieces of information may be available depending on where in the operation lifecycle it failed. + /// In the event of retries, the `InterceptorContext` will not include changes made + /// in previous attempts (e.g. by request signers or other interceptors). /// /// **Error Behavior:** Errors raised by this hook will be stored /// until all interceptors have had their `after_attempt` invoked. @@ -527,16 +505,14 @@ pub trait Interceptor: fmt::Debug { /// retry strategy determines that the execution is retryable, /// execution will then jump to `before_attempt`. Otherwise, /// execution will jump to `modify_before_attempt_completion` with the - /// raised error as the [InterceptorContext::output_or_error()]. + /// raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). fn read_after_attempt( &self, context: &FinalizerInterceptorContextRef<'_>, runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let _ctx = context; - let _rc = runtime_components; - let _cfg = cfg; + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); Ok(()) } @@ -547,15 +523,19 @@ pub trait Interceptor: fmt::Debug { /// /// **When:** This will **ALWAYS** be called once per execution. /// - /// **Available Information:** The [InterceptorContext::input()] - /// and [InterceptorContext::output_or_error()] are **ALWAYS** available. The - /// [InterceptorContext::request()] - /// and [InterceptorContext::response()] are available if the - /// execution proceeded far enough for them to be generated. + /// **Available Information:** + /// The [`InterceptorContext::input`](context::InterceptorContext::input), + /// [`InterceptorContext::request`](context::InterceptorContext::request), + /// [`InterceptorContext::response`](context::InterceptorContext::response), or + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available. + /// If the operation succeeded, the `output` will be available. Otherwise, any of the other + /// pieces of information may be available depending on where in the operation lifecycle it failed. + /// In the event of retries, the `InterceptorContext` will not include changes made + /// in previous attempts (e.g. by request signers or other interceptors). /// /// **Error Behavior:** If errors are raised by this /// hook , execution will jump to `after_attempt` with - /// the raised error as the [InterceptorContext::output_or_error()]. + /// the raised error as the [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error). /// /// **Return Constraints:** Any output message returned by this /// hook MUST match the operation being invoked. Any error type can be @@ -566,9 +546,7 @@ pub trait Interceptor: fmt::Debug { runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let _ctx = context; - let _rc = runtime_components; - let _cfg = cfg; + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); Ok(()) } @@ -578,17 +556,21 @@ pub trait Interceptor: fmt::Debug { /// between invocation of this hook and `before_execution` is very /// close to the full duration of the execution. /// - /// **Available Information:** The [InterceptorContext::input()] - /// and [InterceptorContext::output_or_error()] are **ALWAYS** available. The - /// [InterceptorContext::request()] and - /// [InterceptorContext::response()] are available if the - /// execution proceeded far enough for them to be generated. + /// **Available Information:** + /// The [`InterceptorContext::input`](context::InterceptorContext::input), + /// [`InterceptorContext::request`](context::InterceptorContext::request), + /// [`InterceptorContext::response`](context::InterceptorContext::response), or + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) **MAY** be available. + /// If the operation succeeded, the `output` will be available. Otherwise, any of the other + /// pieces of information may be available depending on where in the operation lifecycle it failed. + /// In the event of retries, the `InterceptorContext` will not include changes made + /// in previous attempts (e.g. by request signers or other interceptors). /// /// **Error Behavior:** Errors raised by this hook will be stored /// until all interceptors have had their `after_execution` invoked. /// The error will then be treated as the - /// [InterceptorContext::output_or_error()] to the customer. If multiple - /// `after_execution` methods raise errors , the latest will be + /// [`InterceptorContext::output_or_error`](context::InterceptorContext::output_or_error) + /// to the customer. If multiple `after_execution` methods raise errors , the latest will be /// used and earlier ones will be logged and dropped. fn read_after_execution( &self, @@ -596,9 +578,7 @@ pub trait Interceptor: fmt::Debug { runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let _ctx = context; - let _rc = runtime_components; - let _cfg = cfg; + let (_ctx, _rc, _cfg) = (context, runtime_components, cfg); Ok(()) } } @@ -606,7 +586,7 @@ pub trait Interceptor: fmt::Debug { /// Interceptor wrapper that may be shared #[derive(Clone)] pub struct SharedInterceptor { - interceptor: Arc, + interceptor: Arc, check_enabled: Arc bool + Send + Sync>, } @@ -619,8 +599,8 @@ impl fmt::Debug for SharedInterceptor { } impl SharedInterceptor { - /// Create a new `SharedInterceptor` from `Interceptor` - pub fn new(interceptor: T) -> Self { + /// Create a new `SharedInterceptor` from `Interceptor`. + pub fn new(interceptor: T) -> Self { Self { interceptor: Arc::new(interceptor), check_enabled: Arc::new(|conf: &ConfigBag| { @@ -629,7 +609,8 @@ impl SharedInterceptor { } } - fn enabled(&self, conf: &ConfigBag) -> bool { + /// Checks if this interceptor is enabled in the given config. + pub fn enabled(&self, conf: &ConfigBag) -> bool { (self.check_enabled)(conf) } } @@ -641,84 +622,12 @@ impl AsRef for SharedInterceptor { } impl Deref for SharedInterceptor { - type Target = Arc; + type Target = Arc; fn deref(&self) -> &Self::Target { &self.interceptor } } -/// A interceptor wrapper to conditionally enable the interceptor based on [`DisableInterceptor`] -struct ConditionallyEnabledInterceptor(SharedInterceptor); -impl ConditionallyEnabledInterceptor { - fn if_enabled(&self, cfg: &ConfigBag) -> Option<&dyn Interceptor> { - if self.0.enabled(cfg) { - Some(self.0.as_ref()) - } else { - None - } - } -} - -#[derive(Debug)] -pub struct Interceptors { - interceptors: I, -} - -macro_rules! interceptor_impl_fn { - (mut $interceptor:ident) => { - pub fn $interceptor( - self, - ctx: &mut InterceptorContext, - runtime_components: &RuntimeComponents, - cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - tracing::trace!(concat!( - "running `", - stringify!($interceptor), - "` interceptors" - )); - let mut result: Result<(), BoxError> = Ok(()); - let mut ctx = ctx.into(); - for interceptor in self.into_iter() { - if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = - interceptor.$interceptor(&mut ctx, runtime_components, cfg) - { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); - } - result = Err(new_error); - } - } - } - result.map_err(InterceptorError::$interceptor) - } - }; - (ref $interceptor:ident) => { - pub fn $interceptor( - self, - ctx: &InterceptorContext, - runtime_components: &RuntimeComponents, - cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - let mut result: Result<(), BoxError> = Ok(()); - let ctx = ctx.into(); - for interceptor in self.into_iter() { - if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = interceptor.$interceptor(&ctx, runtime_components, cfg) - { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); - } - result = Err(new_error); - } - } - } - result.map_err(InterceptorError::$interceptor) - } - }; -} - /// Generalized interceptor disabling interface /// /// RuntimePlugins can disable interceptors by inserting [`DisableInterceptor`](DisableInterceptor) into the config bag @@ -744,215 +653,3 @@ pub fn disable_interceptor(cause: &'static str) -> DisableInterc cause, } } - -impl Interceptors -where - I: Iterator, -{ - pub fn new(interceptors: I) -> Self { - Self { interceptors } - } - - fn into_iter(self) -> impl Iterator { - self.interceptors.map(ConditionallyEnabledInterceptor) - } - - pub fn read_before_execution( - self, - operation: bool, - ctx: &InterceptorContext, - cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - tracing::trace!( - "running {} `read_before_execution` interceptors", - if operation { "operation" } else { "client" } - ); - let mut result: Result<(), BoxError> = Ok(()); - let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); - for interceptor in self.into_iter() { - if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); - } - result = Err(new_error); - } - } - } - result.map_err(InterceptorError::read_before_execution) - } - - interceptor_impl_fn!(mut modify_before_serialization); - interceptor_impl_fn!(ref read_before_serialization); - interceptor_impl_fn!(ref read_after_serialization); - interceptor_impl_fn!(mut modify_before_retry_loop); - interceptor_impl_fn!(ref read_before_attempt); - interceptor_impl_fn!(mut modify_before_signing); - interceptor_impl_fn!(ref read_before_signing); - interceptor_impl_fn!(ref read_after_signing); - interceptor_impl_fn!(mut modify_before_transmit); - interceptor_impl_fn!(ref read_before_transmit); - interceptor_impl_fn!(ref read_after_transmit); - interceptor_impl_fn!(mut modify_before_deserialization); - interceptor_impl_fn!(ref read_before_deserialization); - interceptor_impl_fn!(ref read_after_deserialization); - - pub fn modify_before_attempt_completion( - self, - ctx: &mut InterceptorContext, - runtime_components: &RuntimeComponents, - cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - tracing::trace!("running `modify_before_attempt_completion` interceptors"); - let mut result: Result<(), BoxError> = Ok(()); - let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); - for interceptor in self.into_iter() { - if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = - interceptor.modify_before_attempt_completion(&mut ctx, runtime_components, cfg) - { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); - } - result = Err(new_error); - } - } - } - result.map_err(InterceptorError::modify_before_attempt_completion) - } - - pub fn read_after_attempt( - self, - ctx: &InterceptorContext, - runtime_components: &RuntimeComponents, - cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - tracing::trace!("running `read_after_attempt` interceptors"); - let mut result: Result<(), BoxError> = Ok(()); - let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); - for interceptor in self.into_iter() { - if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = - interceptor.read_after_attempt(&ctx, runtime_components, cfg) - { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); - } - result = Err(new_error); - } - } - } - result.map_err(InterceptorError::read_after_attempt) - } - - pub fn modify_before_completion( - self, - ctx: &mut InterceptorContext, - runtime_components: &RuntimeComponents, - cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - tracing::trace!("running `modify_before_completion` interceptors"); - let mut result: Result<(), BoxError> = Ok(()); - let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); - for interceptor in self.into_iter() { - if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = - interceptor.modify_before_completion(&mut ctx, runtime_components, cfg) - { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); - } - result = Err(new_error); - } - } - } - result.map_err(InterceptorError::modify_before_completion) - } - - pub fn read_after_execution( - self, - ctx: &InterceptorContext, - runtime_components: &RuntimeComponents, - cfg: &mut ConfigBag, - ) -> Result<(), InterceptorError> { - tracing::trace!("running `read_after_execution` interceptors"); - let mut result: Result<(), BoxError> = Ok(()); - let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); - for interceptor in self.into_iter() { - if let Some(interceptor) = interceptor.if_enabled(cfg) { - if let Err(new_error) = - interceptor.read_after_execution(&ctx, runtime_components, cfg) - { - if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); - } - result = Err(new_error); - } - } - } - result.map_err(InterceptorError::read_after_execution) - } -} - -#[cfg(all(test, feature = "test-util"))] -mod tests { - use crate::client::interceptors::context::Input; - use crate::client::interceptors::{ - disable_interceptor, BeforeTransmitInterceptorContextRef, BoxError, Interceptor, - InterceptorContext, Interceptors, SharedInterceptor, - }; - use crate::client::runtime_components::{RuntimeComponents, RuntimeComponentsBuilder}; - use aws_smithy_types::config_bag::ConfigBag; - - #[derive(Debug)] - struct TestInterceptor; - impl Interceptor for TestInterceptor {} - - #[test] - fn test_disable_interceptors() { - #[derive(Debug)] - struct PanicInterceptor; - impl Interceptor for PanicInterceptor { - fn read_before_transmit( - &self, - _context: &BeforeTransmitInterceptorContextRef<'_>, - _rc: &RuntimeComponents, - _cfg: &mut ConfigBag, - ) -> Result<(), BoxError> { - Err("boom".into()) - } - } - let rc = RuntimeComponentsBuilder::for_tests() - .with_interceptor(SharedInterceptor::new(PanicInterceptor)) - .with_interceptor(SharedInterceptor::new(TestInterceptor)) - .build() - .unwrap(); - - let mut cfg = ConfigBag::base(); - let interceptors = Interceptors::new(rc.interceptors()); - assert_eq!( - interceptors - .into_iter() - .filter(|i| i.if_enabled(&cfg).is_some()) - .count(), - 2 - ); - - Interceptors::new(rc.interceptors()) - .read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg) - .expect_err("interceptor returns error"); - cfg.interceptor_state() - .store_put(disable_interceptor::("test")); - assert_eq!( - Interceptors::new(rc.interceptors()) - .into_iter() - .filter(|i| i.if_enabled(&cfg).is_some()) - .count(), - 1 - ); - // shouldn't error because interceptors won't run - Interceptors::new(rc.interceptors()) - .read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg) - .expect("interceptor is now disabled"); - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index b97915dcbb..7aeb1904c3 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -37,9 +37,13 @@ use std::{fmt, mem}; use tracing::{debug, error, trace}; // TODO(enableNewSmithyRuntimeLaunch): New-type `Input`/`Output`/`Error` +/// Type-erased operation input. pub type Input = TypeErasedBox; +/// Type-erased operation output. pub type Output = TypeErasedBox; +/// Type-erased operation error. pub type Error = TypeErasedError; +/// Type-erased result for an operation. pub type OutputOrError = Result>; type Request = HttpRequest; @@ -89,38 +93,7 @@ impl InterceptorContext { } } -impl InterceptorContext { - /// Decomposes the context into its constituent parts. - #[doc(hidden)] - #[allow(clippy::type_complexity)] - pub fn into_parts( - self, - ) -> ( - Option, - Option>>, - Option, - Option, - ) { - ( - self.input, - self.output_or_error, - self.request, - self.response, - ) - } - - pub fn finalize(self) -> Result> { - let Self { - output_or_error, - response, - phase, - .. - } = self; - output_or_error - .expect("output_or_error must always be set before finalize is called.") - .map_err(|error| OrchestratorError::into_sdk_error(error, &phase, response)) - } - +impl InterceptorContext { /// Retrieve the input for the operation being invoked. pub fn input(&self) -> Option<&I> { self.input.as_ref() @@ -188,6 +161,14 @@ impl InterceptorContext { self.output_or_error.as_mut() } + /// Return `true` if this context's `output_or_error` is an error. Otherwise, return `false`. + pub fn is_failed(&self) -> bool { + self.output_or_error + .as_ref() + .map(Result::is_err) + .unwrap_or_default() + } + /// Advance to the Serialization phase. #[doc(hidden)] pub fn enter_serialization_phase(&mut self) { @@ -314,6 +295,44 @@ impl InterceptorContext { self.output_or_error = None; RewindResult::Occurred } +} + +impl InterceptorContext +where + E: Debug, +{ + /// Decomposes the context into its constituent parts. + #[doc(hidden)] + #[allow(clippy::type_complexity)] + pub fn into_parts( + self, + ) -> ( + Option, + Option>>, + Option, + Option, + ) { + ( + self.input, + self.output_or_error, + self.request, + self.response, + ) + } + + /// Convert this context into the final operation result that is returned in client's the public API. + #[doc(hidden)] + pub fn finalize(self) -> Result> { + let Self { + output_or_error, + response, + phase, + .. + } = self; + output_or_error + .expect("output_or_error must always be set before finalize is called.") + .map_err(|error| OrchestratorError::into_sdk_error(error, &phase, response)) + } /// Mark this context as failed due to errors during the operation. Any errors already contained /// by the context will be replaced by the given error. @@ -328,14 +347,6 @@ impl InterceptorContext { error!("orchestrator context received an error but one was already present; Throwing away previous error: {:?}", existing_err); } } - - /// Return `true` if this context's `output_or_error` is an error. Otherwise, return `false`. - pub fn is_failed(&self) -> bool { - self.output_or_error - .as_ref() - .map(Result::is_err) - .unwrap_or_default() - } } /// The result of attempting to rewind a request. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs index 64ebdbe08a..77f751e136 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context/wrappers.rs @@ -8,191 +8,222 @@ use crate::client::interceptors::context::{Request, Response}; use crate::client::orchestrator::OrchestratorError; use std::fmt::Debug; -macro_rules! output { - (&Option>) => { - Option> - }; - (&Option<$ty:ty>) => { - Option<&$ty> - }; - (&mut Option<$ty:ty>) => { - Option<&mut $ty> - }; - (&Result<$o_ty:ty, $e_ty:ty>) => { - Result<&$o_ty, &$e_ty> - }; - (&$($tt:tt)+) => { - &$($tt)+ - }; - (&mut $($tt:tt)+) => { - &mut $($tt)+ - }; -} - -macro_rules! declare_method { - (&mut $name:ident, $inner_name:ident, $doc:literal, Option<$ty:ty>) => { - #[doc=$doc] - pub fn $name(&mut self) -> Option<&mut $ty> { - self.inner.$inner_name.as_ref() - } - }; - (&$name:ident, $inner_name:ident, $doc:literal, Option<$ty:ty>) => { - #[doc=$doc] - pub fn $name(&self) -> Option<$ty> { - self.inner.$inner_name.as_mut() - } - }; - (&mut $name:ident, $doc:literal, $($tt:tt)+) => { - #[doc=$doc] - pub fn $name(&mut self) -> output!(&mut $($tt)+) { - self.inner.$name().expect(concat!("`", stringify!($name), "` wasn't set in the underlying interceptor context. This is a bug.")) +macro_rules! impl_from_interceptor_context { + (ref $wrapper:ident) => { + impl<'a, I, O, E> From<&'a InterceptorContext> for $wrapper<'a, I, O, E> { + fn from(inner: &'a InterceptorContext) -> Self { + Self { inner } + } } }; - (&$name:ident, $doc:literal, $($tt:tt)+) => { - #[doc=$doc] - pub fn $name(&self) -> output!(&$($tt)+) { - self.inner.$name().expect(concat!("`", stringify!($name), "` wasn't set in the underlying interceptor context. This is a bug.")) + (mut $wrapper:ident) => { + impl<'a, I, O, E> From<&'a mut InterceptorContext> for $wrapper<'a, I, O, E> { + fn from(inner: &'a mut InterceptorContext) -> Self { + Self { inner } + } } }; } -macro_rules! declare_known_method { - (output_or_error: &mut $($tt:tt)+) => { - declare_method!(&mut output_or_error_mut, "Returns a mutable reference to the deserialized output or error.", $($tt)+); - }; - (output_or_error: &$($tt:tt)+) => { - declare_method!(&output_or_error, "Returns a reference to the deserialized output or error.", $($tt)+); - }; - (input: &mut $($tt:tt)+) => { - declare_method!(&mut input_mut, "Returns a mutable reference to the input.", $($tt)+); - }; - (input: &$($tt:tt)+) => { - declare_method!(&input, "Returns a reference to the input.", $($tt)+); - }; - (request: &mut $($tt:tt)+) => { - declare_method!(&mut request_mut, "Returns a mutable reference to the transmittable request for the operation being invoked.", $($tt)+); - }; - (request: &$($tt:tt)+) => { - declare_method!(&request, "Returns a reference to the transmittable request for the operation being invoked.", $($tt)+); - }; - (response: &mut $($tt:tt)+) => { - declare_method!(&mut response_mut, "Returns a mutable reference to the response.", $($tt)+); - }; - (response: &$($tt:tt)+) => { - declare_method!(&response, "Returns a reference to the response.", $($tt)+); +macro_rules! expect { + ($self:ident, $what:ident) => { + $self.inner.$what().expect(concat!( + "`", + stringify!($what), + "` wasn't set in the underlying interceptor context. This is a bug." + )) }; } -macro_rules! declare_wrapper { - (($ref_struct_name:ident readonly)$($tt:tt)+) => { - pub struct $ref_struct_name<'a, I = Input, O = Output, E = Error> { - inner: &'a InterceptorContext, - } +// +// BeforeSerializationInterceptorContextRef +// - impl<'a, I, O, E: Debug> From<&'a InterceptorContext> for $ref_struct_name<'a, I, O, E> - { - fn from(inner: &'a InterceptorContext) -> Self { - Self { inner } - } - } +/// Interceptor context for the `read_before_execution` and `read_before_serialization` hooks. +/// +/// Only the input is available at this point in the operation. +#[derive(Debug)] +pub struct BeforeSerializationInterceptorContextRef<'a, I = Input, O = Output, E = Error> { + inner: &'a InterceptorContext, +} - impl<'a, I, O, E: Debug> $ref_struct_name<'a, I, O, E> { - declare_ref_wrapper_methods!($($tt)+); - } - }; - (($ref_struct_name:ident $mut_struct_name:ident)$($tt:tt)+) => { - declare_wrapper!(($ref_struct_name readonly) $($tt)+); +impl_from_interceptor_context!(ref BeforeSerializationInterceptorContextRef); - pub struct $mut_struct_name<'a, I = Input, O = Output, E = Error> { - inner: &'a mut InterceptorContext, - } +impl<'a, I, O, E> BeforeSerializationInterceptorContextRef<'a, I, O, E> { + /// Returns a reference to the input. + pub fn input(&self) -> &I { + expect!(self, input) + } +} - impl<'a, I, O, E: Debug> From<&'a mut InterceptorContext> for $mut_struct_name<'a, I, O, E> - { - fn from(inner: &'a mut InterceptorContext) -> Self { - Self { inner } - } - } +// +// BeforeSerializationInterceptorContextMut +// - impl<'a, I, O, E: Debug> $mut_struct_name<'a, I, O, E> { - declare_ref_wrapper_methods!($($tt)+); - declare_mut_wrapper_methods!($($tt)+); - } - }; +/// Interceptor context for the `modify_before_serialization` hook. +/// +/// Only the input is available at this point in the operation. +#[derive(Debug)] +pub struct BeforeSerializationInterceptorContextMut<'a, I = Input, O = Output, E = Error> { + inner: &'a mut InterceptorContext, } -macro_rules! declare_ref_wrapper_methods { - (($field:ident: $($head:tt)+)$($tail:tt)+) => { - declare_known_method!($field: &$($head)+); - declare_ref_wrapper_methods!($($tail)+); - }; - (($field:ident: $($tt:tt)+)) => { - declare_known_method!($field: &$($tt)+); - }; +impl_from_interceptor_context!(mut BeforeSerializationInterceptorContextMut); + +impl<'a, I, O, E> BeforeSerializationInterceptorContextMut<'a, I, O, E> { + /// Returns a reference to the input. + pub fn input(&self) -> &I { + expect!(self, input) + } + + /// Returns a mutable reference to the input. + pub fn input_mut(&mut self) -> &mut I { + expect!(self, input_mut) + } } -macro_rules! declare_mut_wrapper_methods { - (($field:ident: $($head:tt)+)$($tail:tt)+) => { - declare_known_method!($field: &mut $($head)+); - declare_mut_wrapper_methods!($($tail)+); - }; - (($field:ident: $($tt:tt)+)) => { - declare_known_method!($field: &mut $($tt)+); - }; +// +// BeforeSerializationInterceptorContextRef +// + +/// Interceptor context for several hooks in between serialization and transmission. +/// +/// Only the request is available at this point in the operation. +#[derive(Debug)] +pub struct BeforeTransmitInterceptorContextRef<'a, I = Input, O = Output, E = Error> { + inner: &'a InterceptorContext, +} + +impl_from_interceptor_context!(ref BeforeTransmitInterceptorContextRef); + +impl<'a, I, O, E> BeforeTransmitInterceptorContextRef<'a, I, O, E> { + /// Returns a reference to the transmittable request for the operation being invoked. + pub fn request(&self) -> &Request { + expect!(self, request) + } +} + +// +// BeforeSerializationInterceptorContextMut +// + +/// Interceptor context for several hooks in between serialization and transmission. +/// +/// Only the request is available at this point in the operation. +#[derive(Debug)] +pub struct BeforeTransmitInterceptorContextMut<'a, I = Input, O = Output, E = Error> { + inner: &'a mut InterceptorContext, +} + +impl_from_interceptor_context!(mut BeforeTransmitInterceptorContextMut); + +impl<'a, I, O, E> BeforeTransmitInterceptorContextMut<'a, I, O, E> { + /// Returns a reference to the transmittable request for the operation being invoked. + pub fn request(&self) -> &Request { + expect!(self, request) + } + + /// Returns a mutable reference to the transmittable request for the operation being invoked. + pub fn request_mut(&mut self) -> &mut Request { + expect!(self, request_mut) + } +} + +// +// BeforeDeserializationInterceptorContextRef +// + +/// Interceptor context for hooks before deserializing the response. +/// +/// Only the response is available at this point in the operation. +#[derive(Debug)] +pub struct BeforeDeserializationInterceptorContextRef<'a, I = Input, O = Output, E = Error> { + inner: &'a InterceptorContext, } -declare_wrapper!( - (BeforeSerializationInterceptorContextRef BeforeSerializationInterceptorContextMut) - (input: I) -); +impl_from_interceptor_context!(ref BeforeDeserializationInterceptorContextRef); -declare_wrapper!( - (BeforeTransmitInterceptorContextRef BeforeTransmitInterceptorContextMut) - (request: Request) -); +impl<'a, I, O, E> BeforeDeserializationInterceptorContextRef<'a, I, O, E> { + /// Returns a reference to the response. + pub fn response(&self) -> &Response { + expect!(self, response) + } +} + +// +// BeforeDeserializationInterceptorContextMut +// + +/// Interceptor context for hooks before deserializing the response. +/// +/// Only the response is available at this point in the operation. +pub struct BeforeDeserializationInterceptorContextMut<'a, I = Input, O = Output, E = Error> { + inner: &'a mut InterceptorContext, +} -declare_wrapper!( - (BeforeDeserializationInterceptorContextRef BeforeDeserializationInterceptorContextMut) - (input: I) - (request: Request) - (response: Response) -); +impl_from_interceptor_context!(mut BeforeDeserializationInterceptorContextMut); + +impl<'a, I, O, E> BeforeDeserializationInterceptorContextMut<'a, I, O, E> { + /// Returns a reference to the response. + pub fn response(&self) -> &Response { + expect!(self, response) + } + + /// Returns a mutable reference to the response. + pub fn response_mut(&mut self) -> &mut Response { + expect!(self, response_mut) + } -impl<'a, I, O, E: Debug> BeforeDeserializationInterceptorContextMut<'a, I, O, E> { #[doc(hidden)] /// Downgrade this helper struct, returning the underlying InterceptorContext. There's no good /// reason to use this unless you're writing tests or you have to interact with an API that /// doesn't support the helper structs. - pub fn into_inner(&mut self) -> &'_ mut InterceptorContext { + pub fn inner_mut(&mut self) -> &'_ mut InterceptorContext { self.inner } } -declare_wrapper!( - (AfterDeserializationInterceptorContextRef readonly) - (input: I) - (request: Request) - (response: Response) - (output_or_error: Result> -)); - -// Why are all the rest of these defined with a macro but these last two aren't? I simply ran out of -// time. Consider updating the macros to support these last two if you're looking for a challenge. -// - Zelda +// +// AfterDeserializationInterceptorContextRef +// -pub struct FinalizerInterceptorContextRef<'a, I = Input, O = Output, E = Error> { +/// Interceptor context for hooks after deserializing the response. +/// +/// The response and the deserialized output or error are available at this point in the operation. +pub struct AfterDeserializationInterceptorContextRef<'a, I = Input, O = Output, E = Error> { inner: &'a InterceptorContext, } -impl<'a, I, O, E: Debug> From<&'a InterceptorContext> - for FinalizerInterceptorContextRef<'a, I, O, E> -{ - fn from(inner: &'a InterceptorContext) -> Self { - Self { inner } +impl_from_interceptor_context!(ref AfterDeserializationInterceptorContextRef); + +impl<'a, I, O, E> AfterDeserializationInterceptorContextRef<'a, I, O, E> { + /// Returns a reference to the response. + pub fn response(&self) -> &Response { + expect!(self, response) + } + + /// Returns a reference to the deserialized output or error. + pub fn output_or_error(&self) -> Result<&O, &OrchestratorError> { + expect!(self, output_or_error) } } -impl<'a, I, O, E: Debug> FinalizerInterceptorContextRef<'a, I, O, E> { +// +// FinalizerInterceptorContextRef +// + +/// Interceptor context for finalization hooks. +/// +/// This context is used by the `read_after_attempt` and `read_after_execution` hooks +/// that are all called upon both success and failure, and may have varying levels +/// of context available depending on where a failure occurred if the operation failed. +pub struct FinalizerInterceptorContextRef<'a, I = Input, O = Output, E = Error> { + inner: &'a InterceptorContext, +} + +impl_from_interceptor_context!(ref FinalizerInterceptorContextRef); + +impl<'a, I, O, E> FinalizerInterceptorContextRef<'a, I, O, E> { /// Returns the operation input. pub fn input(&self) -> Option<&I> { self.inner.input.as_ref() @@ -214,19 +245,22 @@ impl<'a, I, O, E: Debug> FinalizerInterceptorContextRef<'a, I, O, E> { } } +// +// FinalizerInterceptorContextMut +// + +/// Interceptor context for finalization hooks. +/// +/// This context is used by the `modify_before_attempt_completion` and `modify_before_completion` hooks +/// that are all called upon both success and failure, and may have varying levels +/// of context available depending on where a failure occurred if the operation failed. pub struct FinalizerInterceptorContextMut<'a, I = Input, O = Output, E = Error> { inner: &'a mut InterceptorContext, } -impl<'a, I, O, E: Debug> From<&'a mut InterceptorContext> - for FinalizerInterceptorContextMut<'a, I, O, E> -{ - fn from(inner: &'a mut InterceptorContext) -> Self { - Self { inner } - } -} +impl_from_interceptor_context!(mut FinalizerInterceptorContextMut); -impl<'a, I, O, E: Debug> FinalizerInterceptorContextMut<'a, I, O, E> { +impl<'a, I, O, E> FinalizerInterceptorContextMut<'a, I, O, E> { /// Returns the operation input. pub fn input(&self) -> Option<&I> { self.inner.input.as_ref() diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index cadeccf0d0..f47169cb7b 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -71,62 +71,110 @@ impl Storable for LoadedRequestBody { type Storer = StoreReplace; } -// TODO(enableNewSmithyRuntimeLaunch): Make OrchestratorError adhere to the errors RFC -/// Errors that can occur while running the orchestrator. #[derive(Debug)] -#[non_exhaustive] -pub enum OrchestratorError { +enum ErrorKind { /// An error occurred within an interceptor. - Interceptor { err: InterceptorError }, + Interceptor { source: InterceptorError }, /// An error returned by a service. Operation { err: E }, /// An error that occurs when a request times out. - Timeout { err: BoxError }, + Timeout { source: BoxError }, /// An error that occurs when request dispatch fails. - Connector { err: ConnectorError }, + Connector { source: ConnectorError }, /// An error that occurs when a response can't be deserialized. - Response { err: BoxError }, + Response { source: BoxError }, /// A general orchestrator error. - Other { err: BoxError }, + Other { source: BoxError }, } -impl OrchestratorError { - /// Create a new `OrchestratorError` from a [`BoxError`]. - pub fn other(err: impl Into>) -> Self { - let err = err.into(); - Self::Other { err } +/// Errors that can occur while running the orchestrator. +#[derive(Debug)] +pub struct OrchestratorError { + kind: ErrorKind, +} + +impl OrchestratorError { + /// Create a new `OrchestratorError` from the given source. + pub fn other(source: impl Into>) -> Self { + Self { + kind: ErrorKind::Other { + source: source.into(), + }, + } } - /// Create a new `OrchestratorError` from an error received from a service. + /// Create an operation error. pub fn operation(err: E) -> Self { - Self::Operation { err } + Self { + kind: ErrorKind::Operation { err }, + } } - /// Create a new `OrchestratorError::Interceptor` from an [`InterceptorError`]. - pub fn interceptor(err: InterceptorError) -> Self { - Self::Interceptor { err } + /// True if the underlying error is an operation error. + pub fn is_operation_error(&self) -> bool { + matches!(self.kind, ErrorKind::Operation { .. }) } - /// Create a new `OrchestratorError::Timeout` from a [`BoxError`]. - pub fn timeout(err: BoxError) -> Self { - Self::Timeout { err } + /// Return this orchestrator error as an operation error if possible. + pub fn as_operation_error(&self) -> Option<&E> { + match &self.kind { + ErrorKind::Operation { err } => Some(err), + _ => None, + } } - /// Create a new `OrchestratorError::Response` from a [`BoxError`]. - pub fn response(err: BoxError) -> Self { - Self::Response { err } + /// Create an interceptor error with the given source. + pub fn interceptor(source: InterceptorError) -> Self { + Self { + kind: ErrorKind::Interceptor { source }, + } } - /// Create a new `OrchestratorError::Connector` from a [`ConnectorError`]. - pub fn connector(err: ConnectorError) -> Self { - Self::Connector { err } + /// True if the underlying error is an interceptor error. + pub fn is_interceptor_error(&self) -> bool { + matches!(self.kind, ErrorKind::Interceptor { .. }) } - /// Convert the `OrchestratorError` into `Some` operation specific error if it is one. Otherwise, - /// return `None`. - pub fn as_operation_error(&self) -> Option<&E> { - match self { - Self::Operation { err } => Some(err), + /// Create a timeout error with the given source. + pub fn timeout(source: BoxError) -> Self { + Self { + kind: ErrorKind::Timeout { source }, + } + } + + /// True if the underlying error is a timeout error. + pub fn is_timeout_error(&self) -> bool { + matches!(self.kind, ErrorKind::Timeout { .. }) + } + + /// Create a response error with the given source. + pub fn response(source: BoxError) -> Self { + Self { + kind: ErrorKind::Response { source }, + } + } + + /// True if the underlying error is a response error. + pub fn is_response_error(&self) -> bool { + matches!(self.kind, ErrorKind::Response { .. }) + } + + /// Create a connector error with the given source. + pub fn connector(source: ConnectorError) -> Self { + Self { + kind: ErrorKind::Connector { source }, + } + } + + /// True if the underlying error is a [`ConnectorError`]. + pub fn is_connector_error(&self) -> bool { + matches!(self.kind, ErrorKind::Connector { .. }) + } + + /// Return this orchestrator error as a connector error if possible. + pub fn as_connector_error(&self) -> Option<&ConnectorError> { + match &self.kind { + ErrorKind::Connector { source } => Some(source), _ => None, } } @@ -137,34 +185,36 @@ impl OrchestratorError { phase: &Phase, response: Option, ) -> SdkError { - match self { - Self::Interceptor { err } => { + match self.kind { + ErrorKind::Interceptor { source } => { use Phase::*; match phase { - BeforeSerialization | Serialization => SdkError::construction_failure(err), + BeforeSerialization | Serialization => SdkError::construction_failure(source), BeforeTransmit | Transmit => match response { - Some(response) => SdkError::response_error(err, response), - None => SdkError::dispatch_failure(ConnectorError::other(err.into(), None)), + Some(response) => SdkError::response_error(source, response), + None => { + SdkError::dispatch_failure(ConnectorError::other(source.into(), None)) + } }, BeforeDeserialization | Deserialization | AfterDeserialization => { - SdkError::response_error(err, response.expect("phase has a response")) + SdkError::response_error(source, response.expect("phase has a response")) } } } - Self::Operation { err } => { + ErrorKind::Operation { err } => { debug_assert!(phase.is_after_deserialization(), "operation errors are a result of successfully receiving and parsing a response from the server. Therefore, we must be in the 'After Deserialization' phase."); SdkError::service_error(err, response.expect("phase has a response")) } - Self::Connector { err } => SdkError::dispatch_failure(err), - Self::Timeout { err } => SdkError::timeout_error(err), - Self::Response { err } => SdkError::response_error(err, response.unwrap()), - Self::Other { err } => { + ErrorKind::Connector { source } => SdkError::dispatch_failure(source), + ErrorKind::Timeout { source } => SdkError::timeout_error(source), + ErrorKind::Response { source } => SdkError::response_error(source, response.unwrap()), + ErrorKind::Other { source } => { use Phase::*; match phase { - BeforeSerialization | Serialization => SdkError::construction_failure(err), - BeforeTransmit | Transmit => convert_dispatch_error(err, response), + BeforeSerialization | Serialization => SdkError::construction_failure(source), + BeforeTransmit | Transmit => convert_dispatch_error(source, response), BeforeDeserialization | Deserialization | AfterDeserialization => { - SdkError::response_error(err, response.expect("phase has a response")) + SdkError::response_error(source, response.expect("phase has a response")) } } } @@ -202,12 +252,3 @@ impl From for OrchestratorError { Self::operation(err) } } - -impl From for OrchestratorError -where - E: Debug + std::error::Error + 'static, -{ - fn from(err: aws_smithy_http::byte_stream::error::Error) -> Self { - Self::other(err) - } -} diff --git a/rust-runtime/aws-smithy-runtime-api/src/lib.rs b/rust-runtime/aws-smithy-runtime-api/src/lib.rs index 3f95c9304a..ebc9225a8a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/lib.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/lib.rs @@ -4,8 +4,7 @@ */ #![warn( - // TODO(enableNewSmithyRuntimeLaunch): Add in remaining missing docs - // missing_docs, + missing_docs, rustdoc::missing_crate_level_docs, unreachable_pub, rust_2018_idioms diff --git a/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs index 6e3d2aadfb..1903198a95 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs @@ -72,7 +72,7 @@ impl Interceptor for ConnectionPoisoningInterceptor { .ok_or("retry classifiers are required for connection poisoning to work")?; let error_is_transient = retry_classifiers - .classify_retry(context.into_inner()) + .classify_retry(context.inner_mut()) .map(|reason| reason == RetryReason::Error(ErrorKind::TransientError)) .unwrap_or_default(); let connection_poisoning_is_enabled = diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs index 396aeb6078..931866c621 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs @@ -5,15 +5,246 @@ use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::box_error::BoxError; -use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; -use aws_smithy_runtime_api::client::interceptors::Interceptor; +use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeSerializationInterceptorContextRef, BeforeTransmitInterceptorContextMut, + FinalizerInterceptorContextMut, FinalizerInterceptorContextRef, +}; +use aws_smithy_runtime_api::client::interceptors::context::{ + Error, Input, InterceptorContext, Output, +}; +use aws_smithy_runtime_api::client::interceptors::{ + Interceptor, InterceptorError, SharedInterceptor, +}; use aws_smithy_runtime_api::client::orchestrator::HttpRequest; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; +use aws_smithy_types::error::display::DisplayErrorContext; use std::error::Error as StdError; use std::fmt; use std::marker::PhantomData; +macro_rules! interceptor_impl_fn { + (mut $interceptor:ident) => { + pub(crate) fn $interceptor( + self, + ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!(concat!( + "running `", + stringify!($interceptor), + "` interceptors" + )); + let mut result: Result<(), BoxError> = Ok(()); + let mut ctx = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = + interceptor.$interceptor(&mut ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + } + result.map_err(InterceptorError::$interceptor) + } + }; + (ref $interceptor:ident) => { + pub(crate) fn $interceptor( + self, + ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + let mut result: Result<(), BoxError> = Ok(()); + let ctx = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.$interceptor(&ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + } + result.map_err(InterceptorError::$interceptor) + } + }; +} + +#[derive(Debug)] +pub(crate) struct Interceptors { + interceptors: I, +} + +impl Interceptors +where + I: Iterator, +{ + pub(crate) fn new(interceptors: I) -> Self { + Self { interceptors } + } + + fn into_iter(self) -> impl Iterator { + self.interceptors.map(ConditionallyEnabledInterceptor) + } + + pub(crate) fn read_before_execution( + self, + operation: bool, + ctx: &InterceptorContext, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!( + "running {} `read_before_execution` interceptors", + if operation { "operation" } else { "client" } + ); + let mut result: Result<(), BoxError> = Ok(()); + let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + } + result.map_err(InterceptorError::read_before_execution) + } + + interceptor_impl_fn!(mut modify_before_serialization); + interceptor_impl_fn!(ref read_before_serialization); + interceptor_impl_fn!(ref read_after_serialization); + interceptor_impl_fn!(mut modify_before_retry_loop); + interceptor_impl_fn!(ref read_before_attempt); + interceptor_impl_fn!(mut modify_before_signing); + interceptor_impl_fn!(ref read_before_signing); + interceptor_impl_fn!(ref read_after_signing); + interceptor_impl_fn!(mut modify_before_transmit); + interceptor_impl_fn!(ref read_before_transmit); + interceptor_impl_fn!(ref read_after_transmit); + interceptor_impl_fn!(mut modify_before_deserialization); + interceptor_impl_fn!(ref read_before_deserialization); + interceptor_impl_fn!(ref read_after_deserialization); + + pub(crate) fn modify_before_attempt_completion( + self, + ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!("running `modify_before_attempt_completion` interceptors"); + let mut result: Result<(), BoxError> = Ok(()); + let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = + interceptor.modify_before_attempt_completion(&mut ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + } + result.map_err(InterceptorError::modify_before_attempt_completion) + } + + pub(crate) fn read_after_attempt( + self, + ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!("running `read_after_attempt` interceptors"); + let mut result: Result<(), BoxError> = Ok(()); + let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = + interceptor.read_after_attempt(&ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + } + result.map_err(InterceptorError::read_after_attempt) + } + + pub(crate) fn modify_before_completion( + self, + ctx: &mut InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!("running `modify_before_completion` interceptors"); + let mut result: Result<(), BoxError> = Ok(()); + let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = + interceptor.modify_before_completion(&mut ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + } + result.map_err(InterceptorError::modify_before_completion) + } + + pub(crate) fn read_after_execution( + self, + ctx: &InterceptorContext, + runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), InterceptorError> { + tracing::trace!("running `read_after_execution` interceptors"); + let mut result: Result<(), BoxError> = Ok(()); + let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); + for interceptor in self.into_iter() { + if let Some(interceptor) = interceptor.if_enabled(cfg) { + if let Err(new_error) = + interceptor.read_after_execution(&ctx, runtime_components, cfg) + { + if let Err(last_error) = result { + tracing::debug!("{}", DisplayErrorContext(&*last_error)); + } + result = Err(new_error); + } + } + } + result.map_err(InterceptorError::read_after_execution) + } +} + +/// A interceptor wrapper to conditionally enable the interceptor based on +/// [`DisableInterceptor`](aws_smithy_runtime_api::client::interceptors::DisableInterceptor) +struct ConditionallyEnabledInterceptor(SharedInterceptor); +impl ConditionallyEnabledInterceptor { + fn if_enabled(&self, cfg: &ConfigBag) -> Option<&dyn Interceptor> { + if self.0.enabled(cfg) { + Some(self.0.as_ref()) + } else { + None + } + } +} + pub struct MapRequestInterceptor { f: F, _phantom: PhantomData, @@ -86,3 +317,71 @@ where Ok(()) } } + +#[cfg(all(test, feature = "test-util"))] +mod tests { + use super::*; + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::interceptors::context::{ + BeforeTransmitInterceptorContextRef, Input, InterceptorContext, + }; + use aws_smithy_runtime_api::client::interceptors::{ + disable_interceptor, Interceptor, SharedInterceptor, + }; + use aws_smithy_runtime_api::client::runtime_components::{ + RuntimeComponents, RuntimeComponentsBuilder, + }; + use aws_smithy_types::config_bag::ConfigBag; + + #[derive(Debug)] + struct TestInterceptor; + impl Interceptor for TestInterceptor {} + + #[test] + fn test_disable_interceptors() { + #[derive(Debug)] + struct PanicInterceptor; + impl Interceptor for PanicInterceptor { + fn read_before_transmit( + &self, + _context: &BeforeTransmitInterceptorContextRef<'_>, + _rc: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + Err("boom".into()) + } + } + let rc = RuntimeComponentsBuilder::for_tests() + .with_interceptor(SharedInterceptor::new(PanicInterceptor)) + .with_interceptor(SharedInterceptor::new(TestInterceptor)) + .build() + .unwrap(); + + let mut cfg = ConfigBag::base(); + let interceptors = Interceptors::new(rc.interceptors()); + assert_eq!( + interceptors + .into_iter() + .filter(|i| i.if_enabled(&cfg).is_some()) + .count(), + 2 + ); + + Interceptors::new(rc.interceptors()) + .read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg) + .expect_err("interceptor returns error"); + cfg.interceptor_state() + .store_put(disable_interceptor::("test")); + assert_eq!( + Interceptors::new(rc.interceptors()) + .into_iter() + .filter(|i| i.if_enabled(&cfg).is_some()) + .count(), + 1 + ); + // shouldn't error because interceptors won't run + Interceptors::new(rc.interceptors()) + .read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg) + .expect("interceptor is now disabled"); + } +} diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 15c3b0791c..2adaab14e2 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -7,6 +7,7 @@ #![allow(unknown_lints)] use self::auth::orchestrate_auth; +use crate::client::interceptors::Interceptors; use crate::client::orchestrator::endpoints::orchestrate_endpoint; use crate::client::orchestrator::http::read_body; use crate::client::timeout::{MaybeTimeout, MaybeTimeoutConfig, TimeoutKind}; @@ -19,7 +20,6 @@ use aws_smithy_runtime_api::client::connectors::HttpConnector; use aws_smithy_runtime_api::client::interceptors::context::{ Error, Input, InterceptorContext, Output, RewindResult, }; -use aws_smithy_runtime_api::client::interceptors::Interceptors; use aws_smithy_runtime_api::client::orchestrator::{ HttpResponse, LoadedRequestBody, OrchestratorError, }; @@ -200,7 +200,10 @@ async fn try_op( debug!("loading request body into memory"); let mut body = SdkBody::taken(); mem::swap(&mut body, ctx.request_mut().expect("set above").body_mut()); - let loaded_body = halt_on_err!([ctx] => ByteStream::new(body).collect().await).into_bytes(); + let loaded_body = halt_on_err!([ctx] => + ByteStream::new(body).collect().await.map_err(OrchestratorError::other) + ) + .into_bytes(); *ctx.request_mut().as_mut().expect("set above").body_mut() = SdkBody::from(loaded_body.clone()); cfg.interceptor_state() diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs index 86bbdde0f6..5ff4911f0f 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs @@ -4,7 +4,6 @@ */ use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; -use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; use std::borrow::Cow; @@ -77,17 +76,16 @@ where Err(err) => err, }; - match error { - OrchestratorError::Response { .. } | OrchestratorError::Timeout { .. } => { - Some(RetryReason::Error(ErrorKind::TransientError)) - } - OrchestratorError::Connector { err } if err.is_timeout() || err.is_io() => { + if error.is_response_error() || error.is_timeout_error() { + Some(RetryReason::Error(ErrorKind::TransientError)) + } else if let Some(error) = error.as_connector_error() { + if error.is_timeout() || error.is_io() { Some(RetryReason::Error(ErrorKind::TransientError)) + } else { + error.as_other().map(RetryReason::Error) } - OrchestratorError::Connector { err } if err.is_other().is_some() => { - err.is_other().map(RetryReason::Error) - } - _ => None, + } else { + None } } From 5d0887034eccf2dec9d41615fba54b9c26b037b2 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 25 Jul 2023 12:59:20 -0700 Subject: [PATCH 230/253] Remove hybrid runtime modes (#2870) This PR removes the `both_default_middleware` and `both_default_orchestrator` runtime modes since they no longer compile, and there's no easy way to fix them with the divergences between the two in service config. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../rustsdk/AwsFluentClientDecorator.kt | 2 +- .../smithy/rustsdk/AwsPresigningDecorator.kt | 4 +- .../amazon/smithy/rustsdk/CredentialCaches.kt | 10 +- .../smithy/rustsdk/CredentialProviders.kt | 4 +- .../rustsdk/EndpointBuiltInsDecorator.kt | 4 +- .../HttpConnectorConfigCustomization.kt | 12 +- .../amazon/smithy/rustsdk/RegionDecorator.kt | 12 +- .../smithy/rustsdk/SigV4SigningDecorator.kt | 2 +- .../smithy/rustsdk/UserAgentDecorator.kt | 10 +- .../customize/DisabledAuthDecorator.kt | 2 +- .../timestream/TimestreamDecorator.kt | 2 +- .../test/kotlin/SdkCodegenIntegrationTest.kt | 4 +- .../rustsdk/CredentialCacheConfigTest.kt | 2 +- .../amazon/smithy/rustsdk/TestUtil.kt | 6 +- aws/sra-test/build.gradle.kts | 4 +- .../integration-tests/aws-sdk-s3/Cargo.toml | 4 +- .../benches/middleware_vs_orchestrator.rs | 5 +- .../aws-sdk-s3/tests/sra_test.rs | 45 -------- .../codegen/client/smithy/ClientRustModule.kt | 2 +- .../client/smithy/ClientRustSettings.kt | 28 +---- .../customizations/ApiKeyAuthDecorator.kt | 6 +- .../HttpConnectorConfigDecorator.kt | 12 +- .../ResiliencyConfigCustomization.kt | 16 +-- .../customizations/TimeSourceCustomization.kt | 10 +- .../endpoint/EndpointConfigCustomization.kt | 12 +- .../EndpointParamsInterceptorGenerator.kt | 2 +- .../smithy/generators/PaginatorGenerator.kt | 8 +- .../client/CustomizableOperationGenerator.kt | 2 +- .../client/FluentClientGenerator.kt | 107 ++++++------------ .../IdempotencyTokenProviderCustomization.kt | 10 +- .../config/ServiceConfigGenerator.kt | 22 ++-- .../protocol/ProtocolTestGenerator.kt | 8 +- .../testutil/TestConfigCustomization.kt | 10 +- .../customizations/HttpAuthDecoratorTest.kt | 14 +-- .../ClientContextConfigCustomizationTest.kt | 4 +- .../config/ServiceConfigGeneratorTest.kt | 12 +- 36 files changed, 155 insertions(+), 264 deletions(-) delete mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index 2a558daf03..cf8b107a01 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -104,7 +104,7 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { baseGenerator.operationShape, renderClientCreation = { params -> rust("let mut ${params.configBuilderName} = ${params.configBuilderName};") - if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { // TODO(enableNewSmithyRuntimeLaunch): A builder field could not be accessed directly in the orchestrator // mode when this code change was made. smithy-rs#2792 will enable us to use getters in builders. // Make this `set_region` conditional by checking if `config_builder.region().is_none()` once the PR diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index fc1833688c..522b791fab 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -286,13 +286,13 @@ class AwsPresignedFluentBuilderMethod( """, *codegenScope, "OpError" to section.operationErrorType, - "RawResponseType" to if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + "RawResponseType" to if (codegenContext.smithyRuntimeMode.generateMiddleware) { RuntimeType.smithyHttp(runtimeConfig).resolve("operation::Response") } else { RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse") }, ) { - if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { renderPresignedMethodBodyMiddleware() } else { renderPresignedMethodBody(section) diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt index 4f826ef35d..c25001cb30 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialCaches.kt @@ -66,7 +66,7 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom override fun section(section: ServiceConfig) = writable { when (section) { ServiceConfig.ConfigStruct -> { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate( """pub(crate) credentials_cache: #{SharedCredentialsCache},""", *codegenScope, @@ -75,7 +75,7 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom } ServiceConfig.ConfigImpl -> { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Returns the credentials cache. @@ -99,7 +99,7 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom } ServiceConfig.BuilderStruct -> - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("credentials_cache: #{Option}<#{CredentialsCache}>,", *codegenScope) } @@ -116,7 +116,7 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Sets the credentials cache for this service @@ -142,7 +142,7 @@ class CredentialCacheConfig(codegenContext: ClientCodegenContext) : ConfigCustom } ServiceConfig.BuilderBuild -> { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ if let Some(credentials_provider) = layer.load::<#{SharedCredentialsProvider}>().cloned() { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt index 6be60295d5..0437075d3e 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/CredentialProviders.kt @@ -80,7 +80,7 @@ class CredentialProviderConfig(codegenContext: ClientCodegenContext) : ConfigCus override fun section(section: ServiceConfig) = writable { when (section) { ServiceConfig.BuilderStruct -> { - if (smithyRuntimeMode.defaultToMiddleware) { + if (smithyRuntimeMode.generateMiddleware) { rustTemplate("credentials_provider: #{Option}<#{SharedCredentialsProvider}>,", *codegenScope) } } @@ -96,7 +96,7 @@ class CredentialProviderConfig(codegenContext: ClientCodegenContext) : ConfigCus *codegenScope, ) - if (smithyRuntimeMode.defaultToOrchestrator) { + if (smithyRuntimeMode.generateOrchestrator) { rustTemplate( """ /// Sets the credentials provider for this service diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt index 1d79f10a6a..e59eaa7bc8 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/EndpointBuiltInsDecorator.kt @@ -141,7 +141,7 @@ fun decoratorForBuiltIn( override fun loadBuiltInFromServiceConfig(parameter: Parameter, configRef: String): Writable? = when (parameter.builtIn) { builtIn.builtIn -> writable { - if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { val newtype = configParamNewtype(parameter, name, codegenContext.runtimeConfig) val symbol = parameter.symbol().mapRustType { t -> t.stripOuter() } rustTemplate( @@ -151,7 +151,7 @@ fun decoratorForBuiltIn( } else { rust("$configRef.$name") } - if (codegenContext.smithyRuntimeMode.defaultToMiddleware && parameter.type == ParameterType.STRING) { + if (codegenContext.smithyRuntimeMode.generateMiddleware && parameter.type == ParameterType.STRING) { rust(".clone()") } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt index 911ec7ea27..d2d653e1b5 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/HttpConnectorConfigCustomization.kt @@ -26,7 +26,7 @@ class HttpConnectorDecorator : ClientCodegenDecorator { codegenContext: ClientCodegenContext, baseCustomizations: List, ): List = - baseCustomizations.letIf(codegenContext.smithyRuntimeMode.exclusivelyGenerateMiddleware) { + baseCustomizations.letIf(codegenContext.smithyRuntimeMode.generateMiddleware) { it + HttpConnectorConfigCustomization(codegenContext) } } @@ -45,12 +45,12 @@ class HttpConnectorConfigCustomization( override fun section(section: ServiceConfig): Writable { return when (section) { is ServiceConfig.ConfigStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) } } is ServiceConfig.ConfigImpl -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Return an [`HttpConnector`](#{HttpConnector}) to use when making requests, if any. @@ -73,7 +73,7 @@ class HttpConnectorConfigCustomization( } } is ServiceConfig.BuilderStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) } } @@ -157,7 +157,7 @@ class HttpConnectorConfigCustomization( """, *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ pub fn set_http_connector(&mut self, http_connector: #{Option}>) -> &mut Self { @@ -180,7 +180,7 @@ class HttpConnectorConfigCustomization( } } is ServiceConfig.BuilderBuild -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rust("http_connector: self.http_connector,") } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt index 5c9a0669f6..5dacc952bd 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RegionDecorator.kt @@ -131,7 +131,7 @@ class RegionDecorator : ClientCodegenDecorator { override fun loadBuiltInFromServiceConfig(parameter: Parameter, configRef: String): Writable? { return when (parameter.builtIn) { Builtins.REGION.builtIn -> writable { - if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { rustTemplate( "$configRef.load::<#{Region}>().map(|r|r.as_ref().to_owned())", "Region" to region(codegenContext.runtimeConfig).resolve("Region"), @@ -171,12 +171,12 @@ class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomi override fun section(section: ServiceConfig) = writable { when (section) { ServiceConfig.ConfigStruct -> { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("pub(crate) region: #{Option}<#{Region}>,", *codegenScope) } } ServiceConfig.ConfigImpl -> { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Returns the AWS region, if it was provided. @@ -200,7 +200,7 @@ class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomi } ServiceConfig.BuilderStruct -> { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("pub(crate) region: #{Option}<#{Region}>,", *codegenScope) } } @@ -227,7 +227,7 @@ class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomi *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Sets the AWS region to use when making requests. @@ -253,7 +253,7 @@ class RegionProviderConfig(codegenContext: ClientCodegenContext) : ConfigCustomi } ServiceConfig.BuilderBuild -> { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rust("region: self.region,") } } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt index a34c89f6e5..c7ba6f9c60 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/SigV4SigningDecorator.kt @@ -110,7 +110,7 @@ class SigV4SigningConfig( ) } ServiceConfig.BuilderBuild -> { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ layer.store_put(#{SigningService}::from_static(${sigV4Trait.name.dq()})); diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt index 0ca5ce335c..bd38c4f7ed 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/UserAgentDecorator.kt @@ -154,7 +154,7 @@ class UserAgentDecorator : ClientCodegenDecorator { override fun section(section: ServiceConfig): Writable = when (section) { is ServiceConfig.BuilderStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("app_name: #{Option}<#{AppName}>,", *codegenScope) } } @@ -174,7 +174,7 @@ class UserAgentDecorator : ClientCodegenDecorator { *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Sets the name of the app that is using the client. @@ -206,7 +206,7 @@ class UserAgentDecorator : ClientCodegenDecorator { } is ServiceConfig.BuilderBuild -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rust("layer.store_put(#T.clone());", ClientRustModule.Meta.toType().resolve("API_METADATA")) } else { rust("app_name: self.app_name,") @@ -214,13 +214,13 @@ class UserAgentDecorator : ClientCodegenDecorator { } is ServiceConfig.ConfigStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("app_name: #{Option}<#{AppName}>,", *codegenScope) } } is ServiceConfig.ConfigImpl -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Returns the name of the app that is using the client, if it was provided. diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt index 6cc7345dfa..98a37d778c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/DisabledAuthDecorator.kt @@ -39,7 +39,7 @@ class DisabledAuthDecorator() : ClientCodegenDecorator { val optionalOperations = optionalAuth[service.id]!! return ModelTransformer.create().mapShapes(model) { if (optionalOperations.contains(it.id) && it is OperationShape) { - if (settings.codegenConfig.enableNewSmithyRuntime.defaultToOrchestrator) { + if (settings.codegenConfig.enableNewSmithyRuntime.generateOrchestrator) { it.toBuilder().addTrait(OptionalAuthTrait()).build() } else { // In middleware, having an empty @auth trait completely disabled diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt index 44c7767bd3..57c033421c 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/customize/timestream/TimestreamDecorator.kt @@ -33,7 +33,7 @@ class TimestreamDecorator : ClientCodegenDecorator { override val order: Byte = -1 private fun applies(codegenContext: ClientCodegenContext): Boolean = - codegenContext.smithyRuntimeMode.defaultToOrchestrator + codegenContext.smithyRuntimeMode.generateOrchestrator override fun extraSections(codegenContext: ClientCodegenContext): List = emptyList().letIf(applies(codegenContext)) { diff --git a/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt b/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt index 8ece4a5aa1..02ae9071d3 100644 --- a/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/SdkCodegenIntegrationTest.kt @@ -50,7 +50,7 @@ class SdkCodegenIntegrationTest { fun smokeTestSdkCodegen() { awsSdkIntegrationTest( model, - defaultToOrchestrator = true, + generateOrchestrator = true, ) { _, _ -> /* it should compile */ } } @@ -58,7 +58,7 @@ class SdkCodegenIntegrationTest { fun smokeTestSdkCodegenMiddleware() { awsSdkIntegrationTest( model, - defaultToOrchestrator = false, + generateOrchestrator = false, ) { _, _ -> /* it should compile */ } } } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt index 6d1d7f8adc..960d3adbaf 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/CredentialCacheConfigTest.kt @@ -48,7 +48,7 @@ internal class CredentialCacheConfigTest { @Test fun `config override for credentials`() { - awsSdkIntegrationTest(model, defaultToOrchestrator = true) { clientCodegenContext, rustCrate -> + awsSdkIntegrationTest(model, generateOrchestrator = true) { clientCodegenContext, rustCrate -> val runtimeConfig = clientCodegenContext.runtimeConfig val codegenScope = arrayOf( *RuntimeType.preludeScope, diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt index ca5deedba8..5638aca834 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt @@ -37,10 +37,10 @@ fun awsTestCodegenContext(model: Model? = null, settings: ClientRustSettings? = settings = settings ?: testClientRustSettings(runtimeConfig = AwsTestRuntimeConfig), ) -// TODO(enableNewSmithyRuntimeCleanup): Remove defaultToOrchestrator once the runtime switches to the orchestrator +// TODO(enableNewSmithyRuntimeCleanup): Remove generateOrchestrator once the runtime switches to the orchestrator fun awsSdkIntegrationTest( model: Model, - defaultToOrchestrator: Boolean = true, + generateOrchestrator: Boolean = true, test: (ClientCodegenContext, RustCrate) -> Unit = { _, _ -> }, ) = clientIntegrationTest( @@ -62,7 +62,7 @@ fun awsSdkIntegrationTest( "codegen", ObjectNode.builder() .withMember("includeFluentClient", false) - .letIf(defaultToOrchestrator) { + .letIf(generateOrchestrator) { it.withMember("enableNewSmithyRuntime", StringNode.from("orchestrator")) } .build(), diff --git a/aws/sra-test/build.gradle.kts b/aws/sra-test/build.gradle.kts index 669f231741..a9bb7c328f 100644 --- a/aws/sra-test/build.gradle.kts +++ b/aws/sra-test/build.gradle.kts @@ -65,8 +65,8 @@ val allCodegenTests = servicesToGenerate.map { , "codegen": { "includeFluentClient": false, - ${ ""/* "enableNewSmithyRuntime": "both_default_middleware" */ } - "enableNewSmithyRuntime": "orchestrator" + "enableNewSmithyRuntime": "orchestrator", + "includeEndpointUrlConfig": false }, "customizationConfig": { "awsSdk": { diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml index d8057c6de7..82abf8c094 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml +++ b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml @@ -18,8 +18,8 @@ aws-types = { path = "../../../rust-runtime/aws-types" } criterion = { version = "0.4", features = ["async_tokio"] } http = "0.2.3" http-body = "0.4.5" -last-release-smithy-client = { version = "0.55", package = "aws-smithy-client", features = ["test-util", "rustls"] } -last-release-s3 = { version = "0.26", package = "aws-sdk-s3", features = ["test-util"] } +last-release-smithy-client = { version = "0.55.3", package = "aws-smithy-client", features = ["test-util", "rustls"] } +last-release-s3 = { version = "0.28", package = "aws-sdk-s3", features = ["test-util"] } tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3.15", features = ["env-filter", "json"] } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs index fd4e5440a6..b728a6fa14 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs +++ b/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs @@ -6,9 +6,6 @@ #[macro_use] extern crate criterion; use aws_sdk_s3 as s3; -use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; -use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_types::config_bag::ConfigBag; use criterion::{BenchmarkId, Criterion}; macro_rules! test_connection { @@ -92,7 +89,7 @@ async fn orchestrator(client: &s3::Client) { .list_objects_v2() .bucket("test-bucket") .prefix("prefix~") - .send_orchestrator() + .send() .await .expect("successful execution"); } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs deleted file mode 100644 index 3e6c64be5d..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/sra_test.rs +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -mod util; - -use aws_sdk_s3::config::{Credentials, Region}; -use aws_sdk_s3::Client; -use aws_smithy_client::dvr; -use aws_smithy_client::dvr::MediaType; -use aws_smithy_client::erase::DynConnector; -use std::time::{Duration, UNIX_EPOCH}; - -const LIST_BUCKETS_PATH: &str = "test-data/list-objects-v2.json"; - -#[tokio::test] -async fn sra_test() { - tracing_subscriber::fmt::init(); - - let conn = dvr::ReplayingConnection::from_file(LIST_BUCKETS_PATH).unwrap(); - - let config = aws_sdk_s3::Config::builder() - .credentials_provider(Credentials::for_tests()) - .region(Region::new("us-east-1")) - .http_connector(DynConnector::new(conn.clone())) - .interceptor(util::TestUserAgentInterceptor) - .build(); - let client = Client::from_conf(config); - let fixup = util::FixupPlugin; - - let resp = dbg!( - client - .list_objects_v2() - .bucket("test-bucket") - .prefix("prefix~") - .send_orchestrator_with_plugin(Some(fixup)) - .await - ); - // To regenerate the test: - // conn.dump_to_file("test-data/list-objects-v2.json").unwrap(); - let resp = resp.expect("valid e2e test"); - assert_eq!(resp.name(), Some("test-bucket")); - conn.full_validate(MediaType::Xml).await.expect("success") -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt index 93a0c2de16..afe7b70574 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustModule.kt @@ -78,7 +78,7 @@ object ClientRustModule { val Endpoint = RustModule.public("endpoint") // TODO(enableNewSmithyRuntimeCleanup): Just use Config.endpoint directly and delete this function - fun endpoint(codegenContext: ClientCodegenContext): RustModule.LeafModule = if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + fun endpoint(codegenContext: ClientCodegenContext): RustModule.LeafModule = if (codegenContext.smithyRuntimeMode.generateMiddleware) { Endpoint } else { Config.endpoint diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt index 908c936be2..ca532b19b4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt @@ -74,36 +74,16 @@ data class ClientRustSettings( // TODO(enableNewSmithyRuntimeCleanup): Remove this mode after switching to the orchestrator enum class SmithyRuntimeMode { - Middleware, BothDefaultMiddleware, BothDefaultOrchestrator, Orchestrator, + Middleware, Orchestrator, ; - val exclusivelyGenerateMiddleware: Boolean get() = generateMiddleware && !generateOrchestrator - - val generateMiddleware: Boolean - get() = when (this) { - Middleware, BothDefaultMiddleware, BothDefaultOrchestrator -> true - else -> false - } - - val generateOrchestrator: Boolean - get() = when (this) { - Orchestrator, BothDefaultMiddleware, BothDefaultOrchestrator -> true - else -> false - } - - val defaultToMiddleware: Boolean - get() = when (this) { - Middleware, BothDefaultMiddleware -> true - else -> false - } - val defaultToOrchestrator: Boolean get() = !defaultToMiddleware + val generateMiddleware: Boolean get() = this == Middleware + val generateOrchestrator: Boolean get() = this == Orchestrator companion object { fun fromString(value: String): SmithyRuntimeMode = when (value) { "middleware" -> Middleware "orchestrator" -> Orchestrator - "both_default_middleware" -> BothDefaultMiddleware - "both_default_orchestrator" -> BothDefaultOrchestrator else -> throw IllegalArgumentException("unknown runtime mode: $value") } } @@ -123,7 +103,7 @@ data class ClientCodegenConfig( val addMessageToErrors: Boolean = defaultAddMessageToErrors, // TODO(EventStream): [CLEANUP] Remove this property when turning on Event Stream for all services val eventStreamAllowList: Set = defaultEventStreamAllowList, - // TODO(SmithyRuntime): Remove this once we commit to switch to aws-smithy-runtime and aws-smithy-runtime-api + // TODO(enableNewSmithyRuntimeCleanup): Remove this once we commit to switch to aws-smithy-runtime and aws-smithy-runtime-api val enableNewSmithyRuntime: SmithyRuntimeMode = defaultEnableNewSmithyRuntime, /** If true, adds `endpoint_url`/`set_endpoint_url` methods to the service config */ val includeEndpointUrlConfig: Boolean = defaultIncludeEndpointUrlConfig, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt index 8c48d4ce1d..fb02e28271 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ApiKeyAuthDecorator.kt @@ -189,19 +189,19 @@ private class ApiKeyConfigCustomization(codegenContext: ClientCodegenContext) : ) } is ServiceConfig.BuilderBuild -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rust("layer.store_or_unset(self.api_key);") } else { rust("api_key: self.api_key,") } } is ServiceConfig.ConfigStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("api_key: #{Option}<#{ApiKey}>,", *codegenScope) } } is ServiceConfig.ConfigImpl -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Returns API key used by the client, if it was provided. diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt index 94e0f47daf..49dbce324b 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpConnectorConfigDecorator.kt @@ -115,13 +115,13 @@ private class HttpConnectorConfigCustomization( override fun section(section: ServiceConfig): Writable { return when (section) { is ServiceConfig.ConfigStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) } } is ServiceConfig.ConfigImpl -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Return the [`SharedHttpConnector`](#{SharedHttpConnector}) to use when making requests, if any. @@ -145,7 +145,7 @@ private class HttpConnectorConfigCustomization( } is ServiceConfig.BuilderStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("http_connector: Option<#{HttpConnector}>,", *codegenScope) } } @@ -229,7 +229,7 @@ private class HttpConnectorConfigCustomization( """, *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ pub fn set_http_connector(&mut self, http_connector: Option>) -> &mut Self { @@ -253,7 +253,7 @@ private class HttpConnectorConfigCustomization( } is ServiceConfig.BuilderBuild -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( "#{set_connector}(&mut resolver);", "set_connector" to setConnectorFn(), @@ -264,7 +264,7 @@ private class HttpConnectorConfigCustomization( } is ServiceConfig.OperationConfigOverride -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( "#{set_connector}(&mut resolver);", "set_connector" to setConnectorFn(), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index fc74d456c3..a79e2be687 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -51,7 +51,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon writable { when (section) { is ServiceConfig.ConfigStruct -> { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate( """ retry_config: #{Option}<#{RetryConfig}>, @@ -64,7 +64,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon } is ServiceConfig.ConfigImpl -> { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Return a reference to the retry configuration contained in this config, if any. @@ -117,7 +117,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon } is ServiceConfig.BuilderStruct -> { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate( """ retry_config: #{Option}<#{RetryConfig}>, @@ -167,7 +167,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ pub fn set_retry_config(&mut self, retry_config: #{Option}<#{RetryConfig}>) -> &mut Self { @@ -245,7 +245,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ pub fn set_sleep_impl(&mut self, sleep_impl: #{Option}<#{SharedAsyncSleep}>) -> &mut Self { @@ -313,7 +313,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ pub fn set_timeout_config(&mut self, timeout_config: #{Option}<#{TimeoutConfig}>) -> &mut Self { @@ -335,7 +335,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon ) } - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { Attribute.DocHidden.render(this) rustTemplate( """ @@ -367,7 +367,7 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon } is ServiceConfig.BuilderBuild -> { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ let retry_partition = layer.load::<#{RetryPartition}>().cloned().unwrap_or_else(|| #{RetryPartition}::new("${codegenContext.serviceShape.sdkId()}")); diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt index 44579aec51..ab9a96155c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt @@ -29,7 +29,7 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust writable { when (section) { is ServiceConfig.ConfigStruct -> { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate( """ pub(crate) time_source: #{SharedTimeSource}, @@ -45,7 +45,7 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust "pub fn time_source(&self) -> #{Option}<#{SharedTimeSource}>", *codegenScope, ) { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """self.runtime_components.time_source()""", *codegenScope, @@ -57,7 +57,7 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust } is ServiceConfig.BuilderStruct -> { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate( "time_source: #{Option}<#{SharedTimeSource}>,", *codegenScope, @@ -80,7 +80,7 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Sets the time source used for this service @@ -112,7 +112,7 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust } ServiceConfig.BuilderBuild -> { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ if self.runtime_components.time_source().is_none() { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt index 1cd523ad0f..b1871c6ffc 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointConfigCustomization.kt @@ -45,7 +45,7 @@ internal class EndpointConfigCustomization( val resolverTrait = "#{SmithyResolver}<#{Params}>" when (section) { is ServiceConfig.ConfigStruct -> { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate( "pub (crate) endpoint_resolver: $sharedEndpointResolver,", *codegenScope, @@ -54,7 +54,7 @@ internal class EndpointConfigCustomization( } is ServiceConfig.ConfigImpl -> { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Returns the endpoint resolver. @@ -78,7 +78,7 @@ internal class EndpointConfigCustomization( } is ServiceConfig.BuilderStruct -> { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate( "endpoint_resolver: #{Option}<$sharedEndpointResolver>,", *codegenScope, @@ -181,7 +181,7 @@ internal class EndpointConfigCustomization( *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ pub fn set_endpoint_resolver(&mut self, endpoint_resolver: #{Option}<$sharedEndpointResolver>) -> &mut Self { @@ -205,7 +205,7 @@ internal class EndpointConfigCustomization( } ServiceConfig.BuilderBuild -> { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( "#{set_endpoint_resolver}(&mut resolver);", "set_endpoint_resolver" to setEndpointResolverFn(), @@ -224,7 +224,7 @@ internal class EndpointConfigCustomization( } is ServiceConfig.OperationConfigOverride -> { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( "#{set_endpoint_resolver}(&mut resolver);", "set_endpoint_resolver" to setEndpointResolverFn(), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 6b99410512..2cd374ad6e 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -106,7 +106,7 @@ class EndpointParamsInterceptorGenerator( val builtInParams = params.toList().filter { it.isBuiltIn } // first load builtins and their defaults builtInParams.forEach { param -> - val config = if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + val config = if (codegenContext.smithyRuntimeMode.generateOrchestrator) { "cfg" } else { "_config" diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt index 3f86fe720d..f68aed2dec 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/PaginatorGenerator.kt @@ -204,14 +204,14 @@ class PaginatorGenerator private constructor( "items_fn" to itemsFn(), "output_token" to outputTokenLens, "item_type" to writable { - if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { rustTemplate("#{Result}<#{Output}, #{SdkError}<#{Error}>>", *codegenScope) } else { rustTemplate("#{Result}<#{Output}, #{SdkError}<#{Error}, #{HttpResponse}>>", *codegenScope) } }, "orchestrate" to writable { - if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { rustTemplate( """ { @@ -237,7 +237,7 @@ class PaginatorGenerator private constructor( } }, "runtime_plugin_init" to writable { - if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { rustTemplate( """ let runtime_plugins = #{operation}::operation_runtime_plugins( @@ -317,7 +317,7 @@ class PaginatorGenerator private constructor( paginationInfo.itemsMemberPath, ), "item_type" to writable { - if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { rustTemplate("#{Result}<${itemType()}, #{SdkError}<#{Error}>>", *codegenScope) } else { rustTemplate("#{Result}<${itemType()}, #{SdkError}<#{Error}, #{HttpResponse}>>", *codegenScope) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt index 4046d29e7f..5c2c4e63eb 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/CustomizableOperationGenerator.kt @@ -374,7 +374,7 @@ fun renderCustomizableOperationSend(codegenContext: ClientCodegenContext, generi """, *codegenScope, ) - } else if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + } else if (codegenContext.smithyRuntimeMode.generateMiddleware) { writer.rustTemplate( """ impl#{combined_generics_decl:W} CustomizableOperation#{combined_generics_decl:W} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt index 368d70ae43..d7fb4560b5 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/client/FluentClientGenerator.kt @@ -347,7 +347,7 @@ class FluentClientGenerator( } """, *preludeScope, - "RawResponseType" to if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + "RawResponseType" to if (codegenContext.smithyRuntimeMode.generateMiddleware) { RuntimeType.smithyHttp(runtimeConfig).resolve("operation::Response") } else { RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::HttpResponse") @@ -437,36 +437,7 @@ class FluentClientGenerator( generics.toRustGenerics(), ), ) - rustTemplate( - """ - // This function will go away in the near future. Do not rely on it. - ##[doc(hidden)] - pub async fn customize_middleware(self) -> #{Result}< - #{CustomizableOperation}#{customizable_op_type_params:W}, - #{SdkError}<#{OperationError}> - > #{send_bounds:W} { - let handle = self.handle.clone(); - let operation = self.inner.build().map_err(#{SdkError}::construction_failure)? - .make_operation(&handle.conf) - .await - .map_err(#{SdkError}::construction_failure)?; - #{Ok}(#{CustomizableOperation} { handle, operation }) - } - - // This function will go away in the near future. Do not rely on it. - ##[doc(hidden)] - pub async fn send_middleware(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}>> - #{send_bounds:W} { - let op = self.inner.build().map_err(#{SdkError}::construction_failure)? - .make_operation(&self.handle.conf) - .await - .map_err(#{SdkError}::construction_failure)?; - self.handle.client.call(op).await - } - """, - *middlewareScope, - ) - if (smithyRuntimeMode.defaultToMiddleware) { + if (smithyRuntimeMode.generateMiddleware) { rustTemplate( """ /// Sends the request and returns the response. @@ -479,7 +450,11 @@ class FluentClientGenerator( /// set when configuring the client. pub async fn send(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}>> #{send_bounds:W} { - self.send_middleware().await + let op = self.inner.build().map_err(#{SdkError}::construction_failure)? + .make_operation(&self.handle.conf) + .await + .map_err(#{SdkError}::construction_failure)?; + self.handle.client.call(op).await } /// Consumes this builder, creating a customizable operation that can be modified before being @@ -488,7 +463,12 @@ class FluentClientGenerator( #{CustomizableOperation}#{customizable_op_type_params:W}, #{SdkError}<#{OperationError}> > #{send_bounds:W} { - self.customize_middleware().await + let handle = self.handle.clone(); + let operation = self.inner.build().map_err(#{SdkError}::construction_failure)? + .make_operation(&handle.conf) + .await + .map_err(#{SdkError}::construction_failure)?; + #{Ok}(#{CustomizableOperation} { handle, operation }) } """, *middlewareScope, @@ -511,45 +491,7 @@ class FluentClientGenerator( .resolve("internal::SendResult"), "SdkError" to RuntimeType.sdkError(runtimeConfig), ) - rustTemplate( - """ - ##[doc(hidden)] - pub async fn send_orchestrator(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { - let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; - let runtime_plugins = #{Operation}::operation_runtime_plugins( - self.handle.runtime_plugins.clone(), - &self.handle.conf, - self.config_override, - ); - #{Operation}::orchestrate(&runtime_plugins, input).await - } - - ##[doc(hidden)] - // TODO(enableNewSmithyRuntimeCleanup): Remove `async` once we switch to orchestrator - pub async fn customize_orchestrator( - self, - ) -> #{CustomizableOperation}< - #{OperationOutput}, - #{OperationError}, - > - { - #{CustomizableOperation} { - customizable_send: #{Box}::new(move |config_override| { - #{Box}::pin(async { - self.config_override(config_override) - .send_orchestrator() - .await - }) - }), - config_override: None, - interceptors: vec![], - runtime_plugins: vec![], - } - } - """, - *orchestratorScope, - ) - if (smithyRuntimeMode.defaultToOrchestrator) { + if (smithyRuntimeMode.generateOrchestrator) { rustTemplate( """ /// Sends the request and returns the response. @@ -561,7 +503,13 @@ class FluentClientGenerator( /// is configurable with the [RetryConfig](aws_smithy_types::retry::RetryConfig), which can be /// set when configuring the client. pub async fn send(self) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { - self.send_orchestrator().await + let input = self.inner.build().map_err(#{SdkError}::construction_failure)?; + let runtime_plugins = #{Operation}::operation_runtime_plugins( + self.handle.runtime_plugins.clone(), + &self.handle.conf, + self.config_override, + ); + #{Operation}::orchestrate(&runtime_plugins, input).await } /// Consumes this builder, creating a customizable operation that can be modified before being @@ -577,7 +525,18 @@ class FluentClientGenerator( #{SdkError}<#{OperationError}>, > { - #{Ok}(self.customize_orchestrator().await) + #{Ok}(#{CustomizableOperation} { + customizable_send: #{Box}::new(move |config_override| { + #{Box}::pin(async { + self.config_override(config_override) + .send() + .await + }) + }), + config_override: None, + interceptors: vec![], + runtime_plugins: vec![], + }) } """, *orchestratorScope, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt index 58a3a67438..2606ff5f43 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/IdempotencyTokenProviderCustomization.kt @@ -29,13 +29,13 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext override fun section(section: ServiceConfig): Writable { return when (section) { is ServiceConfig.ConfigStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("pub (crate) idempotency_token_provider: #{IdempotencyTokenProvider},", *codegenScope) } } ServiceConfig.ConfigImpl -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Returns a copy of the idempotency token provider. @@ -63,7 +63,7 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext } ServiceConfig.BuilderStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rustTemplate("idempotency_token_provider: #{Option}<#{IdempotencyTokenProvider}>,", *codegenScope) } } @@ -80,7 +80,7 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext *codegenScope, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ /// Sets the idempotency token provider to use for service calls that require tokens. @@ -106,7 +106,7 @@ class IdempotencyTokenProviderCustomization(codegenContext: ClientCodegenContext } ServiceConfig.BuilderBuild -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ if !resolver.is_set::<#{IdempotencyTokenProvider}>() { diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt index ba400c85bf..bfa244e8d4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGenerator.kt @@ -196,7 +196,7 @@ fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext override fun section(section: ServiceConfig): Writable { return when (section) { ServiceConfig.ConfigStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { docsOrFallback(param.getterDocs) val t = when (param.optional) { true -> param.type.makeOptional() @@ -207,7 +207,7 @@ fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext } ServiceConfig.BuilderStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rust("${param.name}: #T,", param.type.makeOptional()) } } @@ -224,7 +224,7 @@ fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext ) docsOrFallback(param.setterDocs) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ pub fn set_${param.name}(&mut self, ${param.name}: Option<#{T}>) -> &mut Self { @@ -249,7 +249,7 @@ fun standardConfigParam(param: ConfigParam, codegenContext: ClientCodegenContext } ServiceConfig.BuilderBuild -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { val default = "".letIf(!param.optional) { ".unwrap_or_default() " } rust("${param.name}: self.${param.name}$default,") } @@ -334,7 +334,7 @@ class ServiceConfigGenerator( Attribute(Attribute.derive(RuntimeType.Debug)).render(writer) } writer.rustBlock("pub struct Config") { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ // Both `config` and `cloneable` are the same config, but the cloneable one @@ -353,7 +353,7 @@ class ServiceConfigGenerator( } } - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { // Custom implementation for Debug so we don't need to enforce Debug down the chain writer.rustBlock("impl std::fmt::Debug for Config") { writer.rustTemplate( @@ -374,7 +374,7 @@ class ServiceConfigGenerator( pub fn builder() -> Builder { Builder::default() } """, ) - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { writer.rustTemplate( """ /// Converts this config back into a builder so that it can be tweaked. @@ -394,13 +394,13 @@ class ServiceConfigGenerator( } writer.docs("Builder for creating a `Config`.") - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { Attribute(Attribute.derive(RuntimeType.Clone, RuntimeType.Default)).render(writer) } else { Attribute(Attribute.derive(RuntimeType.Clone, RuntimeType.Debug)).render(writer) } writer.rustBlock("pub struct Builder") { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ pub(crate) config: #{CloneableLayer}, @@ -415,7 +415,7 @@ class ServiceConfigGenerator( } } - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { // Custom implementation for Debug so we don't need to enforce Debug down the chain writer.rustBlock("impl std::fmt::Debug for Builder") { writer.rustTemplate( @@ -498,7 +498,7 @@ class ServiceConfigGenerator( } docs("Builds a [`Config`].") - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rust("##[allow(unused_mut)]") rustBlock("pub fn build(mut self) -> Config") { rustTemplate( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index e3f61df3b0..583324b0b6 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -70,7 +70,7 @@ class DefaultProtocolTestGenerator( override val operationShape: OperationShape, private val renderClientCreation: RustWriter.(ClientCreationParams) -> Unit = { params -> - if (params.codegenContext.smithyRuntimeMode.defaultToMiddleware) { + if (params.codegenContext.smithyRuntimeMode.generateMiddleware) { rustTemplate( """ let smithy_client = #{Builder}::new() @@ -347,7 +347,7 @@ class DefaultProtocolTestGenerator( """, RT.sdkBody(runtimeConfig = rc), ) - if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { rust( "let mut op_response = #T::new(http_response);", RT.operationModule(rc).resolve("Response"), @@ -397,7 +397,7 @@ class DefaultProtocolTestGenerator( val errorSymbol = codegenContext.symbolProvider.symbolForOperationError(operationShape) val errorVariant = codegenContext.symbolProvider.toSymbol(expectedShape).name rust("""let parsed = parsed.expect_err("should be error response");""") - if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { rustTemplate( """let parsed: &#{Error} = parsed.as_operation_error().expect("operation error").downcast_ref().unwrap();""", "Error" to codegenContext.symbolProvider.symbolForOperationError(operationShape), @@ -410,7 +410,7 @@ class DefaultProtocolTestGenerator( rust("panic!(\"wrong variant: Got: {:?}. Expected: {:?}\", parsed, expected_output);") } } else { - if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { rust("let parsed = parsed.unwrap();") } else { rustTemplate( diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt index 0f96cf3e0f..dbfa3b3c6c 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/testutil/TestConfigCustomization.kt @@ -31,12 +31,12 @@ fun stubConfigCustomization(name: String, codegenContext: ClientCodegenContext): override fun section(section: ServiceConfig): Writable = writable { when (section) { ServiceConfig.ConfigStruct -> { - if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { rust("_$name: u64,") } } ServiceConfig.ConfigImpl -> { - if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { rustTemplate( """ ##[allow(missing_docs)] @@ -61,12 +61,12 @@ fun stubConfigCustomization(name: String, codegenContext: ClientCodegenContext): } } ServiceConfig.BuilderStruct -> { - if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { rust("_$name: Option,") } } ServiceConfig.BuilderImpl -> { - if (codegenContext.smithyRuntimeMode.defaultToOrchestrator) { + if (codegenContext.smithyRuntimeMode.generateOrchestrator) { rustTemplate( """ /// docs! @@ -93,7 +93,7 @@ fun stubConfigCustomization(name: String, codegenContext: ClientCodegenContext): } } ServiceConfig.BuilderBuild -> { - if (codegenContext.smithyRuntimeMode.defaultToMiddleware) { + if (codegenContext.smithyRuntimeMode.generateMiddleware) { rust( """ _$name: self._$name.unwrap_or(123), diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt index 474ed7b43e..29f25e98af 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/HttpAuthDecoratorTest.kt @@ -57,7 +57,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_orchestrator() + .send() .await .expect("success"); connector.assert_requests_match(&[]); @@ -91,7 +91,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_orchestrator() + .send() .await .expect("success"); connector.assert_requests_match(&[]); @@ -136,7 +136,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_orchestrator() + .send() .await .expect("success"); connector.assert_requests_match(&[]); @@ -182,7 +182,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_orchestrator() + .send() .await .expect("success"); connector.assert_requests_match(&[]); @@ -228,7 +228,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_orchestrator() + .send() .await .expect("success"); connector.assert_requests_match(&[]); @@ -274,7 +274,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_orchestrator() + .send() .await .expect("success"); connector.assert_requests_match(&[]); @@ -316,7 +316,7 @@ class HttpAuthDecoratorTest { .build_dyn(); let client = $moduleName::Client::with_config(smithy_client, config); let _ = client.some_operation() - .send_orchestrator() + .send() .await .expect("success"); connector.assert_requests_match(&[]); diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt index 94bd6524a1..b855c31366 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/ClientContextConfigCustomizationTest.kt @@ -41,7 +41,7 @@ class ClientContextConfigCustomizationTest { val smithyRuntimeMode = SmithyRuntimeMode.fromString(smithyRuntimeModeStr) val context = testClientCodegenContext(model).withSmithyRuntimeMode(smithyRuntimeMode) project.unitTest { - if (smithyRuntimeMode.defaultToOrchestrator) { + if (smithyRuntimeMode.generateOrchestrator) { rustTemplate( """ use #{RuntimePlugin}; @@ -74,7 +74,7 @@ class ClientContextConfigCustomizationTest { } // unset fields project.unitTest { - if (smithyRuntimeMode.defaultToOrchestrator) { + if (smithyRuntimeMode.generateOrchestrator) { rustTemplate( """ use #{RuntimePlugin}; diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt index 9f8056931b..fc439f764c 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/config/ServiceConfigGeneratorTest.kt @@ -96,13 +96,13 @@ internal class ServiceConfigGeneratorTest { return when (section) { ServiceConfig.ConfigStructAdditionalDocs -> emptySection ServiceConfig.ConfigStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rust("config_field: u64,") } } ServiceConfig.ConfigImpl -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ ##[allow(missing_docs)] @@ -128,12 +128,12 @@ internal class ServiceConfigGeneratorTest { } ServiceConfig.BuilderStruct -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rust("config_field: Option") } } ServiceConfig.BuilderImpl -> writable { - if (runtimeMode.defaultToOrchestrator) { + if (runtimeMode.generateOrchestrator) { rustTemplate( """ ##[allow(missing_docs)] @@ -150,7 +150,7 @@ internal class ServiceConfigGeneratorTest { } } ServiceConfig.BuilderBuild -> writable { - if (runtimeMode.defaultToMiddleware) { + if (runtimeMode.generateMiddleware) { rust("config_field: self.config_field.unwrap_or_default(),") } } @@ -170,7 +170,7 @@ internal class ServiceConfigGeneratorTest { val project = TestWorkspace.testProject(symbolProvider) project.withModule(ClientRustModule.config) { sut.render(this) - if (smithyRuntimeMode.defaultToOrchestrator) { + if (smithyRuntimeMode.generateOrchestrator) { unitTest( "set_config_fields", """ From 9a4156de0424e06ea8882c5bf4a24dd64c6c5fd6 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 26 Jul 2023 19:51:36 -0400 Subject: [PATCH 231/253] Remove vestiges of the old TimeSource implementation & refactor (#2877) ## Motivation and Context When `TimeSource` was created, we didn't carefully track down all the places and left a bit of existing implementation to limit the scope of the change. ## Description This removes the public (doc hidden) Testing code from aws-credential-types and our other test time sources instead. ## Testing - IT/UT ## Checklist - [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._ --------- Co-authored-by: John DiSanti --- CHANGELOG.next.toml | 12 ++ aws/rust-runtime/aws-config/Cargo.toml | 1 + .../src/default_provider/credentials.rs | 17 +-- .../aws-config/src/imds/client.rs | 16 +- .../aws-config/src/imds/credentials.rs | 23 +-- aws/rust-runtime/aws-config/src/lib.rs | 5 +- .../aws-config/src/provider_config.rs | 10 +- .../aws-config/src/sts/assume_role.rs | 15 +- .../aws-credential-types/Cargo.toml | 2 +- .../src/cache/lazy_caching.rs | 90 ++++++----- .../aws-credential-types/src/lib.rs | 2 - .../aws-credential-types/src/time_source.rs | 142 ------------------ .../aws-smithy-async/src/test_util.rs | 34 ++++- 13 files changed, 131 insertions(+), 238 deletions(-) delete mode 100644 aws/rust-runtime/aws-credential-types/src/time_source.rs diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 741a454094..0018f2deda 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -686,3 +686,15 @@ x-amzn-errortype: InvalidRequestException references = ["smithy-rs#2866"] meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "server" } author = "david-perez" + +[[aws-sdk-rust]] +message = """The `doc(hidden)` `time_source` in `aws-credential-types` was removed. Use `aws_smithy_async::time` instead.""" +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "rcoh" +references = ["smithy-rs#2877"] + +[[aws-sdk-rust]] +message = """The `doc(hidden)` `with_env` in `ProviderConfig` was removed.""" +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "rcoh" +references = ["smithy-rs#2877"] diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index c19658d9c3..997f7b86ee 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -66,6 +66,7 @@ aws-smithy-client = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-client", fe # used for a usage example hyper-rustls = { version = "0.24", features = ["webpki-tokio", "http2", "http1"] } +aws-smithy-async = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] } [package.metadata.docs.rs] all-features = true diff --git a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs index 115cafcab1..a5e1fb0dc5 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/credentials.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/credentials.rs @@ -199,6 +199,8 @@ impl Builder { #[cfg(test)] mod test { use aws_credential_types::provider::ProvideCredentials; + use aws_smithy_async::time::StaticTimeSource; + use std::time::UNIX_EPOCH; use crate::default_provider::credentials::DefaultCredentialsChain; @@ -279,21 +281,15 @@ mod test { make_test!(imds_no_iam_role); make_test!(imds_default_chain_error); make_test!(imds_default_chain_success, builder: |config| { - config.with_time_source(aws_credential_types::time_source::TimeSource::testing( - &aws_credential_types::time_source::TestingTimeSource::new(std::time::UNIX_EPOCH), - )) + config.with_time_source(StaticTimeSource::new(UNIX_EPOCH)) }); make_test!(imds_assume_role); make_test!(imds_config_with_no_creds, builder: |config| { - config.with_time_source(aws_credential_types::time_source::TimeSource::testing( - &aws_credential_types::time_source::TestingTimeSource::new(std::time::UNIX_EPOCH), - )) + config.with_time_source(StaticTimeSource::new(UNIX_EPOCH)) }); make_test!(imds_disabled); make_test!(imds_default_chain_retries, builder: |config| { - config.with_time_source(aws_credential_types::time_source::TimeSource::testing( - &aws_credential_types::time_source::TestingTimeSource::new(std::time::UNIX_EPOCH), - )) + config.with_time_source(StaticTimeSource::new(UNIX_EPOCH)) }); make_test!(ecs_assume_role); make_test!(ecs_credentials); @@ -335,7 +331,6 @@ mod test { async fn no_providers_configured_err() { use crate::provider_config::ProviderConfig; use aws_credential_types::provider::error::CredentialsError; - use aws_credential_types::time_source::TimeSource; use aws_smithy_async::rt::sleep::TokioSleep; use aws_smithy_client::erase::boxclone::BoxCloneService; use aws_smithy_client::never::NeverConnected; @@ -343,7 +338,7 @@ mod test { tokio::time::pause(); let conf = ProviderConfig::no_configuration() .with_tcp_connector(BoxCloneService::new(NeverConnected::new())) - .with_time_source(TimeSource::default()) + .with_time_source(StaticTimeSource::new(UNIX_EPOCH)) .with_sleep(TokioSleep::new()); let provider = DefaultCredentialsChain::builder() .configure(conf) diff --git a/aws/rust-runtime/aws-config/src/imds/client.rs b/aws/rust-runtime/aws-config/src/imds/client.rs index cb77b0759d..f084bc9a83 100644 --- a/aws/rust-runtime/aws-config/src/imds/client.rs +++ b/aws/rust-runtime/aws-config/src/imds/client.rs @@ -571,8 +571,8 @@ impl ClassifyRetry, SdkError> for ImdsResponseRetryClassi pub(crate) mod test { use crate::imds::client::{Client, EndpointMode, ImdsResponseRetryClassifier}; use crate::provider_config::ProviderConfig; - use aws_credential_types::time_source::{TestingTimeSource, TimeSource}; use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::test_util::instant_time_and_sleep; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::{capture_request, TestConnection}; use aws_smithy_client::{SdkError, SdkSuccess}; @@ -700,14 +700,13 @@ pub(crate) mod test { imds_response(r#"test-imds-output2"#), ), ]); - let mut time_source = TestingTimeSource::new(UNIX_EPOCH); - tokio::time::pause(); + let (time_source, sleep) = instant_time_and_sleep(UNIX_EPOCH); let client = super::Client::builder() .configure( &ProviderConfig::no_configuration() .with_http_connector(DynConnector::new(connection.clone())) - .with_time_source(TimeSource::testing(&time_source)) - .with_sleep(TokioSleep::new()), + .with_time_source(time_source.clone()) + .with_sleep(sleep), ) .endpoint_mode(EndpointMode::IpV6) .token_ttl(Duration::from_secs(600)) @@ -752,14 +751,13 @@ pub(crate) mod test { imds_response(r#"test-imds-output3"#), ), ]); - tokio::time::pause(); - let mut time_source = TestingTimeSource::new(UNIX_EPOCH); + let (time_source, sleep) = instant_time_and_sleep(UNIX_EPOCH); let client = super::Client::builder() .configure( &ProviderConfig::no_configuration() - .with_sleep(TokioSleep::new()) + .with_sleep(sleep) .with_http_connector(DynConnector::new(connection.clone())) - .with_time_source(TimeSource::testing(&time_source)), + .with_time_source(time_source.clone()), ) .endpoint_mode(EndpointMode::IpV6) .token_ttl(Duration::from_secs(600)) diff --git a/aws/rust-runtime/aws-config/src/imds/credentials.rs b/aws/rust-runtime/aws-config/src/imds/credentials.rs index c0c5707f24..ccc65eaf99 100644 --- a/aws/rust-runtime/aws-config/src/imds/credentials.rs +++ b/aws/rust-runtime/aws-config/src/imds/credentials.rs @@ -306,8 +306,7 @@ mod test { }; use crate::provider_config::ProviderConfig; use aws_credential_types::provider::ProvideCredentials; - use aws_credential_types::time_source::{TestingTimeSource, TimeSource}; - use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::test_util::instant_time_and_sleep; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::TestConnection; use tracing_test::traced_test; @@ -369,16 +368,12 @@ mod test { // set to 2021-09-21T04:16:50Z that makes returned credentials' expiry (2021-09-21T04:16:53Z) // not stale let time_of_request_to_fetch_credentials = UNIX_EPOCH + Duration::from_secs(1632197810); - let time_source = TimeSource::testing(&TestingTimeSource::new( - time_of_request_to_fetch_credentials, - )); - - tokio::time::pause(); + let (time_source, sleep) = instant_time_and_sleep(time_of_request_to_fetch_credentials); let provider_config = ProviderConfig::no_configuration() .with_http_connector(DynConnector::new(connection.clone())) - .with_time_source(time_source) - .with_sleep(TokioSleep::new()); + .with_sleep(sleep) + .with_time_source(time_source); let client = crate::imds::Client::builder() .configure(&provider_config) .build() @@ -419,16 +414,12 @@ mod test { // set to 2021-09-21T17:41:25Z that renders fetched credentials already expired (2021-09-21T04:16:53Z) let time_of_request_to_fetch_credentials = UNIX_EPOCH + Duration::from_secs(1632246085); - let time_source = TimeSource::testing(&TestingTimeSource::new( - time_of_request_to_fetch_credentials, - )); - - tokio::time::pause(); + let (time_source, sleep) = instant_time_and_sleep(time_of_request_to_fetch_credentials); let provider_config = ProviderConfig::no_configuration() .with_http_connector(DynConnector::new(connection.clone())) - .with_time_source(time_source) - .with_sleep(TokioSleep::new()); + .with_sleep(sleep) + .with_time_source(time_source); let client = crate::imds::Client::builder() .configure(&provider_config) .build() diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 154659377f..e7c035ea80 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -620,9 +620,8 @@ mod loader { let credentials_cache = if credentials_provider.is_some() { Some(self.credentials_cache.unwrap_or_else(|| { - let mut builder = CredentialsCache::lazy_builder().time_source( - aws_credential_types::time_source::TimeSource::shared(conf.time_source()), - ); + let mut builder = + CredentialsCache::lazy_builder().time_source(conf.time_source()); builder.set_sleep(conf.sleep()); builder.into_credentials_cache() })) diff --git a/aws/rust-runtime/aws-config/src/provider_config.rs b/aws/rust-runtime/aws-config/src/provider_config.rs index 27bc23108d..3712376b42 100644 --- a/aws/rust-runtime/aws-config/src/provider_config.rs +++ b/aws/rust-runtime/aws-config/src/provider_config.rs @@ -5,7 +5,6 @@ //! Configuration Options for Credential Providers -use aws_credential_types::time_source::TimeSource; use aws_smithy_async::rt::sleep::{default_async_sleep, AsyncSleep, SharedAsyncSleep}; use aws_smithy_async::time::SharedTimeSource; use aws_smithy_client::erase::DynConnector; @@ -282,7 +281,7 @@ impl ProviderConfig { } } - #[doc(hidden)] + #[cfg(test)] pub fn with_env(self, env: Env) -> Self { ProviderConfig { parsed_profile: Default::default(), @@ -291,8 +290,11 @@ impl ProviderConfig { } } - #[doc(hidden)] - pub fn with_time_source(self, time_source: TimeSource) -> Self { + /// Override the time source for this configuration + pub fn with_time_source( + self, + time_source: impl aws_smithy_async::time::TimeSource + 'static, + ) -> Self { ProviderConfig { time_source: SharedTimeSource::new(time_source), ..self diff --git a/aws/rust-runtime/aws-config/src/sts/assume_role.rs b/aws/rust-runtime/aws-config/src/sts/assume_role.rs index 50a83b4b6d..90cd91195f 100644 --- a/aws/rust-runtime/aws-config/src/sts/assume_role.rs +++ b/aws/rust-runtime/aws-config/src/sts/assume_role.rs @@ -291,9 +291,10 @@ mod test { use crate::sts::AssumeRoleProvider; use aws_credential_types::credential_fn::provide_credentials_fn; use aws_credential_types::provider::ProvideCredentials; - use aws_credential_types::time_source::{TestingTimeSource, TimeSource}; use aws_credential_types::Credentials; use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::test_util::instant_time_and_sleep; + use aws_smithy_async::time::StaticTimeSource; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::test_connection::{capture_request, TestConnection}; use aws_smithy_http::body::SdkBody; @@ -305,9 +306,9 @@ mod test { let (server, request) = capture_request(None); let provider_conf = ProviderConfig::empty() .with_sleep(TokioSleep::new()) - .with_time_source(TimeSource::testing(&TestingTimeSource::new( + .with_time_source(StaticTimeSource::new( UNIX_EPOCH + Duration::from_secs(1234567890 - 120), - ))) + )) .with_http_connector(DynConnector::new(server)); let provider = AssumeRoleProvider::builder("myrole") .configure(&provider_conf) @@ -335,13 +336,13 @@ mod test { )).unwrap()), ]); - let mut testing_time_source = TestingTimeSource::new( + let (testing_time_source, sleep) = instant_time_and_sleep( UNIX_EPOCH + Duration::from_secs(1234567890 - 120), // 1234567890 since UNIX_EPOCH is 2009-02-13T23:31:30Z ); let provider_conf = ProviderConfig::empty() - .with_sleep(TokioSleep::new()) - .with_time_source(TimeSource::testing(&testing_time_source)) + .with_sleep(sleep) + .with_time_source(testing_time_source.clone()) .with_http_connector(DynConnector::new(conn)); let credentials_list = std::sync::Arc::new(std::sync::Mutex::new(vec![ Credentials::new( @@ -371,8 +372,6 @@ mod test { } })); - tokio::time::pause(); - let creds_first = provider .provide_credentials() .await diff --git a/aws/rust-runtime/aws-credential-types/Cargo.toml b/aws/rust-runtime/aws-credential-types/Cargo.toml index cbd1426fe2..e109667525 100644 --- a/aws/rust-runtime/aws-credential-types/Cargo.toml +++ b/aws/rust-runtime/aws-credential-types/Cargo.toml @@ -20,7 +20,7 @@ tracing = "0.1" zeroize = "1" [dev-dependencies] -aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["rt-tokio"] } +aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async", features = ["rt-tokio", "test-util"] } # used to test compatibility async-trait = "0.1.51" diff --git a/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs b/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs index ae1a6e2d36..6169c2b930 100644 --- a/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs +++ b/aws/rust-runtime/aws-credential-types/src/cache/lazy_caching.rs @@ -5,16 +5,16 @@ //! Lazy, credentials cache implementation -use std::time::{Duration, Instant}; +use std::time::Duration; use aws_smithy_async::future::timeout::Timeout; use aws_smithy_async::rt::sleep::{AsyncSleep, SharedAsyncSleep}; +use aws_smithy_async::time::SharedTimeSource; use tracing::{debug, info, info_span, Instrument}; use crate::cache::{ExpiringCache, ProvideCachedCredentials}; use crate::provider::SharedCredentialsProvider; use crate::provider::{error::CredentialsError, future, ProvideCredentials}; -use crate::time_source::TimeSource; const DEFAULT_LOAD_TIMEOUT: Duration = Duration::from_secs(5); const DEFAULT_CREDENTIAL_EXPIRATION: Duration = Duration::from_secs(15 * 60); @@ -23,7 +23,7 @@ const DEFAULT_BUFFER_TIME_JITTER_FRACTION: fn() -> f64 = fastrand::f64; #[derive(Debug)] pub(crate) struct LazyCredentialsCache { - time: TimeSource, + time: SharedTimeSource, sleeper: SharedAsyncSleep, cache: ExpiringCache, provider: SharedCredentialsProvider, @@ -35,7 +35,7 @@ pub(crate) struct LazyCredentialsCache { impl LazyCredentialsCache { fn new( - time: TimeSource, + time: SharedTimeSource, sleeper: SharedAsyncSleep, provider: SharedCredentialsProvider, load_timeout: Duration, @@ -79,7 +79,7 @@ impl ProvideCachedCredentials for LazyCredentialsCache { // since the futures are not eagerly executed, and the cache will only run one // of them. let future = Timeout::new(provider.provide_credentials(), timeout_future); - let start_time = Instant::now(); + let start_time = self.time.now(); let result = cache .get_or_load(|| { let span = info_span!("lazy_load_credentials"); @@ -111,14 +111,14 @@ impl ProvideCachedCredentials for LazyCredentialsCache { // only once for the first thread that succeeds in populating a cache value. info!( "credentials cache miss occurred; added new AWS credentials (took {:?})", - start_time.elapsed() + self.time.now().duration_since(start_time) ); Ok((credentials, expiry + jitter)) } - // Only instrument the the actual load future so that no span - // is opened if the cache decides not to execute it. - .instrument(span) + // Only instrument the the actual load future so that no span + // is opened if the cache decides not to execute it. + .instrument(span) }) .await; debug!("loaded credentials"); @@ -137,8 +137,8 @@ mod builder { use crate::cache::{CredentialsCache, Inner}; use crate::provider::SharedCredentialsProvider; use aws_smithy_async::rt::sleep::{default_async_sleep, SharedAsyncSleep}; + use aws_smithy_async::time::SharedTimeSource; - use super::TimeSource; use super::{ LazyCredentialsCache, DEFAULT_BUFFER_TIME, DEFAULT_BUFFER_TIME_JITTER_FRACTION, DEFAULT_CREDENTIAL_EXPIRATION, DEFAULT_LOAD_TIMEOUT, @@ -159,7 +159,7 @@ mod builder { #[derive(Clone, Debug, Default)] pub struct Builder { sleep: Option, - time_source: Option, + time_source: Option, load_timeout: Option, buffer_time: Option, buffer_time_jitter_fraction: Option f64>, @@ -193,13 +193,13 @@ mod builder { } #[doc(hidden)] // because they only exist for tests - pub fn time_source(mut self, time_source: TimeSource) -> Self { + pub fn time_source(mut self, time_source: SharedTimeSource) -> Self { self.set_time_source(Some(time_source)); self } #[doc(hidden)] // because they only exist for tests - pub fn set_time_source(&mut self, time_source: Option) -> &mut Self { + pub fn set_time_source(&mut self, time_source: Option) -> &mut Self { self.time_source = time_source; self } @@ -346,37 +346,45 @@ mod tests { use std::time::{Duration, SystemTime, UNIX_EPOCH}; use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; + use aws_smithy_async::test_util::{instant_time_and_sleep, ManualTimeSource}; + use aws_smithy_async::time::{SharedTimeSource, TimeSource}; use tracing::info; use tracing_test::traced_test; use crate::provider::SharedCredentialsProvider; use crate::{ cache::ProvideCachedCredentials, credential_fn::provide_credentials_fn, - provider::error::CredentialsError, time_source::TestingTimeSource, Credentials, + provider::error::CredentialsError, Credentials, }; use super::{ - LazyCredentialsCache, TimeSource, DEFAULT_BUFFER_TIME, DEFAULT_CREDENTIAL_EXPIRATION, + LazyCredentialsCache, DEFAULT_BUFFER_TIME, DEFAULT_CREDENTIAL_EXPIRATION, DEFAULT_LOAD_TIMEOUT, }; const BUFFER_TIME_NO_JITTER: fn() -> f64 = || 0_f64; fn test_provider( - time: TimeSource, + time: impl TimeSource + 'static, buffer_time_jitter_fraction: fn() -> f64, load_list: Vec, ) -> LazyCredentialsCache { let load_list = Arc::new(Mutex::new(load_list)); LazyCredentialsCache::new( - time, + SharedTimeSource::new(time), SharedAsyncSleep::new(TokioSleep::new()), SharedCredentialsProvider::new(provide_credentials_fn(move || { let list = load_list.clone(); async move { - let next = list.lock().unwrap().remove(0); - info!("refreshing the credentials to {:?}", next); - next + let mut list = list.lock().unwrap(); + if list.len() > 0 { + let next = list.remove(0); + info!("refreshing the credentials to {:?}", next); + next + } else { + drop(list); + panic!("no more credentials") + } } })), DEFAULT_LOAD_TIMEOUT, @@ -405,13 +413,13 @@ mod tests { #[traced_test] #[tokio::test] async fn initial_populate_credentials() { - let time = TestingTimeSource::new(UNIX_EPOCH); + let time = ManualTimeSource::new(UNIX_EPOCH); let provider = SharedCredentialsProvider::new(provide_credentials_fn(|| async { info!("refreshing the credentials"); Ok(credentials(1000)) })); let credentials_cache = LazyCredentialsCache::new( - TimeSource::testing(&time), + SharedTimeSource::new(time), SharedAsyncSleep::new(TokioSleep::new()), provider, DEFAULT_LOAD_TIMEOUT, @@ -433,9 +441,9 @@ mod tests { #[traced_test] #[tokio::test] async fn reload_expired_credentials() { - let mut time = TestingTimeSource::new(epoch_secs(100)); + let time = ManualTimeSource::new(epoch_secs(100)); let credentials_cache = test_provider( - TimeSource::testing(&time), + time.clone(), BUFFER_TIME_NO_JITTER, vec![ Ok(credentials(1000)), @@ -457,9 +465,9 @@ mod tests { #[traced_test] #[tokio::test] async fn load_failed_error() { - let mut time = TestingTimeSource::new(epoch_secs(100)); + let time = ManualTimeSource::new(epoch_secs(100)); let credentials_cache = test_provider( - TimeSource::testing(&time), + time.clone(), BUFFER_TIME_NO_JITTER, vec![ Ok(credentials(1000)), @@ -484,9 +492,9 @@ mod tests { .build() .unwrap(); - let time = TestingTimeSource::new(epoch_secs(0)); + let time = ManualTimeSource::new(epoch_secs(0)); let credentials_cache = Arc::new(test_provider( - TimeSource::testing(&time), + time.clone(), BUFFER_TIME_NO_JITTER, vec![ Ok(credentials(500)), @@ -497,16 +505,15 @@ mod tests { ], )); - let locked_time = Arc::new(Mutex::new(time)); - - for i in 0..4 { + // credentials are available up until 4500 seconds after the unix epoch + // 4*50 = 200 tasks are launched => we can advance time 4500/20 => 225 seconds per advance + for _ in 0..4 { let mut tasks = Vec::new(); - for j in 0..50 { + for _ in 0..50 { let credentials_cache = credentials_cache.clone(); - let time = locked_time.clone(); + let time = time.clone(); tasks.push(rt.spawn(async move { - let now = epoch_secs(i * 1000 + (4 * j)); - time.lock().unwrap().set_time(now); + let now = time.advance(Duration::from_secs(22)); let creds = credentials_cache .provide_cached_credentials() @@ -529,15 +536,15 @@ mod tests { #[tokio::test] #[traced_test] async fn load_timeout() { - let time = TestingTimeSource::new(epoch_secs(100)); + let (time, sleep) = instant_time_and_sleep(epoch_secs(100)); let credentials_cache = LazyCredentialsCache::new( - TimeSource::testing(&time), - SharedAsyncSleep::new(TokioSleep::new()), + SharedTimeSource::new(time.clone()), + SharedAsyncSleep::new(sleep), SharedCredentialsProvider::new(provide_credentials_fn(|| async { aws_smithy_async::future::never::Never::new().await; Ok(credentials(1000)) })), - Duration::from_millis(5), + Duration::from_secs(5), DEFAULT_BUFFER_TIME, BUFFER_TIME_NO_JITTER, DEFAULT_CREDENTIAL_EXPIRATION, @@ -547,14 +554,15 @@ mod tests { credentials_cache.provide_cached_credentials().await, Err(CredentialsError::ProviderTimedOut { .. }) )); + assert_eq!(time.now(), epoch_secs(105)); } #[tokio::test] async fn buffer_time_jitter() { - let mut time = TestingTimeSource::new(epoch_secs(100)); + let time = ManualTimeSource::new(epoch_secs(100)); let buffer_time_jitter_fraction = || 0.5_f64; let credentials_cache = test_provider( - TimeSource::testing(&time), + time.clone(), buffer_time_jitter_fraction, vec![Ok(credentials(1000)), Ok(credentials(2000))], ); diff --git a/aws/rust-runtime/aws-credential-types/src/lib.rs b/aws/rust-runtime/aws-credential-types/src/lib.rs index b2f8330b58..de662f6a50 100644 --- a/aws/rust-runtime/aws-credential-types/src/lib.rs +++ b/aws/rust-runtime/aws-credential-types/src/lib.rs @@ -21,7 +21,5 @@ pub mod cache; pub mod credential_fn; mod credentials_impl; pub mod provider; -#[doc(hidden)] -pub mod time_source; pub use credentials_impl::Credentials; diff --git a/aws/rust-runtime/aws-credential-types/src/time_source.rs b/aws/rust-runtime/aws-credential-types/src/time_source.rs deleted file mode 100644 index 212c7aa904..0000000000 --- a/aws/rust-runtime/aws-credential-types/src/time_source.rs +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_smithy_async::time::{SharedTimeSource, TimeSource as TimeSourceTrait}; -use std::ops::Deref; -use std::sync::{Arc, Mutex}; -use std::time::{Duration, SystemTime}; - -impl TimeSourceTrait for TimeSource { - fn now(&self) -> SystemTime { - self.now() - } -} - -/// Time source abstraction -/// -/// Simple abstraction representing time either real-time or manually-specified for testing -#[derive(Debug, Clone)] -// TODO(breakingChangeWindow): Delete this struct -pub struct TimeSource(Inner); - -impl TimeSource { - /// Creates `TimeSource` from the manually specified `time_source`. - pub fn testing(time_source: &TestingTimeSource) -> Self { - TimeSource(Inner::Testing(time_source.clone())) - } - - /// Creates `TimeSource` from a shared time source - pub fn shared(time_source: SharedTimeSource) -> Self { - TimeSource(Inner::Shared(time_source)) - } - - /// Returns the current system time based on the mode. - pub fn now(&self) -> SystemTime { - match &self.0 { - Inner::Default => SystemTime::now(), - Inner::Testing(testing) => testing.now(), - Inner::Shared(ts) => ts.now(), - } - } -} - -impl Default for TimeSource { - /// Creates `TimeSource` from the current system time. - fn default() -> Self { - TimeSource(Inner::Default) - } -} - -/// Time Source that can be manually moved for tests -/// > This has been superseded by [`aws_smithy_async::time::TimeSource`] and will be removed in a -/// > future release. -/// -/// # Examples -/// -/// ```rust -/// # struct Client { -/// # // stub -/// # } -/// # -/// # impl Client { -/// # fn with_timesource(ts: TimeSource) -> Self { -/// # Client { } -/// # } -/// # } -/// use aws_credential_types::time_source::{TestingTimeSource, TimeSource}; -/// use std::time::{UNIX_EPOCH, Duration}; -/// let mut time = TestingTimeSource::new(UNIX_EPOCH); -/// let client = Client::with_timesource(TimeSource::testing(&time)); -/// time.advance(Duration::from_secs(100)); -/// ``` -#[derive(Clone, Debug)] -pub struct TestingTimeSource { - queries: Arc>>, - now: Arc>, -} - -impl TestingTimeSource { - /// Creates `TestingTimeSource` with `start_time`. - pub fn new(start_time: SystemTime) -> Self { - Self { - queries: Default::default(), - now: Arc::new(Mutex::new(start_time)), - } - } - - /// Sets time to the specified `time`. - pub fn set_time(&mut self, time: SystemTime) { - let mut now = self.now.lock().unwrap(); - *now = time; - } - - /// Advances time by `delta`. - pub fn advance(&mut self, delta: Duration) { - let mut now = self.now.lock().unwrap(); - *now += delta; - } - - /// Returns a `Vec` of queried times so far. - pub fn queries(&self) -> impl Deref> + '_ { - self.queries.lock().unwrap() - } - - /// Returns the current time understood by `TestingTimeSource`. - pub fn now(&self) -> SystemTime { - let ts = *self.now.lock().unwrap(); - self.queries.lock().unwrap().push(ts); - ts - } -} - -#[derive(Debug, Clone)] -enum Inner { - Default, - Testing(TestingTimeSource), - Shared(SharedTimeSource), -} - -#[cfg(test)] -mod test { - use super::{TestingTimeSource, TimeSource}; - - use std::time::{Duration, UNIX_EPOCH}; - - #[test] - fn default_time_source_should_not_panic_on_calling_now() { - let time_source = TimeSource::default(); - // no panics - let _ = time_source.now(); - } - - #[test] - fn testing_time_source_should_behave_as_expected() { - let mut testing = TestingTimeSource::new(UNIX_EPOCH); - let time_source = TimeSource::testing(&testing); - assert_eq!(time_source.now(), UNIX_EPOCH); - testing.advance(Duration::from_secs(10)); - assert_eq!(time_source.now(), UNIX_EPOCH + Duration::from_secs(10)); - } -} diff --git a/rust-runtime/aws-smithy-async/src/test_util.rs b/rust-runtime/aws-smithy-async/src/test_util.rs index fa1dfe300b..e323478d84 100644 --- a/rust-runtime/aws-smithy-async/src/test_util.rs +++ b/rust-runtime/aws-smithy-async/src/test_util.rs @@ -35,11 +35,43 @@ impl ManualTimeSource { .unwrap() .as_secs_f64() } + + /// Creates a new [`ManualTimeSource`] + pub fn new(start_time: SystemTime) -> ManualTimeSource { + Self { + start_time, + log: Default::default(), + } + } + + /// Advances the time of this time source by `duration`. + pub fn advance(&self, duration: Duration) -> SystemTime { + let mut log = self.log.lock().unwrap(); + log.push(duration); + self._now(&log) + } + + fn _now(&self, log: &[Duration]) -> SystemTime { + self.start_time + log.iter().sum::() + } + + /// Sets the `time` of this manual time source. + /// + /// # Panics + /// This function panics if `time` < `now()` + pub fn set_time(&self, time: SystemTime) { + let mut log = self.log.lock().unwrap(); + let now = self._now(&log); + if time < now { + panic!("Cannot move time backwards!"); + } + log.push(time.duration_since(now).unwrap()); + } } impl TimeSource for ManualTimeSource { fn now(&self) -> SystemTime { - self.start_time + self.log.lock().unwrap().iter().sum::() + self._now(&self.log.lock().unwrap()) } } From 8eeb21c4901030ddbfa5c6056195200143b01248 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 27 Jul 2023 11:52:12 -0400 Subject: [PATCH 232/253] Split test-util feature to allow test-util to be utilized in WASM (#2873) ## Motivation and Context - #2087 test-util pulled in Hyper but that's not strictly necessary ## Description Split out wiremock as it's own features and do some other cleanup. ## Testing CI ## Checklist - [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._ --- CHANGELOG.next.toml | 7 +++++++ aws/rust-runtime/aws-config/Cargo.toml | 2 +- .../rustsdk/IntegrationTestDependencies.kt | 2 +- aws/sdk/integration-tests/s3/Cargo.toml | 2 +- rust-runtime/aws-smithy-async/additional-ci | 3 +++ rust-runtime/aws-smithy-client/Cargo.toml | 7 ++++--- rust-runtime/aws-smithy-client/additional-ci | 4 ++++ rust-runtime/aws-smithy-client/src/conns.rs | 19 +++++++++++++------ .../aws-smithy-client/src/dvr/record.rs | 6 ++---- rust-runtime/aws-smithy-client/src/lib.rs | 2 +- .../aws-smithy-client/src/test_connection.rs | 3 ++- .../tests/reconnect_on_transient_error.rs | 2 +- 12 files changed, 40 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 0018f2deda..eec4009c22 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -687,6 +687,13 @@ references = ["smithy-rs#2866"] meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "server" } author = "david-perez" + +[[smithy-rs]] +message = "The `test-util` feature in aws-smithy-client has been split to include a separate `wiremock` feature. This allows test-util to be used without a Hyper server dependency making it usable in webassembly targets." +meta = { "breaking" = true, "tada" = false , "bug" = false, "target" = "client" } +author = "rcoh" +references = ["smithy-rs#2873"] + [[aws-sdk-rust]] message = """The `doc(hidden)` `time_source` in `aws-credential-types` was removed. Use `aws_smithy_async::time` instead.""" meta = { "breaking" = true, "tada" = false, "bug" = false } diff --git a/aws/rust-runtime/aws-config/Cargo.toml b/aws/rust-runtime/aws-config/Cargo.toml index 997f7b86ee..60fb0f5261 100644 --- a/aws/rust-runtime/aws-config/Cargo.toml +++ b/aws/rust-runtime/aws-config/Cargo.toml @@ -62,7 +62,7 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" aws-credential-types = { path = "../../sdk/build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } -aws-smithy-client = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-client", features = ["test-util"] } +aws-smithy-client = { path = "../../sdk/build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rt-tokio", "client-hyper"] } # used for a usage example hyper-rustls = { version = "0.24", features = ["webpki-tokio", "http2", "http1"] } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt index 38c7d76a83..5a39041ef2 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt @@ -83,7 +83,7 @@ class IntegrationTestDependencies( val smithyAsync = CargoDependency.smithyAsync(codegenContext.runtimeConfig) .copy(features = setOf("test-util"), scope = DependencyScope.Dev) val smithyClient = CargoDependency.smithyClient(codegenContext.runtimeConfig) - .copy(features = setOf("test-util"), scope = DependencyScope.Dev) + .copy(features = setOf("test-util", "wiremock"), scope = DependencyScope.Dev) val smithyTypes = CargoDependency.smithyTypes(codegenContext.runtimeConfig) .copy(features = setOf("test-util"), scope = DependencyScope.Dev) addDependency(smithyAsync) diff --git a/aws/sdk/integration-tests/s3/Cargo.toml b/aws/sdk/integration-tests/s3/Cargo.toml index 010d73f659..650d1a5db0 100644 --- a/aws/sdk/integration-tests/s3/Cargo.toml +++ b/aws/sdk/integration-tests/s3/Cargo.toml @@ -18,7 +18,7 @@ aws-http = { path = "../../build/aws-sdk/sdk/aws-http" } aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3" } 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-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } +aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "wiremock"] } aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } 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 = ["test-util"] } diff --git a/rust-runtime/aws-smithy-async/additional-ci b/rust-runtime/aws-smithy-async/additional-ci index fde542e9fc..2e342db6fa 100755 --- a/rust-runtime/aws-smithy-async/additional-ci +++ b/rust-runtime/aws-smithy-async/additional-ci @@ -8,6 +8,9 @@ set -e +echo '### Checking compilation under WASM' +cargo check --target wasm32-unknown-unknown + echo "### Checking for duplicate dependency versions in the normal dependency graph with all features enabled" cargo tree -d --edges normal --all-features diff --git a/rust-runtime/aws-smithy-client/Cargo.toml b/rust-runtime/aws-smithy-client/Cargo.toml index 826ce5eb3d..5426587c9d 100644 --- a/rust-runtime/aws-smithy-client/Cargo.toml +++ b/rust-runtime/aws-smithy-client/Cargo.toml @@ -9,11 +9,12 @@ repository = "https://github.com/awslabs/smithy-rs" [features] rt-tokio = ["aws-smithy-async/rt-tokio"] -test-util = ["dep:aws-smithy-protocol-test", "dep:hyper", "hyper?/server", "hyper?/h2", "dep:serde", "dep:serde_json", "serde?/derive", "rustls", "tokio/full"] +test-util = ["dep:aws-smithy-protocol-test", "dep:serde", "dep:serde_json", "serde?/derive"] +wiremock = ["test-util", "dep:hyper", "hyper?/server", "hyper?/h2", "rustls", "tokio/full"] native-tls = [] allow-compilation = [] # our tests use `cargo test --all-features` and native-tls breaks CI rustls = ["dep:hyper-rustls", "dep:lazy_static", "dep:rustls", "client-hyper", "rt-tokio"] -client-hyper = ["dep:hyper"] +client-hyper = ["dep:hyper", "hyper?/client", "hyper?/http2", "hyper?/http1", "hyper?/tcp"] hyper-webpki-doctest-only = ["dep:hyper-rustls", "hyper-rustls?/webpki-roots"] [dependencies] @@ -26,7 +27,7 @@ bytes = "1" fastrand = "2.0.0" http = "0.2.3" http-body = "0.4.4" -hyper = { version = "0.14.26", features = ["client", "http2", "http1", "tcp"], optional = true } +hyper = { version = "0.14.26", default-features = false, optional = true } # cargo does not support optional test dependencies, so to completely disable rustls # we need to add the webpki-roots feature here. # https://github.com/rust-lang/cargo/issues/1596 diff --git a/rust-runtime/aws-smithy-client/additional-ci b/rust-runtime/aws-smithy-client/additional-ci index bb21b8b01f..e2ada6a73e 100755 --- a/rust-runtime/aws-smithy-client/additional-ci +++ b/rust-runtime/aws-smithy-client/additional-ci @@ -8,6 +8,10 @@ set -e +# TODO(msrvUpgrade): This can be enabled when upgrading to Rust 1.71 +# echo '### Checking compilation under WASM' +# cargo hack check --no-dev-deps --target wasm32-unknown-unknown + echo "### Checking for duplicate dependency versions in the normal dependency graph with all features enabled" cargo tree -d --edges normal --all-features diff --git a/rust-runtime/aws-smithy-client/src/conns.rs b/rust-runtime/aws-smithy-client/src/conns.rs index d4e25c45aa..8ee1d8a929 100644 --- a/rust-runtime/aws-smithy-client/src/conns.rs +++ b/rust-runtime/aws-smithy-client/src/conns.rs @@ -202,12 +202,19 @@ mod custom_tls_tests { async fn send_request_and_assert_success(conn: DynConnector, uri: &Uri) { let mut svc = ServiceBuilder::new().service(conn); - let req = Request::builder() - .uri(uri) - .method(Method::GET) - .body(SdkBody::empty()) - .unwrap(); - let res = svc.call(req).await.unwrap(); + let mut att = 0; + let res = loop { + let req = Request::builder() + .uri(uri) + .method(Method::GET) + .body(SdkBody::empty()) + .unwrap(); + if let Ok(res) = svc.call(req).await { + break res; + } + assert!(att < 5); + att += 1; + }; assert!(res.status().is_success()); } } diff --git a/rust-runtime/aws-smithy-client/src/dvr/record.rs b/rust-runtime/aws-smithy-client/src/dvr/record.rs index 234bc23d64..9415528f73 100644 --- a/rust-runtime/aws-smithy-client/src/dvr/record.rs +++ b/rust-runtime/aws-smithy-client/src/dvr/record.rs @@ -18,8 +18,6 @@ use aws_smithy_http::body::SdkBody; use crate::dvr::{self, Action, BodyData, ConnectionId, Direction, Error, NetworkTraffic, Version}; use super::Event; -use crate::conns::Https; -use crate::hyper_ext::Adapter; use std::fmt::Display; use std::io; use std::path::Path; @@ -34,9 +32,9 @@ pub struct RecordingConnection { pub(crate) inner: S, } -impl RecordingConnection> { +#[cfg(all(feature = "rustls", feature = "client-hyper"))] +impl RecordingConnection> { /// Construct a recording connection wrapping a default HTTPS implementation - #[cfg(feature = "rustls")] pub fn https() -> Self { Self { data: Default::default(), diff --git a/rust-runtime/aws-smithy-client/src/lib.rs b/rust-runtime/aws-smithy-client/src/lib.rs index 935dddd2f0..82ddc4cacb 100644 --- a/rust-runtime/aws-smithy-client/src/lib.rs +++ b/rust-runtime/aws-smithy-client/src/lib.rs @@ -34,7 +34,7 @@ pub mod timeout; mod builder; pub use builder::Builder; -#[cfg(feature = "test-util")] +#[cfg(all(feature = "test-util", feature = "client-hyper"))] pub mod dvr; #[cfg(feature = "test-util")] pub mod test_connection; diff --git a/rust-runtime/aws-smithy-client/src/test_connection.rs b/rust-runtime/aws-smithy-client/src/test_connection.rs index 86466944df..a339eac6bb 100644 --- a/rust-runtime/aws-smithy-client/src/test_connection.rs +++ b/rust-runtime/aws-smithy-client/src/test_connection.rs @@ -358,6 +358,7 @@ impl tower::Service> for ConnectionFn { /// match_events!(ev!(dns), ev!(connect), ev!(http(200)))(&mock.events()); /// # } /// ``` +#[cfg(feature = "wiremock")] pub mod wire_mock { use bytes::Bytes; use http::{Request, Response}; @@ -673,7 +674,7 @@ pub mod wire_mock { #[cfg(test)] mod tests { - use hyper::service::Service; + use tower::Service; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; diff --git a/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs b/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs index 3fc0fe133a..695e069cf1 100644 --- a/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs +++ b/rust-runtime/aws-smithy-client/tests/reconnect_on_transient_error.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#![cfg(feature = "test-util")] +#![cfg(feature = "wiremock")] mod test_operation; From 2e82790a34aa209008c72f7431b8428f248a8aa8 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 27 Jul 2023 08:56:18 -0700 Subject: [PATCH 233/253] Update tool dependencies (#2879) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- tools/ci-build/changelogger/Cargo.lock | 517 ++++---- tools/ci-build/crate-hasher/Cargo.lock | 385 +++--- tools/ci-build/difftags/Cargo.lock | 68 +- tools/ci-build/publisher/Cargo.lock | 620 ++++----- tools/ci-build/sdk-lints/Cargo.lock | 472 ++++--- tools/ci-build/sdk-versioner/Cargo.lock | 531 ++++---- tools/ci-cdk/canary-runner/Cargo.lock | 1138 ++++++++--------- tools/ci-cdk/canary-runner/Cargo.toml | 10 +- .../canary-runner/src/generate_matrix.rs | 1 + tools/ci-cdk/canary-runner/src/run.rs | 12 +- tools/echo-server/Cargo.lock | 308 +++-- 11 files changed, 1943 insertions(+), 2119 deletions(-) diff --git a/tools/ci-build/changelogger/Cargo.lock b/tools/ci-build/changelogger/Cargo.lock index 91da13c161..682f4e1217 100644 --- a/tools/ci-build/changelogger/Cargo.lock +++ b/tools/ci-build/changelogger/Cargo.lock @@ -2,30 +2,45 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -34,7 +49,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] @@ -45,11 +60,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bitflags" @@ -57,11 +87,17 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" @@ -100,12 +136,12 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.24" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eef2b3ded6a26dfaec672a742c93c8cf6b689220324da509ec5caa20de55dc83" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -117,9 +153,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.24" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d756c5824fc5c0c1ee8e36000f576968dbcb2081def956c83fad6f40acd46f96" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck", "proc-macro-error", @@ -153,16 +189,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "diff" version = "0.1.13" @@ -186,7 +212,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -201,12 +227,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "fnv" @@ -231,9 +254,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -277,11 +300,17 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.18" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -317,12 +346,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - [[package]] name = "http" version = "0.2.9" @@ -359,9 +382,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -396,9 +419,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -414,43 +437,23 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -463,24 +466,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" @@ -494,16 +494,24 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -536,9 +544,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] @@ -552,19 +560,28 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -581,7 +598,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -592,9 +609,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ "cc", "libc", @@ -613,30 +630,21 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - -[[package]] -name = "output_vt100" -version = "0.1.3" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -646,19 +654,17 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] @@ -688,18 +694,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -710,14 +716,26 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ "aho-corasick", "memchr", @@ -726,15 +744,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64", "bytes", @@ -767,42 +785,47 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustix" -version = "0.37.14" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ - "bitflags", + "bitflags 2.3.3", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -811,9 +834,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -821,35 +844,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -922,9 +945,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -933,15 +956,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -961,9 +984,9 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "time" -version = "0.3.20" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" dependencies = [ "libc", "num_threads", @@ -973,9 +996,9 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "tinyvec" @@ -994,17 +1017,18 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio", "pin-project-lite", "socket2", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1019,9 +1043,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -1049,10 +1073,11 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.38" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ + "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1060,20 +1085,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] @@ -1092,9 +1117,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -1107,9 +1132,9 @@ dependencies = [ [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -1130,11 +1155,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -1146,9 +1170,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1156,24 +1180,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -1183,9 +1207,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1193,28 +1217,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -1251,147 +1275,66 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" diff --git a/tools/ci-build/crate-hasher/Cargo.lock b/tools/ci-build/crate-hasher/Cargo.lock index 6c034cc675..bc931d5984 100644 --- a/tools/ci-build/crate-hasher/Cargo.lock +++ b/tools/ci-build/crate-hasher/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -10,27 +19,29 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] -name = "aho-corasick" -version = "1.0.1" +name = "anyhow" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" -dependencies = [ - "memchr", -] +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] -name = "anyhow" -version = "1.0.70" +name = "async-trait" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] [[package]] name = "atty" @@ -38,7 +49,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] @@ -49,12 +60,33 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "block-buffer" version = "0.10.4" @@ -66,14 +98,20 @@ dependencies = [ [[package]] name = "bstr" -version = "1.4.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" dependencies = [ "memchr", "serde", ] +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + [[package]] name = "cc" version = "1.0.79" @@ -93,7 +131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -113,7 +151,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -127,9 +165,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -159,9 +197,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -176,16 +214,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "diff" version = "0.1.13" @@ -194,9 +222,9 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -210,7 +238,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -225,12 +253,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "filetime" @@ -241,14 +266,14 @@ dependencies = [ "cfg-if", "libc", "redox_syscall 0.2.16", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "miniz_oxide", @@ -270,13 +295,19 @@ dependencies = [ "version_check", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "globset" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" +checksum = "1391ab1f92ffcc08911957149833e682aa3fe252b9f45f966d2ef972274c97df" dependencies = [ - "aho-corasick 0.7.20", + "aho-corasick", "bstr", "fnv", "log", @@ -304,12 +335,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - [[package]] name = "hex" version = "0.4.3" @@ -344,26 +369,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -372,24 +377,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" @@ -399,43 +401,47 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] -name = "output_vt100" -version = "0.1.3" +name = "pin-project-lite" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] @@ -448,7 +454,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -465,18 +471,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -487,7 +493,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -496,38 +502,55 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ - "aho-corasick 1.0.1", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.37.14" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ - "bitflags", + "bitflags 2.3.3", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -541,15 +564,15 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -558,12 +581,15 @@ dependencies = [ [[package]] name = "sha256" -version = "1.1.3" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f8b5de2bac3a4ae28e9b611072a8e326d9b26c8189c0972d4c321fa684f1f" +checksum = "386f700b0c798d92ac20a53342c240ff9d58030c3b845fbaeb92eead3a774792" dependencies = [ + "async-trait", + "bytes", "hex", "sha2", + "tokio", ] [[package]] @@ -583,11 +609,22 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tar" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "ec96d2ffad078296368d46ff1cb309be1c23c513b4ab0e22a45de0185275ac96" dependencies = [ "filetime", "libc", @@ -596,15 +633,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -632,6 +669,18 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "pin-project-lite", +] + [[package]] name = "typenum" version = "1.16.0" @@ -640,9 +689,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "version_check" @@ -691,132 +740,66 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" diff --git a/tools/ci-build/difftags/Cargo.lock b/tools/ci-build/difftags/Cargo.lock index 849338e259..1bbe447c25 100644 --- a/tools/ci-build/difftags/Cargo.lock +++ b/tools/ci-build/difftags/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] @@ -36,9 +36,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "clap" -version = "3.2.23" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags", @@ -53,9 +53,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.18" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck", "proc-macro-error", @@ -90,9 +90,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" @@ -114,9 +114,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", @@ -130,9 +130,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "memchr" @@ -142,15 +142,15 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "once_cell" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "os_str_bytes" -version = "6.4.1" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "proc-macro-error" @@ -178,27 +178,39 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.50" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.23" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.7.1" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ "aho-corasick", "memchr", @@ -207,9 +219,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.28" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "strsim" @@ -219,9 +231,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.107" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -245,9 +257,9 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "unicode-ident" -version = "1.0.6" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unidiff" diff --git a/tools/ci-build/publisher/Cargo.lock b/tools/ci-build/publisher/Cargo.lock index 9f4f267aed..4ac8cf16e6 100644 --- a/tools/ci-build/publisher/Cargo.lock +++ b/tools/ci-build/publisher/Cargo.lock @@ -2,20 +2,41 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "async-recursion" @@ -30,13 +51,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -56,11 +77,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bitflags" @@ -68,6 +104,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "block-buffer" version = "0.10.4" @@ -79,9 +121,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" @@ -114,11 +156,11 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ - "num-integer", + "android-tzdata", "num-traits", "serde", ] @@ -130,7 +172,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -195,9 +237,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] @@ -229,16 +271,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "dialoguer" version = "0.8.0" @@ -259,9 +291,9 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -290,7 +322,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -305,12 +337,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "fnv" @@ -335,9 +364,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -404,7 +433,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -447,11 +476,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.18" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -468,9 +503,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "4.3.6" +version = "4.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "035ef95d03713f2c347a72547b7cd38cbc9af7cd51e6099fb62d586d4a6dee3a" +checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d" dependencies = [ "log", "pest", @@ -503,18 +538,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -558,9 +584,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -595,9 +621,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -613,43 +639,23 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -662,21 +668,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -684,12 +690,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "matchers" @@ -697,7 +700,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -712,16 +715,24 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -753,47 +764,46 @@ dependencies = [ ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "num-traits" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", - "num-traits", ] [[package]] -name = "num-traits" -version = "0.2.15" +name = "num_cpus" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "autocfg", + "hermit-abi 0.3.2", + "libc", ] [[package]] -name = "num_cpus" -version = "1.15.0" +name = "object" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" dependencies = [ - "hermit-abi 0.2.6", - "libc", + "memchr", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -810,7 +820,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -821,9 +831,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ "cc", "libc", @@ -833,18 +843,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - -[[package]] -name = "output_vt100" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "overload" @@ -864,28 +865,28 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall", "smallvec", - "windows-sys 0.45.0", + "windows-targets", ] [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.5.7" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" +checksum = "0d2d1d55045829d65aad9d389139882ad623b33b904e7c9f1b10c5b8927298e5" dependencies = [ "thiserror", "ucd-trie", @@ -893,9 +894,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.7" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15" +checksum = "5f94bca7e7a599d89dea5dfa309e217e7906c3c007fb9c3299c40b10d6a315d3" dependencies = [ "pest", "pest_generator", @@ -903,22 +904,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.7" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e" +checksum = "99d490fe7e8556575ff6911e45567ab95e71617f43781e5c05490dc8d75c965c" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] name = "pest_meta" -version = "2.5.7" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e" +checksum = "2674c66ebb4b4d9036012091b537aae5878970d6999f81a265034d85b136b341" dependencies = [ "once_cell", "pest", @@ -927,9 +928,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -939,19 +940,17 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] @@ -981,9 +980,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -1019,40 +1018,32 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.1", + "regex-automata 0.3.3", + "regex-syntax 0.7.4", ] [[package]] @@ -1064,6 +1055,17 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -1072,15 +1074,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64", "bytes", @@ -1113,48 +1115,53 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustix" -version = "0.37.14" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ - "bitflags", + "bitflags 2.3.3", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -1163,9 +1170,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -1173,35 +1180,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -1222,9 +1229,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -1233,12 +1240,15 @@ dependencies = [ [[package]] name = "sha256" -version = "1.1.3" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f8b5de2bac3a4ae28e9b611072a8e326d9b26c8189c0972d4c321fa684f1f" +checksum = "386f700b0c798d92ac20a53342c240ff9d58030c3b845fbaeb92eead3a774792" dependencies = [ + "async-trait", + "bytes", "hex", "sha2", + "tokio", ] [[package]] @@ -1270,9 +1280,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "smithy-rs-tool-common" @@ -1320,9 +1330,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -1331,15 +1341,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1369,22 +1379,22 @@ checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -1414,11 +1424,12 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.28.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio", @@ -1428,7 +1439,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -1439,7 +1450,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -1484,10 +1495,11 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.38" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ + "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1495,20 +1507,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -1557,9 +1569,9 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "ucd-trie" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" [[package]] name = "unicode-bidi" @@ -1569,9 +1581,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -1590,9 +1602,9 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -1619,11 +1631,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -1635,9 +1646,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1645,24 +1656,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -1672,9 +1683,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1682,28 +1693,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -1740,147 +1751,66 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" diff --git a/tools/ci-build/sdk-lints/Cargo.lock b/tools/ci-build/sdk-lints/Cargo.lock index a3b2511898..a998177790 100644 --- a/tools/ci-build/sdk-lints/Cargo.lock +++ b/tools/ci-build/sdk-lints/Cargo.lock @@ -2,30 +2,45 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -34,7 +49,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] @@ -45,11 +60,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bitflags" @@ -57,11 +87,17 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" @@ -99,7 +135,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -164,7 +200,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -179,12 +215,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "fnv" @@ -209,9 +242,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -255,11 +288,17 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.18" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -295,12 +334,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - [[package]] name = "http" version = "0.2.9" @@ -337,9 +370,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -374,9 +407,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -392,43 +425,23 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -441,24 +454,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" @@ -472,16 +482,24 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -502,19 +520,28 @@ dependencies = [ "tempfile", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -531,7 +558,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -542,9 +569,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ "cc", "libc", @@ -554,21 +581,21 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -578,9 +605,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "proc-macro-error" @@ -608,18 +635,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -630,14 +657,26 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ "aho-corasick", "memchr", @@ -646,15 +685,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64", "bytes", @@ -687,33 +726,38 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustix" -version = "0.37.14" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ - "bitflags", + "bitflags 2.3.3", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -731,11 +775,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -744,9 +788,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -754,35 +798,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -855,9 +899,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -866,15 +910,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -909,17 +953,18 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio", "pin-project-lite", "socket2", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -934,9 +979,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -964,10 +1009,11 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.38" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ + "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -975,20 +1021,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] @@ -1007,9 +1053,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -1022,9 +1068,9 @@ dependencies = [ [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -1045,11 +1091,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -1061,9 +1106,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1071,24 +1116,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -1098,9 +1143,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1108,28 +1153,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -1166,147 +1211,66 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" diff --git a/tools/ci-build/sdk-versioner/Cargo.lock b/tools/ci-build/sdk-versioner/Cargo.lock index eafd9037ee..0ff7ddce42 100644 --- a/tools/ci-build/sdk-versioner/Cargo.lock +++ b/tools/ci-build/sdk-versioner/Cargo.lock @@ -2,30 +2,45 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -34,7 +49,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] @@ -45,11 +60,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "bitflags" @@ -57,11 +87,17 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "bytes" @@ -88,10 +124,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", - "indexmap", + "indexmap 1.9.3", "lazy_static", "strsim", "termcolor", @@ -136,16 +172,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "diff" version = "0.1.13" @@ -161,6 +187,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.1" @@ -169,7 +201,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -184,12 +216,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "fnv" @@ -214,9 +243,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -260,11 +289,17 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.18" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -272,7 +307,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -285,6 +320,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "heck" version = "0.4.1" @@ -300,12 +341,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - [[package]] name = "http" version = "0.2.9" @@ -342,9 +377,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -379,9 +414,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -394,46 +429,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", + "hashbrown 0.12.3", ] [[package]] -name = "io-lifetimes" -version = "1.0.10" +name = "indexmap" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", + "equivalent", + "hashbrown 0.14.0", ] [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -446,24 +471,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "memchr" @@ -477,16 +499,24 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -507,19 +537,28 @@ dependencies = [ "tempfile", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -536,7 +575,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -547,9 +586,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ "cc", "libc", @@ -559,18 +598,9 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" - -[[package]] -name = "output_vt100" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] name = "pathdiff" @@ -580,15 +610,15 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -598,19 +628,17 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] @@ -640,18 +668,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -662,14 +690,26 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" dependencies = [ "aho-corasick", "memchr", @@ -678,15 +718,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ "base64", "bytes", @@ -719,33 +759,38 @@ dependencies = [ "winreg", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustix" -version = "0.37.14" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ - "bitflags", + "bitflags 2.3.3", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -763,11 +808,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -776,9 +821,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -786,35 +831,35 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -887,9 +932,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -898,15 +943,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -941,17 +986,18 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio", "pin-project-lite", "socket2", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -966,9 +1012,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -984,23 +1030,23 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ - "indexmap", + "indexmap 1.9.3", "serde", ] [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ - "indexmap", + "indexmap 2.0.0", "toml_datetime", "winnow", ] @@ -1013,10 +1059,11 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.38" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ + "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1024,20 +1071,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", ] @@ -1056,9 +1103,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -1071,9 +1118,9 @@ dependencies = [ [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -1094,11 +1141,10 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -1110,9 +1156,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1120,24 +1166,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -1147,9 +1193,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1157,28 +1203,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -1215,147 +1261,66 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" @@ -1364,9 +1329,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "25b5872fa2e10bd067ae946f927e726d7d603eaeb6e02fa6a350e0722d2b8c11" dependencies = [ "memchr", ] diff --git a/tools/ci-cdk/canary-runner/Cargo.lock b/tools/ci-cdk/canary-runner/Cargo.lock index c4a5f3fe86..6edc67f2c4 100644 --- a/tools/ci-cdk/canary-runner/Cargo.lock +++ b/tools/ci-cdk/canary-runner/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -10,13 +19,19 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + [[package]] name = "android_system_properties" version = "0.1.5" @@ -28,9 +43,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" [[package]] name = "async-recursion" @@ -40,18 +55,18 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -73,10 +88,11 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "aws-config" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a3ad9e793335d75b2d2faad583487efcc0df9154aff06f299a5c1fc8795698" +checksum = "bcdcf0d683fe9c23d32cf5b53c9918ea0a500375a9fb20109802552658e576c9" dependencies = [ + "aws-credential-types", "aws-http", "aws-sdk-sso", "aws-sdk-sts", @@ -88,6 +104,7 @@ dependencies = [ "aws-smithy-types", "aws-types", "bytes", + "fastrand 1.9.0", "hex", "http", "hyper", @@ -99,13 +116,28 @@ dependencies = [ "zeroize", ] +[[package]] +name = "aws-credential-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcdb2f7acbc076ff5ad05e7864bdb191ca70a6fd07668dc3a1a8bcd051de5ae" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "fastrand 1.9.0", + "tokio", + "tracing", + "zeroize", +] + [[package]] name = "aws-endpoint" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd4e9dad553017821ee529f186e033700e8d61dd5c4b60066b4d8fe805b8cfc" +checksum = "8cce1c41a6cfaa726adee9ebb9a56fcd2bbfd8be49fd8a04c5e20fd968330b04" dependencies = [ "aws-smithy-http", + "aws-smithy-types", "aws-types", "http", "regex", @@ -114,10 +146,11 @@ dependencies = [ [[package]] name = "aws-http" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ef5a579a51d352b628b76f4855ba716be686305e5e59970c476d1ae2214e90d" +checksum = "aadbc44e7a8f3e71c8b374e03ecd972869eb91dd2bc89ed018954a52ba84bc44" dependencies = [ + "aws-credential-types", "aws-smithy-http", "aws-smithy-types", "aws-types", @@ -132,10 +165,11 @@ dependencies = [ [[package]] name = "aws-sdk-cloudwatch" -version = "0.17.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5b3d07682c55fa19d9006ef6c4739ea59d8410955b67449bf208c61ffdf834" +checksum = "7263a92103fcd82ad1cb882274ece03a84a1041d08a487bb3d783c60acdf8f49" dependencies = [ + "aws-credential-types", "aws-endpoint", "aws-http", "aws-sig-auth", @@ -143,22 +177,26 @@ dependencies = [ "aws-smithy-client", "aws-smithy-http", "aws-smithy-http-tower", + "aws-smithy-json", "aws-smithy-query", "aws-smithy-types", "aws-smithy-xml", "aws-types", "bytes", "http", + "regex", "tokio-stream", "tower", + "tracing", ] [[package]] name = "aws-sdk-lambda" -version = "0.17.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f4cd6001d23ff50c00caf6079e1666060215914f8801a49f614252bc04883d" +checksum = "b3ad176ffaa3aafa532246eb6a9f18a7d68da19950704ecc95d33d9dc3c62a9b" dependencies = [ + "aws-credential-types", "aws-endpoint", "aws-http", "aws-sig-auth", @@ -171,16 +209,19 @@ dependencies = [ "aws-types", "bytes", "http", + "regex", "tokio-stream", "tower", + "tracing", ] [[package]] name = "aws-sdk-s3" -version = "0.17.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2c19b69297f16b3f18936e363f954e7504c23a4a0dc3f2833712313c09c2aa" +checksum = "fba197193cbb4bcb6aad8d99796b2291f36fa89562ded5d4501363055b0de89f" dependencies = [ + "aws-credential-types", "aws-endpoint", "aws-http", "aws-sig-auth", @@ -191,24 +232,29 @@ dependencies = [ "aws-smithy-eventstream", "aws-smithy-http", "aws-smithy-http-tower", + "aws-smithy-json", "aws-smithy-types", "aws-smithy-xml", "aws-types", "bytes", - "bytes-utils", "http", "http-body", + "once_cell", + "percent-encoding", + "regex", "tokio-stream", "tower", "tracing", + "url", ] [[package]] name = "aws-sdk-sso" -version = "0.17.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f014b8ad3178b414bf732b36741325ef659fc40752f8c292400fb7c4ecb7fdd0" +checksum = "c8b812340d86d4a766b2ca73f740dfd47a97c2dff0c06c8517a16d88241957e4" dependencies = [ + "aws-credential-types", "aws-endpoint", "aws-http", "aws-sig-auth", @@ -221,16 +267,19 @@ dependencies = [ "aws-types", "bytes", "http", + "regex", "tokio-stream", "tower", + "tracing", ] [[package]] name = "aws-sdk-sts" -version = "0.17.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37e45fdce84327c69fb924b9188fd889056c6afafbd494e8dd0daa400f9c082" +checksum = "265fac131fbfc188e5c3d96652ea90ecc676a934e3174eaaee523c6cec040b3b" dependencies = [ + "aws-credential-types", "aws-endpoint", "aws-http", "aws-sig-auth", @@ -238,21 +287,25 @@ dependencies = [ "aws-smithy-client", "aws-smithy-http", "aws-smithy-http-tower", + "aws-smithy-json", "aws-smithy-query", "aws-smithy-types", "aws-smithy-xml", "aws-types", "bytes", "http", + "regex", "tower", + "tracing", ] [[package]] name = "aws-sig-auth" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6530e72945c11439e9b3c423c95a656a233d73c3a7d4acaf9789048e1bdf7da7" +checksum = "3b94acb10af0c879ecd5c7bdf51cda6679a0a4f4643ce630905a77673bfa3c61" dependencies = [ + "aws-credential-types", "aws-sigv4", "aws-smithy-eventstream", "aws-smithy-http", @@ -263,29 +316,30 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "0.47.1" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0973cb4a7b44ca2b54b3c7c2d01217fae5dbf9ebb96c2098c5c332829e5d352c" +checksum = "9d2ce6f507be68e968a33485ced670111d1cbad161ddbbab1e313c03d37d8f4c" dependencies = [ "aws-smithy-eventstream", "aws-smithy-http", "bytes", "form_urlencoded", "hex", + "hmac", "http", "once_cell", "percent-encoding", "regex", - "ring", + "sha2", "time", "tracing", ] [[package]] name = "aws-smithy-async" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fc23ad8d050c241bdbfa74ae360be94a844ace8e218f64a2b2de77bfa9a707" +checksum = "13bda3996044c202d75b91afeb11a9afae9db9a721c6a7a427410018e286b880" dependencies = [ "futures-util", "pin-project-lite", @@ -295,9 +349,9 @@ dependencies = [ [[package]] name = "aws-smithy-checksums" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd674df030b337a84eb67539db048676c691d9c88f0c54cf7748da11836cfd8" +checksum = "07ed8b96d95402f3f6b8b57eb4e0e45ee365f78b1a924faf20ff6e97abf1eae6" dependencies = [ "aws-smithy-http", "aws-smithy-types", @@ -316,22 +370,23 @@ dependencies = [ [[package]] name = "aws-smithy-client" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e147b157f49ce77f2a86ec693a14c84b2441fa28be58ffb2febb77d5726c934" +checksum = "0a86aa6e21e86c4252ad6a0e3e74da9617295d8d6e374d552be7d3059c41cedd" dependencies = [ "aws-smithy-async", "aws-smithy-http", "aws-smithy-http-tower", "aws-smithy-types", "bytes", - "fastrand", + "fastrand 1.9.0", "http", "http-body", "hyper", - "hyper-rustls 0.22.1", + "hyper-rustls 0.23.2", "lazy_static", "pin-project-lite", + "rustls 0.20.8", "tokio", "tower", "tracing", @@ -339,9 +394,9 @@ dependencies = [ [[package]] name = "aws-smithy-eventstream" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da29e67a0b90a2bc5f2bd0a06fd43e728de62e02048879c15f646a3edf8db012" +checksum = "460c8da5110835e3d9a717c61f5556b20d03c32a1dec57f8fc559b360f733bb8" dependencies = [ "aws-smithy-types", "bytes", @@ -350,9 +405,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc1af50eac644ab6f58e5bae29328ba3092851fc2ce648ad139134699b2b66f" +checksum = "2b3b693869133551f135e1f2c77cb0b8277d9e3e17feaf2213f735857c4f0d28" dependencies = [ "aws-smithy-eventstream", "aws-smithy-types", @@ -365,6 +420,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "pin-utils", "tokio", "tokio-util", "tracing", @@ -372,11 +428,12 @@ dependencies = [ [[package]] name = "aws-smithy-http-tower" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1bf4c4664dff2febf91f8796505c5bc8f38a0bff0d1397d1d3fdda17bd5c5d1" +checksum = "3ae4f6c5798a247fac98a867698197d9ac22643596dc3777f0c76b91917616b9" dependencies = [ "aws-smithy-http", + "aws-smithy-types", "bytes", "http", "http-body", @@ -387,18 +444,18 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e6ebc76c3c108dd2a96506bf47dc31f75420811a19f1a09907524d1451789d2" +checksum = "23f9f42fbfa96d095194a632fbac19f60077748eba536eb0b9fecc28659807f8" dependencies = [ "aws-smithy-types", ] [[package]] name = "aws-smithy-query" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2956f1385c4daa883907a2c81d32256af8f95834c9de1bc0613fa68db63b88c4" +checksum = "98819eb0b04020a1c791903533b638534ae6c12e2aceda3e6e6fba015608d51d" dependencies = [ "aws-smithy-types", "urlencoding", @@ -406,10 +463,11 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "352fb335ec1d57160a17a13e87aaa0a172ab780ddf58bfc85caedd3b7e47caed" +checksum = "16a3d0bf4f324f4ef9793b86a1701d9700fbcdbd12a846da45eed104c634c6e8" dependencies = [ + "base64-simd", "itoa", "num-integer", "ryu", @@ -418,19 +476,20 @@ dependencies = [ [[package]] name = "aws-smithy-xml" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cf2807fa715a5a3296feffb06ce45252bd0dfd48f52838128c48fb339ddbf5c" +checksum = "b1b9d12875731bd07e767be7baad95700c3137b56730ec9ddeedb52a5e5ca63b" dependencies = [ "xmlparser", ] [[package]] name = "aws-types" -version = "0.47.0" +version = "0.55.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8140b89d76f67be2c136d7393e7e6d8edd65424eb58214839efbf4a2e4f7e8a3" +checksum = "6dd209616cc8d7bfb82f87811a5c655dc97537f592689b18743bddf5dc5c4829" dependencies = [ + "aws-credential-types", "aws-smithy-async", "aws-smithy-client", "aws-smithy-http", @@ -438,7 +497,21 @@ dependencies = [ "http", "rustc_version", "tracing", - "zeroize", +] + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", ] [[package]] @@ -449,9 +522,19 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] [[package]] name = "bitflags" @@ -459,6 +542,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "block-buffer" version = "0.10.4" @@ -470,9 +559,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byteorder" @@ -485,6 +574,9 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "bytes-utils" @@ -538,12 +630,12 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.24" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" dependencies = [ + "android-tzdata", "iana-time-zone", - "num-integer", "num-traits", "serde", "winapi", @@ -551,12 +643,12 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.24" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eef2b3ded6a26dfaec672a742c93c8cf6b689220324da509ec5caa20de55dc83" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", - "bitflags", + "bitflags 1.3.2", "clap_derive", "clap_lex", "indexmap", @@ -568,9 +660,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.2.24" +version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d756c5824fc5c0c1ee8e36000f576968dbcb2081def956c83fad6f40acd46f96" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck", "proc-macro-error", @@ -588,16 +680,6 @@ dependencies = [ "os_str_bytes", ] -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - [[package]] name = "core-foundation" version = "0.9.3" @@ -616,18 +698,18 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" dependencies = [ "libc", ] [[package]] name = "crc32c" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfea2db42e9927a3845fb268a10a72faed6d416065f77873f05e411457c363e" +checksum = "d8f48d60e5b4d2c53d5c2b1d8a58c849a70ae5e5509b08a48d047e3b65714a74" dependencies = [ "rustc_version", ] @@ -641,21 +723,11 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] @@ -670,69 +742,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "ct-logs" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8" -dependencies = [ - "sct 0.6.1", -] - -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "cxx" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 2.0.15", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.94" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - [[package]] name = "diff" version = "0.1.13" @@ -741,25 +750,26 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] name = "dyn-clone" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" +checksum = "304e6508efa593091e97a9abbc10f90aa7ca635b6d2784feff3c89d41dd12272" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encoding_rs" @@ -778,7 +788,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -800,11 +810,17 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "miniz_oxide", @@ -833,9 +849,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -896,7 +912,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -941,20 +957,28 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.18" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -992,18 +1016,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "hex" @@ -1011,6 +1026,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "http" version = "0.2.9" @@ -1047,9 +1071,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -1071,32 +1095,31 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.22.1" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ - "ct-logs", - "futures-util", + "http", "hyper", "log", - "rustls 0.19.1", + "rustls 0.20.8", "rustls-native-certs", "tokio", - "tokio-rustls 0.22.0", - "webpki 0.21.4", + "tokio-rustls 0.23.4", ] [[package]] name = "hyper-rustls" -version = "0.23.2" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ + "futures-util", "http", "hyper", - "rustls 0.20.8", + "rustls 0.21.5", "tokio", - "tokio-rustls 0.23.4", + "tokio-rustls 0.24.1", ] [[package]] @@ -1114,9 +1137,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1128,19 +1151,18 @@ dependencies = [ [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1163,36 +1185,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", + "js-sys", + "wasm-bindgen", + "web-sys", ] [[package]] name = "ipnet" -version = "2.7.2" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -1203,7 +1217,7 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", "pem", "ring", "serde", @@ -1219,30 +1233,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" - -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -1250,11 +1255,10 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" dependencies = [ - "cfg-if", "serde", ] @@ -1264,9 +1268,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" + [[package]] name = "md-5" version = "0.10.5" @@ -1300,23 +1310,22 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -1370,31 +1379,41 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.2.6", + "hermit-abi 0.3.2", "libc", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "octorust" -version = "0.3.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fac860084f1858d4207a3242df22e2a60dbae86d3df6878ce8eac2a51eb5c9" +checksum = "35a776c05d0cdd02480c12cf1484e0f3bf610eab717ba6fb2c51449b60c7a88f" dependencies = [ - "anyhow", "async-recursion", + "async-trait", + "bytes", "chrono", "http", "jsonwebtoken", @@ -1413,23 +1432,25 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "thiserror", "tokio", "url", + "uuid", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.52" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -1446,7 +1467,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -1457,9 +1478,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.87" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ "cc", "libc", @@ -1467,39 +1488,17 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "opentelemetry" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6105e89802af13fdf48c49d7646d3b533a70e536d818aae7e78ba0433d01acb8" -dependencies = [ - "async-trait", - "crossbeam-channel", - "futures-channel", - "futures-executor", - "futures-util", - "js-sys", - "lazy_static", - "percent-encoding", - "pin-project", - "rand", - "thiserror", -] - [[package]] name = "os_str_bytes" -version = "6.5.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" [[package]] -name = "output_vt100" -version = "0.1.3" +name = "outref" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" [[package]] name = "overload" @@ -1507,6 +1506,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -1514,20 +1524,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.8", ] [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if", + "instant", "libc", "redox_syscall 0.2.16", "smallvec", - "windows-sys 0.45.0", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets", ] [[package]] @@ -1552,35 +1576,35 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.27", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -1590,9 +1614,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "ppv-lite86" @@ -1602,13 +1626,11 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] @@ -1638,18 +1660,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -1690,7 +1712,7 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -1699,18 +1721,19 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.1", + "regex-automata 0.3.3", + "regex-syntax 0.7.4", ] [[package]] @@ -1722,6 +1745,17 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -1730,17 +1764,17 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", "bytes", "encoding_rs", "futures-core", @@ -1749,7 +1783,7 @@ dependencies = [ "http", "http-body", "hyper", - "hyper-rustls 0.23.2", + "hyper-rustls 0.24.1", "hyper-tls", "ipnet", "js-sys", @@ -1760,14 +1794,14 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.20.8", + "rustls 0.21.5", "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", "tokio", "tokio-native-tls", - "tokio-rustls 0.23.4", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", @@ -1779,9 +1813,9 @@ dependencies = [ [[package]] name = "reqwest-conditional-middleware" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bce134f515eb4c2748bbd928086e7b0aae0d1568daf6c63b51e829aa6f2cf464" +checksum = "59e50a2e70970896c99d1b8f20ddc30a70b30d3ac6e619a03a8353b64a49b277" dependencies = [ "async-trait", "reqwest", @@ -1791,13 +1825,12 @@ dependencies = [ [[package]] name = "reqwest-middleware" -version = "0.1.6" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69539cea4148dce683bec9dc95be3f0397a9bb2c248a49c8296a9d21659a8cdd" +checksum = "4531c89d50effe1fac90d095c8b133c20c5c714204feee0bfc3fd158e784209d" dependencies = [ "anyhow", "async-trait", - "futures", "http", "reqwest", "serde", @@ -1807,38 +1840,41 @@ dependencies = [ [[package]] name = "reqwest-retry" -version = "0.1.5" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce246a729eaa6aff5e215aee42845bf5fed9893cc6cd51aeeb712f34e04dd9f3" +checksum = "48d0fd6ef4c6d23790399fe15efc8d12cd9f3d4133958f9bd7801ee5cbaec6c4" dependencies = [ "anyhow", "async-trait", "chrono", "futures", + "getrandom", "http", "hyper", + "parking_lot 0.11.2", "reqwest", "reqwest-middleware", "retry-policies", "task-local-extensions", "tokio", "tracing", + "wasm-timer", ] [[package]] name = "reqwest-tracing" -version = "0.3.1" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64977f9a47fa7768cc88751e29026e569730ac1667c2eaeaac04b32624849fbe" +checksum = "1b97ad83c2fc18113346b7158d79732242002427c30f620fa817c1f32901e0a8" dependencies = [ + "anyhow", "async-trait", - "opentelemetry", + "getrandom", + "matchit", "reqwest", "reqwest-middleware", "task-local-extensions", - "tokio", "tracing", - "tracing-opentelemetry", ] [[package]] @@ -1867,6 +1903,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc_version" version = "0.4.0" @@ -1878,77 +1920,85 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.14" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" dependencies = [ - "bitflags", + "bitflags 2.3.3", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "rustls" -version = "0.19.1" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ - "base64 0.13.1", "log", "ring", - "sct 0.6.1", - "webpki 0.21.4", + "sct", + "webpki", ] [[package]] name = "rustls" -version = "0.20.8" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" dependencies = [ "log", "ring", - "sct 0.7.0", - "webpki 0.22.0", + "rustls-webpki", + "sct", ] [[package]] name = "rustls-native-certs" -version = "0.5.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" dependencies = [ "openssl-probe", - "rustls 0.19.1", + "rustls-pemfile", "schannel", "security-framework", ] [[package]] name = "rustls-pemfile" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.0", + "base64 0.21.2", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" +dependencies = [ + "ring", + "untrusted", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schannel" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -1981,25 +2031,9 @@ dependencies = [ [[package]] name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" - -[[package]] -name = "sct" -version = "0.6.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -2013,11 +2047,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -2026,9 +2060,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -2036,28 +2070,28 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -2073,9 +2107,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -2107,9 +2141,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" dependencies = [ "cfg-if", "cpufeatures", @@ -2157,9 +2191,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "smithy-rs-tool-common" @@ -2200,6 +2234,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -2213,9 +2253,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -2233,15 +2273,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ "cfg-if", - "fastrand", + "fastrand 2.0.0", "redox_syscall 0.3.5", "rustix", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] @@ -2261,22 +2301,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -2291,9 +2331,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" dependencies = [ "itoa", "serde", @@ -2303,15 +2343,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" dependencies = [ "time-core", ] @@ -2333,32 +2373,33 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio", "num_cpus", - "parking_lot", + "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] @@ -2373,31 +2414,30 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.22.0" +version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls 0.19.1", + "rustls 0.20.8", "tokio", - "webpki 0.21.4", + "webpki", ] [[package]] name = "tokio-rustls" -version = "0.23.4" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.20.8", + "rustls 0.21.5", "tokio", - "webpki 0.22.0", ] [[package]] name = "tokio-stream" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" dependencies = [ "futures-core", "pin-project-lite", @@ -2406,9 +2446,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -2458,10 +2498,11 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.38" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ + "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -2470,20 +2511,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.27", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -2500,20 +2541,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-opentelemetry" -version = "0.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbbe89715c1dbbb790059e2565353978564924ee85017b5fff365c872ff6721f" -dependencies = [ - "once_cell", - "opentelemetry", - "tracing", - "tracing-core", - "tracing-log", - "tracing-subscriber", -] - [[package]] name = "tracing-subscriber" version = "0.3.17" @@ -2561,9 +2588,9 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-normalization" @@ -2574,12 +2601,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - [[package]] name = "untrusted" version = "0.7.1" @@ -2588,9 +2609,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -2600,15 +2621,19 @@ dependencies = [ [[package]] name = "urlencoding" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "uuid" -version = "1.3.1" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b55a3fef2a1e3b3a00ce878640918820d3c51081576ac657d23af9fc7928fdb" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +dependencies = [ + "getrandom", + "serde", +] [[package]] name = "valuable" @@ -2628,13 +2653,18 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -2646,9 +2676,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2656,24 +2686,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.27", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.34" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" dependencies = [ "cfg-if", "js-sys", @@ -2683,9 +2713,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2693,41 +2723,46 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] -name = "web-sys" -version = "0.3.61" +name = "wasm-timer" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ + "futures", "js-sys", + "parking_lot 0.11.2", + "pin-utils", "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", ] [[package]] -name = "webpki" -version = "0.21.4" +name = "web-sys" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ - "ring", - "untrusted", + "js-sys", + "wasm-bindgen", ] [[package]] @@ -2746,7 +2781,7 @@ version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ - "webpki 0.22.0", + "webpki", ] [[package]] @@ -2786,31 +2821,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", + "windows-targets", ] [[package]] @@ -2819,117 +2830,60 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" @@ -2947,9 +2901,9 @@ dependencies = [ [[package]] name = "xmlparser" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" +checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" [[package]] name = "yansi" @@ -2965,9 +2919,9 @@ checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" [[package]] name = "zip" -version = "0.6.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" dependencies = [ "byteorder", "crc32fast", diff --git a/tools/ci-cdk/canary-runner/Cargo.toml b/tools/ci-cdk/canary-runner/Cargo.toml index b1de3e0aff..85e8a03be1 100644 --- a/tools/ci-cdk/canary-runner/Cargo.toml +++ b/tools/ci-cdk/canary-runner/Cargo.toml @@ -12,15 +12,15 @@ publish = false [dependencies] anyhow = "1" async-trait = "0.1.56" -aws-config = "0.47.0" -aws-sdk-cloudwatch = "0.17.0" -aws-sdk-lambda = "0.17.0" -aws-sdk-s3 = "0.17.0" +aws-config = "0.55.3" +aws-sdk-cloudwatch = "0.28.0" +aws-sdk-lambda = "0.28.0" +aws-sdk-s3 = "0.28.0" base64 = "0.13" clap = { version = "3.2.17", features = ["derive"] } hex = "0.4.3" lazy_static = "1" -octorust = "0.3.0" +octorust = "0.7.0" regex = "1.6.0" semver = "1" serde = { version = "1", features = ["derive"] } diff --git a/tools/ci-cdk/canary-runner/src/generate_matrix.rs b/tools/ci-cdk/canary-runner/src/generate_matrix.rs index 401e18a99a..bb2cb48da4 100644 --- a/tools/ci-cdk/canary-runner/src/generate_matrix.rs +++ b/tools/ci-cdk/canary-runner/src/generate_matrix.rs @@ -65,6 +65,7 @@ impl RetrieveReleases for GitHubRetrieveReleases { .repos() .list_tags(owner, repo, 100, page_num) .await? + .body .into_iter() .filter_map(|tag| ReleaseTag::from_str(&tag.name).ok()) .collect(); diff --git a/tools/ci-cdk/canary-runner/src/run.rs b/tools/ci-cdk/canary-runner/src/run.rs index b5e0058d74..1952323dab 100644 --- a/tools/ci-cdk/canary-runner/src/run.rs +++ b/tools/ci-cdk/canary-runner/src/run.rs @@ -21,8 +21,8 @@ use std::{env, path::Path}; use anyhow::{bail, Context, Result}; use clap::Parser; -use cloudwatch::model::StandardUnit; -use s3::types::ByteStream; +use cloudwatch::types::StandardUnit; +use s3::primitives::ByteStream; use serde::Deserialize; use smithy_rs_tool_common::git::{find_git_repository_root, Git, GitCLI}; use smithy_rs_tool_common::macros::here; @@ -202,7 +202,7 @@ pub async fn run(opt: RunArgs) -> Result<()> { .namespace("aws-sdk-rust-canary"); for metric in metrics { request_builder = request_builder.metric_data( - cloudwatch::model::MetricDatum::builder() + cloudwatch::types::MetricDatum::builder() .metric_name(metric.0) .value(metric.1) .timestamp(SystemTime::now().into()) @@ -337,7 +337,7 @@ async fn create_lambda_fn( code_s3_bucket: &str, test_s3_bucket: &str, ) -> Result<()> { - use lambda::model::*; + use lambda::types::*; let env_builder = match expected_speech_text_by_transcribe { Some(expected_speech_text_by_transcribe) => Environment::builder() @@ -394,8 +394,8 @@ async fn create_lambda_fn( } async fn invoke_lambda(lambda_client: lambda::Client, bundle_name: &str) -> Result<()> { - use lambda::model::*; - use lambda::types::Blob; + use lambda::primitives::Blob; + use lambda::types::*; let response = lambda_client .invoke() diff --git a/tools/echo-server/Cargo.lock b/tools/echo-server/Cargo.lock index bebe33f6fd..fd15209512 100644 --- a/tools/echo-server/Cargo.lock +++ b/tools/echo-server/Cargo.lock @@ -2,15 +2,39 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + [[package]] name = "async-trait" -version = "0.1.68" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn", ] [[package]] @@ -66,6 +90,21 @@ dependencies = [ "tower-service", ] +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -78,6 +117,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + [[package]] name = "cfg-if" version = "1.0.0" @@ -104,9 +149,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -150,11 +195,17 @@ dependencies = [ "pin-utils", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + [[package]] name = "h2" -version = "0.3.18" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" dependencies = [ "bytes", "fnv", @@ -177,12 +228,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "http" @@ -208,9 +256,9 @@ dependencies = [ [[package]] name = "http-range-header" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" @@ -226,9 +274,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.26" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", @@ -260,9 +308,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "lazy_static" @@ -272,15 +320,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.142" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -288,12 +336,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "matchers" @@ -301,7 +346,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -322,14 +367,22 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", - "log", "wasi", "windows-sys", ] @@ -346,19 +399,28 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "overload" @@ -378,48 +440,48 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-targets", ] [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" [[package]] name = "pin-utils" @@ -429,38 +491,41 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ - "regex-syntax 0.7.1", + "aho-corasick", + "memchr", + "regex-automata 0.3.3", + "regex-syntax 0.7.4", ] [[package]] @@ -472,6 +537,17 @@ dependencies = [ "regex-syntax 0.6.29", ] +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", +] + [[package]] name = "regex-syntax" version = "0.6.29" @@ -480,33 +556,39 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.1" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.160" +version = "1.0.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" dependencies = [ "itoa", "ryu", @@ -554,9 +636,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "socket2" @@ -570,20 +652,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.109" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -608,11 +679,12 @@ dependencies = [ [[package]] name = "tokio" -version = "1.27.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" dependencies = [ "autocfg", + "backtrace", "bytes", "libc", "mio", @@ -627,20 +699,20 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn", ] [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -699,10 +771,11 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.38" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ + "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -711,20 +784,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -767,9 +840,9 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "valuable" @@ -779,11 +852,10 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] @@ -817,18 +889,18 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" -version = "0.42.2" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -841,42 +913,42 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_msvc" -version = "0.42.2" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" From 1fbafc83669ff3137bbc1763d1b7669e161f72b4 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 27 Jul 2023 12:00:18 -0400 Subject: [PATCH 234/253] Fix default features test (#2881) ## Motivation and Context ## Description The default features test is actually broken and failed during the release dry run ## Testing - ~Added the default features test to CI~ got a linker error ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/sdk/integration-tests/dynamodb/Cargo.toml | 2 +- .../no-default-features/tests/client-construction.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aws/sdk/integration-tests/dynamodb/Cargo.toml b/aws/sdk/integration-tests/dynamodb/Cargo.toml index 3cd3a98b49..61663dbbed 100644 --- a/aws/sdk/integration-tests/dynamodb/Cargo.toml +++ b/aws/sdk/integration-tests/dynamodb/Cargo.toml @@ -16,7 +16,7 @@ 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-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async" } +aws-smithy-async = { path = "../../build/aws-sdk/sdk/aws-smithy-async", features = ["test-util"] } aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "rustls"] } aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } aws-smithy-protocol-test = { path = "../../build/aws-sdk/sdk/aws-smithy-protocol-test" } diff --git a/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs b/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs index ada59bc6f4..26d84c96f6 100644 --- a/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs +++ b/aws/sdk/integration-tests/no-default-features/tests/client-construction.rs @@ -9,10 +9,10 @@ use aws_smithy_async::rt::sleep::AsyncSleep; use std::time::Duration; // This will fail due to lack of a connector when constructing the SDK Config +// If this test doesn't panic, you may have accidentally unified features, resulting in +// the connector being enabled transitively #[tokio::test] -#[should_panic( - expected = "No HTTP connector was available. Enable the `rustls` crate feature or set a connector to fix this." -)] +#[should_panic(expected = "Enable the `rustls` crate feature or set a connector to fix this.")] async fn test_clients_from_sdk_config() { aws_config::load_from_env().await; } From 79be3105466f10762ad2073ae93746712d83359e Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 27 Jul 2023 12:15:43 -0400 Subject: [PATCH 235/253] Share HTTP connectors between providers and clients (#2876) ## Motivation and Context Clients using separate connectors is mostly confusing and troublesome for customers. ## Description Change the behavior of `ConfigLoader::http_connector` to set both the client & credential provider HTTP connector. **Note**: It is still possible to separate control clients for the credential provider. Because `HttpConnector` is used, the timeout settings can still be late-bound. ## Testing Unit tests. ## Checklist - [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._ --- CHANGELOG.next.toml | 6 + .../src/default_provider/app_name.rs | 9 +- aws/rust-runtime/aws-config/src/lib.rs | 190 +++++++++++++----- .../aws-config/src/provider_config.rs | 34 ++-- 4 files changed, 165 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index eec4009c22..394665e33e 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -665,6 +665,12 @@ references = ["smithy-rs#2865"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } author = "david-perez" +[[aws-sdk-rust]] +message = "**Behavior change**: Credential providers now share the HTTP connector used by the SDK. If you want to keep a separate connector for clients, use `::ConfigBuilder::http_connector` when constructing the client." +references = ["aws-sdk-rust#579", "aws-sdk-rust#338"] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "rcoh" + [[smithy-rs]] message = """ [RestJson1](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restjson1-protocol.html#operation-error-serialization) server SDKs now serialize only the [shape name](https://smithy.io/2.0/spec/model.html#shape-id) in operation error responses. Previously (from versions 0.52.0 to 0.55.4), the full shape ID was rendered. diff --git a/aws/rust-runtime/aws-config/src/default_provider/app_name.rs b/aws/rust-runtime/aws-config/src/default_provider/app_name.rs index 7d91c6d363..041d0f8261 100644 --- a/aws/rust-runtime/aws-config/src/default_provider/app_name.rs +++ b/aws/rust-runtime/aws-config/src/default_provider/app_name.rs @@ -118,12 +118,9 @@ mod tests { async fn profile_name_override() { let fs = Fs::from_slice(&[("test_config", "[profile custom]\nsdk_ua_app_id = correct")]); let conf = crate::from_env() - .configure( - ProviderConfig::empty() - .with_fs(fs) - .with_sleep(InstantSleep) - .with_http_connector(no_traffic_connector()), - ) + .sleep_impl(InstantSleep) + .fs(fs) + .http_connector(no_traffic_connector()) .profile_name("custom") .profile_files( ProfileFiles::builder() diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index e7c035ea80..9185672577 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -161,6 +161,7 @@ mod loader { use aws_smithy_types::timeout::TimeoutConfig; use aws_types::app_name::AppName; use aws_types::docs_for; + use aws_types::os_shim_internal::{Env, Fs}; use aws_types::SdkConfig; use crate::connector::default_connector; @@ -205,6 +206,8 @@ mod loader { use_fips: Option, use_dual_stack: Option, time_source: Option, + env: Option, + fs: Option, } impl ConfigLoader { @@ -281,35 +284,43 @@ mod loader { self } - /// Override the [`HttpConnector`] for this [`ConfigLoader`]. The connector will be used when - /// sending operations. This **does not set** the HTTP connector used by config providers. - /// To change that connector, use [ConfigLoader::configure]. + /// Override the [`HttpConnector`] for this [`ConfigLoader`]. The connector will be used for + /// both AWS services and credential providers. When [`HttpConnector::ConnectorFn`] is used, + /// the connector will be lazily instantiated as needed based on the provided settings. /// + /// **Note**: In order to take advantage of late-configured timeout settings, you MUST use + /// [`HttpConnector::ConnectorFn`] + /// when configuring this connector. + /// + /// If you wish to use a separate connector when creating clients, use the client-specific config. /// ## Examples /// ```no_run - /// # #[cfg(feature = "client-hyper")] + /// # use aws_smithy_async::rt::sleep::SharedAsyncSleep; + /// use aws_smithy_client::http_connector::HttpConnector; + /// #[cfg(feature = "client-hyper")] /// # async fn create_config() { /// use std::time::Duration; /// use aws_smithy_client::{Client, hyper_ext}; /// use aws_smithy_client::erase::DynConnector; /// use aws_smithy_client::http_connector::ConnectorSettings; /// - /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() - /// .with_webpki_roots() - /// .https_only() - /// .enable_http1() - /// .enable_http2() - /// .build(); - /// let smithy_connector = hyper_ext::Adapter::builder() - /// // Optionally set things like timeouts as well - /// .connector_settings( - /// ConnectorSettings::builder() - /// .connect_timeout(Duration::from_secs(5)) - /// .build() - /// ) - /// .build(https_connector); + /// let connector_fn = |settings: &ConnectorSettings, sleep: Option| { + /// let https_connector = hyper_rustls::HttpsConnectorBuilder::new() + /// .with_webpki_roots() + /// // NOTE: setting `https_only()` will not allow this connector to work with IMDS. + /// .https_only() + /// .enable_http1() + /// .enable_http2() + /// .build(); + /// let mut smithy_connector = hyper_ext::Adapter::builder() + /// // Optionally set things like timeouts as well + /// .connector_settings(settings.clone()); + /// smithy_connector.set_sleep_impl(sleep); + /// Some(DynConnector::new(smithy_connector.build(https_connector))) + /// }; + /// let connector = HttpConnector::ConnectorFn(std::sync::Arc::new(connector_fn)); /// let sdk_config = aws_config::from_env() - /// .http_connector(smithy_connector) + /// .http_connector(connector) /// .load() /// .await; /// # } @@ -532,6 +543,9 @@ mod loader { /// let shared_config = aws_config::from_env().configure(provider_config).load().await; /// # } /// ``` + #[deprecated( + note = "Use setters on this builder instead. configure is very hard to use correctly." + )] pub fn configure(mut self, provider_config: ProviderConfig) -> Self { self.provider_config = Some(provider_config); self @@ -547,9 +561,35 @@ mod loader { /// This means that if you provide a region provider that does not return a region, no region will /// be set in the resulting [`SdkConfig`](aws_types::SdkConfig) pub async fn load(self) -> SdkConfig { + let http_connector = self + .http_connector + .unwrap_or_else(|| HttpConnector::ConnectorFn(Arc::new(default_connector))); + + let time_source = self.time_source.unwrap_or_default(); + + let sleep_impl = if self.sleep.is_some() { + self.sleep + } else { + if default_async_sleep().is_none() { + tracing::warn!( + "An implementation of AsyncSleep was requested by calling default_async_sleep \ + but no default was set. + This happened when ConfigLoader::load was called during Config construction. \ + You can fix this by setting a sleep_impl on the ConfigLoader before calling \ + load or by enabling the rt-tokio feature" + ); + } + default_async_sleep() + }; + let conf = self .provider_config - .unwrap_or_default() + .unwrap_or_else(|| { + ProviderConfig::init(time_source.clone(), sleep_impl.clone()) + .with_fs(self.fs.unwrap_or_default()) + .with_env(self.env.unwrap_or_default()) + .with_http_connector(http_connector.clone()) + }) .with_profile_config(self.profile_files_override, self.profile_name_override); let region = if let Some(provider) = self.region { provider.region().await @@ -579,21 +619,6 @@ mod loader { .await }; - let sleep_impl = if self.sleep.is_some() { - self.sleep - } else { - if default_async_sleep().is_none() { - tracing::warn!( - "An implementation of AsyncSleep was requested by calling default_async_sleep \ - but no default was set. - This happened when ConfigLoader::load was called during Config construction. \ - You can fix this by setting a sleep_impl on the ConfigLoader before calling \ - load or by enabling the rt-tokio feature" - ); - } - default_async_sleep() - }; - let timeout_config = if let Some(timeout_config) = self.timeout_config { timeout_config } else { @@ -603,10 +628,6 @@ mod loader { .await }; - let http_connector = self - .http_connector - .unwrap_or_else(|| HttpConnector::ConnectorFn(Arc::new(default_connector))); - let credentials_provider = match self.credentials_provider { CredentialsProviderOption::Set(provider) => Some(provider), CredentialsProviderOption::NotSet => { @@ -641,13 +662,11 @@ mod loader { use_dual_stack_provider(&conf).await }; - let ts = self.time_source.unwrap_or_default(); - let mut builder = SdkConfig::builder() .region(region) .retry_config(retry_config) .timeout_config(timeout_config) - .time_source(ts) + .time_source(time_source) .http_connector(http_connector); builder.set_app_name(app_name); @@ -661,18 +680,35 @@ mod loader { } } + #[cfg(test)] + impl ConfigLoader { + pub(crate) fn env(mut self, env: Env) -> Self { + self.env = Some(env); + self + } + + pub(crate) fn fs(mut self, fs: Fs) -> Self { + self.fs = Some(fs); + self + } + } + #[cfg(test)] mod test { use aws_credential_types::provider::ProvideCredentials; use aws_smithy_async::rt::sleep::TokioSleep; + use aws_smithy_async::time::{StaticTimeSource, TimeSource}; use aws_smithy_client::erase::DynConnector; use aws_smithy_client::never::NeverConnector; + use aws_smithy_client::test_connection::infallible_connection_fn; use aws_types::app_name::AppName; use aws_types::os_shim_internal::{Env, Fs}; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; + use std::time::{SystemTime, UNIX_EPOCH}; use tracing_test::traced_test; use crate::profile::profile_file::{ProfileFileKind, ProfileFiles}; - use crate::provider_config::ProviderConfig; use crate::test_case::{no_traffic_connector, InstantSleep}; use crate::{from_env, ConfigLoader}; @@ -688,13 +724,10 @@ mod loader { let fs = Fs::from_slice(&[("test_config", "[profile custom]\nsdk-ua-app-id = correct")]); let loader = from_env() - .configure( - ProviderConfig::empty() - .with_sleep(TokioSleep::new()) - .with_env(env) - .with_fs(fs) - .with_http_connector(DynConnector::new(NeverConnector::new())), - ) + .sleep_impl(TokioSleep::new()) + .env(env) + .fs(fs) + .http_connector(DynConnector::new(NeverConnector::new())) .profile_name("custom") .profile_files( ProfileFiles::builder() @@ -734,11 +767,9 @@ mod loader { } fn base_conf() -> ConfigLoader { - from_env().configure( - ProviderConfig::empty() - .with_sleep(InstantSleep) - .with_http_connector(no_traffic_connector()), - ) + from_env() + .sleep_impl(InstantSleep) + .http_connector(no_traffic_connector()) } #[tokio::test] @@ -770,5 +801,54 @@ mod loader { assert!(config.credentials_cache().is_none()); assert!(config.credentials_provider().is_none()); } + + #[tokio::test] + async fn connector_is_shared() { + let num_requests = Arc::new(AtomicUsize::new(0)); + let movable = num_requests.clone(); + let conn = infallible_connection_fn(move |_req| { + movable.fetch_add(1, Ordering::Relaxed); + http::Response::new("ok!") + }); + let config = from_env().http_connector(conn.clone()).load().await; + config + .credentials_provider() + .unwrap() + .provide_credentials() + .await + .expect_err("no traffic is allowed"); + let num_requests = num_requests.load(Ordering::Relaxed); + assert!(num_requests > 0, "{}", num_requests); + } + + #[tokio::test] + async fn time_source_is_passed() { + #[derive(Debug)] + struct PanicTs; + impl TimeSource for PanicTs { + fn now(&self) -> SystemTime { + panic!("timesource-was-used") + } + } + let config = from_env() + .sleep_impl(InstantSleep) + .time_source(StaticTimeSource::new(UNIX_EPOCH)) + .http_connector(no_traffic_connector()) + .load() + .await; + // assert that the innards contain the customized fields + for inner in ["InstantSleep", "StaticTimeSource"] { + assert!( + format!("{:#?}", config.credentials_cache()).contains(inner), + "{:#?}", + config.credentials_cache() + ); + assert!( + format!("{:#?}", config.credentials_provider()).contains(inner), + "{:#?}", + config.credentials_cache() + ); + } + } } } diff --git a/aws/rust-runtime/aws-config/src/provider_config.rs b/aws/rust-runtime/aws-config/src/provider_config.rs index 3712376b42..f1caa75e51 100644 --- a/aws/rust-runtime/aws-config/src/provider_config.rs +++ b/aws/rust-runtime/aws-config/src/provider_config.rs @@ -150,6 +150,21 @@ impl ProviderConfig { } } + /// Initializer for ConfigBag to avoid possibly setting incorrect defaults. + pub(crate) fn init(time_source: SharedTimeSource, sleep: Option) -> Self { + Self { + parsed_profile: Default::default(), + profile_files: ProfileFiles::default(), + env: Env::default(), + fs: Fs::default(), + time_source, + connector: HttpConnector::Prebuilt(None), + sleep, + region: None, + profile_name_override: None, + } + } + /// Create a default provider config with the region region automatically loaded from the default chain. /// /// # Examples @@ -270,10 +285,7 @@ impl ProviderConfig { self.with_region(provider_chain.region().await) } - // these setters are doc(hidden) because they only exist for tests - - #[doc(hidden)] - pub fn with_fs(self, fs: Fs) -> Self { + pub(crate) fn with_fs(self, fs: Fs) -> Self { ProviderConfig { parsed_profile: Default::default(), fs, @@ -281,8 +293,7 @@ impl ProviderConfig { } } - #[cfg(test)] - pub fn with_env(self, env: Env) -> Self { + pub(crate) fn with_env(self, env: Env) -> Self { ProviderConfig { parsed_profile: Default::default(), env, @@ -303,14 +314,11 @@ impl ProviderConfig { /// Override the HTTPS connector for this configuration /// - /// **Warning**: Use of this method will prevent you from taking advantage of the HTTP connect timeouts. - /// Consider [`ProviderConfig::with_tcp_connector`]. - /// - /// # Stability - /// This method is expected to change to support HTTP configuration. - pub fn with_http_connector(self, connector: DynConnector) -> Self { + /// **Note**: In order to take advantage of late-configured timeout settings, use [`HttpConnector::ConnectorFn`] + /// when configuring this connector. + pub fn with_http_connector(self, connector: impl Into) -> Self { ProviderConfig { - connector: HttpConnector::Prebuilt(Some(connector)), + connector: connector.into(), ..self } } From 911aa716852d1d478a2740d2de43f43a8f30a028 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 27 Jul 2023 10:06:39 -0700 Subject: [PATCH 236/253] Add names to interceptors (#2878) This PR adds a `name` function to the `Interceptor` trait so that interceptor errors are easier to narrow down. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../src/apigateway_interceptors.rs | 4 + .../src/glacier_interceptors.rs | 12 ++ .../src/http_request_checksum.rs | 4 + .../src/http_response_checksum.rs | 4 + .../src/presigning_interceptors.rs | 4 + .../src/route53_resource_id_preprocessor.rs | 4 + .../aws-runtime/src/invocation_id.rs | 4 + .../aws-runtime/src/recursion_detection.rs | 4 + .../aws-runtime/src/request_info.rs | 9 +- .../aws-runtime/src/user_agent.rs | 4 + .../AwsCustomizableOperationDecorator.kt | 4 + .../EndpointParamsInterceptorGenerator.kt | 4 + .../MetadataCustomizationTest.kt | 4 + .../smithy/endpoint/EndpointsDecoratorTest.kt | 4 + .../generators/EndpointTraitBindingsTest.kt | 4 + .../protocol/ProtocolTestGeneratorTest.kt | 4 + .../src/client/interceptors.rs | 3 + .../src/client/interceptors/error.rs | 15 ++- .../client/connectors/connection_poisoning.rs | 4 + .../src/client/interceptors.rs | 109 +++++++++++++----- .../src/client/orchestrator.rs | 86 ++++++++------ .../interceptors/service_clock_skew.rs | 4 + .../src/client_http_checksum_required.rs | 4 + .../src/client_idempotency_token.rs | 4 + 24 files changed, 233 insertions(+), 73 deletions(-) diff --git a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs index 261932f3fb..7dc1a687df 100644 --- a/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/apigateway_interceptors.rs @@ -18,6 +18,10 @@ use http::HeaderValue; pub(crate) struct AcceptHeaderInterceptor; impl Interceptor for AcceptHeaderInterceptor { + fn name(&self) -> &'static str { + "AcceptHeaderInterceptor" + } + fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs index efd71582af..3b20b5fff7 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -68,6 +68,10 @@ impl GlacierAccountIdAutofillInterceptor { impl Interceptor for GlacierAccountIdAutofillInterceptor { + fn name(&self) -> &'static str { + "GlacierAccountIdAutofillInterceptor" + } + fn modify_before_serialization( &self, context: &mut BeforeSerializationInterceptorContextMut<'_>, @@ -97,6 +101,10 @@ impl GlacierApiVersionInterceptor { } impl Interceptor for GlacierApiVersionInterceptor { + fn name(&self) -> &'static str { + "GlacierApiVersionInterceptor" + } + fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, @@ -116,6 +124,10 @@ impl Interceptor for GlacierApiVersionInterceptor { pub(crate) struct GlacierTreeHashHeaderInterceptor; impl Interceptor for GlacierTreeHashHeaderInterceptor { + fn name(&self) -> &'static str { + "GlacierTreeHashHeaderInterceptor" + } + fn modify_before_serialization( &self, _context: &mut BeforeSerializationInterceptorContextMut<'_>, diff --git a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs index 702fbe56c2..aff4e925e4 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs @@ -79,6 +79,10 @@ impl Interceptor for RequestChecksumInterceptor where AP: Fn(&Input) -> Result, BoxError> + Send + Sync, { + fn name(&self) -> &'static str { + "RequestChecksumInterceptor" + } + fn read_before_serialization( &self, context: &BeforeSerializationInterceptorContextRef<'_>, diff --git a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs index 2a98830d8e..729eb97c4c 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_response_checksum.rs @@ -56,6 +56,10 @@ impl Interceptor for ResponseChecksumInterceptor where VE: Fn(&Input) -> bool + Send + Sync, { + fn name(&self) -> &'static str { + "ResponseChecksumInterceptor" + } + fn read_before_serialization( &self, context: &BeforeSerializationInterceptorContextRef<'_>, diff --git a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs index 7161134ac8..3ded9fcd68 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning_interceptors.rs @@ -47,6 +47,10 @@ impl SigV4PresigningInterceptor { } impl Interceptor for SigV4PresigningInterceptor { + fn name(&self) -> &'static str { + "SigV4PresigningInterceptor" + } + fn modify_before_serialization( &self, _context: &mut BeforeSerializationInterceptorContextMut<'_>, diff --git a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs index 00f158317f..32fad6b160 100644 --- a/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs +++ b/aws/rust-runtime/aws-inlineable/src/route53_resource_id_preprocessor.rs @@ -72,6 +72,10 @@ where G: for<'a> Fn(&'a mut T) -> &'a mut Option + Send + Sync, T: fmt::Debug + Send + Sync + 'static, { + fn name(&self) -> &'static str { + "Route53ResourceIdInterceptor" + } + fn modify_before_serialization( &self, context: &mut BeforeSerializationInterceptorContextMut<'_>, diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 16d3fc9811..3ee5923f07 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -93,6 +93,10 @@ impl InvocationIdInterceptor { } impl Interceptor for InvocationIdInterceptor { + fn name(&self) -> &'static str { + "InvocationIdInterceptor" + } + fn modify_before_retry_loop( &self, _ctx: &mut BeforeTransmitInterceptorContextMut<'_>, diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs index 3cbda55c4d..2e87f24edb 100644 --- a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -40,6 +40,10 @@ impl RecursionDetectionInterceptor { } impl Interceptor for RecursionDetectionInterceptor { + fn name(&self) -> &'static str { + "RecursionDetectionInterceptor" + } + fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index f0abff3041..fcea15ef51 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -87,6 +87,10 @@ impl RequestInfoInterceptor { } impl Interceptor for RequestInfoInterceptor { + fn name(&self) -> &'static str { + "RequestInfoInterceptor" + } + fn modify_before_transmit( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, @@ -166,13 +170,12 @@ mod tests { use super::RequestInfoInterceptor; use crate::request_info::RequestPairs; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; - use aws_smithy_types::type_erasure::TypeErasedBox; use http::HeaderValue; use std::time::Duration; @@ -190,7 +193,7 @@ mod tests { #[test] fn test_request_pairs_for_initial_attempt() { let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); - let mut context = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + let mut context = InterceptorContext::new(Input::doesnt_matter()); context.enter_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 5e91bd8d23..75e48766b5 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -72,6 +72,10 @@ fn header_values( } impl Interceptor for UserAgentInterceptor { + fn name(&self) -> &'static str { + "UserAgentInterceptor" + } + fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt index 17e8dd248b..8e15437aa3 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsCustomizableOperationDecorator.kt @@ -60,6 +60,10 @@ class CustomizableOperationTestHelpers(runtimeConfig: RuntimeConfig) : where F: Fn(&mut BeforeTransmitInterceptorContextMut<'_>, &mut ConfigBag) + Send + Sync + 'static, { + fn name(&self) -> &'static str { + "TestParamsSetterInterceptor" + } + fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt index 2cd374ad6e..2c46cf4bba 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/generators/EndpointParamsInterceptorGenerator.kt @@ -74,6 +74,10 @@ class EndpointParamsInterceptorGenerator( struct $interceptorName; impl #{Interceptor} for $interceptorName { + fn name(&self) -> &'static str { + ${interceptorName.dq()} + } + fn read_before_execution( &self, context: &#{BeforeSerializationInterceptorContextRef}<'_, #{Input}, #{Output}, #{Error}>, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt index 0f087d50ad..3463ab1e73 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/MetadataCustomizationTest.kt @@ -63,6 +63,10 @@ class MetadataCustomizationTest { ); impl #{Interceptor} for ExtractMetadataInterceptor { + fn name(&self) -> &'static str { + "ExtractMetadataInterceptor" + } + fn modify_before_signing( &self, _context: &mut #{BeforeTransmitInterceptorContextMut}<'_>, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt index 0b64de45cf..9d2e74c1b7 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/EndpointsDecoratorTest.kt @@ -206,6 +206,10 @@ class EndpointsDecoratorTest { called: Arc, } impl Interceptor for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + fn read_before_transmit( &self, _context: &BeforeTransmitInterceptorContextRef<'_>, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt index a94148d21c..845501945b 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/EndpointTraitBindingsTest.kt @@ -237,6 +237,10 @@ internal class EndpointTraitBindingsTest { last_endpoint_prefix: Arc>>, } impl Interceptor for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + fn read_before_transmit( &self, _context: &BeforeTransmitInterceptorContextRef<'_>, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt index 99beb8d86b..f214b9d53d 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt @@ -41,6 +41,10 @@ private class TestServiceRuntimePluginCustomization( ##[derive(::std::fmt::Debug)] struct TestInterceptor; impl #{Interceptor} for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + fn modify_before_retry_loop( &self, context: &mut #{BeforeTransmitInterceptorContextMut}<'_>, diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs index 91d7225262..443af2962a 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors.rs @@ -65,6 +65,9 @@ macro_rules! interceptor_trait_fn { /// to read in-flight request or response messages, or "read/write" hooks, which make it possible /// to modify in-flight request or output messages. pub trait Interceptor: fmt::Debug + Send + Sync { + /// The name of this interceptor, used in error messages for debugging. + fn name(&self) -> &'static str; + /// A hook called at the start of an execution, before the SDK /// does anything else. /// diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs index 11ffbd60c9..cba0829791 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/error.rs @@ -12,10 +12,12 @@ macro_rules! interceptor_error_fn { ($fn_name:ident => $error_kind:ident (with source)) => { #[doc = concat!("Create a new error indicating a failure with a ", stringify!($fn_name), " interceptor.")] pub fn $fn_name( + interceptor_name: impl Into, source: impl Into>, ) -> Self { Self { kind: ErrorKind::$error_kind, + interceptor_name: Some(interceptor_name.into()), source: Some(source.into()), } } @@ -25,6 +27,7 @@ macro_rules! interceptor_error_fn { pub fn $fn_name() -> Self { Self { kind: ErrorKind::$error_kind, + interceptor_name: None, source: None, } } @@ -35,6 +38,7 @@ macro_rules! interceptor_error_fn { #[derive(Debug)] pub struct InterceptorError { kind: ErrorKind, + interceptor_name: Option, source: Option, } @@ -125,14 +129,15 @@ macro_rules! display_interceptor_err { { use ErrorKind::*; match &$self.kind { - $($error_kind => display_interceptor_err!($f, $fn_name, ($($option)+)),)+ + $($error_kind => display_interceptor_err!($self, $f, $fn_name, ($($option)+)),)+ } } }; - ($f:ident, $fn_name:ident, (interceptor error)) => { - $f.write_str(concat!(stringify!($fn_name), " interceptor encountered an error")) - }; - ($f:ident, $fn_name:ident, (invalid access $name:ident $message:literal)) => { + ($self:ident, $f:ident, $fn_name:ident, (interceptor error)) => {{ + $f.write_str($self.interceptor_name.as_deref().unwrap_or_default())?; + $f.write_str(concat!(" ", stringify!($fn_name), " interceptor encountered an error")) + }}; + ($self:ident, $f:ident, $fn_name:ident, (invalid access $name:ident $message:literal)) => { $f.write_str(concat!("tried to access the ", stringify!($name), " ", $message)) }; } diff --git a/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs index 1903198a95..7c1517550d 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs @@ -40,6 +40,10 @@ impl ConnectionPoisoningInterceptor { } impl Interceptor for ConnectionPoisoningInterceptor { + fn name(&self) -> &'static str { + "ConnectionPoisoningInterceptor" + } + fn modify_before_transmit( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs index 931866c621..f676f1baf2 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs @@ -36,7 +36,7 @@ macro_rules! interceptor_impl_fn { stringify!($interceptor), "` interceptors" )); - let mut result: Result<(), BoxError> = Ok(()); + let mut result: Result<(), (&str, BoxError)> = Ok(()); let mut ctx = ctx.into(); for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { @@ -44,13 +44,18 @@ macro_rules! interceptor_impl_fn { interceptor.$interceptor(&mut ctx, runtime_components, cfg) { if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + tracing::debug!( + "{}::{}: {}", + last_error.0, + stringify!($interceptor), + DisplayErrorContext(&*last_error.1) + ); } - result = Err(new_error); + result = Err((interceptor.name(), new_error)); } } } - result.map_err(InterceptorError::$interceptor) + result.map_err(|(name, err)| InterceptorError::$interceptor(name, err)) } }; (ref $interceptor:ident) => { @@ -60,20 +65,25 @@ macro_rules! interceptor_impl_fn { runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { - let mut result: Result<(), BoxError> = Ok(()); + let mut result: Result<(), (&str, BoxError)> = Ok(()); let ctx = ctx.into(); for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { if let Err(new_error) = interceptor.$interceptor(&ctx, runtime_components, cfg) { if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + tracing::debug!( + "{}::{}: {}", + last_error.0, + stringify!($interceptor), + DisplayErrorContext(&*last_error.1) + ); } - result = Err(new_error); + result = Err((interceptor.name(), new_error)); } } } - result.map_err(InterceptorError::$interceptor) + result.map_err(|(name, err)| InterceptorError::$interceptor(name, err)) } }; } @@ -105,19 +115,24 @@ where "running {} `read_before_execution` interceptors", if operation { "operation" } else { "client" } ); - let mut result: Result<(), BoxError> = Ok(()); + let mut result: Result<(), (&str, BoxError)> = Ok(()); let ctx: BeforeSerializationInterceptorContextRef<'_> = ctx.into(); for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { if let Err(new_error) = interceptor.read_before_execution(&ctx, cfg) { if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + tracing::debug!( + "{}::{}: {}", + last_error.0, + "read_before_execution", + DisplayErrorContext(&*last_error.1) + ); } - result = Err(new_error); + result = Err((interceptor.name(), new_error)); } } } - result.map_err(InterceptorError::read_before_execution) + result.map_err(|(name, err)| InterceptorError::read_before_execution(name, err)) } interceptor_impl_fn!(mut modify_before_serialization); @@ -142,7 +157,7 @@ where cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { tracing::trace!("running `modify_before_attempt_completion` interceptors"); - let mut result: Result<(), BoxError> = Ok(()); + let mut result: Result<(), (&str, BoxError)> = Ok(()); let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { @@ -150,13 +165,18 @@ where interceptor.modify_before_attempt_completion(&mut ctx, runtime_components, cfg) { if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + tracing::debug!( + "{}::{}: {}", + last_error.0, + "modify_before_attempt_completion", + DisplayErrorContext(&*last_error.1) + ); } - result = Err(new_error); + result = Err((interceptor.name(), new_error)); } } } - result.map_err(InterceptorError::modify_before_attempt_completion) + result.map_err(|(name, err)| InterceptorError::modify_before_attempt_completion(name, err)) } pub(crate) fn read_after_attempt( @@ -166,7 +186,7 @@ where cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { tracing::trace!("running `read_after_attempt` interceptors"); - let mut result: Result<(), BoxError> = Ok(()); + let mut result: Result<(), (&str, BoxError)> = Ok(()); let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { @@ -174,13 +194,18 @@ where interceptor.read_after_attempt(&ctx, runtime_components, cfg) { if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + tracing::debug!( + "{}::{}: {}", + last_error.0, + "read_after_attempt", + DisplayErrorContext(&*last_error.1) + ); } - result = Err(new_error); + result = Err((interceptor.name(), new_error)); } } } - result.map_err(InterceptorError::read_after_attempt) + result.map_err(|(name, err)| InterceptorError::read_after_attempt(name, err)) } pub(crate) fn modify_before_completion( @@ -190,7 +215,7 @@ where cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { tracing::trace!("running `modify_before_completion` interceptors"); - let mut result: Result<(), BoxError> = Ok(()); + let mut result: Result<(), (&str, BoxError)> = Ok(()); let mut ctx: FinalizerInterceptorContextMut<'_> = ctx.into(); for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { @@ -198,13 +223,18 @@ where interceptor.modify_before_completion(&mut ctx, runtime_components, cfg) { if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + tracing::debug!( + "{}::{}: {}", + last_error.0, + "modify_before_completion", + DisplayErrorContext(&*last_error.1) + ); } - result = Err(new_error); + result = Err((interceptor.name(), new_error)); } } } - result.map_err(InterceptorError::modify_before_completion) + result.map_err(|(name, err)| InterceptorError::modify_before_completion(name, err)) } pub(crate) fn read_after_execution( @@ -214,7 +244,7 @@ where cfg: &mut ConfigBag, ) -> Result<(), InterceptorError> { tracing::trace!("running `read_after_execution` interceptors"); - let mut result: Result<(), BoxError> = Ok(()); + let mut result: Result<(), (&str, BoxError)> = Ok(()); let ctx: FinalizerInterceptorContextRef<'_> = ctx.into(); for interceptor in self.into_iter() { if let Some(interceptor) = interceptor.if_enabled(cfg) { @@ -222,13 +252,18 @@ where interceptor.read_after_execution(&ctx, runtime_components, cfg) { if let Err(last_error) = result { - tracing::debug!("{}", DisplayErrorContext(&*last_error)); + tracing::debug!( + "{}::{}: {}", + last_error.0, + "read_after_execution", + DisplayErrorContext(&*last_error.1) + ); } - result = Err(new_error); + result = Err((interceptor.name(), new_error)); } } } - result.map_err(InterceptorError::read_after_execution) + result.map_err(|(name, err)| InterceptorError::read_after_execution(name, err)) } } @@ -270,6 +305,10 @@ where F: Fn(HttpRequest) -> Result + Send + Sync + 'static, E: StdError + Send + Sync + 'static, { + fn name(&self) -> &'static str { + "MapRequestInterceptor" + } + fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, @@ -305,6 +344,10 @@ impl Interceptor for MutateRequestInterceptor where F: Fn(&mut HttpRequest) + Send + Sync + 'static, { + fn name(&self) -> &'static str { + "MutateRequestInterceptor" + } + fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, @@ -335,13 +378,21 @@ mod tests { #[derive(Debug)] struct TestInterceptor; - impl Interceptor for TestInterceptor {} + impl Interceptor for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + } #[test] fn test_disable_interceptors() { #[derive(Debug)] struct PanicInterceptor; impl Interceptor for PanicInterceptor { + fn name(&self) -> &'static str { + "PanicInterceptor" + } + fn read_before_transmit( &self, _context: &BeforeTransmitInterceptorContextRef<'_>, diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 2adaab14e2..10154a12c4 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -534,6 +534,8 @@ mod tests { #[derive(Debug)] struct FailingInterceptorA; impl Interceptor for FailingInterceptorA { + fn name(&self) -> &'static str { "FailingInterceptorA" } + fn $interceptor( &self, _ctx: $ctx, @@ -548,6 +550,8 @@ mod tests { #[derive(Debug)] struct FailingInterceptorB; impl Interceptor for FailingInterceptorB { + fn name(&self) -> &'static str { "FailingInterceptorB" } + fn $interceptor( &self, _ctx: $ctx, @@ -562,6 +566,8 @@ mod tests { #[derive(Debug)] struct FailingInterceptorC; impl Interceptor for FailingInterceptorC { + fn name(&self) -> &'static str { "FailingInterceptorC" } + fn $interceptor( &self, _ctx: $ctx, @@ -624,7 +630,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_before_execution_error_handling() { - let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ReadBeforeExecution, source: Some(\"FailingInterceptorC\") } })""#.to_string(); + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ReadBeforeExecution, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") } })""#.to_string(); interceptor_error_handling_test!( read_before_execution, &BeforeSerializationInterceptorContextRef<'_>, @@ -635,7 +641,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_serialization_error_handling() { - let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeSerialization, source: Some(\"FailingInterceptorC\") } })""#.to_string(); + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeSerialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") } })""#.to_string(); interceptor_error_handling_test!( modify_before_serialization, &mut BeforeSerializationInterceptorContextMut<'_>, @@ -646,7 +652,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_before_serialization_error_handling() { - let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ReadBeforeSerialization, source: Some(\"FailingInterceptorC\") } })""#.to_string(); + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ReadBeforeSerialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") } })""#.to_string(); interceptor_error_handling_test!( read_before_serialization, &BeforeSerializationInterceptorContextRef<'_>, @@ -657,7 +663,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_after_serialization_error_handling() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadAfterSerialization, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadAfterSerialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( read_after_serialization, &BeforeTransmitInterceptorContextRef<'_>, @@ -668,7 +674,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_retry_loop_error_handling() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeRetryLoop, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeRetryLoop, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( modify_before_retry_loop, &mut BeforeTransmitInterceptorContextMut<'_>, @@ -679,7 +685,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_before_attempt_error_handling() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeAttempt, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeAttempt, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( read_before_attempt, &BeforeTransmitInterceptorContextRef<'_>, @@ -690,7 +696,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_signing_error_handling() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeSigning, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeSigning, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( modify_before_signing, &mut BeforeTransmitInterceptorContextMut<'_>, @@ -701,7 +707,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_before_signing_error_handling() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeSigning, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeSigning, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( read_before_signing, &BeforeTransmitInterceptorContextRef<'_>, @@ -712,7 +718,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_after_signing_error_handling() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadAfterSigning, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadAfterSigning, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( read_after_signing, &BeforeTransmitInterceptorContextRef<'_>, @@ -723,7 +729,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_transmit_error_handling() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeTransmit, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeTransmit, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( modify_before_transmit, &mut BeforeTransmitInterceptorContextMut<'_>, @@ -734,7 +740,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_before_transmit_error_handling() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeTransmit, source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ReadBeforeTransmit, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, connection: Unknown } })""#.to_string(); interceptor_error_handling_test!( read_before_transmit, &BeforeTransmitInterceptorContextRef<'_>, @@ -745,7 +751,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_after_transmit_error_handling() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterTransmit, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterTransmit, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( read_after_transmit, &BeforeDeserializationInterceptorContextRef<'_>, @@ -756,7 +762,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_deserialization_error_handling() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeDeserialization, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeDeserialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( modify_before_deserialization, &mut BeforeDeserializationInterceptorContextMut<'_>, @@ -767,7 +773,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_before_deserialization_error_handling() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadBeforeDeserialization, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadBeforeDeserialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( read_before_deserialization, &BeforeDeserializationInterceptorContextRef<'_>, @@ -778,7 +784,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_after_deserialization_error_handling() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterDeserialization, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterDeserialization, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( read_after_deserialization, &AfterDeserializationInterceptorContextRef<'_>, @@ -789,7 +795,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_attempt_completion_error_handling() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( modify_before_attempt_completion, &mut FinalizerInterceptorContextMut<'_>, @@ -800,7 +806,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_after_attempt_error_handling() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterAttempt, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterAttempt, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( read_after_attempt, &FinalizerInterceptorContextRef<'_>, @@ -811,7 +817,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_completion_error_handling() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( modify_before_completion, &mut FinalizerInterceptorContextMut<'_>, @@ -822,7 +828,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_after_execution_error_handling() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterExecution, source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterExecution, interceptor_name: Some(\"FailingInterceptorC\"), source: Some(\"FailingInterceptorC\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_handling_test!( read_after_execution, &FinalizerInterceptorContextRef<'_>, @@ -841,6 +847,8 @@ mod tests { #[derive(Debug)] struct OriginInterceptor; impl Interceptor for OriginInterceptor { + fn name(&self) -> &'static str { "OriginInterceptor" } + fn $origin_interceptor( &self, _ctx: $origin_ctx, @@ -855,6 +863,8 @@ mod tests { #[derive(Debug)] struct DestinationInterceptor; impl Interceptor for DestinationInterceptor { + fn name(&self) -> &'static str { "DestinationInterceptor" } + fn $destination_interceptor( &self, _ctx: $destination_ctx, @@ -902,7 +912,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_before_execution_error_causes_jump_to_modify_before_completion() { - let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"DestinationInterceptor\") } })""#.to_string(); + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") } })""#.to_string(); interceptor_error_redirection_test!( read_before_execution, &BeforeSerializationInterceptorContextRef<'_>, @@ -915,7 +925,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_serialization_error_causes_jump_to_modify_before_completion() { - let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"DestinationInterceptor\") } })""#.to_string(); + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") } })""#.to_string(); interceptor_error_redirection_test!( modify_before_serialization, &mut BeforeSerializationInterceptorContextMut<'_>, @@ -928,7 +938,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_before_serialization_error_causes_jump_to_modify_before_completion() { - let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"DestinationInterceptor\") } })""#.to_string(); + let expected = r#""ConstructionFailure(ConstructionFailure { source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") } })""#.to_string(); interceptor_error_redirection_test!( read_before_serialization, &BeforeSerializationInterceptorContextRef<'_>, @@ -941,7 +951,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_after_serialization_error_causes_jump_to_modify_before_completion() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); interceptor_error_redirection_test!( read_after_serialization, &BeforeTransmitInterceptorContextRef<'_>, @@ -954,7 +964,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_retry_loop_error_causes_jump_to_modify_before_completion() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); interceptor_error_redirection_test!( modify_before_retry_loop, &mut BeforeTransmitInterceptorContextMut<'_>, @@ -967,7 +977,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_before_attempt_error_causes_jump_to_modify_before_attempt_completion() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); interceptor_error_redirection_test!( read_before_attempt, &BeforeTransmitInterceptorContextRef<'_>, @@ -980,7 +990,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_signing_error_causes_jump_to_modify_before_attempt_completion() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); interceptor_error_redirection_test!( modify_before_signing, &mut BeforeTransmitInterceptorContextMut<'_>, @@ -993,7 +1003,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_before_signing_error_causes_jump_to_modify_before_attempt_completion() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); interceptor_error_redirection_test!( read_before_signing, &BeforeTransmitInterceptorContextRef<'_>, @@ -1006,7 +1016,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_after_signing_error_causes_jump_to_modify_before_attempt_completion() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); interceptor_error_redirection_test!( read_after_signing, &BeforeTransmitInterceptorContextRef<'_>, @@ -1019,7 +1029,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_transmit_error_causes_jump_to_modify_before_attempt_completion() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); interceptor_error_redirection_test!( modify_before_transmit, &mut BeforeTransmitInterceptorContextMut<'_>, @@ -1032,7 +1042,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_before_transmit_error_causes_jump_to_modify_before_attempt_completion() { - let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); + let expected = r#""DispatchFailure(DispatchFailure { source: ConnectorError { kind: Other(None), source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, connection: Unknown } })""#.to_string(); interceptor_error_redirection_test!( read_before_transmit, &BeforeTransmitInterceptorContextRef<'_>, @@ -1045,7 +1055,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_read_after_transmit_error_causes_jump_to_modify_before_attempt_completion() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); interceptor_error_redirection_test!( read_after_transmit, &BeforeDeserializationInterceptorContextRef<'_>, @@ -1059,7 +1069,7 @@ mod tests { #[traced_test] async fn test_modify_before_deserialization_error_causes_jump_to_modify_before_attempt_completion( ) { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); interceptor_error_redirection_test!( modify_before_deserialization, &mut BeforeDeserializationInterceptorContextMut<'_>, @@ -1073,7 +1083,7 @@ mod tests { #[traced_test] async fn test_read_before_deserialization_error_causes_jump_to_modify_before_attempt_completion( ) { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(None), retryable: true } } })""#.to_string(); interceptor_error_redirection_test!( read_before_deserialization, &BeforeDeserializationInterceptorContextRef<'_>, @@ -1087,7 +1097,7 @@ mod tests { #[traced_test] async fn test_read_after_deserialization_error_causes_jump_to_modify_before_attempt_completion() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ModifyBeforeAttemptCompletion, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_redirection_test!( read_after_deserialization, &AfterDeserializationInterceptorContextRef<'_>, @@ -1100,7 +1110,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_attempt_completion_error_causes_jump_to_read_after_attempt() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterAttempt, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterAttempt, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_redirection_test!( modify_before_attempt_completion, &mut FinalizerInterceptorContextMut<'_>, @@ -1113,7 +1123,7 @@ mod tests { #[tokio::test] #[traced_test] async fn test_modify_before_completion_error_causes_jump_to_read_after_execution() { - let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterExecution, source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); + let expected = r#""ResponseError(ResponseError { source: InterceptorError { kind: ReadAfterExecution, interceptor_name: Some(\"DestinationInterceptor\"), source: Some(\"DestinationInterceptor\") }, raw: Response { status: 200, version: HTTP/1.1, headers: {}, body: SdkBody { inner: Once(Some(b\"\")), retryable: true } } })""#.to_string(); interceptor_error_redirection_test!( modify_before_completion, &mut FinalizerInterceptorContextMut<'_>, @@ -1171,6 +1181,10 @@ mod tests { } impl Interceptor for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + fn modify_before_retry_loop( &self, _context: &mut BeforeTransmitInterceptorContextMut<'_>, diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs index 110f720a81..16cb5d18b6 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs @@ -66,6 +66,10 @@ fn extract_time_sent_from_response( } impl Interceptor for ServiceClockSkewInterceptor { + fn name(&self) -> &'static str { + "ServiceClockSkewInterceptor" + } + fn modify_before_deserialization( &self, ctx: &mut BeforeDeserializationInterceptorContextMut<'_>, diff --git a/rust-runtime/inlineable/src/client_http_checksum_required.rs b/rust-runtime/inlineable/src/client_http_checksum_required.rs index 50fe4d18e1..10c129b3bf 100644 --- a/rust-runtime/inlineable/src/client_http_checksum_required.rs +++ b/rust-runtime/inlineable/src/client_http_checksum_required.rs @@ -39,6 +39,10 @@ impl RuntimePlugin for HttpChecksumRequiredRuntimePlugin { struct HttpChecksumRequiredInterceptor; impl Interceptor for HttpChecksumRequiredInterceptor { + fn name(&self) -> &'static str { + "HttpChecksumRequiredInterceptor" + } + fn modify_before_signing( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, diff --git a/rust-runtime/inlineable/src/client_idempotency_token.rs b/rust-runtime/inlineable/src/client_idempotency_token.rs index 5e46f6a01e..778fc1133b 100644 --- a/rust-runtime/inlineable/src/client_idempotency_token.rs +++ b/rust-runtime/inlineable/src/client_idempotency_token.rs @@ -56,6 +56,10 @@ impl Interceptor for IdempotencyTokenInterceptor where S: Fn(IdempotencyTokenProvider, &mut Input) + Send + Sync, { + fn name(&self) -> &'static str { + "IdempotencyTokenInterceptor" + } + fn modify_before_serialization( &self, context: &mut BeforeSerializationInterceptorContextMut<'_>, From 748d05cd0b0b1430fac7ca1f112761219c20b283 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 27 Jul 2023 10:51:12 -0700 Subject: [PATCH 237/253] New-type the interceptor context `Input`, `Output`, and `Error` (#2872) This PR creates new-types for `Input`, `Output`, and `Error` so that the type system can enforce that an output isn't given to an input and vice versa. It also removes `TypedBox` since that isn't really needed anymore. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-config/src/lib.rs | 2 - .../src/glacier_interceptors.rs | 11 +- .../aws-runtime/src/invocation_id.rs | 7 +- .../aws-runtime/src/recursion_detection.rs | 5 +- .../aws-runtime/src/retries/classifier.rs | 18 +- .../aws-runtime/src/user_agent.rs | 5 +- .../aws-sig-auth/src/middleware.rs | 1 - .../rustsdk/AwsFluentClientDecorator.kt | 4 - .../smithy/rustsdk/AwsPresigningDecorator.kt | 5 +- aws/sdk/integration-tests/kms/Cargo.toml | 2 +- .../kms/tests/retryable_errors.rs | 11 +- .../smithy/generators/OperationGenerator.kt | 16 +- .../protocol/ProtocolTestGenerator.kt | 2 +- .../protocol/RequestSerializerGenerator.kt | 3 +- .../protocol/ResponseDeserializerGenerator.kt | 5 +- ...onfigOverrideRuntimePluginGeneratorTest.kt | 4 +- .../protocol/ProtocolTestGeneratorTest.kt | 5 +- .../aws-smithy-runtime-api/Cargo.toml | 2 +- .../aws-smithy-runtime-api/src/client/auth.rs | 4 +- .../src/client/endpoint.rs | 4 +- .../src/client/interceptors/context.rs | 105 +++++++---- .../src/client/orchestrator.rs | 6 +- rust-runtime/aws-smithy-runtime/Cargo.toml | 2 +- rust-runtime/aws-smithy-runtime/additional-ci | 12 ++ .../src/client/interceptors.rs | 12 +- .../src/client/orchestrator.rs | 13 +- .../src/client/orchestrator/auth.rs | 9 +- .../src/client/retries/classifier.rs | 15 +- .../src/client/retries/strategy/standard.rs | 16 +- .../aws-smithy-types/src/type_erasure.rs | 170 +++--------------- .../ci-scripts/codegen-diff/semver-checks.py | 6 +- 31 files changed, 193 insertions(+), 289 deletions(-) create mode 100755 rust-runtime/aws-smithy-runtime/additional-ci diff --git a/aws/rust-runtime/aws-config/src/lib.rs b/aws/rust-runtime/aws-config/src/lib.rs index 9185672577..1deffdbe16 100644 --- a/aws/rust-runtime/aws-config/src/lib.rs +++ b/aws/rust-runtime/aws-config/src/lib.rs @@ -376,8 +376,6 @@ mod loader { self } - // TODO(enableNewSmithyRuntimeLaunch): Remove the doc hidden from this function - #[doc(hidden)] /// Don't use credentials to sign requests. /// /// Turning off signing with credentials is necessary in some cases, such as using diff --git a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs index 3b20b5fff7..53500eae50 100644 --- a/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs +++ b/aws/rust-runtime/aws-inlineable/src/glacier_interceptors.rs @@ -252,9 +252,8 @@ fn compute_hash_tree(mut hashes: Vec) -> Digest { #[cfg(test)] mod account_id_autofill_tests { use super::*; - use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; - use aws_smithy_types::type_erasure::TypedBox; #[test] fn autofill_account_id() { @@ -270,8 +269,7 @@ mod account_id_autofill_tests { let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut cfg = ConfigBag::base(); - let mut context = - InterceptorContext::new(TypedBox::new(SomeInput { account_id: None }).erase()); + let mut context = InterceptorContext::new(Input::erase(SomeInput { account_id: None })); let mut context = BeforeSerializationInterceptorContextMut::from(&mut context); let interceptor = GlacierAccountIdAutofillInterceptor::::new(); interceptor @@ -293,15 +291,14 @@ mod account_id_autofill_tests { #[cfg(test)] mod api_version_tests { use super::*; - use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; - use aws_smithy_types::type_erasure::TypedBox; #[test] fn api_version_interceptor() { let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); let mut cfg = ConfigBag::base(); - let mut context = InterceptorContext::new(TypedBox::new("dontcare").erase()); + let mut context = InterceptorContext::new(Input::doesnt_matter()); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let mut context = BeforeTransmitInterceptorContextMut::from(&mut context); diff --git a/aws/rust-runtime/aws-runtime/src/invocation_id.rs b/aws/rust-runtime/aws-runtime/src/invocation_id.rs index 3ee5923f07..18fcac7c4a 100644 --- a/aws/rust-runtime/aws-runtime/src/invocation_id.rs +++ b/aws/rust-runtime/aws-runtime/src/invocation_id.rs @@ -215,12 +215,11 @@ mod tests { use super::*; use aws_smithy_http::body::SdkBody; use aws_smithy_runtime_api::client::interceptors::context::{ - BeforeTransmitInterceptorContextMut, InterceptorContext, + BeforeTransmitInterceptorContextMut, Input, InterceptorContext, }; use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::config_bag::{ConfigBag, Layer}; - use aws_smithy_types::type_erasure::TypeErasedBox; use http::HeaderValue; fn expect_header<'a>( @@ -233,7 +232,7 @@ mod tests { #[test] fn default_id_generator() { let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); - let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.enter_serialization_phase(); ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let _ = ctx.take_input(); @@ -260,7 +259,7 @@ mod tests { #[test] fn custom_id_generator() { let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); - let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.enter_serialization_phase(); ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let _ = ctx.take_input(); diff --git a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs index 2e87f24edb..368843a865 100644 --- a/aws/rust-runtime/aws-runtime/src/recursion_detection.rs +++ b/aws/rust-runtime/aws-runtime/src/recursion_detection.rs @@ -80,9 +80,8 @@ mod tests { use super::*; use aws_smithy_http::body::SdkBody; use aws_smithy_protocol_test::{assert_ok, validate_headers}; - use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; - use aws_smithy_types::type_erasure::TypeErasedBox; use aws_types::os_shim_internal::Env; use http::HeaderValue; use proptest::{prelude::*, proptest}; @@ -156,7 +155,7 @@ mod tests { request = request.header(name, value); } let request = request.body(SdkBody::empty()).expect("must be valid"); - let mut context = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + let mut context = InterceptorContext::new(Input::doesnt_matter()); context.enter_serialization_phase(); context.set_request(request); let _ = context.take_input(); diff --git a/aws/rust-runtime/aws-runtime/src/retries/classifier.rs b/aws/rust-runtime/aws-runtime/src/retries/classifier.rs index 5a7675dba2..d73cdf4212 100644 --- a/aws/rust-runtime/aws-runtime/src/retries/classifier.rs +++ b/aws/rust-runtime/aws-runtime/src/retries/classifier.rs @@ -107,9 +107,9 @@ impl ClassifyRetry for AmzRetryAfterHeaderClassifier { mod test { use super::*; use aws_smithy_http::body::SdkBody; + use aws_smithy_runtime_api::client::interceptors::context::{Error, Input}; use aws_smithy_types::error::ErrorMetadata; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; - use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; use std::fmt; use std::time::Duration; @@ -164,8 +164,8 @@ mod test { #[test] fn classify_by_error_code() { let policy = AwsErrorCodeClassifier::::new(); - let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); - ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new( + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase( CodedError::new("Throttling"), )))); @@ -174,8 +174,8 @@ mod test { Some(RetryReason::Error(ErrorKind::ThrottlingError)) ); - let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); - ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new( + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase( CodedError::new("RequestTimeout"), )))); assert_eq!( @@ -190,9 +190,9 @@ mod test { let err = aws_smithy_types::Error::builder().code("SlowDown").build(); let test_response = http::Response::new("OK").map(SdkBody::from); - let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.set_response(test_response); - ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new(err)))); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase(err)))); assert_eq!( policy.classify_retry(&ctx), @@ -208,9 +208,9 @@ mod test { .body("retry later") .unwrap() .map(SdkBody::from); - let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.set_response(res); - ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new( + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase( UnmodeledError, )))); diff --git a/aws/rust-runtime/aws-runtime/src/user_agent.rs b/aws/rust-runtime/aws-runtime/src/user_agent.rs index 75e48766b5..7310820f86 100644 --- a/aws/rust-runtime/aws-runtime/src/user_agent.rs +++ b/aws/rust-runtime/aws-runtime/src/user_agent.rs @@ -114,12 +114,11 @@ impl Interceptor for UserAgentInterceptor { mod tests { use super::*; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::error::display::DisplayErrorContext; - use aws_smithy_types::type_erasure::TypeErasedBox; fn expect_header<'a>(context: &'a InterceptorContext, header_name: &str) -> &'a str { context @@ -133,7 +132,7 @@ mod tests { } fn context() -> InterceptorContext { - let mut context = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + let mut context = InterceptorContext::new(Input::doesnt_matter()); context.enter_serialization_phase(); context.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let _ = context.take_input(); diff --git a/aws/rust-runtime/aws-sig-auth/src/middleware.rs b/aws/rust-runtime/aws-sig-auth/src/middleware.rs index fd0b1474a0..b901f3a856 100644 --- a/aws/rust-runtime/aws-sig-auth/src/middleware.rs +++ b/aws/rust-runtime/aws-sig-auth/src/middleware.rs @@ -151,7 +151,6 @@ fn signing_config( request_ts: config .get::() .map(|t| t.now()) - // TODO(enableNewSmithyRuntimeLaunch): Remove this fallback .unwrap_or_else(|| SharedTimeSource::default().now()), region, payload_override, diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt index cf8b107a01..30340e61cc 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsFluentClientDecorator.kt @@ -105,10 +105,6 @@ class AwsFluentClientDecorator : ClientCodegenDecorator { renderClientCreation = { params -> rust("let mut ${params.configBuilderName} = ${params.configBuilderName};") if (codegenContext.smithyRuntimeMode.generateOrchestrator) { - // TODO(enableNewSmithyRuntimeLaunch): A builder field could not be accessed directly in the orchestrator - // mode when this code change was made. smithy-rs#2792 will enable us to use getters in builders. - // Make this `set_region` conditional by checking if `config_builder.region().is_none()` once the PR - // has been merged to main. rust("""${params.configBuilderName}.set_region(Some(crate::config::Region::new("us-east-1")));""") } else { rust( diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt index 522b791fab..3957f77391 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/AwsPresigningDecorator.kt @@ -336,9 +336,7 @@ class AwsPresignedFluentBuilderMethod( .await .map_err(|err| { err.map_service_error(|err| { - #{TypedBox}::<#{OperationError}>::assume_from(err.into()) - .expect("correct error type") - .unwrap() + err.downcast::<#{OperationError}>().expect("correct error type") }) })?; let request = context.take_request().expect("request set before transmit"); @@ -353,7 +351,6 @@ class AwsPresignedFluentBuilderMethod( "SigV4PresigningRuntimePlugin" to AwsRuntimeType.presigningInterceptor(runtimeConfig) .resolve("SigV4PresigningRuntimePlugin"), "StopPoint" to RuntimeType.smithyRuntime(runtimeConfig).resolve("client::orchestrator::StopPoint"), - "TypedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypedBox"), "USER_AGENT" to CargoDependency.Http.toType().resolve("header::USER_AGENT"), "alternate_presigning_serializer" to writable { if (presignableOp.hasModelTransforms()) { diff --git a/aws/sdk/integration-tests/kms/Cargo.toml b/aws/sdk/integration-tests/kms/Cargo.toml index 188a2d42dd..2c76644e94 100644 --- a/aws/sdk/integration-tests/kms/Cargo.toml +++ b/aws/sdk/integration-tests/kms/Cargo.toml @@ -19,7 +19,7 @@ aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", featur 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" } -aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api" } +aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["test-util"] } bytes = "1.0.0" http = "0.2.0" tokio = { version = "1.23.1", features = ["full", "test-util"] } diff --git a/aws/sdk/integration-tests/kms/tests/retryable_errors.rs b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs index 3a275b460d..ba9090837c 100644 --- a/aws/sdk/integration-tests/kms/tests/retryable_errors.rs +++ b/aws/sdk/integration-tests/kms/tests/retryable_errors.rs @@ -73,11 +73,10 @@ mod orchestrator_mode_tests { use aws_sdk_kms as kms; use aws_smithy_client::test_connection::infallible_connection_fn; use aws_smithy_http::result::SdkError; - use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, InterceptorContext}; use aws_smithy_runtime_api::client::orchestrator::{HttpResponse, OrchestratorError}; use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::retry::ErrorKind; - use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; use bytes::Bytes; use kms::operation::create_alias::CreateAliasError; @@ -113,9 +112,9 @@ mod orchestrator_mode_tests { dbg!(&err); let classifier = AwsErrorCodeClassifier::::new(); - let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); let err = err.into_service_error(); - ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new(err)))); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase(err)))); let retry_kind = classifier.classify_retry(&ctx); assert_eq!( Some(RetryReason::Error(ErrorKind::ThrottlingError)), @@ -135,9 +134,9 @@ mod orchestrator_mode_tests { dbg!(&err); let classifier = AwsErrorCodeClassifier::::new(); - let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); let err = err.into_service_error(); - ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new(err)))); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase(err)))); let retry_kind = classifier.classify_retry(&ctx); assert_eq!( Some(RetryReason::Error(ErrorKind::ThrottlingError)), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt index 90b970d1d1..10f52767f4 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/OperationGenerator.kt @@ -111,7 +111,8 @@ open class OperationGenerator( val codegenScope = arrayOf( *preludeScope, "Arc" to RuntimeType.Arc, - "Input" to symbolProvider.toSymbol(operationShape.inputShape(model)), + "ConcreteInput" to symbolProvider.toSymbol(operationShape.inputShape(model)), + "Input" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::Input"), "Operation" to symbolProvider.toSymbol(operationShape), "OperationError" to errorType, "OperationOutput" to outputType, @@ -129,28 +130,26 @@ open class OperationGenerator( """ pub(crate) async fn orchestrate( runtime_plugins: &#{RuntimePlugins}, - input: #{Input}, + input: #{ConcreteInput}, ) -> #{Result}<#{OperationOutput}, #{SdkError}<#{OperationError}, #{HttpResponse}>> { let map_err = |err: #{SdkError}<#{Error}, #{HttpResponse}>| { err.map_service_error(|err| { - #{TypedBox}::<#{OperationError}>::assume_from(err.into()) - .expect("correct error type") - .unwrap() + err.downcast::<#{OperationError}>().expect("correct error type") }) }; let context = Self::orchestrate_with_stop_point(runtime_plugins, input, #{StopPoint}::None) .await .map_err(map_err)?; let output = context.finalize().map_err(map_err)?; - #{Ok}(#{TypedBox}::<#{OperationOutput}>::assume_from(output).expect("correct output type").unwrap()) + #{Ok}(output.downcast::<#{OperationOutput}>().expect("correct output type")) } pub(crate) async fn orchestrate_with_stop_point( runtime_plugins: &#{RuntimePlugins}, - input: #{Input}, + input: #{ConcreteInput}, stop_point: #{StopPoint}, ) -> #{Result}<#{InterceptorContext}, #{SdkError}<#{Error}, #{HttpResponse}>> { - let input = #{TypedBox}::new(input).erase(); + let input = #{Input}::erase(input); #{invoke_with_stop_point}( ${codegenContext.serviceShape.sdkId().dq()}, ${operationName.dq()}, @@ -180,7 +179,6 @@ open class OperationGenerator( """, *codegenScope, "Error" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::Error"), - "TypedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypedBox"), "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), "OrchestratorError" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::orchestrator::error::OrchestratorError"), "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index 583324b0b6..6c08785bfa 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -414,7 +414,7 @@ class DefaultProtocolTestGenerator( rust("let parsed = parsed.unwrap();") } else { rustTemplate( - """let parsed: #{Output} = *parsed.expect("should be successful response").downcast().unwrap();""", + """let parsed = parsed.expect("should be successful response").downcast::<#{Output}>().unwrap();""", "Output" to codegenContext.symbolProvider.toSymbol(expectedShape), ) } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt index 0a1385dcd3..66b9d7e722 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/RequestSerializerGenerator.kt @@ -56,7 +56,6 @@ class RequestSerializerGenerator( codegenContext.runtimeConfig, ), ).resolve("HeaderSerializationSettings"), - "TypedBox" to smithyTypes.resolve("type_erasure::TypedBox"), ) } @@ -72,7 +71,7 @@ class RequestSerializerGenerator( impl #{RequestSerializer} for $serializerName { ##[allow(unused_mut, clippy::let_and_return, clippy::needless_borrow, clippy::useless_conversion)] fn serialize_input(&self, input: #{Input}, _cfg: &mut #{ConfigBag}) -> #{Result}<#{HttpRequest}, #{BoxError}> { - let input = #{TypedBox}::<#{ConcreteInput}>::assume_from(input).expect("correct type").unwrap(); + let input = input.downcast::<#{ConcreteInput}>().expect("correct type"); let _header_serialization_settings = _cfg.load::<#{HeaderSerializationSettings}>().cloned().unwrap_or_default(); let mut request_builder = { #{create_http_request} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt index 59d3956a9f..accd436e42 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ResponseDeserializerGenerator.kt @@ -47,7 +47,6 @@ class ResponseDeserializerGenerator( "ResponseDeserializer" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::ser_de::ResponseDeserializer"), "SdkBody" to RuntimeType.sdkBody(runtimeConfig), "SdkError" to RuntimeType.sdkError(runtimeConfig), - "TypedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypedBox"), "debug_span" to RuntimeType.Tracing.resolve("debug_span"), "type_erase_result" to typeEraseResult(), ) @@ -162,8 +161,8 @@ class ResponseDeserializerGenerator( O: ::std::fmt::Debug + #{Send} + #{Sync} + 'static, E: ::std::error::Error + std::fmt::Debug + #{Send} + #{Sync} + 'static, { - result.map(|output| #{TypedBox}::new(output).erase()) - .map_err(|error| #{TypedBox}::new(error).erase_error()) + result.map(|output| #{Output}::erase(output)) + .map_err(|error| #{Error}::erase(error)) .map_err(#{Into}::into) } """, diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt index bdbee67f3a..48821e380d 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/ConfigOverrideRuntimePluginGeneratorTest.kt @@ -174,6 +174,7 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { .resolve("client::retries::AlwaysRetry"), "ConfigBag" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::ConfigBag"), "ErrorKind" to RuntimeType.smithyTypes(runtimeConfig).resolve("retry::ErrorKind"), + "Input" to RuntimeType.smithyRuntimeApi(runtimeConfig).resolve("client::interceptors::context::Input"), "InterceptorContext" to RuntimeType.interceptorContext(runtimeConfig), "Layer" to RuntimeType.smithyTypes(runtimeConfig).resolve("config_bag::Layer"), "OrchestratorError" to RuntimeType.smithyRuntimeApi(runtimeConfig) @@ -188,7 +189,6 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { "RuntimePlugin" to RuntimeType.runtimePlugin(runtimeConfig), "ShouldAttempt" to RuntimeType.smithyRuntimeApi(runtimeConfig) .resolve("client::retries::ShouldAttempt"), - "TypeErasedBox" to RuntimeType.smithyTypes(runtimeConfig).resolve("type_erasure::TypeErasedBox"), ) rustCrate.testModule { unitTest("test_operation_overrides_retry_strategy") { @@ -201,7 +201,7 @@ internal class ConfigOverrideRuntimePluginGeneratorTest { .retry_config(#{RetryConfig}::standard().with_max_attempts(3)) .build(); - let mut ctx = #{InterceptorContext}::new(#{TypeErasedBox}::new(())); + let mut ctx = #{InterceptorContext}::new(#{Input}::doesnt_matter()); ctx.set_output_or_error(#{Err}(#{OrchestratorError}::other("doesn't matter"))); let mut layer = #{Layer}::new("test"); diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt index f214b9d53d..066c25b8fd 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGeneratorTest.kt @@ -102,8 +102,8 @@ private class TestOperationCustomization( crate::operation::say_hello::SayHelloError, > = $fakeOutput; fake_out - .map(|o| #{TypedBox}::new(o).erase()) - .map_err(|e| #{OrchestratorError}::operation(#{TypedBox}::new(e).erase_error())) + .map(|o| #{Output}::erase(o)) + .map_err(|e| #{OrchestratorError}::operation(#{Error}::erase(e))) } } cfg.store_put(#{SharedResponseDeserializer}::new(TestDeser)); @@ -115,7 +115,6 @@ private class TestOperationCustomization( "OrchestratorError" to RT.smithyRuntimeApi(rc).resolve("client::orchestrator::OrchestratorError"), "Output" to RT.smithyRuntimeApi(rc).resolve("client::interceptors::context::Output"), "ResponseDeserializer" to RT.smithyRuntimeApi(rc).resolve("client::ser_de::ResponseDeserializer"), - "TypedBox" to RT.smithyTypes(rc).resolve("type_erasure::TypedBox"), ) } } diff --git a/rust-runtime/aws-smithy-runtime-api/Cargo.toml b/rust-runtime/aws-smithy-runtime-api/Cargo.toml index 64b3c524c4..426c449ccc 100644 --- a/rust-runtime/aws-smithy-runtime-api/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime-api/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/awslabs/smithy-rs" default = [] client = [] http-auth = ["dep:zeroize"] -test-util = [] +test-util = ["aws-smithy-types/test-util"] [dependencies] aws-smithy-async = { path = "../aws-smithy-async" } diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs index acd96739f6..79a28bef35 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/auth.rs @@ -10,7 +10,7 @@ use crate::client::identity::{Identity, SharedIdentityResolver}; use crate::client::orchestrator::HttpRequest; use crate::client::runtime_components::{GetIdentityResolver, RuntimeComponents}; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; -use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; +use aws_smithy_types::type_erasure::TypeErasedBox; use aws_smithy_types::Document; use std::borrow::Cow; use std::fmt; @@ -66,7 +66,7 @@ pub struct AuthSchemeOptionResolverParams(TypeErasedBox); impl AuthSchemeOptionResolverParams { /// Creates a new [`AuthSchemeOptionResolverParams`]. pub fn new(params: T) -> Self { - Self(TypedBox::new(params).erase()) + Self(TypeErasedBox::new(params)) } /// Returns the underlying parameters as the type `T` if they are that type. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/endpoint.rs b/rust-runtime/aws-smithy-runtime-api/src/client/endpoint.rs index 654d7affb5..a32ed602fe 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/endpoint.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/endpoint.rs @@ -8,7 +8,7 @@ use crate::client::orchestrator::Future; use aws_smithy_types::config_bag::{Storable, StoreReplace}; use aws_smithy_types::endpoint::Endpoint; -use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; +use aws_smithy_types::type_erasure::TypeErasedBox; use std::fmt; use std::sync::Arc; @@ -23,7 +23,7 @@ pub struct EndpointResolverParams(TypeErasedBox); impl EndpointResolverParams { /// Creates a new [`EndpointResolverParams`] from a concrete parameters instance. pub fn new(params: T) -> Self { - Self(TypedBox::new(params).erase()) + Self(TypeErasedBox::new(params)) } /// Attempts to downcast the underlying concrete parameters to `T` and return it as a reference. diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs index 7aeb1904c3..63a54767d4 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/interceptors/context.rs @@ -36,13 +36,65 @@ use std::fmt::Debug; use std::{fmt, mem}; use tracing::{debug, error, trace}; -// TODO(enableNewSmithyRuntimeLaunch): New-type `Input`/`Output`/`Error` -/// Type-erased operation input. -pub type Input = TypeErasedBox; -/// Type-erased operation output. -pub type Output = TypeErasedBox; -/// Type-erased operation error. -pub type Error = TypeErasedError; +macro_rules! new_type_box { + ($name:ident, $doc:literal) => { + new_type_box!($name, TypeErasedBox, $doc, Send, Sync, fmt::Debug,); + }; + ($name:ident, $underlying:ident, $doc:literal, $($additional_bound:path,)*) => { + #[doc = $doc] + #[derive(Debug)] + pub struct $name($underlying); + + impl $name { + #[doc = concat!("Creates a new `", stringify!($name), "` with the provided concrete input value.")] + pub fn erase(input: T) -> Self { + Self($underlying::new(input)) + } + + #[doc = concat!("Downcasts to the concrete input value.")] + pub fn downcast_ref(&self) -> Option<&T> { + self.0.downcast_ref() + } + + #[doc = concat!("Downcasts to the concrete input value.")] + pub fn downcast_mut(&mut self) -> Option<&mut T> { + self.0.downcast_mut() + } + + #[doc = concat!("Downcasts to the concrete input value.")] + pub fn downcast(self) -> Result { + self.0.downcast::().map(|v| *v).map_err(Self) + } + + #[doc = concat!("Returns a `", stringify!($name), "` with a fake/test value with the expectation that it won't be downcast in the test.")] + #[cfg(feature = "test-util")] + pub fn doesnt_matter() -> Self { + Self($underlying::doesnt_matter()) + } + } + }; +} + +new_type_box!(Input, "Type-erased operation input."); +new_type_box!(Output, "Type-erased operation output."); +new_type_box!( + Error, + TypeErasedError, + "Type-erased operation error.", + std::error::Error, +); + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&self.0, f) + } +} +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } +} + /// Type-erased result for an operation. pub type OutputOrError = Result>; @@ -391,27 +443,20 @@ fn try_clone(request: &HttpRequest) -> Option { ) } -#[cfg(test)] +#[cfg(all(test, feature = "test-util"))] mod tests { use super::*; use aws_smithy_http::body::SdkBody; - use aws_smithy_types::type_erasure::TypedBox; use http::header::{AUTHORIZATION, CONTENT_LENGTH}; use http::{HeaderValue, Uri}; #[test] fn test_success_transitions() { - let input = TypedBox::new("input".to_string()).erase(); - let output = TypedBox::new("output".to_string()).erase(); + let input = Input::doesnt_matter(); + let output = Output::erase("output".to_string()); let mut context = InterceptorContext::new(input); - assert_eq!( - "input", - context - .input() - .and_then(|i| i.downcast_ref::()) - .unwrap() - ); + assert!(context.input().is_some()); context.input_mut(); context.enter_serialization_phase(); @@ -447,29 +492,13 @@ mod tests { #[test] fn test_rewind_for_retry() { - use std::fmt; - #[derive(Debug)] - struct Error; - impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("don't care") - } - } - impl std::error::Error for Error {} - let mut cfg = ConfigBag::base(); - let input = TypedBox::new("input".to_string()).erase(); - let output = TypedBox::new("output".to_string()).erase(); - let error = TypedBox::new(Error).erase_error(); + let input = Input::doesnt_matter(); + let output = Output::erase("output".to_string()); + let error = Error::doesnt_matter(); let mut context = InterceptorContext::new(input); - assert_eq!( - "input", - context - .input() - .and_then(|i| i.downcast_ref::()) - .unwrap() - ); + assert!(context.input().is_some()); context.enter_serialization_phase(); let _ = context.take_input(); diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs index f47169cb7b..76f1bfadce 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/orchestrator.rs @@ -18,12 +18,12 @@ use crate::box_error::BoxError; use crate::client::interceptors::context::phase::Phase; +use crate::client::interceptors::context::Error; use crate::client::interceptors::InterceptorError; use aws_smithy_async::future::now_or_later::NowOrLater; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::{ConnectorError, SdkError}; use aws_smithy_types::config_bag::{Storable, StoreReplace}; -use aws_smithy_types::type_erasure::TypeErasedError; use bytes::Bytes; use std::fmt::Debug; use std::future::Future as StdFuture; @@ -247,8 +247,8 @@ where } } -impl From for OrchestratorError { - fn from(err: TypeErasedError) -> Self { +impl From for OrchestratorError { + fn from(err: Error) -> Self { Self::operation(err) } } diff --git a/rust-runtime/aws-smithy-runtime/Cargo.toml b/rust-runtime/aws-smithy-runtime/Cargo.toml index a59e20abdd..e761544d3d 100644 --- a/rust-runtime/aws-smithy-runtime/Cargo.toml +++ b/rust-runtime/aws-smithy-runtime/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/awslabs/smithy-rs" [features] client = ["aws-smithy-runtime-api/client"] http-auth = ["aws-smithy-runtime-api/http-auth"] -test-util = ["dep:aws-smithy-protocol-test", "dep:tracing-subscriber"] +test-util = ["aws-smithy-runtime-api/test-util", "dep:aws-smithy-protocol-test", "dep:tracing-subscriber"] [dependencies] aws-smithy-async = { path = "../aws-smithy-async" } diff --git a/rust-runtime/aws-smithy-runtime/additional-ci b/rust-runtime/aws-smithy-runtime/additional-ci new file mode 100755 index 0000000000..b44c6c05be --- /dev/null +++ b/rust-runtime/aws-smithy-runtime/additional-ci @@ -0,0 +1,12 @@ +#!/bin/bash +# +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +# + +# This script contains additional CI checks to run for this specific package + +set -e + +echo "### Testing every combination of features (excluding --all-features)" +cargo hack test --feature-powerset --exclude-all-features diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs index f676f1baf2..a6a73c2875 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs @@ -419,7 +419,11 @@ mod tests { ); Interceptors::new(rc.interceptors()) - .read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg) + .read_before_transmit( + &InterceptorContext::new(Input::doesnt_matter()), + &rc, + &mut cfg, + ) .expect_err("interceptor returns error"); cfg.interceptor_state() .store_put(disable_interceptor::("test")); @@ -432,7 +436,11 @@ mod tests { ); // shouldn't error because interceptors won't run Interceptors::new(rc.interceptors()) - .read_before_transmit(&InterceptorContext::new(Input::new(5)), &rc, &mut cfg) + .read_before_transmit( + &InterceptorContext::new(Input::doesnt_matter()), + &rc, + &mut cfg, + ) .expect("interceptor is now disabled"); } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 10154a12c4..404ffdae5a 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -445,7 +445,6 @@ mod tests { use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_runtime_api::client::runtime_plugin::{RuntimePlugin, RuntimePlugins}; use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; - use aws_smithy_types::type_erasure::{TypeErasedBox, TypedBox}; use std::borrow::Cow; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -465,7 +464,7 @@ mod tests { .status(StatusCode::OK) .body(SdkBody::empty()) .map_err(|err| OrchestratorError::other(Box::new(err))) - .map(|res| Output::new(Box::new(res))), + .map(|res| Output::erase(res)), ) } @@ -609,7 +608,7 @@ mod tests { } } - let input = TypeErasedBox::new(Box::new(())); + let input = Input::doesnt_matter(); let runtime_plugins = RuntimePlugins::new() .with_client_plugin(FailingInterceptorsClientRuntimePlugin::new()) .with_operation_plugin(TestOperationRuntimePlugin::new()) @@ -893,7 +892,7 @@ mod tests { } } - let input = TypeErasedBox::new(Box::new(())); + let input = Input::doesnt_matter(); let runtime_plugins = RuntimePlugins::new() .with_operation_plugin(TestOperationRuntimePlugin::new()) .with_operation_plugin(NoAuthRuntimePlugin::new()) @@ -1145,7 +1144,7 @@ mod tests { let context = invoke_with_stop_point( "test", "test", - TypedBox::new(()).erase(), + Input::doesnt_matter(), &runtime_plugins(), StopPoint::None, ) @@ -1157,7 +1156,7 @@ mod tests { let context = invoke_with_stop_point( "test", "test", - TypedBox::new(()).erase(), + Input::doesnt_matter(), &runtime_plugins(), StopPoint::BeforeTransmit, ) @@ -1247,7 +1246,7 @@ mod tests { let context = invoke_with_stop_point( "test", "test", - TypedBox::new(()).erase(), + Input::doesnt_matter(), &runtime_plugins(), StopPoint::BeforeTransmit, ) diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs index f300750b2d..71a9b04873 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/auth.rs @@ -161,13 +161,12 @@ mod tests { use aws_smithy_runtime_api::client::identity::{ Identity, IdentityResolver, SharedIdentityResolver, }; - use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; use aws_smithy_runtime_api::client::orchestrator::{Future, HttpRequest}; use aws_smithy_runtime_api::client::runtime_components::{ GetIdentityResolver, RuntimeComponentsBuilder, }; use aws_smithy_types::config_bag::Layer; - use aws_smithy_types::type_erasure::TypedBox; use std::collections::HashMap; #[tokio::test] @@ -222,7 +221,7 @@ mod tests { } } - let mut ctx = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.enter_serialization_phase(); ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let _ = ctx.take_input(); @@ -268,7 +267,7 @@ mod tests { }; use aws_smithy_runtime_api::client::identity::http::{Login, Token}; - let mut ctx = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.enter_serialization_phase(); ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let _ = ctx.take_input(); @@ -317,7 +316,7 @@ mod tests { // Next, test the presence of a bearer token and absence of basic auth let (runtime_components, cfg) = config_with_identity(HTTP_BEARER_AUTH_SCHEME_ID, Token::new("t", None)); - let mut ctx = InterceptorContext::new(TypedBox::new("doesnt-matter").erase()); + let mut ctx = InterceptorContext::new(Input::erase("doesnt-matter")); ctx.enter_serialization_phase(); ctx.set_request(http::Request::builder().body(SdkBody::empty()).unwrap()); let _ = ctx.take_input(); diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs index 5ff4911f0f..f22c765136 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs @@ -150,11 +150,10 @@ mod test { HttpStatusCodeClassifier, ModeledAsRetryableClassifier, }; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; + use aws_smithy_runtime_api::client::interceptors::context::{Error, Input, InterceptorContext}; use aws_smithy_runtime_api::client::orchestrator::OrchestratorError; use aws_smithy_runtime_api::client::retries::{ClassifyRetry, RetryReason}; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; - use aws_smithy_types::type_erasure::{TypeErasedBox, TypeErasedError}; use std::fmt; use super::SmithyErrorClassifier; @@ -178,7 +177,7 @@ mod test { .body("error!") .unwrap() .map(SdkBody::from); - let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.set_response(res); assert_eq!( policy.classify_retry(&ctx), @@ -194,7 +193,7 @@ mod test { .body("error!") .unwrap() .map(SdkBody::from); - let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.set_response(res); assert_eq!(policy.classify_retry(&ctx), None); } @@ -224,8 +223,8 @@ mod test { impl std::error::Error for RetryableError {} let policy = ModeledAsRetryableClassifier::::new(); - let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); - ctx.set_output_or_error(Err(OrchestratorError::operation(TypeErasedError::new( + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); + ctx.set_output_or_error(Err(OrchestratorError::operation(Error::erase( RetryableError, )))); @@ -238,7 +237,7 @@ mod test { #[test] fn classify_response_error() { let policy = SmithyErrorClassifier::::new(); - let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.set_output_or_error(Err(OrchestratorError::response( "I am a response error".into(), ))); @@ -251,7 +250,7 @@ mod test { #[test] fn test_timeout_error() { let policy = SmithyErrorClassifier::::new(); - let mut ctx = InterceptorContext::new(TypeErasedBox::new("doesntmatter")); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.set_output_or_error(Err(OrchestratorError::timeout( "I am a timeout error".into(), ))); diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index 160fe71ca6..915f8fad0f 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -317,21 +317,21 @@ mod tests { use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::config_bag::Layer; use aws_smithy_types::retry::{ErrorKind, ProvideErrorKind}; - use aws_smithy_types::type_erasure::TypeErasedBox; use std::fmt; use std::sync::Mutex; use std::time::Duration; #[cfg(feature = "test-util")] use crate::client::retries::token_bucket::TokenBucket; + use aws_smithy_runtime_api::client::interceptors::context::{Input, Output}; #[test] fn no_retry_necessary_for_ok_result() { let cfg = ConfigBag::base(); let rc = RuntimeComponentsBuilder::for_tests().build().unwrap(); - let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); let strategy = StandardRetryStrategy::default(); - ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); + ctx.set_output_or_error(Ok(Output::doesnt_matter())); let actual = strategy .should_attempt_retry(&ctx, &rc, &cfg) .expect("method is infallible for this use"); @@ -342,7 +342,7 @@ mod tests { error_kind: ErrorKind, current_request_attempts: u32, ) -> (InterceptorContext, RuntimeComponents, ConfigBag) { - let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); ctx.set_output_or_error(Err(OrchestratorError::other("doesn't matter"))); let rc = RuntimeComponentsBuilder::for_tests() .with_retry_classifiers(Some( @@ -469,7 +469,7 @@ mod tests { .build() .unwrap(); let cfg = ConfigBag::base(); - let mut ctx = InterceptorContext::new(TypeErasedBox::doesnt_matter()); + let mut ctx = InterceptorContext::new(Input::doesnt_matter()); // This type doesn't matter b/c the classifier will just return whatever we tell it to. ctx.set_output_or_error(Err(OrchestratorError::other("doesn't matter"))); @@ -498,7 +498,7 @@ mod tests { assert_eq!(dur, Duration::from_secs(2)); assert_eq!(token_bucket.available_permits(), 490); - ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); + ctx.set_output_or_error(Ok(Output::doesnt_matter())); cfg.interceptor_state().store_put(RequestAttempts::new(3)); let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); @@ -581,7 +581,7 @@ mod tests { assert_eq!(dur, Duration::from_secs(1)); assert_eq!(token_bucket.available_permits(), 90); - ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); + ctx.set_output_or_error(Ok(Output::doesnt_matter())); cfg.interceptor_state().store_put(RequestAttempts::new(3)); let no_retry = strategy.should_attempt_retry(&ctx, &rc, &cfg).unwrap(); @@ -623,7 +623,7 @@ mod tests { let permit = strategy.retry_permit.lock().unwrap().take().unwrap(); permit.forget(); - ctx.set_output_or_error(Ok(TypeErasedBox::doesnt_matter())); + ctx.set_output_or_error(Ok(Output::doesnt_matter())); // Replenish permits until we get back to `PERMIT_COUNT` while token_bucket.available_permits() < PERMIT_COUNT { diff --git a/rust-runtime/aws-smithy-types/src/type_erasure.rs b/rust-runtime/aws-smithy-types/src/type_erasure.rs index b4acdbb72d..86533cb9f3 100644 --- a/rust-runtime/aws-smithy-types/src/type_erasure.rs +++ b/rust-runtime/aws-smithy-types/src/type_erasure.rs @@ -6,125 +6,34 @@ use std::any::Any; use std::error::Error as StdError; use std::fmt; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; use std::sync::Arc; -/// A [`TypeErasedBox`] with type information tracked via generics at compile-time -/// -/// `TypedBox` is used to transition to/from a `TypeErasedBox`. A `TypedBox` can only -/// be created from a `T` or from a `TypeErasedBox` value that _is a_ `T`. Therefore, it can -/// be assumed to be a `T` even though the underlying storage is still a `TypeErasedBox`. -/// Since the `T` is only used in `PhantomData`, it gets compiled down to just a `TypeErasedBox`. +/// Abstraction over `Box` that provides `Debug` and optionally `Clone`. /// /// The orchestrator uses `TypeErasedBox` to avoid the complication of six or more generic parameters -/// and to avoid the monomorphization that brings with it. This `TypedBox` will primarily be useful -/// for operation-specific or service-specific interceptors that need to operate on the actual -/// input/output/error types. -pub struct TypedBox { - inner: TypeErasedBox, - _phantom: PhantomData, -} - -impl TypedBox -where - T: fmt::Debug + Send + Sync + 'static, -{ - /// Creates a new `TypedBox` from `inner` of type `T` - pub fn new(inner: T) -> Self { - Self { - inner: TypeErasedBox::new(inner), - _phantom: Default::default(), - } - } - - /// Tries to create a `TypedBox` from a `TypeErasedBox`. - /// - /// If the `TypedBox` can't be created due to the `TypeErasedBox`'s value consisting - /// of another type, then the original `TypeErasedBox` will be returned in the `Err` variant. - pub fn assume_from(type_erased: TypeErasedBox) -> Result, TypeErasedBox> { - if type_erased.downcast_ref::().is_some() { - Ok(TypedBox { - inner: type_erased, - _phantom: Default::default(), - }) - } else { - Err(type_erased) - } - } - - /// Converts the `TypedBox` back into `T`. - pub fn unwrap(self) -> T { - *self.inner.downcast::().expect("type checked") - } - - /// Converts the `TypedBox` into a `TypeErasedBox`. - pub fn erase(self) -> TypeErasedBox { - self.inner - } -} - -impl TypedBox -where - T: StdError + fmt::Debug + Send + Sync + 'static, -{ - /// Converts `TypedBox` to a `TypeErasedError` where `T` implements `Error`. - pub fn erase_error(self) -> TypeErasedError { - TypeErasedError::new(self.unwrap()) - } -} - -impl fmt::Debug for TypedBox -where - T: Send + Sync + 'static, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("TypedBox:")?; - (self.inner.debug)(&self.inner.field, f) - } -} - -impl Deref for TypedBox { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.inner.downcast_ref().expect("type checked") - } -} - -impl DerefMut for TypedBox { - fn deref_mut(&mut self) -> &mut Self::Target { - self.inner.downcast_mut().expect("type checked") - } -} - -/// Abstraction over `Box` that provides `Debug` and optionally `Clone`. +/// and to avoid the monomorphization that brings with it. /// /// # Examples /// /// Creating a box: /// ```no_run -/// use aws_smithy_types::type_erasure::{TypedBox, TypeErasedBox}; +/// use aws_smithy_types::type_erasure::TypeErasedBox; /// -/// // Creating it directly: /// let boxed = TypeErasedBox::new("some value"); -/// -/// // Creating it from a `TypedBox`: -/// let boxed = TypedBox::new("some value").erase(); /// ``` /// /// Downcasting a box: /// ```no_run -/// # use aws_smithy_types::type_erasure::{TypedBox, TypeErasedBox}; -/// # let boxed = TypedBox::new("some value".to_string()).erase(); +/// # use aws_smithy_types::type_erasure::TypeErasedBox; +/// # let boxed = TypeErasedBox::new("some value".to_string()); /// let value: Option<&String> = boxed.downcast_ref::(); /// ``` /// /// Converting a box back into its value: /// ``` -/// # use aws_smithy_types::type_erasure::{TypedBox, TypeErasedBox}; -/// let boxed = TypedBox::new("some value".to_string()).erase(); -/// let value: String = TypedBox::::assume_from(boxed).expect("it is a string").unwrap(); +/// # use aws_smithy_types::type_erasure::TypeErasedBox; +/// let boxed = TypeErasedBox::new("some value".to_string()); +/// let value: String = *boxed.downcast::().expect("it is a string"); /// ``` pub struct TypeErasedBox { field: Box, @@ -302,11 +211,25 @@ impl TypeErasedError { ) -> Option<&mut T> { self.field.downcast_mut() } + + /// Returns a `TypeErasedError` with a fake/test value with the expectation that it won't be downcast in the test. + #[cfg(feature = "test-util")] + pub fn doesnt_matter() -> Self { + #[derive(Debug)] + struct FakeError; + impl fmt::Display for FakeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "FakeError") + } + } + impl StdError for FakeError {} + Self::new(FakeError) + } } #[cfg(test)] mod tests { - use super::{TypeErasedBox, TypeErasedError, TypedBox}; + use super::{TypeErasedBox, TypeErasedError}; use std::fmt; #[derive(Debug)] @@ -314,45 +237,6 @@ mod tests { #[derive(Debug)] struct Bar(isize); - #[test] - fn test_typed_boxes() { - let foo = TypedBox::new(Foo("1")); - let bar = TypedBox::new(Bar(2)); - - assert_eq!("TypedBox:Foo(\"1\")", format!("{foo:?}")); - assert_eq!("TypedBox:Bar(2)", format!("{bar:?}")); - - let mut foo_erased = foo.erase(); - foo_erased - .downcast_mut::() - .expect("I know its a Foo") - .0 = "3"; - - let bar_erased = bar.erase(); - assert_eq!( - "TypeErasedBox[!Clone]:Foo(\"3\")", - format!("{foo_erased:?}") - ); - assert_eq!("TypeErasedBox[!Clone]:Bar(2)", format!("{bar_erased:?}")); - - let bar_erased = TypedBox::::assume_from(bar_erased).expect_err("it's not a Foo"); - let mut bar = TypedBox::::assume_from(bar_erased).expect("it's a Bar"); - assert_eq!(2, bar.0); - bar.0 += 1; - - let bar = bar.unwrap(); - assert_eq!(3, bar.0); - - assert!(foo_erased.downcast_ref::().is_none()); - assert!(foo_erased.downcast_mut::().is_none()); - let mut foo_erased = foo_erased.downcast::().expect_err("it's not a Bar"); - - assert_eq!("3", foo_erased.downcast_ref::().expect("it's a Foo").0); - foo_erased.downcast_mut::().expect("it's a Foo").0 = "4"; - let foo = *foo_erased.downcast::().expect("it's a Foo"); - assert_eq!("4", foo.0); - } - #[derive(Debug, Clone, PartialEq, Eq)] struct TestErr { inner: &'static str, @@ -385,10 +269,10 @@ mod tests { #[test] fn test_typed_erased_errors_can_be_unwrapped() { let test_err = TestErr::new("something failed!"); - let type_erased_test_err = TypedBox::new(test_err.clone()).erase_error(); - let actual = TypedBox::::assume_from(type_erased_test_err.into()) - .expect("type erased error can be downcast into original type") - .unwrap(); + let type_erased_test_err = TypeErasedError::new(test_err.clone()); + let actual = *type_erased_test_err + .downcast::() + .expect("type erased error can be downcast into original type"); assert_eq!(test_err, actual); } diff --git a/tools/ci-scripts/codegen-diff/semver-checks.py b/tools/ci-scripts/codegen-diff/semver-checks.py index 3210e564e9..24f6785c81 100755 --- a/tools/ci-scripts/codegen-diff/semver-checks.py +++ b/tools/ci-scripts/codegen-diff/semver-checks.py @@ -34,12 +34,8 @@ def main(skip_generation=False): os.chdir(sdk_directory) failed = False - # TODO(enableNewSmithyRuntimeLaunch): Remove the deny list below deny_list = [ - "aws-runtime", - "aws-runtime-api", - "aws-smithy-runtime", - "aws-smithy-runtime-api", + # add crate names here to exclude them from the semver checks ] for path in os.listdir(): eprint(f'checking {path}...', end='') From e4099600e5c44189dad64409b3b782ed2ff726f7 Mon Sep 17 00:00:00 2001 From: david-perez Date: Thu, 27 Jul 2023 22:35:45 +0200 Subject: [PATCH 238/253] Update `smithy-rs-maintainers.txt` (#2874) ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: Russell Cohen --- tools/ci-build/changelogger/smithy-rs-maintainers.txt | 8 -------- tools/ci-build/changelogger/tests/e2e_test.rs | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/tools/ci-build/changelogger/smithy-rs-maintainers.txt b/tools/ci-build/changelogger/smithy-rs-maintainers.txt index 4d980016b9..f5732607b1 100644 --- a/tools/ci-build/changelogger/smithy-rs-maintainers.txt +++ b/tools/ci-build/changelogger/smithy-rs-maintainers.txt @@ -1,15 +1,7 @@ 82marbag aws-sdk-rust-ci -crisidev david-perez -drganjoo -hlbarber jdisanti -jjant -LukeMathWalker -pose rcoh -unexge velfi -weihanglo ysaito1001 diff --git a/tools/ci-build/changelogger/tests/e2e_test.rs b/tools/ci-build/changelogger/tests/e2e_test.rs index 745e73ef47..7f1a292df8 100644 --- a/tools/ci-build/changelogger/tests/e2e_test.rs +++ b/tools/ci-build/changelogger/tests/e2e_test.rs @@ -467,7 +467,7 @@ author = "rcoh" message = "Fourth change - client" references = ["smithy-rs#4"] meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } -author = "LukeMathWalker" +author = "rcoh" "#; let tmp_dir = TempDir::new().unwrap(); let source_path = tmp_dir.path().join("source.toml"); From b62756ca155dc73cb67d50be561b80ad50d1d48e Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 27 Jul 2023 15:06:54 -0700 Subject: [PATCH 239/253] Add missing docs to `aws-smithy-runtime` and move the clock skew interceptor (#2871) This PR adds missing documentation to the `aws-smith-runtime` crate, and moves the `ServiceClockSkewInterceptor` into the `aws-runtime` crate. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- aws/rust-runtime/aws-inlineable/Cargo.toml | 4 +- aws/rust-runtime/aws-runtime/Cargo.toml | 1 - aws/rust-runtime/aws-runtime/src/lib.rs | 3 ++ .../aws-runtime/src/request_info.rs | 2 +- .../aws-runtime/src}/service_clock_skew.rs | 11 +++-- .../RetryInformationHeaderDecorator.kt | 4 +- rust-runtime/aws-smithy-runtime/src/client.rs | 1 + .../src/client/auth/http.rs | 4 ++ .../src/client/auth/no_auth.rs | 9 ++++ .../src/client/config_override.rs | 11 ++++- .../src/client/connectors.rs | 8 ++++ .../client/connectors/connection_poisoning.rs | 9 +++- .../src/client/connectors/test_util.rs | 41 +++++++++++++------ .../aws-smithy-runtime/src/client/identity.rs | 1 + .../src/client/identity/no_auth.rs | 4 ++ .../src/client/interceptors.rs | 4 ++ .../src/client/orchestrator.rs | 16 +++++++- .../src/client/orchestrator/endpoints.rs | 14 ++++++- .../src/client/orchestrator/interceptors.rs | 8 ---- .../aws-smithy-runtime/src/client/retries.rs | 6 ++- .../src/client/retries/classifier.rs | 1 + .../src/client/retries/client_rate_limiter.rs | 5 ++- .../src/client/retries/strategy.rs | 9 ++-- .../client/retries/strategy/fixed_delay.rs | 6 ++- .../src/client/retries/strategy/never.rs | 4 +- .../src/client/retries/strategy/standard.rs | 7 +++- .../src/client/retries/token_bucket.rs | 5 ++- .../src/client/test_util.rs | 3 ++ .../src/client/test_util/deserializer.rs | 4 +- .../src/client/test_util/serializer.rs | 5 ++- rust-runtime/aws-smithy-runtime/src/lib.rs | 3 +- .../src/static_partition_map.rs | 8 +++- .../src/test_util/capture_test_logs.rs | 17 +++++--- 33 files changed, 176 insertions(+), 62 deletions(-) rename {rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors => aws/rust-runtime/aws-runtime/src}/service_clock_skew.rs (91%) delete mode 100644 rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs diff --git a/aws/rust-runtime/aws-inlineable/Cargo.toml b/aws/rust-runtime/aws-inlineable/Cargo.toml index 4a6630ea6f..0b8141b092 100644 --- a/aws/rust-runtime/aws-inlineable/Cargo.toml +++ b/aws/rust-runtime/aws-inlineable/Cargo.toml @@ -22,8 +22,8 @@ aws-smithy-checksums = { path = "../../../rust-runtime/aws-smithy-checksums" } aws-smithy-client = { path = "../../../rust-runtime/aws-smithy-client" } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } aws-smithy-http-tower = { path = "../../../rust-runtime/aws-smithy-http-tower" } -aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api" } -aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime" } +aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["client"] } +aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime", features = ["client"] } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-types = { path = "../aws-types" } diff --git a/aws/rust-runtime/aws-runtime/Cargo.toml b/aws/rust-runtime/aws-runtime/Cargo.toml index 9600d1f0a9..fddff0c9fe 100644 --- a/aws/rust-runtime/aws-runtime/Cargo.toml +++ b/aws/rust-runtime/aws-runtime/Cargo.toml @@ -18,7 +18,6 @@ aws-sigv4 = { path = "../aws-sigv4" } aws-smithy-async = { path = "../../../rust-runtime/aws-smithy-async" } aws-smithy-eventstream = { path = "../../../rust-runtime/aws-smithy-eventstream", optional = true } aws-smithy-http = { path = "../../../rust-runtime/aws-smithy-http" } -aws-smithy-runtime = { path = "../../../rust-runtime/aws-smithy-runtime", features = ["client"] } aws-smithy-runtime-api = { path = "../../../rust-runtime/aws-smithy-runtime-api", features = ["client"] } aws-smithy-types = { path = "../../../rust-runtime/aws-smithy-types" } aws-types = { path = "../aws-types" } diff --git a/aws/rust-runtime/aws-runtime/src/lib.rs b/aws/rust-runtime/aws-runtime/src/lib.rs index a86f74d056..6ad3cec356 100644 --- a/aws/rust-runtime/aws-runtime/src/lib.rs +++ b/aws/rust-runtime/aws-runtime/src/lib.rs @@ -33,3 +33,6 @@ pub mod invocation_id; /// Supporting code for request metadata headers in the AWS SDK. pub mod request_info; + +/// Interceptor that determines the clock skew between the client and service. +pub mod service_clock_skew; diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index fcea15ef51..ab3311f338 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -use aws_smithy_runtime::client::orchestrator::interceptors::ServiceClockSkew; +use crate::service_clock_skew::ServiceClockSkew; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs b/aws/rust-runtime/aws-runtime/src/service_clock_skew.rs similarity index 91% rename from rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs rename to aws/rust-runtime/aws-runtime/src/service_clock_skew.rs index 16cb5d18b6..7ada75c2bf 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors/service_clock_skew.rs +++ b/aws/rust-runtime/aws-runtime/src/service_clock_skew.rs @@ -12,9 +12,10 @@ use aws_smithy_types::date_time::Format; use aws_smithy_types::DateTime; use std::time::{Duration, SystemTime}; +/// Amount of clock skew between the client and the service. #[derive(Debug, Clone)] #[non_exhaustive] -pub struct ServiceClockSkew { +pub(crate) struct ServiceClockSkew { inner: Duration, } @@ -22,10 +23,6 @@ impl ServiceClockSkew { fn new(inner: Duration) -> Self { Self { inner } } - - pub fn skew(&self) -> Duration { - self.inner - } } impl Storable for ServiceClockSkew { @@ -38,11 +35,13 @@ impl From for Duration { } } +/// Interceptor that determines the clock skew between the client and service. #[derive(Debug, Default)] #[non_exhaustive] -pub struct ServiceClockSkewInterceptor {} +pub struct ServiceClockSkewInterceptor; impl ServiceClockSkewInterceptor { + /// Creates a new `ServiceClockSkewInterceptor`. pub fn new() -> Self { Self::default() } diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt index 221c2ec430..e3d9fb2176 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/RetryInformationHeaderDecorator.kt @@ -12,7 +12,6 @@ import software.amazon.smithy.rust.codegen.client.smithy.generators.ServiceRunti import software.amazon.smithy.rust.codegen.core.rustlang.Writable import software.amazon.smithy.rust.codegen.core.rustlang.rust import software.amazon.smithy.rust.codegen.core.rustlang.writable -import software.amazon.smithy.rust.codegen.core.smithy.RuntimeType import software.amazon.smithy.rust.codegen.core.util.letIf class RetryInformationHeaderDecorator : ClientCodegenDecorator { @@ -31,7 +30,6 @@ class RetryInformationHeaderDecorator : ClientCodegenDecorator { private class AddRetryInformationHeaderInterceptors(codegenContext: ClientCodegenContext) : ServiceRuntimePluginCustomization() { private val runtimeConfig = codegenContext.runtimeConfig - private val smithyRuntime = RuntimeType.smithyRuntime(runtimeConfig) private val awsRuntime = AwsRuntimeType.awsRuntime(runtimeConfig) override fun section(section: ServiceRuntimePluginSection): Writable = writable { @@ -40,7 +38,7 @@ private class AddRetryInformationHeaderInterceptors(codegenContext: ClientCodege section.registerInterceptor(runtimeConfig, this) { rust( "#T::new()", - smithyRuntime.resolve("client::orchestrator::interceptors::ServiceClockSkewInterceptor"), + awsRuntime.resolve("service_clock_skew::ServiceClockSkewInterceptor"), ) } diff --git a/rust-runtime/aws-smithy-runtime/src/client.rs b/rust-runtime/aws-smithy-runtime/src/client.rs index 31722c94d4..629b558c32 100644 --- a/rust-runtime/aws-smithy-runtime/src/client.rs +++ b/rust-runtime/aws-smithy-runtime/src/client.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +/// Smithy auth scheme implementations. pub mod auth; /// Smithy code related to connectors and connections. diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs index 8593f0d9a5..1e0efcd1c4 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/http.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +//! Auth scheme implementations for HTTP API Key, Basic Auth, Bearer Token, and Digest auth. + use aws_smithy_http::query_writer::QueryWriter; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::auth::http::{ @@ -24,7 +26,9 @@ use http::HeaderValue; /// Destination for the API key #[derive(Copy, Clone, Debug)] pub enum ApiKeyLocation { + /// Place the API key in the URL query parameters Query, + /// Place the API key in the request headers Header, } diff --git a/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs index 115f78b8e8..ebda0f6943 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/auth/no_auth.rs @@ -19,6 +19,7 @@ use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; use aws_smithy_types::config_bag::ConfigBag; use std::borrow::Cow; +/// Auth scheme ID for "no auth". pub const NO_AUTH_SCHEME_ID: AuthSchemeId = AuthSchemeId::new("no_auth"); /// A [`RuntimePlugin`] that registers a "no auth" identity resolver and auth scheme. @@ -36,6 +37,7 @@ impl Default for NoAuthRuntimePlugin { } impl NoAuthRuntimePlugin { + /// Creates a new `NoAuthRuntimePlugin`. pub fn new() -> Self { Self( RuntimeComponentsBuilder::new("NoAuthRuntimePlugin") @@ -54,12 +56,19 @@ impl RuntimePlugin for NoAuthRuntimePlugin { } } +/// The "no auth" auth scheme. +/// +/// The orchestrator requires an auth scheme, so Smithy's `@optionalAuth` trait is implemented +/// by placing a "no auth" auth scheme at the end of the auth scheme options list so that it is +/// used if there's no identity resolver available for the other auth schemes. It's also used +/// for models that don't have auth at all. #[derive(Debug, Default)] pub struct NoAuthScheme { signer: NoAuthSigner, } impl NoAuthScheme { + /// Creates a new `NoAuthScheme`. pub fn new() -> Self { Self::default() } diff --git a/rust-runtime/aws-smithy-runtime/src/client/config_override.rs b/rust-runtime/aws-smithy-runtime/src/client/config_override.rs index c69770a974..07886a9526 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/config_override.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/config_override.rs @@ -10,11 +10,13 @@ use aws_smithy_types::config_bag::{ }; macro_rules! component { - ($typ:ty, $accessor:ident, $latest_accessor:ident) => { + ($typ:ty, $accessor:ident, $latest_accessor:ident, $doc:tt) => { + #[doc = $doc] pub fn $accessor(&self) -> Option<$typ> { fallback_component!(self, $typ, $accessor) } + #[doc = $doc] pub fn $latest_accessor(&self) -> Option<$typ> { latest_component!(self, $typ, $accessor) } @@ -162,7 +164,12 @@ impl<'a> Resolver<'a> { } // Add additional component methods as needed - component!(SharedAsyncSleep, sleep_impl, latest_sleep_impl); + component!( + SharedAsyncSleep, + sleep_impl, + latest_sleep_impl, + "The async sleep implementation." + ); fn config(&self) -> &Layer { match &self.inner { diff --git a/rust-runtime/aws-smithy-runtime/src/client/connectors.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors.rs index 445425680b..75da05271e 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connectors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors.rs @@ -3,7 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +/// Interceptor for connection poisoning. pub mod connection_poisoning; + #[cfg(feature = "test-util")] pub mod test_util; @@ -17,6 +19,11 @@ pub mod adapter { use aws_smithy_runtime_api::client::orchestrator::{BoxFuture, HttpRequest, HttpResponse}; use std::sync::{Arc, Mutex}; + /// Adapts a [`DynConnector`] to the [`HttpConnector`] trait. + /// + /// This is a temporary adapter that allows the old-style tower-based connectors to + /// work with the new non-tower based architecture of the generated clients. + /// It will be removed in a future release. #[derive(Debug)] pub struct DynConnectorAdapter { // `DynConnector` requires `&mut self`, so we need interior mutability to adapt to it @@ -24,6 +31,7 @@ pub mod adapter { } impl DynConnectorAdapter { + /// Creates a new `DynConnectorAdapter`. pub fn new(dyn_connector: DynConnector) -> Self { Self { dyn_connector: Arc::new(Mutex::new(dyn_connector)), diff --git a/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs index 7c1517550d..5f6f4e7862 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors/connection_poisoning.rs @@ -34,6 +34,7 @@ use tracing::{debug, error}; pub struct ConnectionPoisoningInterceptor {} impl ConnectionPoisoningInterceptor { + /// Create a new `ConnectionPoisoningInterceptor`. pub fn new() -> Self { Self::default() } @@ -99,28 +100,32 @@ impl Interceptor for ConnectionPoisoningInterceptor { } } -// TODO(enableNewSmithyRuntimeLaunch) We won't need this once we absorb aws_smithy_http into the -// new runtime crate. +// TODO(enableNewSmithyRuntimeCleanup): A storable wrapper won't be needed anymore once we absorb aws_smithy_http into the new runtime crate. +/// A wrapper around CaptureSmithyConnection that implements `Storable` so that it can be added to the `ConfigBag`. #[derive(Clone, Default)] pub struct CaptureSmithyConnectionWrapper { inner: CaptureSmithyConnection, } impl CaptureSmithyConnectionWrapper { + /// Creates a new `CaptureSmithyConnectionWrapper`. pub fn new() -> Self { Self { inner: CaptureSmithyConnection::new(), } } + /// Returns a reference to the inner `CaptureSmithyConnection`. pub fn clone_inner(&self) -> CaptureSmithyConnection { self.inner.clone() } + /// Returns the captured connection metadata, if any. pub fn get(&self) -> Option { self.inner.get() } + /// Sets the connection retriever function. pub fn set_connection_retriever(&self, f: F) where F: Fn() -> Option + Send + Sync + 'static, diff --git a/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs b/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs index 457b09888d..c7afd868d4 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/connectors/test_util.rs @@ -94,6 +94,11 @@ pub fn capture_request( type ConnectionEvents = Vec; +/// Test data for the [`TestConnector`]. +/// +/// Each `ConnectionEvent` represents one HTTP request and response +/// through the connector. Optionally, a latency value can be set to simulate +/// network latency (done via async sleep in the `TestConnector`). #[derive(Debug)] pub struct ConnectionEvent { latency: Duration, @@ -102,6 +107,7 @@ pub struct ConnectionEvent { } impl ConnectionEvent { + /// Creates a new `ConnectionEvent`. pub fn new(req: HttpRequest, res: HttpResponse) -> Self { Self { res, @@ -116,11 +122,13 @@ impl ConnectionEvent { self } - pub fn req(&self) -> &HttpRequest { + /// Returns the test request. + pub fn request(&self) -> &HttpRequest { &self.req } - pub fn res(&self) -> &HttpResponse { + /// Returns the test response. + pub fn response(&self) -> &HttpResponse { &self.res } } @@ -132,13 +140,13 @@ impl From<(HttpRequest, HttpResponse)> for ConnectionEvent { } #[derive(Debug)] -pub struct ValidateRequest { - pub expected: HttpRequest, - pub actual: HttpRequest, +struct ValidateRequest { + expected: HttpRequest, + actual: HttpRequest, } impl ValidateRequest { - pub fn assert_matches(&self, index: usize, ignore_headers: &[HeaderName]) { + fn assert_matches(&self, index: usize, ignore_headers: &[HeaderName]) { let (actual, expected) = (&self.actual, &self.expected); assert_eq!( actual.uri(), @@ -181,32 +189,41 @@ impl ValidateRequest { } } -/// TestConnection for use as a [`HttpConnector`]. +/// Test connector for use as a [`HttpConnector`]. /// /// A basic test connection. It will: /// - Respond to requests with a preloaded series of responses /// - Record requests for future examination #[derive(Debug, Clone)] -pub struct TestConnection { +pub struct TestConnector { data: Arc>, requests: Arc>>, sleep_impl: SharedAsyncSleep, } -impl TestConnection { +impl TestConnector { + /// Creates a new test connector. pub fn new(mut data: ConnectionEvents, sleep_impl: impl Into) -> Self { data.reverse(); - TestConnection { + TestConnector { data: Arc::new(Mutex::new(data)), requests: Default::default(), sleep_impl: sleep_impl.into(), } } - pub fn requests(&self) -> impl Deref> + '_ { + fn requests(&self) -> impl Deref> + '_ { self.requests.lock().unwrap() } + /// Asserts the expected requests match the actual requests. + /// + /// The expected requests are given as the connection events when the `TestConnector` + /// is created. The `TestConnector` will record the actual requests and assert that + /// they match the expected requests. + /// + /// A list of headers that should be ignored when comparing requests can be passed + /// for cases where headers are non-deterministic or are irrelevant to the test. #[track_caller] pub fn assert_requests_match(&self, ignore_headers: &[HeaderName]) { for (i, req) in self.requests().iter().enumerate() { @@ -222,7 +239,7 @@ impl TestConnection { } } -impl HttpConnector for TestConnection { +impl HttpConnector for TestConnector { fn call(&self, request: HttpRequest) -> BoxFuture { let (res, simulated_latency) = if let Some(event) = self.data.lock().unwrap().pop() { self.requests.lock().unwrap().push(ValidateRequest { diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity.rs b/rust-runtime/aws-smithy-runtime/src/client/identity.rs index a8b8769057..7aaef9c3ca 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/identity.rs @@ -3,4 +3,5 @@ * SPDX-License-Identifier: Apache-2.0 */ +/// Identity resolver implementation for "no auth". pub mod no_auth; diff --git a/rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs b/rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs index 8814336b3a..01121eab90 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/identity/no_auth.rs @@ -7,19 +7,23 @@ use aws_smithy_runtime_api::client::identity::{Identity, IdentityResolver}; use aws_smithy_runtime_api::client::orchestrator::Future; use aws_smithy_types::config_bag::ConfigBag; +/// Identity for the [`NoAuthScheme`](crate::client::auth::no_auth::NoAuthScheme) auth scheme. #[derive(Debug, Default)] pub struct NoAuthIdentity; impl NoAuthIdentity { + /// Creates a new `NoAuthIdentity`. pub fn new() -> Self { Self } } +/// Identity resolver for the [`NoAuthScheme`](crate::client::auth::no_auth::NoAuthScheme) auth scheme. #[derive(Debug, Default)] pub struct NoAuthIdentityResolver; impl NoAuthIdentityResolver { + /// Creates a new `NoAuthIdentityResolver`. pub fn new() -> Self { Self } diff --git a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs index a6a73c2875..c9dd074605 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/interceptors.rs @@ -280,6 +280,7 @@ impl ConditionallyEnabledInterceptor { } } +/// Interceptor that maps the request with a given function. pub struct MapRequestInterceptor { f: F, _phantom: PhantomData, @@ -292,6 +293,7 @@ impl fmt::Debug for MapRequestInterceptor { } impl MapRequestInterceptor { + /// Creates a new `MapRequestInterceptor`. pub fn new(f: F) -> Self { Self { f, @@ -324,6 +326,7 @@ where } } +/// Interceptor that mutates the request with a given function. pub struct MutateRequestInterceptor { f: F, } @@ -335,6 +338,7 @@ impl fmt::Debug for MutateRequestInterceptor { } impl MutateRequestInterceptor { + /// Creates a new `MutateRequestInterceptor`. pub fn new(f: F) -> Self { Self { f } } diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index 404ffdae5a..b86953dded 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -37,7 +37,6 @@ mod auth; /// Defines types that implement a trait for endpoint resolution pub mod endpoints; mod http; -pub mod interceptors; macro_rules! halt { ([$ctx:ident] => $err:expr) => {{ @@ -83,6 +82,15 @@ macro_rules! run_interceptors { }; } +/// Orchestrates the execution of a request and handling of a response. +/// +/// The given `runtime_plugins` will be used to generate a `ConfigBag` for this request, +/// and then the given `input` will be serialized and transmitted. When a response is +/// received, it will be deserialized and returned. +/// +/// This orchestration handles retries, endpoint resolution, identity resolution, and signing. +/// Each of these are configurable via the config and runtime components given by the runtime +/// plugins. pub async fn invoke( service_name: &str, operation_name: &str, @@ -111,6 +119,12 @@ pub enum StopPoint { BeforeTransmit, } +/// Same as [`invoke`], but allows for returning early at different points during orchestration. +/// +/// Orchestration will cease at the point specified by `stop_point`. This is useful for orchestrations +/// that don't need to actually transmit requests, such as for generating presigned requests. +/// +/// See the docs on [`invoke`] for more details. pub async fn invoke_with_stop_point( service_name: &str, operation_name: &str, diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs index 4f170ec641..76ce7f4037 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/endpoints.rs @@ -21,12 +21,14 @@ use std::fmt::Debug; use std::str::FromStr; use tracing::trace; -#[derive(Debug, Clone)] +/// An endpoint resolver that uses a static URI. +#[derive(Clone, Debug)] pub struct StaticUriEndpointResolver { endpoint: Uri, } impl StaticUriEndpointResolver { + /// Create a resolver that resolves to `http://localhost:{port}`. pub fn http_localhost(port: u16) -> Self { Self { endpoint: Uri::from_str(&format!("http://localhost:{port}")) @@ -34,6 +36,7 @@ impl StaticUriEndpointResolver { } } + /// Create a resolver that resolves to the given URI. pub fn uri(endpoint: Uri) -> Self { Self { endpoint } } @@ -64,7 +67,13 @@ impl From for EndpointResolverParams { } } -#[derive(Debug, Clone)] +/// Default implementation of [`EndpointResolver`]. +/// +/// This default endpoint resolver implements the `EndpointResolver` trait by +/// converting the type-erased [`EndpointResolverParams`] into the concrete +/// endpoint params for the service. It then delegates endpoint resolution +/// to an underlying resolver that is aware of the concrete type. +#[derive(Clone, Debug)] pub struct DefaultEndpointResolver { inner: SharedEndpointResolver, } @@ -77,6 +86,7 @@ where } impl DefaultEndpointResolver { + /// Creates a new `DefaultEndpointResolver`. pub fn new(resolve_endpoint: SharedEndpointResolver) -> Self { Self { inner: resolve_endpoint, diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs deleted file mode 100644 index b9de2daa37..0000000000 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator/interceptors.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -mod service_clock_skew; - -pub use service_clock_skew::{ServiceClockSkew, ServiceClockSkewInterceptor}; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries.rs b/rust-runtime/aws-smithy-runtime/src/client/retries.rs index 893c5f0163..8ea71ebb5e 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries.rs @@ -3,15 +3,19 @@ * SPDX-License-Identifier: Apache-2.0 */ +/// Smithy retry classifiers. pub mod classifier; + +/// Smithy retry strategies. pub mod strategy; mod client_rate_limiter; mod token_bucket; use aws_smithy_types::config_bag::{Storable, StoreReplace}; -pub use client_rate_limiter::{ClientRateLimiter, ClientRateLimiterRuntimePlugin}; use std::fmt; + +pub use client_rate_limiter::{ClientRateLimiter, ClientRateLimiterRuntimePlugin}; pub use token_bucket::{TokenBucket, TokenBucketRuntimePlugin}; #[doc(hidden)] diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs index f22c765136..254cb636d5 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/classifier.rs @@ -50,6 +50,7 @@ where } } +/// Classifies response, timeout, and connector errors as retryable or not. #[derive(Debug, Default)] pub struct SmithyErrorClassifier { _inner: PhantomData, diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs index 5453cad271..661f0f854c 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/client_rate_limiter.rs @@ -16,7 +16,7 @@ use std::sync::{Arc, Mutex}; use std::time::Duration; use tracing::debug; -/// A [RuntimePlugin] to provide a client rate limiter, usable by a retry strategy. +/// A [`RuntimePlugin`] to provide a client rate limiter, usable by a retry strategy. #[non_exhaustive] #[derive(Debug)] pub struct ClientRateLimiterRuntimePlugin { @@ -24,6 +24,7 @@ pub struct ClientRateLimiterRuntimePlugin { } impl ClientRateLimiterRuntimePlugin { + /// Create a new [`ClientRateLimiterRuntimePlugin`]. pub fn new(seconds_since_unix_epoch: f64) -> Self { Self { rate_limiter: ClientRateLimiter::new(seconds_since_unix_epoch), @@ -65,6 +66,7 @@ const BETA: f64 = 0.7; /// Controls how aggressively we scale up after being throttled const SCALE_CONSTANT: f64 = 0.4; +/// Rate limiter for adaptive retry. #[derive(Clone, Debug)] pub struct ClientRateLimiter { inner: Arc>, @@ -107,6 +109,7 @@ impl Storable for ClientRateLimiter { } impl ClientRateLimiter { + /// Creates a new [`ClientRateLimiter`]. pub fn new(seconds_since_unix_epoch: f64) -> Self { Self::builder() .tokens_retrieved_per_second(MIN_FILL_RATE) diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs index c046a4d9f7..c441aa4816 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy.rs @@ -3,12 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -#[cfg(feature = "test-util")] -mod fixed_delay; mod never; pub(crate) mod standard; -#[cfg(feature = "test-util")] -pub use fixed_delay::FixedDelayRetryStrategy; pub use never::NeverRetryStrategy; pub use standard::StandardRetryStrategy; + +#[cfg(feature = "test-util")] +mod fixed_delay; +#[cfg(feature = "test-util")] +pub use fixed_delay::FixedDelayRetryStrategy; diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs index c6315512a3..886b7a61b2 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/fixed_delay.rs @@ -12,8 +12,8 @@ use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; use std::time::Duration; -// A retry policy used in tests. This relies on an error classifier already present in the config bag. -// If a server response is retryable, it will be retried after a fixed delay. +/// A retry policy used in tests. This relies on an error classifier already present in the config bag. +/// If a server response is retryable, it will be retried after a fixed delay. #[derive(Debug, Clone)] pub struct FixedDelayRetryStrategy { fixed_delay: Duration, @@ -21,6 +21,7 @@ pub struct FixedDelayRetryStrategy { } impl FixedDelayRetryStrategy { + /// Create a new retry policy with a fixed delay. pub fn new(fixed_delay: Duration) -> Self { Self { fixed_delay, @@ -28,6 +29,7 @@ impl FixedDelayRetryStrategy { } } + /// Create a new retry policy with a one second delay. pub fn one_second_delay() -> Self { Self::new(Duration::from_secs(1)) } diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs index b59b8fb7f1..7baf4a1426 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/never.rs @@ -9,11 +9,13 @@ use aws_smithy_runtime_api::client::retries::{RetryStrategy, ShouldAttempt}; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::ConfigBag; +/// A retry strategy that never retries. #[non_exhaustive] #[derive(Debug, Clone, Default)] -pub struct NeverRetryStrategy {} +pub struct NeverRetryStrategy; impl NeverRetryStrategy { + /// Creates a new `NeverRetryStrategy`. pub fn new() -> Self { Self::default() } diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs index 915f8fad0f..ee32a6a63d 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/strategy/standard.rs @@ -24,6 +24,7 @@ use tracing::debug; // The initial attempt, plus three retries. const DEFAULT_MAX_ATTEMPTS: u32 = 4; +/// Retry strategy with exponential backoff, max attempts, and a token bucket. #[derive(Debug)] pub struct StandardRetryStrategy { // Retry settings @@ -39,13 +40,13 @@ impl Storable for StandardRetryStrategy { } impl StandardRetryStrategy { + /// Create a new standard retry strategy with the given config. pub fn new(retry_config: &RetryConfig) -> Self { let base = if retry_config.use_static_exponential_base() { || 1.0 } else { fastrand::f64 }; - // TODO(enableNewSmithyRuntimeLaunch) add support for `retry_config.reconnect_mode()` here or in the orchestrator flow. Self::default() .with_base(base) .with_max_backoff(retry_config.max_backoff()) @@ -53,21 +54,25 @@ impl StandardRetryStrategy { .with_initial_backoff(retry_config.initial_backoff()) } + /// Changes the exponential backoff base. pub fn with_base(mut self, base: fn() -> f64) -> Self { self.base = base; self } + /// Changes the max number of attempts. pub fn with_max_attempts(mut self, max_attempts: u32) -> Self { self.max_attempts = max_attempts; self } + /// Changes the initial backoff time. pub fn with_initial_backoff(mut self, initial_backoff: Duration) -> Self { self.initial_backoff = initial_backoff; self } + /// Changes the maximum backoff time. pub fn with_max_backoff(mut self, max_backoff: Duration) -> Self { self.max_backoff = max_backoff; self diff --git a/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs b/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs index a7c95d2a51..686185b1d1 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/retries/token_bucket.rs @@ -11,7 +11,7 @@ use std::sync::Arc; use tokio::sync::{OwnedSemaphorePermit, Semaphore}; use tracing::trace; -/// A [RuntimePlugin] to provide a token bucket, usable by a retry strategy. +/// A [`RuntimePlugin`] to provide a token bucket, usable by a retry strategy. #[non_exhaustive] #[derive(Debug, Default)] pub struct TokenBucketRuntimePlugin { @@ -19,6 +19,7 @@ pub struct TokenBucketRuntimePlugin { } impl TokenBucketRuntimePlugin { + /// Creates a new `TokenBucketRuntimePlugin` with the given initial quota. pub fn new(initial_tokens: usize) -> Self { Self { token_bucket: TokenBucket::new(initial_tokens), @@ -53,6 +54,7 @@ const RETRY_COST: u32 = 5; const RETRY_TIMEOUT_COST: u32 = RETRY_COST * 2; const PERMIT_REGENERATION_AMOUNT: usize = 1; +/// Token bucket used for standard and adaptive retry. #[derive(Clone, Debug)] pub struct TokenBucket { semaphore: Arc, @@ -77,6 +79,7 @@ impl Default for TokenBucket { } impl TokenBucket { + /// Creates a new `TokenBucket` with the given initial quota. pub fn new(initial_quota: usize) -> Self { Self { semaphore: Arc::new(Semaphore::new(initial_quota)), diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs index 30adb4f6cb..1173adc3db 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util.rs @@ -3,5 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +/// Test response deserializer implementations. pub mod deserializer; + +/// Test request serializer implementations. pub mod serializer; diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs index 4b1d43ae79..4e83052d43 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/deserializer.rs @@ -10,19 +10,21 @@ use aws_smithy_runtime_api::client::ser_de::{ResponseDeserializer, SharedRespons use aws_smithy_types::config_bag::{FrozenLayer, Layer}; use std::sync::Mutex; +/// Test response deserializer that always returns the same canned response. #[derive(Default, Debug)] pub struct CannedResponseDeserializer { inner: Mutex>>>, } impl CannedResponseDeserializer { + /// Creates a new `CannedResponseDeserializer` with the given canned response. pub fn new(output: Result>) -> Self { Self { inner: Mutex::new(Some(output)), } } - pub fn take(&self) -> Option>> { + fn take(&self) -> Option>> { match self.inner.lock() { Ok(mut guard) => guard.take(), Err(_) => None, diff --git a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs index ec3eb2dafd..573eea5293 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/test_util/serializer.rs @@ -11,25 +11,28 @@ use aws_smithy_runtime_api::client::ser_de::{RequestSerializer, SharedRequestSer use aws_smithy_types::config_bag::{ConfigBag, FrozenLayer, Layer}; use std::sync::Mutex; +/// Test [`RequestSerializer`] that returns a canned request. #[derive(Default, Debug)] pub struct CannedRequestSerializer { inner: Mutex>>, } impl CannedRequestSerializer { + /// Create a new [`CannedRequestSerializer`] with a successful canned request. pub fn success(request: HttpRequest) -> Self { Self { inner: Mutex::new(Some(Ok(request))), } } + /// Create a new [`CannedRequestSerializer`] with a canned error. pub fn failure(error: BoxError) -> Self { Self { inner: Mutex::new(Some(Err(error))), } } - pub fn take(&self) -> Option> { + fn take(&self) -> Option> { match self.inner.lock() { Ok(mut guard) => guard.take(), Err(_) => None, diff --git a/rust-runtime/aws-smithy-runtime/src/lib.rs b/rust-runtime/aws-smithy-runtime/src/lib.rs index 3afb476bf9..b40610a77c 100644 --- a/rust-runtime/aws-smithy-runtime/src/lib.rs +++ b/rust-runtime/aws-smithy-runtime/src/lib.rs @@ -12,7 +12,7 @@ //! - `test-util`: Enables utilities for unit tests. DO NOT ENABLE IN PRODUCTION. #![warn( - // missing_docs, + missing_docs, rustdoc::missing_crate_level_docs, unreachable_pub, rust_2018_idioms @@ -22,6 +22,7 @@ #[cfg(feature = "client")] pub mod client; +/// A data structure for persisting and sharing state between multiple clients. pub mod static_partition_map; /// General testing utilities. diff --git a/rust-runtime/aws-smithy-runtime/src/static_partition_map.rs b/rust-runtime/aws-smithy-runtime/src/static_partition_map.rs index 10b0070ccf..2f70869f35 100644 --- a/rust-runtime/aws-smithy-runtime/src/static_partition_map.rs +++ b/rust-runtime/aws-smithy-runtime/src/static_partition_map.rs @@ -77,6 +77,7 @@ pub struct StaticPartitionMap { } impl StaticPartitionMap { + /// Creates a new `StaticPartitionMap`. pub const fn new() -> Self { Self { inner: OnceCell::new(), @@ -102,18 +103,20 @@ where K: Eq + Hash, V: Clone, { + /// Gets the value for the given partition key. #[must_use] pub fn get(&self, partition_key: K) -> Option { self.get_or_init_inner().get(&partition_key).cloned() } + /// Gets the value for the given partition key, initializing it with `init` if it doesn't exist. #[must_use] - pub fn get_or_init(&self, partition_key: K, f: F) -> V + pub fn get_or_init(&self, partition_key: K, init: F) -> V where F: FnOnce() -> V, { let mut inner = self.get_or_init_inner(); - let v = inner.entry(partition_key).or_insert_with(f); + let v = inner.entry(partition_key).or_insert_with(init); v.clone() } } @@ -123,6 +126,7 @@ where K: Eq + Hash, V: Clone + Default, { + /// Gets the value for the given partition key, initializing it if it doesn't exist. #[must_use] pub fn get_or_init_default(&self, partition_key: K) -> V { self.get_or_init(partition_key, V::default) diff --git a/rust-runtime/aws-smithy-runtime/src/test_util/capture_test_logs.rs b/rust-runtime/aws-smithy-runtime/src/test_util/capture_test_logs.rs index a25c97c42f..85730d00bc 100644 --- a/rust-runtime/aws-smithy-runtime/src/test_util/capture_test_logs.rs +++ b/rust-runtime/aws-smithy-runtime/src/test_util/capture_test_logs.rs @@ -10,12 +10,6 @@ use tracing::subscriber::DefaultGuard; use tracing::Level; use tracing_subscriber::fmt::TestWriter; -struct Tee { - buf: Arc>>, - quiet: bool, - inner: W, -} - /// A guard that resets log capturing upon being dropped. #[derive(Debug)] pub struct LogCaptureGuard(DefaultGuard); @@ -44,13 +38,24 @@ pub fn capture_test_logs() -> (LogCaptureGuard, Rx) { (LogCaptureGuard(guard), rx) } +/// Receiver for the captured logs. pub struct Rx(Arc>>); impl Rx { + /// Returns the captured logs as a string. + /// + /// # Panics + /// This will panic if the logs are not valid UTF-8. pub fn contents(&self) -> String { String::from_utf8(self.0.lock().unwrap().clone()).unwrap() } } +struct Tee { + buf: Arc>>, + quiet: bool, + inner: W, +} + impl Tee { fn stdout() -> (Self, Rx) { let buf: Arc>> = Default::default(); From 5129fb3a7065f79c621c2cbebea83978c94bf97a Mon Sep 17 00:00:00 2001 From: ysaito1001 Date: Thu, 27 Jul 2023 18:19:31 -0500 Subject: [PATCH 240/253] Fix flag name to retrieve if runtime plugins are configurable (#2885) ## Motivation and Context Follow-up on #2864. It got it 99% right, it's just that the name of a flag for retrieving its value was incorrect. ## Testing Generated an AWS SDK and confirmed that setters on `Config` for runtime plugins are marked as `pub(crate)` correctly. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ Co-authored-by: ysaito1001 --- .../smithy/rust/codegen/client/smithy/ClientRustSettings.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt index ca532b19b4..c7268a67a3 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/ClientRustSettings.kt @@ -135,7 +135,7 @@ data class ClientCodegenConfig( addMessageToErrors = node.get().getBooleanMemberOrDefault("addMessageToErrors", defaultAddMessageToErrors), enableNewSmithyRuntime = SmithyRuntimeMode.fromString(node.get().getStringMemberOrDefault("enableNewSmithyRuntime", "middleware")), includeEndpointUrlConfig = node.get().getBooleanMemberOrDefault("includeEndpointUrlConfig", defaultIncludeEndpointUrlConfig), - enableUserConfigurableRuntimePlugins = node.get().getBooleanMemberOrDefault("userConfigurableRuntimePlugins", defaultEnableUserConfigurableRuntimePlugins), + enableUserConfigurableRuntimePlugins = node.get().getBooleanMemberOrDefault("enableUserConfigurableRuntimePlugins", defaultEnableUserConfigurableRuntimePlugins), ) } else { ClientCodegenConfig( From 55a1536eced663890ec9a9c76ca3db7a6b66588f Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 28 Jul 2023 07:46:55 -0700 Subject: [PATCH 241/253] Run non-service SDK integration tests in CI (#2884) This fixes #2880. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- tools/ci-scripts/check-aws-sdk-smoketest-unit-tests | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/ci-scripts/check-aws-sdk-smoketest-unit-tests b/tools/ci-scripts/check-aws-sdk-smoketest-unit-tests index 293af9b43f..bbf13d54b3 100755 --- a/tools/ci-scripts/check-aws-sdk-smoketest-unit-tests +++ b/tools/ci-scripts/check-aws-sdk-smoketest-unit-tests @@ -7,3 +7,10 @@ set -eux cd aws-sdk-smoketest cargo test --all-features + +for test_dir in tests/*; do + if [ -f "${test_dir}/Cargo.toml" ]; then + echo "#### Testing ${test_dir}..." + cargo test --all-features --manifest-path "${test_dir}/Cargo.toml" + fi +done From cf8df40f18a554b642aa257973fdf1331d723d2b Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 28 Jul 2023 10:09:18 -0700 Subject: [PATCH 242/253] Merge `sra-test` into the SDK integration tests and fix its tests (#2883) This PR merges the benchmark and integration tests from `aws/sra-test` into the SDK integration tests, and updates the tests so that they compile and pass. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../aws-runtime/src/request_info.rs | 20 +- .../rustsdk/IntegrationTestDependencies.kt | 9 +- .../orchestrator-vs-middleware/Cargo.lock | 2451 +++++++++++++++++ .../orchestrator-vs-middleware/Cargo.toml | 24 + .../benches/README.md | 6 + .../benches/middleware_vs_orchestrator.rs | 89 +- .../s3-throughput}/README.md | 0 .../s3-throughput}/benchmark/Cargo.lock | 0 .../s3-throughput}/benchmark/Cargo.toml | 0 .../s3-throughput}/benchmark/src/get_test.rs | 0 .../s3-throughput}/benchmark/src/latencies.rs | 0 .../s3-throughput}/benchmark/src/main.rs | 0 .../benchmark/src/multipart_get.rs | 0 .../benchmark/src/multipart_put.rs | 0 .../s3-throughput}/benchmark/src/put_test.rs | 0 .../s3-throughput}/benchmark/src/verify.rs | 0 .../infrastructure/.eslintrc.json | 0 .../s3-throughput}/infrastructure/.gitignore | 0 .../s3-throughput}/infrastructure/.npmignore | 0 .../s3-throughput}/infrastructure/.prettierrc | 0 .../infrastructure/assets/init_instance.sh | 0 .../infrastructure/assets/run_benchmark.sh | 0 .../infrastructure/bin/infrastructure.ts | 0 .../infrastructure/cdk.context.json | 0 .../s3-throughput}/infrastructure/cdk.json | 0 .../lib/infrastructure-stack.ts | 0 .../infrastructure/package-lock.json | 0 .../infrastructure/package.json | 0 .../infrastructure/tsconfig.json | 0 aws/sdk/integration-tests/s3/Cargo.toml | 2 + .../slow-network-and-late-client-clock.json | 0 .../three-retries_and-then-success.json | 32 +- .../three-successful-attempts.json | 0 .../s3/tests/interceptors.rs | 125 + .../s3/tests/request_information_headers.rs | 285 ++ aws/sra-test/.gitignore | 1 - aws/sra-test/build.gradle.kts | 126 - .../integration-tests/aws-sdk-s3/.gitignore | 1 - .../integration-tests/aws-sdk-s3/Cargo.toml | 32 - .../aws-sdk-s3/benches/README.md | 6 - .../aws-sdk-s3/test-data/list-objects-v2.json | 105 - .../aws-sdk-s3/tests/interceptors.rs | 170 -- .../tests/request_information_headers.rs | 257 -- .../aws-sdk-s3/tests/util.rs | 53 - .../ResiliencyConfigCustomization.kt | 12 +- .../customizations/TimeSourceCustomization.kt | 2 +- .../aws-smithy-client/src/dvr/replay.rs | 11 +- .../aws-smithy-http/src/connection.rs | 2 +- settings.gradle.kts | 1 - 49 files changed, 2975 insertions(+), 847 deletions(-) create mode 100644 aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.lock create mode 100644 aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.toml create mode 100644 aws/sdk/benchmarks/orchestrator-vs-middleware/benches/README.md rename aws/{sra-test/integration-tests/aws-sdk-s3 => sdk/benchmarks/orchestrator-vs-middleware}/benches/middleware_vs_orchestrator.rs (52%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/README.md (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/benchmark/Cargo.lock (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/benchmark/Cargo.toml (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/benchmark/src/get_test.rs (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/benchmark/src/latencies.rs (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/benchmark/src/main.rs (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/benchmark/src/multipart_get.rs (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/benchmark/src/multipart_put.rs (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/benchmark/src/put_test.rs (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/benchmark/src/verify.rs (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/.eslintrc.json (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/.gitignore (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/.npmignore (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/.prettierrc (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/assets/init_instance.sh (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/assets/run_benchmark.sh (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/bin/infrastructure.ts (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/cdk.context.json (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/cdk.json (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/lib/infrastructure-stack.ts (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/package-lock.json (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/package.json (100%) rename aws/sdk/{s3-benchmark => benchmarks/s3-throughput}/infrastructure/tsconfig.json (100%) rename aws/{sra-test/integration-tests/aws-sdk-s3/test-data => sdk/integration-tests/s3/tests/data}/request-information-headers/slow-network-and-late-client-clock.json (100%) rename aws/{sra-test/integration-tests/aws-sdk-s3/test-data => sdk/integration-tests/s3/tests/data}/request-information-headers/three-retries_and-then-success.json (86%) rename aws/{sra-test/integration-tests/aws-sdk-s3/test-data => sdk/integration-tests/s3/tests/data}/request-information-headers/three-successful-attempts.json (100%) create mode 100644 aws/sdk/integration-tests/s3/tests/interceptors.rs create mode 100644 aws/sdk/integration-tests/s3/tests/request_information_headers.rs delete mode 100644 aws/sra-test/.gitignore delete mode 100644 aws/sra-test/build.gradle.kts delete mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/.gitignore delete mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml delete mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/benches/README.md delete mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/test-data/list-objects-v2.json delete mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs delete mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs delete mode 100644 aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index ab3311f338..5b7dccee55 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -4,6 +4,7 @@ */ use crate::service_clock_skew::ServiceClockSkew; +use aws_smithy_async::time::TimeSource; use aws_smithy_runtime_api::box_error::BoxError; use aws_smithy_runtime_api::client::interceptors::context::BeforeTransmitInterceptorContextMut; use aws_smithy_runtime_api::client::interceptors::Interceptor; @@ -16,7 +17,7 @@ use aws_smithy_types::timeout::TimeoutConfig; use aws_smithy_types::DateTime; use http::{HeaderName, HeaderValue}; use std::borrow::Cow; -use std::time::{Duration, SystemTime}; +use std::time::Duration; #[allow(clippy::declare_interior_mutable_const)] // we will never mutate this const AMZ_SDK_REQUEST: HeaderName = HeaderName::from_static("amz-sdk-request"); @@ -63,11 +64,15 @@ impl RequestInfoInterceptor { } } - fn build_ttl_pair(&self, cfg: &ConfigBag) -> Option<(Cow<'static, str>, Cow<'static, str>)> { + fn build_ttl_pair( + &self, + cfg: &ConfigBag, + timesource: impl TimeSource, + ) -> Option<(Cow<'static, str>, Cow<'static, str>)> { let timeout_config = cfg.load::()?; let socket_read = timeout_config.read_timeout()?; let estimated_skew: Duration = cfg.load::().cloned()?.into(); - let current_time = SystemTime::now(); + let current_time = timesource.now(); let ttl = current_time.checked_add(socket_read + estimated_skew)?; let mut timestamp = DateTime::from(ttl); // Set subsec_nanos to 0 so that the formatted `DateTime` won't have fractional seconds. @@ -94,11 +99,16 @@ impl Interceptor for RequestInfoInterceptor { fn modify_before_transmit( &self, context: &mut BeforeTransmitInterceptorContextMut<'_>, - _runtime_components: &RuntimeComponents, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { let mut pairs = RequestPairs::new(); - if let Some(pair) = self.build_ttl_pair(cfg) { + if let Some(pair) = self.build_ttl_pair( + cfg, + runtime_components + .time_source() + .ok_or("A timesource must be provided")?, + ) { pairs = pairs.with_pair(pair); } if let Some(pair) = self.build_attempts_pair(cfg) { diff --git a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt index 5a39041ef2..3cee565e64 100644 --- a/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt +++ b/aws/sdk-codegen/src/main/kotlin/software/amazon/smithy/rustsdk/IntegrationTestDependencies.kt @@ -35,6 +35,7 @@ import software.amazon.smithy.rust.codegen.core.rustlang.writable import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsCustomization import software.amazon.smithy.rust.codegen.core.smithy.generators.LibRsSection import software.amazon.smithy.rust.codegen.core.testutil.testDependenciesOnly +import software.amazon.smithy.rustsdk.AwsCargoDependency.awsRuntime import java.nio.file.Files import java.nio.file.Paths import kotlin.io.path.absolute @@ -99,6 +100,7 @@ class IntegrationTestDependencies( if (codegenContext.smithyRuntimeMode.generateOrchestrator) { addDependency(smithyRuntime(runtimeConfig).copy(features = setOf("test-util"), scope = DependencyScope.Dev)) addDependency(smithyRuntimeApi(runtimeConfig).copy(features = setOf("test-util"), scope = DependencyScope.Dev)) + addDependency(awsRuntime(runtimeConfig).toDevDependency().withFeature("test-util")) } } if (hasBenches) { @@ -148,12 +150,5 @@ class S3TestDependencies(private val codegenContext: ClientCodegenContext) : Lib addDependency(TempFile) addDependency(TracingAppender) addDependency(TracingTest) - - // TODO(enableNewSmithyRuntimeCleanup): These additional dependencies may not be needed anymore when removing this flag - // depending on if the sra-test is kept around or not. - if (codegenContext.smithyRuntimeMode.generateOrchestrator) { - addDependency(smithyRuntime(codegenContext.runtimeConfig).toDevDependency()) - addDependency(smithyRuntimeApi(codegenContext.runtimeConfig).toDevDependency()) - } } } diff --git a/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.lock b/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.lock new file mode 100644 index 0000000000..7cf3c5ef2e --- /dev/null +++ b/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.lock @@ -0,0 +1,2451 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "assert-json-diff" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4259cbe96513d2f1073027a259fc2ca917feb3026a5a8d984e3628e490255cc0" +dependencies = [ + "extend", + "serde", + "serde_json", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "aws-config" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-http 0.0.0-smithy-rs-head", + "aws-sdk-sso", + "aws-sdk-sts", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-http-tower 0.0.0-smithy-rs-head", + "aws-smithy-json 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "bytes", + "fastrand 2.0.0", + "hex", + "http", + "hyper", + "ring", + "time", + "tokio", + "tower", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "fastrand 2.0.0", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcdb2f7acbc076ff5ad05e7864bdb191ca70a6fd07668dc3a1a8bcd051de5ae" +dependencies = [ + "aws-smithy-async 0.55.3", + "aws-smithy-types 0.55.3", + "fastrand 1.9.0", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-endpoint" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "http", + "regex", + "tracing", +] + +[[package]] +name = "aws-endpoint" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cce1c41a6cfaa726adee9ebb9a56fcd2bbfd8be49fd8a04c5e20fd968330b04" +dependencies = [ + "aws-smithy-http 0.55.3", + "aws-smithy-types 0.55.3", + "aws-types 0.55.3", + "http", + "regex", + "tracing", +] + +[[package]] +name = "aws-http" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "bytes", + "http", + "http-body", + "lazy_static", + "percent-encoding", + "pin-project-lite", + "tracing", +] + +[[package]] +name = "aws-http" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aadbc44e7a8f3e71c8b374e03ecd972869eb91dd2bc89ed018954a52ba84bc44" +dependencies = [ + "aws-credential-types 0.55.3", + "aws-smithy-http 0.55.3", + "aws-smithy-types 0.55.3", + "aws-types 0.55.3", + "bytes", + "http", + "http-body", + "lazy_static", + "percent-encoding", + "pin-project-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-s3" +version = "0.0.0-local" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-endpoint 0.0.0-smithy-rs-head", + "aws-http 0.0.0-smithy-rs-head", + "aws-sig-auth 0.0.0-smithy-rs-head", + "aws-sigv4 0.0.0-smithy-rs-head", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-checksums 0.0.0-smithy-rs-head", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-eventstream 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-http-tower 0.0.0-smithy-rs-head", + "aws-smithy-json 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-smithy-xml 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "bytes", + "http", + "http-body", + "once_cell", + "percent-encoding", + "regex", + "tokio-stream", + "tower", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-s3" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba197193cbb4bcb6aad8d99796b2291f36fa89562ded5d4501363055b0de89f" +dependencies = [ + "aws-credential-types 0.55.3", + "aws-endpoint 0.55.3", + "aws-http 0.55.3", + "aws-sig-auth 0.55.3", + "aws-sigv4 0.55.3", + "aws-smithy-async 0.55.3", + "aws-smithy-checksums 0.55.3", + "aws-smithy-client 0.55.3", + "aws-smithy-eventstream 0.55.3", + "aws-smithy-http 0.55.3", + "aws-smithy-http-tower 0.55.3", + "aws-smithy-json 0.55.3", + "aws-smithy-types 0.55.3", + "aws-smithy-xml 0.55.3", + "aws-types 0.55.3", + "bytes", + "http", + "http-body", + "once_cell", + "percent-encoding", + "regex", + "tokio-stream", + "tower", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sso" +version = "0.0.0-local" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-endpoint 0.0.0-smithy-rs-head", + "aws-http 0.0.0-smithy-rs-head", + "aws-sig-auth 0.0.0-smithy-rs-head", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-http-tower 0.0.0-smithy-rs-head", + "aws-smithy-json 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "bytes", + "http", + "regex", + "tokio-stream", + "tower", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "0.0.0-local" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-endpoint 0.0.0-smithy-rs-head", + "aws-http 0.0.0-smithy-rs-head", + "aws-sig-auth 0.0.0-smithy-rs-head", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-http-tower 0.0.0-smithy-rs-head", + "aws-smithy-json 0.0.0-smithy-rs-head", + "aws-smithy-query", + "aws-smithy-types 0.0.0-smithy-rs-head", + "aws-smithy-xml 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "bytes", + "http", + "regex", + "tower", + "tracing", +] + +[[package]] +name = "aws-sig-auth" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-sigv4 0.0.0-smithy-rs-head", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-eventstream 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-types 0.0.0-smithy-rs-head", + "http", + "tracing", +] + +[[package]] +name = "aws-sig-auth" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b94acb10af0c879ecd5c7bdf51cda6679a0a4f4643ce630905a77673bfa3c61" +dependencies = [ + "aws-credential-types 0.55.3", + "aws-sigv4 0.55.3", + "aws-smithy-eventstream 0.55.3", + "aws-smithy-http 0.55.3", + "aws-types 0.55.3", + "http", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-eventstream 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http", + "once_cell", + "percent-encoding", + "regex", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d2ce6f507be68e968a33485ced670111d1cbad161ddbbab1e313c03d37d8f4c" +dependencies = [ + "aws-smithy-eventstream 0.55.3", + "aws-smithy-http 0.55.3", + "bytes", + "form_urlencoded", + "hex", + "hmac", + "http", + "once_cell", + "percent-encoding", + "regex", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "aws-smithy-async" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bda3996044c202d75b91afeb11a9afae9db9a721c6a7a427410018e286b880" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http", + "http-body", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ed8b96d95402f3f6b8b57eb4e0e45ee365f78b1a924faf20ff6e97abf1eae6" +dependencies = [ + "aws-smithy-http 0.55.3", + "aws-smithy-types 0.55.3", + "bytes", + "crc32c", + "crc32fast", + "hex", + "http", + "http-body", + "md-5", + "pin-project-lite", + "sha1", + "sha2", + "tracing", +] + +[[package]] +name = "aws-smithy-client" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-http-tower 0.0.0-smithy-rs-head", + "aws-smithy-protocol-test 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "bytes", + "fastrand 2.0.0", + "http", + "http-body", + "hyper", + "hyper-rustls 0.24.1", + "lazy_static", + "pin-project-lite", + "rustls 0.21.5", + "serde", + "serde_json", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-client" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a86aa6e21e86c4252ad6a0e3e74da9617295d8d6e374d552be7d3059c41cedd" +dependencies = [ + "aws-smithy-async 0.55.3", + "aws-smithy-http 0.55.3", + "aws-smithy-http-tower 0.55.3", + "aws-smithy-protocol-test 0.55.3", + "aws-smithy-types 0.55.3", + "bytes", + "fastrand 1.9.0", + "http", + "http-body", + "hyper", + "hyper-rustls 0.23.2", + "lazy_static", + "pin-project-lite", + "rustls 0.20.8", + "serde", + "serde_json", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-types 0.0.0-smithy-rs-head", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460c8da5110835e3d9a717c61f5556b20d03c32a1dec57f8fc559b360f733bb8" +dependencies = [ + "aws-smithy-types 0.55.3", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-eventstream 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "bytes", + "bytes-utils", + "futures-core", + "http", + "http-body", + "hyper", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "aws-smithy-http" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b3b693869133551f135e1f2c77cb0b8277d9e3e17feaf2213f735857c4f0d28" +dependencies = [ + "aws-smithy-eventstream 0.55.3", + "aws-smithy-types 0.55.3", + "bytes", + "bytes-utils", + "futures-core", + "http", + "http-body", + "hyper", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "aws-smithy-http-tower" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "bytes", + "http", + "http-body", + "pin-project-lite", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-http-tower" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae4f6c5798a247fac98a867698197d9ac22643596dc3777f0c76b91917616b9" +dependencies = [ + "aws-smithy-http 0.55.3", + "aws-smithy-types 0.55.3", + "bytes", + "http", + "http-body", + "pin-project-lite", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-types 0.0.0-smithy-rs-head", +] + +[[package]] +name = "aws-smithy-json" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f9f42fbfa96d095194a632fbac19f60077748eba536eb0b9fecc28659807f8" +dependencies = [ + "aws-smithy-types 0.55.3", +] + +[[package]] +name = "aws-smithy-protocol-test" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "assert-json-diff", + "http", + "pretty_assertions", + "regex", + "roxmltree", + "serde_json", + "thiserror", +] + +[[package]] +name = "aws-smithy-protocol-test" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabbf8d2bfefa4870ba497c1ae3b40e5e26be18af1cb8c871856b0a393a15ffa" +dependencies = [ + "assert-json-diff", + "http", + "pretty_assertions", + "regex", + "roxmltree", + "serde_json", + "thiserror", +] + +[[package]] +name = "aws-smithy-query" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-smithy-types 0.0.0-smithy-rs-head", + "urlencoding", +] + +[[package]] +name = "aws-smithy-types" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "base64-simd", + "itoa", + "num-integer", + "ryu", + "serde", + "time", +] + +[[package]] +name = "aws-smithy-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a3d0bf4f324f4ef9793b86a1701d9700fbcdbd12a846da45eed104c634c6e8" +dependencies = [ + "base64-simd", + "itoa", + "num-integer", + "ryu", + "time", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1b9d12875731bd07e767be7baad95700c3137b56730ec9ddeedb52a5e5ca63b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "0.0.0-smithy-rs-head" +dependencies = [ + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-smithy-async 0.0.0-smithy-rs-head", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-http 0.0.0-smithy-rs-head", + "aws-smithy-types 0.0.0-smithy-rs-head", + "http", + "rustc_version", + "tracing", +] + +[[package]] +name = "aws-types" +version = "0.55.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd209616cc8d7bfb82f87811a5c655dc97537f592689b18743bddf5dc5c4829" +dependencies = [ + "aws-credential-types 0.55.3", + "aws-smithy-async 0.55.3", + "aws-smithy-client 0.55.3", + "aws-smithy-http 0.55.3", + "aws-smithy-types 0.55.3", + "http", + "rustc_version", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "bytes-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47d3a8076e283f3acd27400535992edb3ba4b5bb72f8891ad8fbe7932a7d4b9" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "bitflags", + "clap_lex", + "indexmap", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32c" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f48d60e5b4d2c53d5c2b1d8a58c849a70ae5e5509b08a48d047e3b65714a74" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" +dependencies = [ + "anes", + "atty", + "cast", + "ciborium", + "clap", + "criterion-plot", + "futures", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "tokio", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "extend" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f47da3a72ec598d9c8937a7ebca8962a5c7a1f28444e38c2b33c771ba3f55f05" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" + +[[package]] +name = "h2" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" +dependencies = [ + "http", + "hyper", + "log", + "rustls 0.20.8", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.23.4", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +dependencies = [ + "futures-util", + "http", + "hyper", + "log", + "rustls 0.21.5", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.2", + "libc", +] + +[[package]] +name = "object" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "orchestrator-vs-middleware" +version = "0.1.0" +dependencies = [ + "aws-config", + "aws-credential-types 0.0.0-smithy-rs-head", + "aws-sdk-s3 0.0.0-local", + "aws-sdk-s3 0.28.0", + "aws-smithy-client 0.0.0-smithy-rs-head", + "aws-smithy-client 0.55.3", + "criterion", + "http", + "tokio", +] + +[[package]] +name = "os_str_bytes" +version = "6.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "pretty_assertions" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "roxmltree" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "serde" +version = "1.0.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63ba2516aa6bf82e0b19ca8b50019d52df58455d3cf9bdaf6315225fdd0c560a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "401797fe7833d72109fedec6bfcbe67c0eed9b99772f26eb8afd261f0abc6fd3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "serde_json" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + +[[package]] +name = "thiserror" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "time" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +dependencies = [ + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +dependencies = [ + "autocfg", + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.8", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.5", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.27", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "xmlparser" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.toml b/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.toml new file mode 100644 index 0000000000..e4b6af798a --- /dev/null +++ b/aws/sdk/benchmarks/orchestrator-vs-middleware/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "orchestrator-vs-middleware" +version = "0.1.0" +edition = "2021" + +# 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" } +aws-credential-types = { path = "../../build/aws-sdk/sdk/aws-credential-types", features = ["test-util"] } +aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3" } +aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", features = ["test-util", "wiremock"] } +criterion = { version = "0.4", features = ["async_tokio"] } +http = "0.2.3" +middleware-s3 = { version = "0.28", package = "aws-sdk-s3", features = ["test-util"] } +middleware-smithy-client = { version = "0.55.3", package = "aws-smithy-client", features = ["test-util", "rustls"] } +tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } + +[profile.release] +debug = 1 + +[[bench]] +name = "middleware_vs_orchestrator" +harness = false diff --git a/aws/sdk/benchmarks/orchestrator-vs-middleware/benches/README.md b/aws/sdk/benchmarks/orchestrator-vs-middleware/benches/README.md new file mode 100644 index 0000000000..0f2e81f432 --- /dev/null +++ b/aws/sdk/benchmarks/orchestrator-vs-middleware/benches/README.md @@ -0,0 +1,6 @@ +### Middleware vs. Orchestrator Benchmark + +To run the benchmark: +```bash +./gradlew :aws:sdk:assemble && (cd aws/sdk/integration-tests/s3 && cargo bench) +``` diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs b/aws/sdk/benchmarks/orchestrator-vs-middleware/benches/middleware_vs_orchestrator.rs similarity index 52% rename from aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs rename to aws/sdk/benchmarks/orchestrator-vs-middleware/benches/middleware_vs_orchestrator.rs index b728a6fa14..6920f80fe0 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/middleware_vs_orchestrator.rs +++ b/aws/sdk/benchmarks/orchestrator-vs-middleware/benches/middleware_vs_orchestrator.rs @@ -9,12 +9,6 @@ use aws_sdk_s3 as s3; use criterion::{BenchmarkId, Criterion}; macro_rules! test_connection { - (head) => { - test_connection!(aws_smithy_client) - }; - (last_release) => { - test_connection!(last_release_smithy_client) - }; ($package:ident) => { $package::test_connection::infallible_connection_fn(|req| { assert_eq!( @@ -46,44 +40,6 @@ macro_rules! test_connection { }; } -macro_rules! create_client { - (head) => { - create_client!(head, aws_sdk_s3) - }; - (last_release) => { - create_client!(last_release, last_release_s3) - }; - ($original:ident, $package:ident) => {{ - let conn = test_connection!($original); - let config = $package::Config::builder() - .credentials_provider($package::config::Credentials::for_tests()) - .region($package::config::Region::new("us-east-1")) - .http_connector(conn.clone()) - .build(); - $package::Client::from_conf(config) - }}; -} - -macro_rules! middleware_bench_fn { - ($fn_name:ident, head) => { - middleware_bench_fn!($fn_name, aws_sdk_s3) - }; - ($fn_name:ident, last_release) => { - middleware_bench_fn!($fn_name, last_release_s3) - }; - ($fn_name:ident, $package:ident) => { - async fn $fn_name(client: &$package::Client) { - client - .list_objects_v2() - .bucket("test-bucket") - .prefix("prefix~") - .send() - .await - .expect("successful execution"); - } - }; -} - async fn orchestrator(client: &s3::Client) { let _output = client .list_objects_v2() @@ -94,34 +50,49 @@ async fn orchestrator(client: &s3::Client) { .expect("successful execution"); } -fn bench(c: &mut Criterion) { - let head_client = create_client!(head); - middleware_bench_fn!(middleware_head, head); +async fn middleware(client: &middleware_s3::Client) { + client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .send() + .await + .expect("successful execution"); +} - let last_release_client = create_client!(last_release); - middleware_bench_fn!(middleware_last_release, last_release); +fn bench(c: &mut Criterion) { + let orchestrator_client = { + let conn = test_connection!(aws_smithy_client); + let config = aws_sdk_s3::Config::builder() + .credentials_provider(aws_sdk_s3::config::Credentials::for_tests()) + .region(aws_sdk_s3::config::Region::new("us-east-1")) + .http_connector(conn.clone()) + .build(); + aws_sdk_s3::Client::from_conf(config) + }; + let middleware_client = { + let conn = test_connection!(middleware_smithy_client); + let config = middleware_s3::Config::builder() + .credentials_provider(middleware_s3::config::Credentials::for_tests()) + .region(middleware_s3::config::Region::new("us-east-1")) + .http_connector(conn.clone()) + .build(); + middleware_s3::Client::from_conf(config) + }; let mut group = c.benchmark_group("compare"); let param = "S3 ListObjectsV2"; - group.bench_with_input( - BenchmarkId::new("middleware (HEAD)", param), - param, - |b, _| { - b.to_async(tokio::runtime::Runtime::new().unwrap()) - .iter(|| async { middleware_head(&head_client).await }) - }, - ); group.bench_with_input( BenchmarkId::new("middleware (last_release)", param), param, |b, _| { b.to_async(tokio::runtime::Runtime::new().unwrap()) - .iter(|| async { middleware_last_release(&last_release_client).await }) + .iter(|| async { middleware(&middleware_client).await }) }, ); group.bench_with_input(BenchmarkId::new("orchestrator", param), param, |b, _| { b.to_async(tokio::runtime::Runtime::new().unwrap()) - .iter(|| async { orchestrator(&head_client).await }) + .iter(|| async { orchestrator(&orchestrator_client).await }) }); group.finish(); } diff --git a/aws/sdk/s3-benchmark/README.md b/aws/sdk/benchmarks/s3-throughput/README.md similarity index 100% rename from aws/sdk/s3-benchmark/README.md rename to aws/sdk/benchmarks/s3-throughput/README.md diff --git a/aws/sdk/s3-benchmark/benchmark/Cargo.lock b/aws/sdk/benchmarks/s3-throughput/benchmark/Cargo.lock similarity index 100% rename from aws/sdk/s3-benchmark/benchmark/Cargo.lock rename to aws/sdk/benchmarks/s3-throughput/benchmark/Cargo.lock diff --git a/aws/sdk/s3-benchmark/benchmark/Cargo.toml b/aws/sdk/benchmarks/s3-throughput/benchmark/Cargo.toml similarity index 100% rename from aws/sdk/s3-benchmark/benchmark/Cargo.toml rename to aws/sdk/benchmarks/s3-throughput/benchmark/Cargo.toml diff --git a/aws/sdk/s3-benchmark/benchmark/src/get_test.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/get_test.rs similarity index 100% rename from aws/sdk/s3-benchmark/benchmark/src/get_test.rs rename to aws/sdk/benchmarks/s3-throughput/benchmark/src/get_test.rs diff --git a/aws/sdk/s3-benchmark/benchmark/src/latencies.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/latencies.rs similarity index 100% rename from aws/sdk/s3-benchmark/benchmark/src/latencies.rs rename to aws/sdk/benchmarks/s3-throughput/benchmark/src/latencies.rs diff --git a/aws/sdk/s3-benchmark/benchmark/src/main.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/main.rs similarity index 100% rename from aws/sdk/s3-benchmark/benchmark/src/main.rs rename to aws/sdk/benchmarks/s3-throughput/benchmark/src/main.rs diff --git a/aws/sdk/s3-benchmark/benchmark/src/multipart_get.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/multipart_get.rs similarity index 100% rename from aws/sdk/s3-benchmark/benchmark/src/multipart_get.rs rename to aws/sdk/benchmarks/s3-throughput/benchmark/src/multipart_get.rs diff --git a/aws/sdk/s3-benchmark/benchmark/src/multipart_put.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/multipart_put.rs similarity index 100% rename from aws/sdk/s3-benchmark/benchmark/src/multipart_put.rs rename to aws/sdk/benchmarks/s3-throughput/benchmark/src/multipart_put.rs diff --git a/aws/sdk/s3-benchmark/benchmark/src/put_test.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/put_test.rs similarity index 100% rename from aws/sdk/s3-benchmark/benchmark/src/put_test.rs rename to aws/sdk/benchmarks/s3-throughput/benchmark/src/put_test.rs diff --git a/aws/sdk/s3-benchmark/benchmark/src/verify.rs b/aws/sdk/benchmarks/s3-throughput/benchmark/src/verify.rs similarity index 100% rename from aws/sdk/s3-benchmark/benchmark/src/verify.rs rename to aws/sdk/benchmarks/s3-throughput/benchmark/src/verify.rs diff --git a/aws/sdk/s3-benchmark/infrastructure/.eslintrc.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/.eslintrc.json similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/.eslintrc.json rename to aws/sdk/benchmarks/s3-throughput/infrastructure/.eslintrc.json diff --git a/aws/sdk/s3-benchmark/infrastructure/.gitignore b/aws/sdk/benchmarks/s3-throughput/infrastructure/.gitignore similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/.gitignore rename to aws/sdk/benchmarks/s3-throughput/infrastructure/.gitignore diff --git a/aws/sdk/s3-benchmark/infrastructure/.npmignore b/aws/sdk/benchmarks/s3-throughput/infrastructure/.npmignore similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/.npmignore rename to aws/sdk/benchmarks/s3-throughput/infrastructure/.npmignore diff --git a/aws/sdk/s3-benchmark/infrastructure/.prettierrc b/aws/sdk/benchmarks/s3-throughput/infrastructure/.prettierrc similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/.prettierrc rename to aws/sdk/benchmarks/s3-throughput/infrastructure/.prettierrc diff --git a/aws/sdk/s3-benchmark/infrastructure/assets/init_instance.sh b/aws/sdk/benchmarks/s3-throughput/infrastructure/assets/init_instance.sh similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/assets/init_instance.sh rename to aws/sdk/benchmarks/s3-throughput/infrastructure/assets/init_instance.sh diff --git a/aws/sdk/s3-benchmark/infrastructure/assets/run_benchmark.sh b/aws/sdk/benchmarks/s3-throughput/infrastructure/assets/run_benchmark.sh similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/assets/run_benchmark.sh rename to aws/sdk/benchmarks/s3-throughput/infrastructure/assets/run_benchmark.sh diff --git a/aws/sdk/s3-benchmark/infrastructure/bin/infrastructure.ts b/aws/sdk/benchmarks/s3-throughput/infrastructure/bin/infrastructure.ts similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/bin/infrastructure.ts rename to aws/sdk/benchmarks/s3-throughput/infrastructure/bin/infrastructure.ts diff --git a/aws/sdk/s3-benchmark/infrastructure/cdk.context.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/cdk.context.json similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/cdk.context.json rename to aws/sdk/benchmarks/s3-throughput/infrastructure/cdk.context.json diff --git a/aws/sdk/s3-benchmark/infrastructure/cdk.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/cdk.json similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/cdk.json rename to aws/sdk/benchmarks/s3-throughput/infrastructure/cdk.json diff --git a/aws/sdk/s3-benchmark/infrastructure/lib/infrastructure-stack.ts b/aws/sdk/benchmarks/s3-throughput/infrastructure/lib/infrastructure-stack.ts similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/lib/infrastructure-stack.ts rename to aws/sdk/benchmarks/s3-throughput/infrastructure/lib/infrastructure-stack.ts diff --git a/aws/sdk/s3-benchmark/infrastructure/package-lock.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/package-lock.json similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/package-lock.json rename to aws/sdk/benchmarks/s3-throughput/infrastructure/package-lock.json diff --git a/aws/sdk/s3-benchmark/infrastructure/package.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/package.json similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/package.json rename to aws/sdk/benchmarks/s3-throughput/infrastructure/package.json diff --git a/aws/sdk/s3-benchmark/infrastructure/tsconfig.json b/aws/sdk/benchmarks/s3-throughput/infrastructure/tsconfig.json similarity index 100% rename from aws/sdk/s3-benchmark/infrastructure/tsconfig.json rename to aws/sdk/benchmarks/s3-throughput/infrastructure/tsconfig.json diff --git a/aws/sdk/integration-tests/s3/Cargo.toml b/aws/sdk/integration-tests/s3/Cargo.toml index 650d1a5db0..74453e21e6 100644 --- a/aws/sdk/integration-tests/s3/Cargo.toml +++ b/aws/sdk/integration-tests/s3/Cargo.toml @@ -15,6 +15,7 @@ async-std = "1.12.0" 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-runtime = { path = "../../build/aws-sdk/sdk/aws-runtime", features = ["test-util"] } aws-sdk-s3 = { path = "../../build/aws-sdk/sdk/s3" } 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"] } @@ -22,6 +23,7 @@ aws-smithy-client = { path = "../../build/aws-sdk/sdk/aws-smithy-client", featur aws-smithy-http = { path = "../../build/aws-sdk/sdk/aws-smithy-http" } 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 = ["test-util"] } +aws-smithy-runtime-api = { path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["test-util"] } aws-smithy-types = { path = "../../build/aws-sdk/sdk/aws-smithy-types" } aws-types = { path = "../../build/aws-sdk/sdk/aws-types" } bytes = "1" diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/slow-network-and-late-client-clock.json b/aws/sdk/integration-tests/s3/tests/data/request-information-headers/slow-network-and-late-client-clock.json similarity index 100% rename from aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/slow-network-and-late-client-clock.json rename to aws/sdk/integration-tests/s3/tests/data/request-information-headers/slow-network-and-late-client-clock.json diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-retries_and-then-success.json b/aws/sdk/integration-tests/s3/tests/data/request-information-headers/three-retries_and-then-success.json similarity index 86% rename from aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-retries_and-then-success.json rename to aws/sdk/integration-tests/s3/tests/data/request-information-headers/three-retries_and-then-success.json index b0aee24861..2903739925 100644 --- a/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-retries_and-then-success.json +++ b/aws/sdk/integration-tests/s3/tests/data/request-information-headers/three-retries_and-then-success.json @@ -11,7 +11,7 @@ "notarealsessiontoken" ], "authorization": [ - "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20190601/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=43970cfd0324cb28a86459789b7a1c7684cf54b0b3c9842a84f3b24343fa038a" ], "x-amz-user-agent": [ "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" @@ -55,9 +55,6 @@ "status": 500, "version": "HTTP/1.1", "headers": { - "server": [ - "AmazonS3" - ], "x-amz-request-id": [ "foo-id" ], @@ -103,7 +100,7 @@ } }, { - "connection_id": 0, + "connection_id": 1, "action": { "Request": { "request": { @@ -113,7 +110,7 @@ "notarealsessiontoken" ], "authorization": [ - "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20190601/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=6d0f0da831a7d3ad1bde4e98580177bc0ef0acc21064dd26394006006392cb14" ], "x-amz-user-agent": [ "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" @@ -140,7 +137,7 @@ } }, { - "connection_id": 0, + "connection_id": 1, "action": { "Eof": { "ok": true, @@ -149,7 +146,7 @@ } }, { - "connection_id": 0, + "connection_id": 1, "action": { "Response": { "response": { @@ -157,9 +154,6 @@ "status": 500, "version": "HTTP/1.1", "headers": { - "server": [ - "AmazonS3" - ], "x-amz-request-id": [ "foo-id" ], @@ -185,7 +179,7 @@ } }, { - "connection_id": 0, + "connection_id": 1, "action": { "Data": { "data": { @@ -196,7 +190,7 @@ } }, { - "connection_id": 0, + "connection_id": 1, "action": { "Eof": { "ok": true, @@ -205,7 +199,7 @@ } }, { - "connection_id": 0, + "connection_id": 2, "action": { "Request": { "request": { @@ -215,7 +209,7 @@ "notarealsessiontoken" ], "authorization": [ - "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" + "AWS4-HMAC-SHA256 Credential=ANOTREAL/20190601/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=8160b1d1200c10cde681ac6f4490c98023af9c4b3b8fd8a82e7560f87c126a53" ], "x-amz-user-agent": [ "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" @@ -242,7 +236,7 @@ } }, { - "connection_id": 0, + "connection_id": 2, "action": { "Eof": { "ok": true, @@ -251,7 +245,7 @@ } }, { - "connection_id": 0, + "connection_id": 2, "action": { "Response": { "response": { @@ -287,7 +281,7 @@ } }, { - "connection_id": 0, + "connection_id": 2, "action": { "Data": { "data": { @@ -298,7 +292,7 @@ } }, { - "connection_id": 0, + "connection_id": 2, "action": { "Eof": { "ok": true, diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-successful-attempts.json b/aws/sdk/integration-tests/s3/tests/data/request-information-headers/three-successful-attempts.json similarity index 100% rename from aws/sra-test/integration-tests/aws-sdk-s3/test-data/request-information-headers/three-successful-attempts.json rename to aws/sdk/integration-tests/s3/tests/data/request-information-headers/three-successful-attempts.json diff --git a/aws/sdk/integration-tests/s3/tests/interceptors.rs b/aws/sdk/integration-tests/s3/tests/interceptors.rs new file mode 100644 index 0000000000..be3caa493a --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/interceptors.rs @@ -0,0 +1,125 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(not(aws_sdk_middleware_mode))] +mod tests { + use aws_sdk_s3::config::interceptors::BeforeTransmitInterceptorContextMut; + use aws_sdk_s3::config::{Credentials, Region}; + use aws_sdk_s3::Client; + use aws_smithy_client::erase::DynConnector; + use aws_smithy_client::test_connection::capture_request; + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::interceptors::Interceptor; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; + use aws_smithy_types::config_bag::{ConfigBag, Layer, Storable, StoreReplace}; + use http::header::USER_AGENT; + use http::HeaderValue; + + #[tokio::test] + async fn interceptor_priority() { + #[derive(Debug, Eq, PartialEq)] + struct TestValue(&'static str); + impl Storable for TestValue { + type Storer = StoreReplace; + } + + #[derive(Debug)] + struct TestInterceptor(&'static str); + impl Interceptor for TestInterceptor { + fn name(&self) -> &'static str { + "TestInterceptor" + } + + fn modify_before_signing( + &self, + _context: &mut BeforeTransmitInterceptorContextMut<'_>, + _components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let mut layer = Layer::new("test"); + layer.store_put(TestValue(self.0)); + cfg.push_layer(layer); + Ok(()) + } + + fn modify_before_transmit( + &self, + context: &mut BeforeTransmitInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let value = cfg.load::().unwrap(); + context + .request_mut() + .headers_mut() + .insert("test-header", HeaderValue::from_static(value.0)); + Ok(()) + } + } + + let (conn, rx) = capture_request(None); + + // The first `TestInterceptor` will put `value1` into config + let config = aws_sdk_s3::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .http_connector(DynConnector::new(conn)) + .interceptor(TestInterceptor("value1")) + .build(); + let client = Client::from_conf(config); + + // The second `TestInterceptor` will replace `value1` with `value2` in config + dbg!( + client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .customize() + .await + .unwrap() + .interceptor(TestInterceptor("value2")) + .send() + .await + ) + .expect_err("no fake response set"); + + let request = rx.expect_request(); + assert_eq!("value2", request.headers()["test-header"]); + } + + #[tokio::test] + async fn set_test_user_agent_through_request_mutation() { + let (conn, rx) = capture_request(None); + + let config = aws_sdk_s3::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .http_connector(DynConnector::new(conn.clone())) + .build(); + let client = Client::from_conf(config); + + dbg!( + client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .customize() + .await + .unwrap() + .mutate_request(|request| { + let headers = request.headers_mut(); + headers.insert(USER_AGENT, HeaderValue::try_from("test").unwrap()); + headers.insert("x-amz-user-agent", HeaderValue::try_from("test").unwrap()); + }) + .send() + .await + ) + .expect_err("no fake response set"); + + let request = rx.expect_request(); + assert_eq!("test", request.headers()[USER_AGENT]); + assert_eq!("test", request.headers()["x-amz-user-agent"]); + } +} diff --git a/aws/sdk/integration-tests/s3/tests/request_information_headers.rs b/aws/sdk/integration-tests/s3/tests/request_information_headers.rs new file mode 100644 index 0000000000..33ee18fb9d --- /dev/null +++ b/aws/sdk/integration-tests/s3/tests/request_information_headers.rs @@ -0,0 +1,285 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#[cfg(not(aws_sdk_middleware_mode))] +mod tests { + use aws_http::user_agent::AwsUserAgent; + use aws_runtime::invocation_id::{InvocationId, PredefinedInvocationIdGenerator}; + use aws_sdk_s3::config::interceptors::BeforeSerializationInterceptorContextMut; + use aws_sdk_s3::config::interceptors::FinalizerInterceptorContextRef; + use aws_sdk_s3::config::retry::RetryConfig; + use aws_sdk_s3::config::timeout::TimeoutConfig; + use aws_sdk_s3::config::{Credentials, Region}; + use aws_sdk_s3::config::{Interceptor, SharedAsyncSleep}; + use aws_sdk_s3::Client; + use aws_smithy_async::test_util::InstantSleep; + use aws_smithy_async::test_util::ManualTimeSource; + use aws_smithy_async::time::SharedTimeSource; + use aws_smithy_client::dvr; + use aws_smithy_client::dvr::MediaType; + use aws_smithy_client::erase::DynConnector; + use aws_smithy_runtime::test_util::capture_test_logs::capture_test_logs; + use aws_smithy_runtime_api::box_error::BoxError; + use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; + use aws_smithy_types::config_bag::{ConfigBag, Layer}; + use std::time::{Duration, UNIX_EPOCH}; + + // # One SDK operation invocation. + // # Client retries 3 times, successful response on 3rd attempt. + // # Fast network, latency + server time is less than one second. + // # No clock skew + // # Client waits 1 second between retry attempts. + #[tokio::test] + async fn three_retries_and_then_success() { + let _logs = capture_test_logs(); + + #[derive(Debug)] + struct TimeInterceptor { + time_source: ManualTimeSource, + } + impl Interceptor for TimeInterceptor { + fn name(&self) -> &'static str { + "TimeInterceptor" + } + + fn modify_before_serialization( + &self, + _context: &mut BeforeSerializationInterceptorContextMut<'_>, + _runtime_components: &RuntimeComponents, + cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + let mut layer = Layer::new("test"); + layer.store_put(AwsUserAgent::for_tests()); + cfg.push_layer(layer); + Ok(()) + } + + fn read_after_attempt( + &self, + _context: &FinalizerInterceptorContextRef<'_>, + _runtime_components: &RuntimeComponents, + _cfg: &mut ConfigBag, + ) -> Result<(), BoxError> { + self.time_source.advance(Duration::from_secs(1)); + tracing::info!( + "################ ADVANCED TIME BY 1 SECOND, {:?}", + &self.time_source + ); + Ok(()) + } + } + + let time_source = ManualTimeSource::new(UNIX_EPOCH + Duration::from_secs(1559347200)); + + let path = "tests/data/request-information-headers/three-retries_and-then-success.json"; + let conn = dvr::ReplayingConnection::from_file(path).unwrap(); + let config = aws_sdk_s3::Config::builder() + .credentials_provider(Credentials::for_tests()) + .region(Region::new("us-east-1")) + .http_connector(DynConnector::new(conn.clone())) + .time_source(SharedTimeSource::new(time_source.clone())) + .sleep_impl(SharedAsyncSleep::new(InstantSleep::new(Default::default()))) + .retry_config(RetryConfig::standard()) + .timeout_config( + TimeoutConfig::builder() + .connect_timeout(Duration::from_secs(10)) + .read_timeout(Duration::from_secs(10)) + .build(), + ) + .invocation_id_generator(PredefinedInvocationIdGenerator::new(vec![ + InvocationId::new_from_str("00000000-0000-4000-8000-000000000000"), + ])) + .interceptor(TimeInterceptor { time_source }) + .build(); + let client = Client::from_conf(config); + + let resp = dbg!( + client + .list_objects_v2() + .bucket("test-bucket") + .prefix("prefix~") + .send() + .await + ); + + let resp = resp.expect("valid e2e test"); + assert_eq!(resp.name(), Some("test-bucket")); + conn.full_validate(MediaType::Xml).await.expect("failed") + } + // + // // # Client makes 3 separate SDK operation invocations + // // # All succeed on first attempt. + // // # Fast network, latency + server time is less than one second. + // // - request: + // // time: 2019-06-01T00:00:00Z + // // headers: + // // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 + // // amz-sdk-request: attempt=1; max=3 + // // response: + // // status: 200 + // // time_received: 2019-06-01T00:00:00Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:00:00 GMT + // // - request: + // // time: 2019-06-01T00:01:01Z + // // headers: + // // # Note the different invocation id because it's a new SDK + // // # invocation operation. + // // amz-sdk-invocation-id: 70370531-7b83-4b90-8b93-46975687ecf6 + // // amz-sdk-request: ttl=20190601T000011Z; attempt=1; max=3 + // // response: + // // status: 200 + // // time_received: 2019-06-01T00:00:01Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:00:01 GMT + // // - request: + // // time: 2019-06-01T00:00:02Z + // // headers: + // // amz-sdk-invocation-id: 910bf450-6c90-43de-a508-3fa126a06b71 + // // amz-sdk-request: ttl=20190601T000012Z; attempt=1; max=3 + // // response: + // // status: 200 + // // time_received: 2019-06-01T00:00:02Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:00:02 GMT + // const THREE_SUCCESSFUL_ATTEMPTS_PATH: &str = "test-data/request-information-headers/three-successful-attempts.json"; + // #[tokio::test] + // async fn three_successful_attempts() { + // tracing_subscriber::fmt::init(); + // + // impl RuntimePlugin for FixupPlugin { + // fn configure( + // &self, + // cfg: &mut ConfigBag, + // ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { + // let params_builder = Params::builder() + // .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) + // .bucket("test-bucket"); + // + // cfg.put(params_builder); + // cfg.set_request_time(RequestTime::new(self.timestamp.clone())); + // cfg.put(AwsUserAgent::for_tests()); + // cfg.put(InvocationId::for_tests()); + // Ok(()) + // } + // } + // + // let conn = dvr::ReplayingConnection::from_file(THREE_SUCCESSFUL_ATTEMPTS_PATH).unwrap(); + // let config = aws_sdk_s3::Config::builder() + // .credentials_provider(Credentials::for_tests()) + // .region(Region::new("us-east-1")) + // .http_connector(DynConnector::new(conn.clone())) + // .build(); + // let client = Client::from_conf(config); + // let fixup = FixupPlugin { + // client: client.clone(), + // timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), + // }; + // + // let resp = dbg!( + // client + // .list_objects_v2() + // .bucket("test-bucket") + // .prefix("prefix~") + // .send_v2_with_plugin(Some(fixup)) + // .await + // ); + // + // let resp = resp.expect("valid e2e test"); + // assert_eq!(resp.name(), Some("test-bucket")); + // conn.full_validate(MediaType::Xml).await.expect("failed") + // } + // + // // # One SDK operation invocation. + // // # Client retries 3 times, successful response on 3rd attempt. + // // # Slow network, one way latency is 2 seconds. + // // # Server takes 1 second to generate response. + // // # Client clock is 10 minutes behind server clock. + // // # One second delay between retries. + // // - request: + // // time: 2019-06-01T00:00:00Z + // // headers: + // // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 + // // amz-sdk-request: attempt=1; max=3 + // // response: + // // status: 500 + // // time_received: 2019-06-01T00:00:05Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:10:03 GMT + // // - request: + // // time: 2019-06-01T00:00:06Z + // // # The ttl is 00:00:16 with the client clock, + // // # but accounting for skew we have + // // # 00:10:03 - 00:00:05 = 00:09:58 + // // # ttl = 00:00:16 + 00:09:58 = 00:10:14 + // // headers: + // // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 + // // amz-sdk-request: ttl=20190601T001014Z; attempt=2; max=3 + // // response: + // // status: 500 + // // time_received: 2019-06-01T00:00:11Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:10:09 GMT + // // - request: + // // time: 2019-06-01T00:00:12Z + // // headers: + // // # ttl = 00:00:12 + 20 = 00:00:22 + // // # skew is: + // // # 00:10:09 - 00:00:11 + // // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 + // // amz-sdk-request: ttl=20190601T001020Z; attempt=3; max=3 + // // response: + // // status: 200 + // // time_received: 2019-06-01T00:00:17Z + // // headers: + // // Date: Sat, 01 Jun 2019 00:10:15 GMT + // const SLOW_NETWORK_AND_LATE_CLIENT_CLOCK_PATH: &str = "test-data/request-information-headers/slow-network-and-late-client-clock.json"; + // #[tokio::test] + // async fn slow_network_and_late_client_clock() { + // tracing_subscriber::fmt::init(); + // + // impl RuntimePlugin for FixupPlugin { + // fn configure( + // &self, + // cfg: &mut ConfigBag, + // ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { + // let params_builder = Params::builder() + // .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) + // .bucket("test-bucket"); + // + // cfg.put(params_builder); + // cfg.set_request_time(RequestTime::new(self.timestamp.clone())); + // cfg.put(AwsUserAgent::for_tests()); + // cfg.put(InvocationId::for_tests()); + // Ok(()) + // } + // } + // + // let conn = dvr::ReplayingConnection::from_file(SLOW_NETWORK_AND_LATE_CLIENT_CLOCK_PATH).unwrap(); + // let config = aws_sdk_s3::Config::builder() + // .credentials_provider(Credentials::for_tests()) + // .region(Region::new("us-east-1")) + // .http_connector(DynConnector::new(conn.clone())) + // .build(); + // let client = Client::from_conf(config); + // let fixup = FixupPlugin { + // client: client.clone(), + // timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), + // }; + // + // let resp = dbg!( + // client + // .list_objects_v2() + // .bucket("test-bucket") + // .prefix("prefix~") + // .send_v2_with_plugin(Some(fixup)) + // .await + // ); + // + // let resp = resp.expect("valid e2e test"); + // assert_eq!(resp.name(), Some("test-bucket")); + // conn.full_validate(MediaType::Xml).await.expect("failed") + // } +} diff --git a/aws/sra-test/.gitignore b/aws/sra-test/.gitignore deleted file mode 100644 index 388d181b4d..0000000000 --- a/aws/sra-test/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/smithy-build.json diff --git a/aws/sra-test/build.gradle.kts b/aws/sra-test/build.gradle.kts deleted file mode 100644 index a9bb7c328f..0000000000 --- a/aws/sra-test/build.gradle.kts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -extra["displayName"] = "Smithy :: Rust :: AWS-SDK :: SRA Test" -extra["moduleName"] = "software.amazon.smithy.rust.awssdk.sra.test" - -tasks["jar"].enabled = false - -plugins { - id("software.amazon.smithy") -} - -val smithyVersion: String by project -val defaultRustDocFlags: String by project -val properties = PropertyRetriever(rootProject, project) - -val pluginName = "rust-client-codegen" -val workingDirUnderBuildDir = "smithyprojections/sdk-sra-test/" - -val publisherToolPath = rootProject.projectDir.resolve("tools/ci-build/publisher") -val outputDir = buildDir.resolve("sdk") - -configure { - outputDirectory = file("$buildDir/$workingDirUnderBuildDir") -} - -buildscript { - val smithyVersion: String by project - dependencies { - classpath("software.amazon.smithy:smithy-cli:$smithyVersion") - } -} - -dependencies { - implementation(project(":aws:sdk-codegen")) - implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion") - implementation("software.amazon.smithy:smithy-aws-traits:$smithyVersion") -} - -data class Service( - val serviceId: String, - val moduleName: String, - val imports: List, -) -val servicesToGenerate = listOf( - Service( - "com.amazonaws.dynamodb#DynamoDB_20120810", - "aws-sdk-dynamodb", - listOf("../sdk/aws-models/dynamodb.json"), - ), - Service( - "com.amazonaws.s3#AmazonS3", - "aws-sdk-s3", - listOf("../sdk/aws-models/s3.json", "../sdk/aws-models/s3-tests.smithy"), - ), -) -val allCodegenTests = servicesToGenerate.map { - CodegenTest( - it.serviceId, - it.moduleName, - imports = it.imports, - extraConfig = """ - , - "codegen": { - "includeFluentClient": false, - "enableNewSmithyRuntime": "orchestrator", - "includeEndpointUrlConfig": false - }, - "customizationConfig": { - "awsSdk": { - "generateReadme": false - } - } - """, - ) -} - -project.registerGenerateSmithyBuildTask(rootProject, pluginName, allCodegenTests) -project.registerGenerateCargoWorkspaceTask(rootProject, pluginName, allCodegenTests, workingDirUnderBuildDir) -project.registerGenerateCargoConfigTomlTask(buildDir.resolve(workingDirUnderBuildDir)) - -tasks["smithyBuildJar"].dependsOn("generateSmithyBuild") -tasks["assemble"].finalizedBy("generateCargoWorkspace") - -project.registerModifyMtimeTask() -project.registerCargoCommandsTasks(buildDir.resolve(workingDirUnderBuildDir), defaultRustDocFlags) - -tasks["test"].finalizedBy(cargoCommands(properties).map { it.toString }) - -tasks["clean"].doFirst { delete("smithy-build.json") } - -/** - * The aws/rust-runtime crates depend on local versions of the Smithy core runtime enabling local compilation. However, - * those paths need to be replaced in the final build. We should probably fix this with some symlinking. - */ -fun rewritePathDependency(line: String): String { - // some runtime crates are actually dependent on the generated bindings: - return line.replace("../sdk/build/aws-sdk/sdk/", "") - // others use relative dependencies:: - .replace("../../rust-runtime/", "") -} - -tasks.register("relocateServices") { - description = "relocate AWS services to their final destination" - doLast { - servicesToGenerate.forEach { service -> - logger.info("Relocating ${service.moduleName}...") - copy { - from("$buildDir/smithyprojections/sdk-sra-test/${service.moduleName}/rust-client-codegen") - into(outputDir.resolve(service.moduleName)) - } - copy { - from(projectDir.resolve("integration-tests/${service.moduleName}/tests")) - into(outputDir.resolve(service.moduleName).resolve("tests")) - } - } - } - dependsOn("smithyBuildJar") - inputs.dir("$buildDir/smithyprojections/sdk-sra-test/") - outputs.dir(outputDir) -} -tasks["assemble"].apply { - dependsOn("relocateServices") -} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/.gitignore b/aws/sra-test/integration-tests/aws-sdk-s3/.gitignore deleted file mode 100644 index 5a44eef09a..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/Cargo.lock diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml b/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml deleted file mode 100644 index 82abf8c094..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "aws-smithy-runtime-test" -version = "0.1.0" -edition = "2021" -publish = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -aws-http = { path = "../../../rust-runtime/aws-http" } -aws-runtime = { path = "../../../rust-runtime/aws-runtime" } -aws-sdk-s3 = { path = "../../build/sdk/aws-sdk-s3", features = ["test-util"] } -aws-smithy-async = { path = "../../../../rust-runtime/aws-smithy-async", features = ["test-util"]} -aws-smithy-client = { path = "../../../../rust-runtime/aws-smithy-client", features = ["test-util", "rustls"] } -aws-smithy-runtime = { path = "../../../../rust-runtime/aws-smithy-runtime" } -aws-smithy-runtime-api = { path = "../../../../rust-runtime/aws-smithy-runtime-api" } -aws-smithy-types = { path = "../../../../rust-runtime/aws-smithy-types" } -aws-types = { path = "../../../rust-runtime/aws-types" } -criterion = { version = "0.4", features = ["async_tokio"] } -http = "0.2.3" -http-body = "0.4.5" -last-release-smithy-client = { version = "0.55.3", package = "aws-smithy-client", features = ["test-util", "rustls"] } -last-release-s3 = { version = "0.28", package = "aws-sdk-s3", features = ["test-util"] } -tokio = { version = "1.23.1", features = ["macros", "test-util", "rt-multi-thread"] } -tracing = "0.1.37" -tracing-subscriber = { version = "0.3.15", features = ["env-filter", "json"] } - -[profile.release] -debug = 1 - -[[bench]] -name = "middleware_vs_orchestrator" -harness = false diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/benches/README.md b/aws/sra-test/integration-tests/aws-sdk-s3/benches/README.md deleted file mode 100644 index cf8f97680c..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/benches/README.md +++ /dev/null @@ -1,6 +0,0 @@ -### Middleware vs. Orchestrator Benchmark - -To run the benchmark: -```bash -./gradlew :aws:sra-test:assemble && (cd aws/sra-test/integration-tests/aws-sdk-s3 && cargo bench) -``` diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/test-data/list-objects-v2.json b/aws/sra-test/integration-tests/aws-sdk-s3/test-data/list-objects-v2.json deleted file mode 100644 index 366e1fab1b..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/test-data/list-objects-v2.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "events": [ - { - "connection_id": 0, - "action": { - "Request": { - "request": { - "uri": "https://test-bucket.s3.us-east-1.amazonaws.com/?list-type=2&prefix=prefix~", - "headers": { - "x-amz-security-token": [ - "notarealsessiontoken" - ], - "authorization": [ - "AWS4-HMAC-SHA256 Credential=ANOTREAL/20210618/us-east-1/s3/aws4_request, SignedHeaders=amz-sdk-invocation-id;host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=e7eccf4e792113f5f17a50bfd8f1719479e89ba0b476894e6f3dba030dc87f82" - ], - "x-amz-user-agent": [ - "aws-sdk-rust/0.123.test api/test-service/0.123 os/windows/XPSP3 lang/rust/1.50.0" - ], - "x-amz-date": [ - "20210618T170728Z" - ], - "x-amz-content-sha256": [ - "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" - ], - "amz-sdk-invocation-id": [ - "00000000-0000-4000-8000-000000000000" - ], - "user-agent": [ - "aws-sdk-rust/0.123.test os/windows/XPSP3 lang/rust/1.50.0" - ] - }, - "method": "GET" - } - } - } - }, - { - "connection_id": 0, - "action": { - "Eof": { - "ok": true, - "direction": "Request" - } - } - }, - { - "connection_id": 0, - "action": { - "Response": { - "response": { - "Ok": { - "status": 200, - "version": "HTTP/1.1", - "headers": { - "x-amz-request-id": [ - "9X5E7C9EAB6AQEP2" - ], - "x-amz-id-2": [ - "gZsrBxajPyo1Q0DE2plGf7T6kAnxd4Xx7/S+8lq18GegL6kFbnVXLLh1LnBzpEpFiHN9XoNHkeA=" - ], - "content-type": [ - "application/xml" - ], - "transfer-encoding": [ - "chunked" - ], - "server": [ - "AmazonS3" - ], - "date": [ - "Wed, 26 Apr 2023 14:00:24 GMT" - ], - "x-amz-bucket-region": [ - "us-east-1" - ] - } - } - } - } - } - }, - { - "connection_id": 0, - "action": { - "Data": { - "data": { - "Utf8": "\n\n test-bucket\n prefix~\n 1\n 1000\n false\n \n some-file.file\n 2009-10-12T17:50:30.000Z\n 434234\n STANDARD\n \n" - }, - "direction": "Response" - } - } - }, - { - "connection_id": 0, - "action": { - "Eof": { - "ok": true, - "direction": "Response" - } - } - } - ], - "docs": "Test sending an S3 ListObjectsV2 operation with a successful response.", - "version": "V0" -} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs deleted file mode 100644 index c65605c8eb..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/interceptors.rs +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -mod util; - -use aws_http::user_agent::AwsUserAgent; -use aws_sdk_s3::config::{Credentials, Region}; -use aws_sdk_s3::Client; -use aws_smithy_async::test_util::StaticTimeSource; -use aws_smithy_client::dvr; -use aws_smithy_client::dvr::MediaType; -use aws_smithy_client::erase::DynConnector; -use aws_smithy_runtime_api::client::interceptors::{ - BeforeTransmitInterceptorContextMut, Interceptor, -}; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; -use aws_smithy_types::config_bag::ConfigBag; -use http::header::USER_AGENT; -use http::HeaderValue; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - -const LIST_BUCKETS_PATH: &str = "test-data/list-objects-v2.json"; - -#[tokio::test] -async fn operation_interceptor_test() { - tracing_subscriber::fmt::init(); - - let conn = dvr::ReplayingConnection::from_file(LIST_BUCKETS_PATH).unwrap(); - - // Not setting `TestUserAgentInterceptor` here, expecting it to be set later by the - // operation-level config. - let config = aws_sdk_s3::Config::builder() - .credentials_provider(Credentials::for_tests()) - .region(Region::new("us-east-1")) - .http_connector(DynConnector::new(conn.clone())) - .build(); - let client = Client::from_conf(config); - let fixup = util::FixupPlugin { - timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), - }; - - let resp = dbg!( - client - .list_objects_v2() - .bucket("test-bucket") - .prefix("prefix~") - .customize() - .await - .unwrap() - .interceptor(util::TestUserAgentInterceptor) - .send_orchestrator_with_plugin(Some(fixup)) - .await - ); - let resp = resp.expect("valid e2e test"); - assert_eq!(resp.name(), Some("test-bucket")); - conn.full_validate(MediaType::Xml).await.expect("success") -} - -#[derive(Debug)] -struct RequestTimeResetInterceptor; -impl Interceptor for RequestTimeResetInterceptor { - fn modify_before_signing( - &self, - _context: &mut BeforeTransmitInterceptorContextMut<'_>, - cfg: &mut ConfigBag, - ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { - cfg.set_request_time(StaticTimeSource::new(UNIX_EPOCH)); - - Ok(()) - } -} - -#[derive(Debug)] -struct RequestTimeAdvanceInterceptor(Duration); -impl Interceptor for RequestTimeAdvanceInterceptor { - fn modify_before_signing( - &self, - _context: &mut BeforeTransmitInterceptorContextMut<'_>, - cfg: &mut ConfigBag, - ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { - let request_time = cfg.request_time().unwrap(); - let request_time = StaticTimeSource::new(request_time.now() + self.0); - cfg.set_request_time(request_time); - - Ok(()) - } -} - -#[tokio::test] -async fn interceptor_priority() { - let conn = dvr::ReplayingConnection::from_file(LIST_BUCKETS_PATH).unwrap(); - - // `RequestTimeResetInterceptor` will reset a `RequestTime` to `UNIX_EPOCH`, whose previous - // value should be `SystemTime::now()` set by `FixupPlugin`. - let config = aws_sdk_s3::Config::builder() - .credentials_provider(Credentials::for_tests()) - .region(Region::new("us-east-1")) - .http_connector(DynConnector::new(conn.clone())) - .interceptor(util::TestUserAgentInterceptor) - .interceptor(RequestTimeResetInterceptor) - .build(); - let client = Client::from_conf(config); - let fixup = util::FixupPlugin { - timestamp: SystemTime::now(), - }; - - // `RequestTimeAdvanceInterceptor` configured at the operation level should run after, - // expecting the `RequestTime` to move forward by the specified amount since `UNIX_EPOCH`. - let resp = dbg!( - client - .list_objects_v2() - .bucket("test-bucket") - .prefix("prefix~") - .customize() - .await - .unwrap() - .interceptor(RequestTimeAdvanceInterceptor(Duration::from_secs( - 1624036048 - ))) - .send_orchestrator_with_plugin(Some(fixup)) - .await - ); - let resp = resp.expect("valid e2e test"); - assert_eq!(resp.name(), Some("test-bucket")); - conn.full_validate(MediaType::Xml).await.expect("success") -} - -#[tokio::test] -async fn set_test_user_agent_through_request_mutation() { - let conn = dvr::ReplayingConnection::from_file(LIST_BUCKETS_PATH).unwrap(); - - let config = aws_sdk_s3::Config::builder() - .credentials_provider(Credentials::for_tests()) - .region(Region::new("us-east-1")) - .http_connector(DynConnector::new(conn.clone())) - .build(); - let client = Client::from_conf(config); - let fixup = util::FixupPlugin { - timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), - }; - - let resp = dbg!( - client - .list_objects_v2() - .bucket("test-bucket") - .prefix("prefix~") - .customize() - .await - .unwrap() - .mutate_request(|request| { - let headers = request.headers_mut(); - let user_agent = AwsUserAgent::for_tests(); - headers.insert( - USER_AGENT, - HeaderValue::try_from(user_agent.ua_header()).unwrap(), - ); - headers.insert( - util::X_AMZ_USER_AGENT, - HeaderValue::try_from(user_agent.aws_ua_header()).unwrap(), - ); - }) - .send_orchestrator_with_plugin(Some(fixup)) - .await - ); - let resp = resp.expect("valid e2e test"); - assert_eq!(resp.name(), Some("test-bucket")); - conn.full_validate(MediaType::Xml).await.expect("success") -} diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs deleted file mode 100644 index 28a8df6c04..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/request_information_headers.rs +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_http::user_agent::AwsUserAgent; -use aws_runtime::invocation_id::InvocationId; -use aws_sdk_s3::config::{Credentials, Region}; -use aws_sdk_s3::endpoint::Params; -use aws_sdk_s3::Client; -use aws_smithy_client::dvr; -use aws_smithy_client::dvr::MediaType; -use aws_smithy_client::erase::DynConnector; -use aws_smithy_runtime::client::retries::strategy::FixedDelayRetryStrategy; -use aws_smithy_runtime_api::client::interceptors::InterceptorRegistrar; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; -use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_types::client::orchestrator::ConfigBagAccessors; -use aws_smithy_types::config_bag::ConfigBag; -use std::time::{Duration, UNIX_EPOCH}; - -#[derive(Debug)] -struct FixupPlugin { - client: Client, -} - -// # One SDK operation invocation. -// # Client retries 3 times, successful response on 3rd attempt. -// # Fast network, latency + server time is less than one second. -// # No clock skew -// # Client waits 1 second between retry attempts. -#[tokio::test] -async fn three_retries_and_then_success() { - tracing_subscriber::fmt::init(); - - impl RuntimePlugin for FixupPlugin { - fn configure( - &self, - cfg: &mut ConfigBag, - _interceptors: &mut InterceptorRegistrar, - ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { - let params_builder = Params::builder() - .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) - .bucket("test-bucket"); - - cfg.put(params_builder); - cfg.put(AwsUserAgent::for_tests()); - cfg.put(InvocationId::for_tests()); - cfg.set_retry_strategy(FixedDelayRetryStrategy::one_second_delay()); - Ok(()) - } - } - - let path = "test-data/request-information-headers/three-retries_and-then-success.json"; - let conn = dvr::ReplayingConnection::from_file(path).unwrap(); - let config = aws_sdk_s3::Config::builder() - .credentials_provider(Credentials::for_tests()) - .region(Region::new("us-east-1")) - .http_connector(DynConnector::new(conn.clone())) - .time_source(UNIX_EPOCH + Duration::from_secs(1624036048)) - .build(); - let client = Client::from_conf(config); - let fixup = FixupPlugin { - client: client.clone(), - }; - - let resp = dbg!( - client - .list_objects_v2() - .bucket("test-bucket") - .prefix("prefix~") - .customize() - .await - .unwrap() - .config_override(aws_sdk_s3::Config::builder().force_path_style(false)) - .send_orchestrator_with_plugin(Some(fixup)) - .await - ); - - let resp = resp.expect("valid e2e test"); - assert_eq!(resp.name(), Some("test-bucket")); - conn.full_validate(MediaType::Xml).await.expect("failed") -} -// -// // # Client makes 3 separate SDK operation invocations -// // # All succeed on first attempt. -// // # Fast network, latency + server time is less than one second. -// // - request: -// // time: 2019-06-01T00:00:00Z -// // headers: -// // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 -// // amz-sdk-request: attempt=1; max=3 -// // response: -// // status: 200 -// // time_received: 2019-06-01T00:00:00Z -// // headers: -// // Date: Sat, 01 Jun 2019 00:00:00 GMT -// // - request: -// // time: 2019-06-01T00:01:01Z -// // headers: -// // # Note the different invocation id because it's a new SDK -// // # invocation operation. -// // amz-sdk-invocation-id: 70370531-7b83-4b90-8b93-46975687ecf6 -// // amz-sdk-request: ttl=20190601T000011Z; attempt=1; max=3 -// // response: -// // status: 200 -// // time_received: 2019-06-01T00:00:01Z -// // headers: -// // Date: Sat, 01 Jun 2019 00:00:01 GMT -// // - request: -// // time: 2019-06-01T00:00:02Z -// // headers: -// // amz-sdk-invocation-id: 910bf450-6c90-43de-a508-3fa126a06b71 -// // amz-sdk-request: ttl=20190601T000012Z; attempt=1; max=3 -// // response: -// // status: 200 -// // time_received: 2019-06-01T00:00:02Z -// // headers: -// // Date: Sat, 01 Jun 2019 00:00:02 GMT -// const THREE_SUCCESSFUL_ATTEMPTS_PATH: &str = "test-data/request-information-headers/three-successful-attempts.json"; -// #[tokio::test] -// async fn three_successful_attempts() { -// tracing_subscriber::fmt::init(); -// -// impl RuntimePlugin for FixupPlugin { -// fn configure( -// &self, -// cfg: &mut ConfigBag, -// ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { -// let params_builder = Params::builder() -// .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) -// .bucket("test-bucket"); -// -// cfg.put(params_builder); -// cfg.set_request_time(RequestTime::new(self.timestamp.clone())); -// cfg.put(AwsUserAgent::for_tests()); -// cfg.put(InvocationId::for_tests()); -// Ok(()) -// } -// } -// -// let conn = dvr::ReplayingConnection::from_file(THREE_SUCCESSFUL_ATTEMPTS_PATH).unwrap(); -// let config = aws_sdk_s3::Config::builder() -// .credentials_provider(Credentials::for_tests()) -// .region(Region::new("us-east-1")) -// .http_connector(DynConnector::new(conn.clone())) -// .build(); -// let client = Client::from_conf(config); -// let fixup = FixupPlugin { -// client: client.clone(), -// timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), -// }; -// -// let resp = dbg!( -// client -// .list_objects_v2() -// .bucket("test-bucket") -// .prefix("prefix~") -// .send_v2_with_plugin(Some(fixup)) -// .await -// ); -// -// let resp = resp.expect("valid e2e test"); -// assert_eq!(resp.name(), Some("test-bucket")); -// conn.full_validate(MediaType::Xml).await.expect("failed") -// } -// -// // # One SDK operation invocation. -// // # Client retries 3 times, successful response on 3rd attempt. -// // # Slow network, one way latency is 2 seconds. -// // # Server takes 1 second to generate response. -// // # Client clock is 10 minutes behind server clock. -// // # One second delay between retries. -// // - request: -// // time: 2019-06-01T00:00:00Z -// // headers: -// // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 -// // amz-sdk-request: attempt=1; max=3 -// // response: -// // status: 500 -// // time_received: 2019-06-01T00:00:05Z -// // headers: -// // Date: Sat, 01 Jun 2019 00:10:03 GMT -// // - request: -// // time: 2019-06-01T00:00:06Z -// // # The ttl is 00:00:16 with the client clock, -// // # but accounting for skew we have -// // # 00:10:03 - 00:00:05 = 00:09:58 -// // # ttl = 00:00:16 + 00:09:58 = 00:10:14 -// // headers: -// // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 -// // amz-sdk-request: ttl=20190601T001014Z; attempt=2; max=3 -// // response: -// // status: 500 -// // time_received: 2019-06-01T00:00:11Z -// // headers: -// // Date: Sat, 01 Jun 2019 00:10:09 GMT -// // - request: -// // time: 2019-06-01T00:00:12Z -// // headers: -// // # ttl = 00:00:12 + 20 = 00:00:22 -// // # skew is: -// // # 00:10:09 - 00:00:11 -// // amz-sdk-invocation-id: 3dfe4f26-c090-4887-8c14-7bac778bca07 -// // amz-sdk-request: ttl=20190601T001020Z; attempt=3; max=3 -// // response: -// // status: 200 -// // time_received: 2019-06-01T00:00:17Z -// // headers: -// // Date: Sat, 01 Jun 2019 00:10:15 GMT -// const SLOW_NETWORK_AND_LATE_CLIENT_CLOCK_PATH: &str = "test-data/request-information-headers/slow-network-and-late-client-clock.json"; -// #[tokio::test] -// async fn slow_network_and_late_client_clock() { -// tracing_subscriber::fmt::init(); -// -// impl RuntimePlugin for FixupPlugin { -// fn configure( -// &self, -// cfg: &mut ConfigBag, -// ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { -// let params_builder = Params::builder() -// .set_region(self.client.conf().region().map(|c| c.as_ref().to_string())) -// .bucket("test-bucket"); -// -// cfg.put(params_builder); -// cfg.set_request_time(RequestTime::new(self.timestamp.clone())); -// cfg.put(AwsUserAgent::for_tests()); -// cfg.put(InvocationId::for_tests()); -// Ok(()) -// } -// } -// -// let conn = dvr::ReplayingConnection::from_file(SLOW_NETWORK_AND_LATE_CLIENT_CLOCK_PATH).unwrap(); -// let config = aws_sdk_s3::Config::builder() -// .credentials_provider(Credentials::for_tests()) -// .region(Region::new("us-east-1")) -// .http_connector(DynConnector::new(conn.clone())) -// .build(); -// let client = Client::from_conf(config); -// let fixup = FixupPlugin { -// client: client.clone(), -// timestamp: UNIX_EPOCH + Duration::from_secs(1624036048), -// }; -// -// let resp = dbg!( -// client -// .list_objects_v2() -// .bucket("test-bucket") -// .prefix("prefix~") -// .send_v2_with_plugin(Some(fixup)) -// .await -// ); -// -// let resp = resp.expect("valid e2e test"); -// assert_eq!(resp.name(), Some("test-bucket")); -// conn.full_validate(MediaType::Xml).await.expect("failed") -// } diff --git a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs b/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs deleted file mode 100644 index 992f9a748e..0000000000 --- a/aws/sra-test/integration-tests/aws-sdk-s3/tests/util.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -use aws_http::user_agent::AwsUserAgent; -use aws_runtime::invocation_id::InvocationId; -use aws_smithy_async::test_util::StaticTimeSource; -use aws_smithy_runtime_api::client::interceptors::{ - BeforeTransmitInterceptorContextMut, Interceptor, InterceptorRegistrar, -}; -use aws_smithy_runtime_api::client::orchestrator::ConfigBagAccessors; -use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin; -use aws_smithy_types::config_bag::ConfigBag; -use http::header::USER_AGENT; -use http::{HeaderName, HeaderValue}; -use std::time::SystemTime; - -pub const X_AMZ_USER_AGENT: HeaderName = HeaderName::from_static("x-amz-user-agent"); - -#[derive(Debug)] -pub struct FixupPlugin; -impl RuntimePlugin for FixupPlugin { - fn configure( - &self, - cfg: &mut ConfigBag, - _interceptors: &mut InterceptorRegistrar, - ) -> Result<(), aws_smithy_runtime_api::client::runtime_plugin::BoxError> { - cfg.put(InvocationId::for_tests()); - Ok(()) - } -} - -#[derive(Debug)] -pub struct TestUserAgentInterceptor; -impl Interceptor for TestUserAgentInterceptor { - fn modify_before_signing( - &self, - context: &mut BeforeTransmitInterceptorContextMut<'_>, - _cfg: &mut ConfigBag, - ) -> Result<(), aws_smithy_runtime_api::client::interceptors::BoxError> { - let headers = context.request_mut().headers_mut(); - let user_agent = AwsUserAgent::for_tests(); - // Overwrite user agent header values provided by `UserAgentInterceptor` - headers.insert(USER_AGENT, HeaderValue::try_from(user_agent.ua_header())?); - headers.insert( - X_AMZ_USER_AGENT, - HeaderValue::try_from(user_agent.aws_ua_header())?, - ); - - Ok(()) - } -} diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt index a79e2be687..8dde7f43a8 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/ResiliencyConfigCustomization.kt @@ -370,8 +370,16 @@ class ResiliencyConfigCustomization(private val codegenContext: ClientCodegenCon if (runtimeMode.generateOrchestrator) { rustTemplate( """ - let retry_partition = layer.load::<#{RetryPartition}>().cloned().unwrap_or_else(|| #{RetryPartition}::new("${codegenContext.serviceShape.sdkId()}")); - let retry_config = layer.load::<#{RetryConfig}>().cloned().unwrap_or_else(#{RetryConfig}::disabled); + if layer.load::<#{RetryConfig}>().is_none() { + layer.store_put(#{RetryConfig}::disabled()); + } + let retry_config = layer.load::<#{RetryConfig}>().expect("set to default above").clone(); + + if layer.load::<#{RetryPartition}>().is_none() { + layer.store_put(#{RetryPartition}::new("${codegenContext.serviceShape.sdkId()}")); + } + let retry_partition = layer.load::<#{RetryPartition}>().expect("set to default above").clone(); + if retry_config.has_retry() { #{debug}!("using retry strategy with partition '{}'", retry_partition); } diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt index ab9a96155c..83e1119013 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt @@ -116,7 +116,7 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust rustTemplate( """ if self.runtime_components.time_source().is_none() { - self.runtime_components.set_time_source(#{Default}::default()); + self.runtime_components.set_time_source(#{Some}(#{Default}::default())); } """, *codegenScope, diff --git a/rust-runtime/aws-smithy-client/src/dvr/replay.rs b/rust-runtime/aws-smithy-client/src/dvr/replay.rs index 94d9123e0e..27e606f082 100644 --- a/rust-runtime/aws-smithy-client/src/dvr/replay.rs +++ b/rust-runtime/aws-smithy-client/src/dvr/replay.rs @@ -19,6 +19,7 @@ use tokio::task::JoinHandle; use aws_smithy_http::body::SdkBody; use aws_smithy_http::result::ConnectorError; use aws_smithy_protocol_test::MediaType; +use aws_smithy_types::error::display::DisplayErrorContext; use crate::dvr::{Action, ConnectionId, Direction, Event, NetworkTraffic}; @@ -137,7 +138,14 @@ impl ReplayingConnection { )) }) .collect::>(); - aws_smithy_protocol_test::validate_headers(actual.headers(), expected_headers)?; + aws_smithy_protocol_test::validate_headers(actual.headers(), expected_headers) + .map_err(|err| { + format!( + "event {} validation failed with: {}", + conn_id.0, + DisplayErrorContext(&err) + ) + })?; } Ok(()) } @@ -272,6 +280,7 @@ impl tower::Service> for ReplayingConnection { fn call(&mut self, mut req: Request) -> Self::Future { let event_id = self.next_id(); + tracing::debug!("received event {}: {req:?}", event_id.0); let mut events = match self.live_events.lock().unwrap().remove(&event_id) { Some(traffic) => traffic, None => { diff --git a/rust-runtime/aws-smithy-http/src/connection.rs b/rust-runtime/aws-smithy-http/src/connection.rs index eb81f63687..f98bb780bd 100644 --- a/rust-runtime/aws-smithy-http/src/connection.rs +++ b/rust-runtime/aws-smithy-http/src/connection.rs @@ -82,7 +82,7 @@ impl CaptureSmithyConnection { match self.loader.lock().unwrap().as_ref() { Some(loader) => loader(), None => { - println!("no loader was set :-/"); + tracing::debug!("no loader was set on the CaptureSmithyConnection"); None } } diff --git a/settings.gradle.kts b/settings.gradle.kts index e33a925bb7..6c920d7217 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,7 +19,6 @@ include(":aws:rust-runtime") include(":aws:sdk") include(":aws:sdk-adhoc-test") include(":aws:sdk-codegen") -include(":aws:sra-test") pluginManagement { val smithyGradlePluginVersion: String by settings From a0b60ed5e01bfe1c733c9778dd9475e482704c93 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Fri, 28 Jul 2023 13:16:44 -0400 Subject: [PATCH 243/253] Add clippy.toml with forbidden methods & fix SystemTime usages (#2882) ## Motivation and Context - #2087 ## Description - remove lingering usages of SystemTime::now() - ensure they don't return by adding a clippy.toml ## Testing - CI - Ran the webassembly example ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --------- Co-authored-by: John DiSanti --- aws/rust-runtime/aws-config/clippy.toml | 1 + aws/rust-runtime/aws-inlineable/src/presigning.rs | 6 +++++- aws/rust-runtime/aws-runtime/src/request_info.rs | 4 +++- .../aws-runtime/src/service_clock_skew.rs | 11 ++++++++--- aws/rust-runtime/aws-sig-auth/src/event_stream.rs | 3 ++- aws/rust-runtime/aws-sig-auth/src/lib.rs | 2 ++ aws/rust-runtime/clippy.toml | 1 + .../smithy/rustsdk/EndpointsCredentialsTest.kt | 2 +- .../smithy/rustsdk/InvocationIdDecoratorTest.kt | 2 +- .../software/amazon/smithy/rustsdk/TestUtil.kt | 1 + aws/sdk/build.gradle.kts | 2 ++ clippy-root.toml | 9 +++++++++ .../customizations/TimeSourceCustomization.kt | 15 +++++++++++++++ .../core/testutil/CodegenIntegrationTest.kt | 3 ++- rust-runtime/aws-smithy-async/src/time.rs | 2 ++ rust-runtime/clippy.toml | 1 + 16 files changed, 56 insertions(+), 9 deletions(-) create mode 120000 aws/rust-runtime/aws-config/clippy.toml create mode 120000 aws/rust-runtime/clippy.toml create mode 100644 clippy-root.toml create mode 120000 rust-runtime/clippy.toml diff --git a/aws/rust-runtime/aws-config/clippy.toml b/aws/rust-runtime/aws-config/clippy.toml new file mode 120000 index 0000000000..85f6167cb1 --- /dev/null +++ b/aws/rust-runtime/aws-config/clippy.toml @@ -0,0 +1 @@ +../clippy.toml \ No newline at end of file diff --git a/aws/rust-runtime/aws-inlineable/src/presigning.rs b/aws/rust-runtime/aws-inlineable/src/presigning.rs index 1bc9f42e6f..c9de75dfb0 100644 --- a/aws/rust-runtime/aws-inlineable/src/presigning.rs +++ b/aws/rust-runtime/aws-inlineable/src/presigning.rs @@ -147,7 +147,11 @@ impl PresigningConfigBuilder { return Err(ErrorKind::ExpiresInDurationTooLong.into()); } Ok(PresigningConfig { - start_time: self.start_time.unwrap_or_else(SystemTime::now), + start_time: self.start_time.unwrap_or_else( + // This usage is OK—customers can easily override this. + #[allow(clippy::disallowed_methods)] + SystemTime::now, + ), expires_in, }) } diff --git a/aws/rust-runtime/aws-runtime/src/request_info.rs b/aws/rust-runtime/aws-runtime/src/request_info.rs index 5b7dccee55..04825141b3 100644 --- a/aws/rust-runtime/aws-runtime/src/request_info.rs +++ b/aws/rust-runtime/aws-runtime/src/request_info.rs @@ -180,12 +180,14 @@ mod tests { use super::RequestInfoInterceptor; use crate::request_info::RequestPairs; use aws_smithy_http::body::SdkBody; - use aws_smithy_runtime_api::client::interceptors::context::{Input, InterceptorContext}; + use aws_smithy_runtime_api::client::interceptors::context::Input; + use aws_smithy_runtime_api::client::interceptors::context::InterceptorContext; use aws_smithy_runtime_api::client::interceptors::Interceptor; use aws_smithy_runtime_api::client::runtime_components::RuntimeComponentsBuilder; use aws_smithy_types::config_bag::{ConfigBag, Layer}; use aws_smithy_types::retry::RetryConfig; use aws_smithy_types::timeout::TimeoutConfig; + use http::HeaderValue; use std::time::Duration; diff --git a/aws/rust-runtime/aws-runtime/src/service_clock_skew.rs b/aws/rust-runtime/aws-runtime/src/service_clock_skew.rs index 7ada75c2bf..a919f37cad 100644 --- a/aws/rust-runtime/aws-runtime/src/service_clock_skew.rs +++ b/aws/rust-runtime/aws-runtime/src/service_clock_skew.rs @@ -10,7 +10,7 @@ use aws_smithy_runtime_api::client::runtime_components::RuntimeComponents; use aws_smithy_types::config_bag::{ConfigBag, Storable, StoreReplace}; use aws_smithy_types::date_time::Format; use aws_smithy_types::DateTime; -use std::time::{Duration, SystemTime}; +use std::time::Duration; /// Amount of clock skew between the client and the service. #[derive(Debug, Clone)] @@ -72,10 +72,15 @@ impl Interceptor for ServiceClockSkewInterceptor { fn modify_before_deserialization( &self, ctx: &mut BeforeDeserializationInterceptorContextMut<'_>, - _runtime_components: &RuntimeComponents, + runtime_components: &RuntimeComponents, cfg: &mut ConfigBag, ) -> Result<(), BoxError> { - let time_received = DateTime::from(SystemTime::now()); + let time_received = DateTime::from( + runtime_components + .time_source() + .ok_or("a time source is required (service clock skew)")? + .now(), + ); let time_sent = match extract_time_sent_from_response(ctx) { Ok(time_sent) => time_sent, Err(e) => { diff --git a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs index 01a1dd55fa..bf51ac2723 100644 --- a/aws/rust-runtime/aws-sig-auth/src/event_stream.rs +++ b/aws/rust-runtime/aws-sig-auth/src/event_stream.rs @@ -3,8 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -// TODO(enableNewSmithyRuntimeCleanup): Remove this blanket allow once the old implementations are deleted +// this code is dead #![allow(deprecated)] +#![allow(clippy::disallowed_methods)] use crate::middleware::Signature; use aws_credential_types::Credentials; diff --git a/aws/rust-runtime/aws-sig-auth/src/lib.rs b/aws/rust-runtime/aws-sig-auth/src/lib.rs index 643b3ff216..90cc88a7e3 100644 --- a/aws/rust-runtime/aws-sig-auth/src/lib.rs +++ b/aws/rust-runtime/aws-sig-auth/src/lib.rs @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +// TODO(enableNewSmithyRuntimeCleanup): Deprecate this crate and replace it with empty contents. Remove references to it in the code generator. + #![allow(clippy::derive_partial_eq_without_eq)] //! AWS Signature Authentication Package diff --git a/aws/rust-runtime/clippy.toml b/aws/rust-runtime/clippy.toml new file mode 120000 index 0000000000..0cbd6319b2 --- /dev/null +++ b/aws/rust-runtime/clippy.toml @@ -0,0 +1 @@ +../../clippy-root.toml \ No newline at end of file diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointsCredentialsTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointsCredentialsTest.kt index 89e11618a6..0708035541 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointsCredentialsTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/EndpointsCredentialsTest.kt @@ -114,7 +114,7 @@ class EndpointsCredentialsTest { .credentials_provider(#{Credentials}::for_tests()) .build(); let client = $moduleName::Client::from_conf(conf); - let _ = client.custom_auth().send().await; + let _ = dbg!(client.custom_auth().send().await); let req = rcvr.expect_request(); let auth_header = req.headers().get("AUTHORIZATION").unwrap().to_str().unwrap(); assert!(auth_header.contains("/region-custom-auth/name-custom-auth/aws4_request"), "{}", auth_header); diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecoratorTest.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecoratorTest.kt index 857139c9f9..3a792e2a0a 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecoratorTest.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/InvocationIdDecoratorTest.kt @@ -41,7 +41,7 @@ class InvocationIdDecoratorTest { let client = $moduleName::Client::from_conf(config); - let _ = client.some_operation().send().await; + let _ = dbg!(client.some_operation().send().await); let request = rx.expect_request(); assert_eq!("custom", request.headers().get("amz-sdk-invocation-id").unwrap()); } diff --git a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt index 5638aca834..44ed5461a9 100644 --- a/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt +++ b/aws/sdk-codegen/src/test/kotlin/software/amazon/smithy/rustsdk/TestUtil.kt @@ -46,6 +46,7 @@ fun awsSdkIntegrationTest( clientIntegrationTest( model, IntegrationTestParams( + cargoCommand = "cargo test --features test-util", runtimeConfig = AwsTestRuntimeConfig, additionalSettings = ObjectNode.builder().withMember( "customizationConfig", diff --git a/aws/sdk/build.gradle.kts b/aws/sdk/build.gradle.kts index 920cb7579e..ebf1b3a705 100644 --- a/aws/sdk/build.gradle.kts +++ b/aws/sdk/build.gradle.kts @@ -338,6 +338,7 @@ tasks.register("generateCargoWorkspace") { doFirst { outputDir.mkdirs() outputDir.resolve("Cargo.toml").writeText(generateCargoWorkspace(awsServices)) + rootProject.rootDir.resolve("clippy-root.toml").copyTo(outputDir.resolve("clippy.toml")) } inputs.property("servicelist", awsServices.moduleNames.toString()) if (awsServices.examples.isNotEmpty()) { @@ -347,6 +348,7 @@ tasks.register("generateCargoWorkspace") { inputs.dir(test.path) } outputs.file(outputDir.resolve("Cargo.toml")) + outputs.file(outputDir.resolve("clippy.toml")) outputs.upToDateWhen { false } } diff --git a/clippy-root.toml b/clippy-root.toml new file mode 100644 index 0000000000..87318f3163 --- /dev/null +++ b/clippy-root.toml @@ -0,0 +1,9 @@ +# this file is named `clippy-root.toml` so it isn't picked up automagically. Clippy +# will search up the filesystem for a clippy.toml and this causes problems with tools. +disallowed-methods = [ + # fully qualified function/method name: + "std::time::SystemTime::now", + "std::time::SystemTime::elapsed", + "std::time::Instant::now", + "std::time::Instant::elapsed" +] diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt index 83e1119013..7dea61de99 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/customizations/TimeSourceCustomization.kt @@ -23,6 +23,9 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust private val codegenScope = arrayOf( *preludeScope, "SharedTimeSource" to RuntimeType.smithyAsync(codegenContext.runtimeConfig).resolve("time::SharedTimeSource"), + "StaticTimeSource" to RuntimeType.smithyAsync(codegenContext.runtimeConfig).resolve("time::StaticTimeSource"), + "UNIX_EPOCH" to RuntimeType.std.resolve("time::UNIX_EPOCH"), + "Duration" to RuntimeType.std.resolve("time::Duration"), ) override fun section(section: ServiceConfig) = @@ -129,6 +132,18 @@ class TimeSourceCustomization(codegenContext: ClientCodegenContext) : ConfigCust } } + is ServiceConfig.DefaultForTests -> { + rustTemplate( + """ + ${section.configBuilderRef} + .set_time_source(#{Some}(#{SharedTimeSource}::new( + #{StaticTimeSource}::new(#{UNIX_EPOCH} + #{Duration}::from_secs(1234567890))) + )); + """, + *codegenScope, + ) + } + else -> emptySection } } diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/CodegenIntegrationTest.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/CodegenIntegrationTest.kt index d1f208c393..4c1d435204 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/CodegenIntegrationTest.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/CodegenIntegrationTest.kt @@ -24,6 +24,7 @@ data class IntegrationTestParams( val additionalSettings: ObjectNode = ObjectNode.builder().build(), val overrideTestDir: File? = null, val command: ((Path) -> Unit)? = null, + val cargoCommand: String? = null, ) /** @@ -40,6 +41,6 @@ fun codegenIntegrationTest(model: Model, params: IntegrationTestParams, invokePl ) invokePlugin(ctx) ctx.fileManifest.printGeneratedFiles() - params.command?.invoke(testDir) ?: "cargo test".runCommand(testDir, environment = mapOf("RUSTFLAGS" to "-D warnings")) + params.command?.invoke(testDir) ?: (params.cargoCommand ?: "cargo test").runCommand(testDir, environment = mapOf("RUSTFLAGS" to "-D warnings")) return testDir } diff --git a/rust-runtime/aws-smithy-async/src/time.rs b/rust-runtime/aws-smithy-async/src/time.rs index 2abe332c88..b1b3f5ac1f 100644 --- a/rust-runtime/aws-smithy-async/src/time.rs +++ b/rust-runtime/aws-smithy-async/src/time.rs @@ -28,6 +28,8 @@ impl SystemTimeSource { impl TimeSource for SystemTimeSource { fn now(&self) -> SystemTime { + // this is the one OK usage + #[allow(clippy::disallowed_methods)] SystemTime::now() } } diff --git a/rust-runtime/clippy.toml b/rust-runtime/clippy.toml new file mode 120000 index 0000000000..939baf7183 --- /dev/null +++ b/rust-runtime/clippy.toml @@ -0,0 +1 @@ +../clippy-root.toml \ No newline at end of file From d2690e7ab19c1ca98124ec1d5870ebfe25613879 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Fri, 28 Jul 2023 11:02:24 -0700 Subject: [PATCH 244/253] Add orchestrator upgrade guides to changelog (#2888) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- CHANGELOG.next.toml | 12 ++++++++++++ .../ci-build/smithy-rs-tool-common/src/changelog.rs | 7 ++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index 394665e33e..fd6119d965 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -711,3 +711,15 @@ message = """The `doc(hidden)` `with_env` in `ProviderConfig` was removed.""" meta = { "breaking" = true, "tada" = false, "bug" = false } author = "rcoh" references = ["smithy-rs#2877"] + +[[aws-sdk-rust]] +message = "The underlying architecture of the SDK clients has been overhauled. This shouldn't require any changes for most projects, but will affect projects that customize the SDK middleware. More details are available in the [upgrade guide](https://github.com/awslabs/aws-sdk-rust/discussions/853) if you are effected by these changes." +references = [] +meta = { "breaking" = true, "tada" = false, "bug" = false } +author = "jdisanti" + +[[smithy-rs]] +message = "The entire architecture of generated clients has been overhauled. See the [upgrade guide](https://github.com/awslabs/smithy-rs/discussions/2887) to get your code working again." +references = [] +meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client"} +author = "jdisanti" diff --git a/tools/ci-build/smithy-rs-tool-common/src/changelog.rs b/tools/ci-build/smithy-rs-tool-common/src/changelog.rs index acdee013ae..579bae2b5a 100644 --- a/tools/ci-build/smithy-rs-tool-common/src/changelog.rs +++ b/tools/ci-build/smithy-rs-tool-common/src/changelog.rs @@ -146,9 +146,10 @@ impl HandAuthoredEntry { if !self.author.chars().all(|c| c.is_alphanumeric() || c == '-') { bail!("Author must be valid GitHub username: [a-zA-Z0-9\\-]") } - if self.references.is_empty() { - bail!("Changelog entry must refer to at least one pull request or issue"); - } + // TODO(enableNewSmithyRuntimeCleanup): Re-add this validation + // if self.references.is_empty() { + // bail!("Changelog entry must refer to at least one pull request or issue"); + // } Ok(()) } From 682dc7fb40805b6fff5062eb4ebd8e6ea943c8d2 Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Tue, 1 Aug 2023 10:06:29 -0700 Subject: [PATCH 245/253] Upgrade Smithy to 1.35 (#2892) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: david-perez --- .../protocol/ProtocolTestGenerator.kt | 16 +++- .../rulesgen/ExpressionGeneratorTest.kt | 19 +---- .../protocol/ServerProtocolTestGenerator.kt | 77 ++++++------------- gradle.properties | 4 +- 4 files changed, 45 insertions(+), 71 deletions(-) diff --git a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt index 6c08785bfa..2f994788fe 100644 --- a/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt +++ b/codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/generators/protocol/ProtocolTestGenerator.kt @@ -614,6 +614,20 @@ class DefaultProtocolTestGenerator( // These tests are not even attempted to be generated, either because they will not compile // or because they are flaky - private val DisableTests = setOf() + private val DisableTests = setOf( + // TODO(https://github.com/awslabs/smithy-rs/issues/2891): Implement support for `@requestCompression` + "SDKAppendedGzipAfterProvidedEncoding_restJson1", + "SDKAppendedGzipAfterProvidedEncoding_restXml", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_0", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_1", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsQuery", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_ec2Query", + "SDKAppliedContentEncoding_awsJson1_0", + "SDKAppliedContentEncoding_awsJson1_1", + "SDKAppliedContentEncoding_awsQuery", + "SDKAppliedContentEncoding_ec2Query", + "SDKAppliedContentEncoding_restJson1", + "SDKAppliedContentEncoding_restXml", + ) } } diff --git a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGeneratorTest.kt b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGeneratorTest.kt index 34781b1eac..823d6ace78 100644 --- a/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGeneratorTest.kt +++ b/codegen-client/src/test/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint/rulesgen/ExpressionGeneratorTest.kt @@ -14,7 +14,6 @@ import software.amazon.smithy.rulesengine.language.syntax.Identifier import software.amazon.smithy.rulesengine.language.syntax.expr.Expression import software.amazon.smithy.rulesengine.language.syntax.expr.Literal import software.amazon.smithy.rulesengine.language.syntax.expr.Template -import software.amazon.smithy.rulesengine.language.syntax.fn.LibraryFunction import software.amazon.smithy.rust.codegen.client.smithy.endpoint.Context import software.amazon.smithy.rust.codegen.client.smithy.endpoint.generators.FunctionRegistry import software.amazon.smithy.rust.codegen.core.rustlang.rust @@ -26,25 +25,13 @@ import software.amazon.smithy.rust.codegen.core.testutil.compileAndTest import software.amazon.smithy.rust.codegen.core.testutil.unitTest internal class ExprGeneratorTest { - /** - * This works around a bug in smithy-endpoint-rules where the constructors on functions like `BooleanEquals` - * hit the wrong branch in the visitor (but when they get parsed, they hit the right branch). - */ - fun Expression.shoop() = Expression.fromNode(this.toNode()) private val testContext = Context(FunctionRegistry(listOf()), TestRuntimeConfig) - @Test - fun `fail when smithy is fixed`() { - check(BooleanEquals.ofExpressions(Expression.of(true), Expression.of(true)) is LibraryFunction) { - "smithy has been fixed, shoop can be removed" - } - } - @Test fun generateExprs() { - val boolEq = Expression.of(true).equal(true).shoop() - val strEq = Expression.of("helloworld").equal("goodbyeworld").not().shoop() - val combined = BooleanEquals.ofExpressions(boolEq, strEq).shoop() + val boolEq = Expression.of(true).equal(true) + val strEq = Expression.of("helloworld").equal("goodbyeworld").not() + val combined = BooleanEquals.ofExpressions(boolEq, strEq) TestWorkspace.testProject().unitTest { val generator = ExpressionGenerator(Ownership.Borrowed, testContext) rust("assert_eq!(true, #W);", generator.generate(boolEq)) diff --git a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt index 74978699ca..ee0038e53c 100644 --- a/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt +++ b/codegen-server/src/main/kotlin/software/amazon/smithy/rust/codegen/server/smithy/generators/protocol/ServerProtocolTestGenerator.kt @@ -230,26 +230,6 @@ class ServerProtocolTestGenerator( // not been written with a server-side perspective in mind. private fun List.fixBroken(): List = this.map { when (it) { - is TestCase.RequestTest -> { - val howToFixIt = BrokenRequestTests[Pair(codegenContext.serviceShape.id.toString(), it.id)] - if (howToFixIt == null) { - it - } else { - val fixed = howToFixIt(it.testCase, it.operationShape) - TestCase.RequestTest(fixed, it.operationShape) - } - } - - is TestCase.ResponseTest -> { - val howToFixIt = BrokenResponseTests[Pair(codegenContext.serviceShape.id.toString(), it.id)] - if (howToFixIt == null) { - it - } else { - val fixed = howToFixIt(it.testCase) - TestCase.ResponseTest(fixed, it.targetShape) - } - } - is TestCase.MalformedRequestTest -> { val howToFixIt = BrokenMalformedRequestTests[Pair(codegenContext.serviceShape.id.toString(), it.id)] if (howToFixIt == null) { @@ -259,6 +239,7 @@ class ServerProtocolTestGenerator( TestCase.MalformedRequestTest(fixed) } } + else -> it } } @@ -847,7 +828,25 @@ class ServerProtocolTestGenerator( // These tests are not even attempted to be generated, either because they will not compile // or because they are flaky - private val DisableTests = setOf() + private val DisableTests = setOf( + // TODO(https://github.com/awslabs/smithy-rs/issues/2891): Implement support for `@requestCompression` + "SDKAppendedGzipAfterProvidedEncoding_restJson1", + "SDKAppendedGzipAfterProvidedEncoding_restXml", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_0", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsJson1_1", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_awsQuery", + "SDKAppendsGzipAndIgnoresHttpProvidedEncoding_ec2Query", + "SDKAppliedContentEncoding_awsJson1_0", + "SDKAppliedContentEncoding_awsJson1_1", + "SDKAppliedContentEncoding_awsQuery", + "SDKAppliedContentEncoding_ec2Query", + "SDKAppliedContentEncoding_restJson1", + "SDKAppliedContentEncoding_restXml", + + // RestXml S3 tests that fail to compile + "S3EscapeObjectKeyInUriLabel", + "S3EscapePathObjectKeyInUriLabel", + ) private fun fixRestJsonAllQueryStringTypes( testCase: HttpRequestTestCase, @@ -903,24 +902,6 @@ class ServerProtocolTestGenerator( ).asObjectNode().get(), ).build() - private fun fixRestJsonQueryStringEscaping( - testCase: HttpRequestTestCase, - @Suppress("UNUSED_PARAMETER") - operationShape: OperationShape, - ): HttpRequestTestCase = - testCase.toBuilder().params( - Node.parse( - """ - { - "queryString": "%:/?#[]@!${'$'}&'()*+,;=😹", - "queryParamsMapOfStringList": { - "String": ["%:/?#[]@!${'$'}&'()*+,;=😹"] - } - } - """.trimMargin(), - ).asObjectNode().get(), - ).build() - // TODO(https://github.com/awslabs/smithy/issues/1506) private fun fixRestJsonMalformedPatternReDOSString(testCase: HttpMalformedRequestTestCase): HttpMalformedRequestTestCase { val brokenResponse = testCase.response @@ -942,19 +923,11 @@ class ServerProtocolTestGenerator( .build() } - // These are tests whose definitions in the `awslabs/smithy` repository are wrong. - // This is because they have not been written from a server perspective, and as such the expected `params` field is incomplete. - // TODO(https://github.com/awslabs/smithy-rs/issues/1288): Contribute a PR to fix them upstream. - private val BrokenRequestTests = mapOf( - // TODO(https://github.com/awslabs/smithy/pull/1564) - // Pair(RestJson, "RestJsonAllQueryStringTypes") to ::fixRestJsonAllQueryStringTypes, - // TODO(https://github.com/awslabs/smithy/pull/1562) - Pair(RestJson, "RestJsonQueryStringEscaping") to ::fixRestJsonQueryStringEscaping, - ) - - private val BrokenResponseTests: Map, KFunction1> = - mapOf() - + // TODO(https://github.com/awslabs/smithy-rs/issues/1288): Move the fixed versions into + // `rest-json-extras.smithy` and put the unfixed ones in `ExpectFail`: this has the + // advantage that once our upstream PRs get merged and we upgrade to the next Smithy release, our build will + // fail and we will take notice to remove the fixes from `rest-json-extras.smithy`. This is exactly what the + // client does. private val BrokenMalformedRequestTests: Map, KFunction1> = // TODO(https://github.com/awslabs/smithy/issues/1506) mapOf( diff --git a/gradle.properties b/gradle.properties index aa791db007..ccadd9016f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,8 +17,8 @@ smithy.rs.runtime.crate.version=0.0.0-smithy-rs-head kotlin.code.style=official # codegen -smithyGradlePluginVersion=0.6.0 -smithyVersion=1.29.0 +smithyGradlePluginVersion=0.7.0 +smithyVersion=1.35.0 # kotlin kotlinVersion=1.7.21 From d4a2308e94d911024a37ed26e05902f8afec4a59 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Tue, 1 Aug 2023 14:37:59 -0500 Subject: [PATCH 246/253] update MSRV to 1.69.0 (#2893) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .github/workflows/ci.yml | 2 +- .github/workflows/claim-crate-names.yml | 2 +- .github/workflows/github-pages.yml | 2 +- .github/workflows/pull-request-bot.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/update-sdk-next.yml | 2 +- CHANGELOG.next.toml | 12 ++++++------ .../aws-inlineable/src/endpoint_discovery.rs | 6 +++--- .../aws-inlineable/src/http_request_checksum.rs | 14 ++++---------- .../aws-sigv4/src/http_request/sign.rs | 4 ++-- .../smithy/rust/codegen/core/testutil/Rust.kt | 2 +- gradle.properties | 2 +- rust-runtime/aws-smithy-http/src/middleware.rs | 2 +- .../aws-smithy-runtime-api/src/client/identity.rs | 6 ++++-- .../aws-smithy-runtime/src/client/orchestrator.rs | 2 +- rust-toolchain.toml | 2 +- tools/ci-build/Dockerfile | 2 +- 17 files changed, 31 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a1e8f274d..f4208b271a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ on: required: false env: - rust_version: 1.68.2 + rust_version: 1.69.0 rust_toolchain_components: clippy,rustfmt ENCRYPTED_DOCKER_PASSWORD: ${{ secrets.ENCRYPTED_DOCKER_PASSWORD }} DOCKER_LOGIN_TOKEN_PASSPHRASE: ${{ secrets.DOCKER_LOGIN_TOKEN_PASSPHRASE }} diff --git a/.github/workflows/claim-crate-names.yml b/.github/workflows/claim-crate-names.yml index d6e105d45f..6f714a7671 100644 --- a/.github/workflows/claim-crate-names.yml +++ b/.github/workflows/claim-crate-names.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true env: - rust_version: 1.68.2 + rust_version: 1.69.0 name: Claim unpublished crate names on crates.io run-name: ${{ github.workflow }} diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index c597e186c6..c15767baa1 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -8,7 +8,7 @@ on: name: Update GitHub Pages env: - rust_version: 1.68.2 + rust_version: 1.69.0 # Allow only one doc pages build to run at a time for the entire smithy-rs repo concurrency: diff --git a/.github/workflows/pull-request-bot.yml b/.github/workflows/pull-request-bot.yml index 28a5c8aea6..82fc80d975 100644 --- a/.github/workflows/pull-request-bot.yml +++ b/.github/workflows/pull-request-bot.yml @@ -28,7 +28,7 @@ concurrency: env: java_version: 11 - rust_version: 1.68.2 + rust_version: 1.69.0 rust_toolchain_components: clippy,rustfmt apt_dependencies: libssl-dev gnuplot jq diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 41a85393f8..7d33ceba4c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true env: - rust_version: 1.68.2 + rust_version: 1.69.0 name: Release smithy-rs run-name: ${{ github.workflow }} ${{ inputs.semantic_version }} (${{ inputs.commit_sha }}) - ${{ inputs.dry_run && 'Dry run' || 'Production run' }} diff --git a/.github/workflows/update-sdk-next.yml b/.github/workflows/update-sdk-next.yml index 47a3af6221..155924020a 100644 --- a/.github/workflows/update-sdk-next.yml +++ b/.github/workflows/update-sdk-next.yml @@ -32,7 +32,7 @@ jobs: - name: Set up Rust uses: dtolnay/rust-toolchain@master with: - toolchain: 1.68.2 + toolchain: 1.69.0 - name: Delete old SDK run: | - name: Generate a fresh SDK diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index fd6119d965..a99d1f3afb 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -159,16 +159,16 @@ meta = { "breaking" = true, "tada" = false, "bug" = false } author = "ysaito1001" [[aws-sdk-rust]] -message = "Update MSRV to Rust 1.68.2" -references = ["smithy-rs#2745"] +message = "Update MSRV to Rust 1.69.0" +references = ["smithy-rs#2893"] meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "jdisanti" +author = "Velfi" [[smithy-rs]] -message = "Update MSRV to Rust 1.68.2" -references = ["smithy-rs#2745"] +message = "Update MSRV to Rust 1.69.0" +references = ["smithy-rs#2893"] meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all" } -author = "jdisanti" +author = "Velfi" [[smithy-rs]] message = """`ShapeId` is the new structure used to represent a shape, with its absolute name, namespace and name. diff --git a/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs index de870fecc9..498844f1b3 100644 --- a/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs +++ b/aws/rust-runtime/aws-inlineable/src/endpoint_discovery.rs @@ -160,11 +160,11 @@ mod test { use crate::endpoint_discovery::create_cache; use aws_smithy_async::rt::sleep::{SharedAsyncSleep, TokioSleep}; use aws_smithy_async::test_util::controlled_time_and_sleep; - use aws_smithy_async::time::{SharedTimeSource, SystemTimeSource}; + use aws_smithy_async::time::{SharedTimeSource, SystemTimeSource, TimeSource}; use aws_smithy_types::endpoint::Endpoint; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; - use std::time::{Duration, SystemTime, UNIX_EPOCH}; + use std::time::{Duration, UNIX_EPOCH}; use tokio::time::timeout; fn check_send_v(t: T) -> T { @@ -178,7 +178,7 @@ mod test { || async { Ok(( Endpoint::builder().url("http://foo.com").build(), - SystemTime::now(), + SystemTimeSource::new().now(), )) }, SharedAsyncSleep::new(TokioSleep::new()), diff --git a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs index aff4e925e4..701a2e36a0 100644 --- a/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs +++ b/aws/rust-runtime/aws-inlineable/src/http_request_checksum.rs @@ -239,11 +239,8 @@ mod tests { let mut body = request.body().try_clone().expect("body is retryable"); let mut body_data = BytesMut::new(); - loop { - match body.data().await { - Some(data) => body_data.extend_from_slice(&data.unwrap()), - None => break, - } + while let Some(data) = body.data().await { + body_data.extend_from_slice(&data.unwrap()) } let body = std::str::from_utf8(&body_data).unwrap(); assert_eq!( @@ -290,11 +287,8 @@ mod tests { let mut body = request.body().try_clone().expect("body is retryable"); let mut body_data = BytesMut::new(); - loop { - match body.data().await { - Some(data) => body_data.extend_from_slice(&data.unwrap()), - None => break, - } + while let Some(data) = body.data().await { + body_data.extend_from_slice(&data.unwrap()) } let body = std::str::from_utf8(&body_data).unwrap(); let expected_checksum = base64::encode(&crc32c_checksum); diff --git a/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs b/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs index 69d646daa2..aaeda06bb7 100644 --- a/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs +++ b/aws/rust-runtime/aws-sigv4/src/http_request/sign.rs @@ -611,7 +611,7 @@ mod tests { security_token: None, region: "us-east-1", service_name: "foo", - time: std::time::SystemTime::now(), + time: std::time::SystemTime::UNIX_EPOCH, settings, }; @@ -640,7 +640,7 @@ mod tests { security_token: None, region: "us-east-1", service_name: "foo", - time: std::time::SystemTime::now(), + time: std::time::SystemTime::UNIX_EPOCH, settings, }; diff --git a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt index 8f8e6ff8b5..0a4a52f839 100644 --- a/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt +++ b/codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/testutil/Rust.kt @@ -111,7 +111,7 @@ object TestWorkspace { // help rust select the right version when we run cargo test // TODO(https://github.com/awslabs/smithy-rs/issues/2048): load this from the msrv property using a // method as we do for runtime crate versions - "[toolchain]\nchannel = \"1.68.2\"\n", + "[toolchain]\nchannel = \"1.69.0\"\n", ) // ensure there at least an empty lib.rs file to avoid broken crates newProject.resolve("src").mkdirs() diff --git a/gradle.properties b/gradle.properties index ccadd9016f..58b9ec31ea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ # # Rust MSRV (entered into the generated README) -rust.msrv=1.68.2 +rust.msrv=1.69.0 # To enable debug, swap out the two lines below. # When changing this value, be sure to run `./gradlew --stop` to kill the Gradle daemon. diff --git a/rust-runtime/aws-smithy-http/src/middleware.rs b/rust-runtime/aws-smithy-http/src/middleware.rs index d3da9dac2d..3285dec872 100644 --- a/rust-runtime/aws-smithy-http/src/middleware.rs +++ b/rust-runtime/aws-smithy-http/src/middleware.rs @@ -112,7 +112,7 @@ where O: ParseHttpResponse>, { if let Some(parsed_response) = - debug_span!("parse_unloaded").in_scope(&mut || handler.parse_unloaded(&mut response)) + debug_span!("parse_unloaded").in_scope(|| handler.parse_unloaded(&mut response)) { trace!(response = ?response, "read HTTP headers for streaming response"); return sdk_result(parsed_response, response); diff --git a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs index eec7c4e32c..27c4accf4e 100644 --- a/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs +++ b/rust-runtime/aws-smithy-runtime-api/src/client/identity.rs @@ -121,7 +121,7 @@ impl Identity { } } -impl fmt::Debug for Identity { +impl Debug for Identity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Identity") .field("data", (self.data_debug)(&self.data)) @@ -133,6 +133,7 @@ impl fmt::Debug for Identity { #[cfg(test)] mod tests { use super::*; + use aws_smithy_async::time::{SystemTimeSource, TimeSource}; #[test] fn check_send_sync() { @@ -148,7 +149,8 @@ mod tests { last: String, } - let expiration = SystemTime::now(); + let ts = SystemTimeSource::new(); + let expiration = ts.now(); let identity = Identity::new( MyIdentityData { first: "foo".into(), diff --git a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs index b86953dded..22bee36367 100644 --- a/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs +++ b/rust-runtime/aws-smithy-runtime/src/client/orchestrator.rs @@ -478,7 +478,7 @@ mod tests { .status(StatusCode::OK) .body(SdkBody::empty()) .map_err(|err| OrchestratorError::other(Box::new(err))) - .map(|res| Output::erase(res)), + .map(Output::erase), ) } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 864d3c411a..f2415f8315 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.68.2" +channel = "1.69.0" diff --git a/tools/ci-build/Dockerfile b/tools/ci-build/Dockerfile index d6ce780e65..f1e74fc79f 100644 --- a/tools/ci-build/Dockerfile +++ b/tools/ci-build/Dockerfile @@ -6,7 +6,7 @@ # This is the base Docker build image used by CI ARG base_image=public.ecr.aws/amazonlinux/amazonlinux:2 -ARG rust_stable_version=1.68.2 +ARG rust_stable_version=1.69.0 ARG rust_nightly_version=nightly-2023-05-31 FROM ${base_image} AS bare_base_image From 6eaacaa96684f662b7d355eea94a526c0b465e7f Mon Sep 17 00:00:00 2001 From: AWS SDK Rust Bot Date: Tue, 1 Aug 2023 22:08:16 +0000 Subject: [PATCH 247/253] Upgrade the smithy-rs runtime crates version to 0.56.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 58b9ec31ea..4b2eb37857 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,7 +12,7 @@ rust.msrv=1.69.0 org.gradle.jvmargs=-Xmx1024M # Version number to use for the generated runtime crates -smithy.rs.runtime.crate.version=0.0.0-smithy-rs-head +smithy.rs.runtime.crate.version=0.56.0 kotlin.code.style=official From e78c60dbf169403eedceb1b718b862b0c5e5ee09 Mon Sep 17 00:00:00 2001 From: AWS SDK Rust Bot Date: Tue, 1 Aug 2023 22:10:49 +0000 Subject: [PATCH 248/253] Update changelog --- CHANGELOG.md | 450 +++++++++++++++++++++++ CHANGELOG.next.toml | 715 +----------------------------------- aws/SDK_CHANGELOG.next.json | 487 +++++++++++++++--------- 3 files changed, 766 insertions(+), 886 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 495ef2143f..9cb6e47746 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,454 @@ +August 1st, 2023 +================ +**Breaking Changes:** +- ⚠🎉 (server, [smithy-rs#2740](https://github.com/awslabs/smithy-rs/issues/2740), [smithy-rs#2759](https://github.com/awslabs/smithy-rs/issues/2759), [smithy-rs#2779](https://github.com/awslabs/smithy-rs/issues/2779), [smithy-rs#2827](https://github.com/awslabs/smithy-rs/issues/2827), @hlbarber) The middleware system has been reworked as we push for a unified, simple, and consistent API. The following changes have been made in service of this goal: + + - A `ServiceShape` trait has been added. + - The `Plugin` trait has been simplified. + - The `HttpMarker` and `ModelMarker` marker traits have been added to better distinguish when plugins run and what they have access to. + - The `Operation` structure has been removed. + - A `Scoped` `Plugin` has been added. + + The `Plugin` trait has now been simplified and the `Operation` struct has been removed. + + ## Addition of `ServiceShape` + + Since the [0.52 release](https://github.com/awslabs/smithy-rs/releases/tag/release-2022-12-12) the `OperationShape` has existed. + + ```rust + /// Models the [Smithy Operation shape]. + /// + /// [Smithy Operation shape]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#operation + pub trait OperationShape { + /// The ID of the operation. + const ID: ShapeId; + + /// The operation input. + type Input; + /// The operation output. + type Output; + /// The operation error. [`Infallible`](std::convert::Infallible) in the case where no error + /// exists. + type Error; + } + ``` + + This allowed `Plugin` authors to access these associated types and constants. See the [`PrintPlugin`](https://github.com/awslabs/smithy-rs/blob/main/examples/pokemon-service/src/plugin.rs) as an example. + + We continue with this approach and introduce the following trait: + + ```rust + /// Models the [Smithy Service shape]. + /// + /// [Smithy Service shape]: https://smithy.io/2.0/spec/service-types.html + pub trait ServiceShape { + /// The [`ShapeId`] of the service. + const ID: ShapeId; + + /// The version of the service. + const VERSION: Option<&'static str>; + + /// The [Protocol] applied to this service. + /// + /// [Protocol]: https://smithy.io/2.0/spec/protocol-traits.html + type Protocol; + + /// An enumeration of all operations contained in this service. + type Operations; + } + ``` + + With the changes to `Plugin`, described below, middleware authors now have access to this information at compile time. + + ## Simplication of the `Plugin` trait + + Previously, + + ```rust + trait Plugin { + type Service; + type Layer; + + fn map(&self, input: Operation) -> Operation; + } + ``` + + modified an `Operation`. + + Now, + + ```rust + trait Plugin { + type Output; + + fn apply(&self, input: T) -> Self::Output; + } + ``` + + maps a `tower::Service` to a `tower::Service`. This is equivalent to `tower::Layer` with two extra type parameters: `Service` and `Operation`, which implement `ServiceShape` and `OperationShape` respectively. + + Having both `Service` and `Operation` as type parameters also provides an even surface for advanced users to extend the codegenerator in a structured way. See [this issue](https://github.com/awslabs/smithy-rs/issues/2777) for more context. + + The following middleware setup + + ```rust + pub struct PrintService { + inner: S, + name: &'static str, + } + + impl Service for PrintService + where + S: Service, + { + async fn call(&mut self, req: R) -> Self::Future { + println!("Hi {}", self.name); + self.inner.call(req) + } + } + + pub struct PrintLayer { + name: &'static str, + } + + impl Layer for PrintLayer { + type Service = PrintService; + + fn layer(&self, service: S) -> Self::Service { + PrintService { + inner: service, + name: self.name, + } + } + } + + pub struct PrintPlugin; + + impl Plugin for PrintPlugin + where + Op: OperationShape, + { + type Service = S; + type Layer = Stack; + + fn map(&self, input: Operation) -> Operation { + input.layer(PrintLayer { name: Op::NAME }) + } + } + ``` + + now becomes + + ```rust + pub struct PrintService { + inner: S, + name: &'static str, + } + + impl Service for PrintService + where + S: Service, + { + async fn call(&mut self, req: R) -> Self::Future { + println!("Hi {}", self.name); + self.inner.call(req) + } + } + + pub struct PrintPlugin; + + impl Plugin for PrintPlugin + where + Op: OperationShape, + { + type Output = PrintService; + + fn apply(&self, inner: T) -> Self::Output { + PrintService { inner, name: Op::ID.name() } + } + } + + impl HttpMarker for PrintPlugin { } + ``` + + Alternatively, using the new `ServiceShape`, implemented on `Ser`: + + ```rust + impl Plugin for PrintPlugin + where + Ser: ServiceShape, + { + type Service = PrintService; + + fn apply(&self, inner: T) -> Self::Service { + PrintService { inner, name: Ser::ID.name() } + } + } + ``` + + A single `Plugin` can no longer apply a `tower::Layer` on HTTP requests/responses _and_ modelled structures at the same time (see middleware positions [C](https://awslabs.github.io/smithy-rs/design/server/middleware.html#c-operation-specific-http-middleware) and [D](https://awslabs.github.io/smithy-rs/design/server/middleware.html#d-operation-specific-model-middleware). Instead one `Plugin` must be specified for each and passed to the service builder constructor separately: + + ```rust + let app = PokemonService::builder_with_plugins(/* HTTP plugins */, /* model plugins */) + /* setters */ + .build() + .unwrap(); + ``` + + To better distinguish when a plugin runs and what it has access to, `Plugin`s now have to additionally implement the `HttpMarker` marker trait, the `ModelMarker` marker trait, or both: + + - A HTTP plugin acts on the HTTP request before it is deserialized, and acts on the HTTP response after it is serialized. + - A model plugin acts on the modeled operation input after it is deserialized, and acts on the modeled operation output or the modeled operation error before it is serialized. + + The motivation behind this change is to simplify the job of middleware authors, separate concerns, accomodate common cases better, and to improve composition internally. + + Because `Plugin` is now closer to `tower::Layer` we have two canonical converters: + + ```rust + use aws_smithy_http_server::plugin::{PluginLayer, LayerPlugin}; + + // Convert from `Layer` to `Plugin` which applies uniformly across all operations + let layer = /* some layer */; + let plugin = PluginLayer(layer); + + // Convert from `Plugin` to `Layer` for some fixed protocol and operation + let plugin = /* some plugin */; + let layer = LayerPlugin::new::(plugin); + ``` + + ## Removal of `PluginPipeline` + + Since plugins now come in two flavors (those marked with `HttpMarker` and those marked with `ModelMarker`) that shouldn't be mixed in a collection of plugins, the primary way of concatenating plugins, `PluginPipeline` has been removed in favor of the `HttpPlugins` and `ModelPlugins` types, which eagerly check that whenever a plugin is pushed, it is of the expected type. + + This worked before, but you wouldn't be able to do apply this collection of plugins anywhere; if you tried to, the compilation error messages would not be very helpful: + + ```rust + use aws_smithy_http_server::plugin::PluginPipeline; + + let pipeline = PluginPipeline::new().push(http_plugin).push(model_plugin); + ``` + + Now collections of plugins must contain plugins of the same flavor: + + ```rust + use aws_smithy_http_server::plugin::{HttpPlugins, ModelPlugins}; + + let http_plugins = HttpPlugins::new() + .push(http_plugin) + // .push(model_plugin) // This fails to compile with a helpful error message. + .push(&http_and_model_plugin); + let model_plugins = ModelPlugins::new() + .push(model_plugin) + .push(&http_and_model_plugin); + ``` + + In the above example, `&http_and_model_plugin` implements both `HttpMarker` and `ModelMarker`, so we can add it to both collections. + + ## Removal of `Operation` + + The `aws_smithy_http_server::operation::Operation` structure has now been removed. Previously, there existed a `{operation_name}_operation` setter on the service builder, which accepted an `Operation`. This allowed users to + + ```rust + let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_service(/* tower::Service */); + + let app = PokemonService::builder_without_plugins() + .get_pokemon_species_operation(operation) + /* other setters */ + .build() + .unwrap(); + ``` + + to set an operation with a `tower::Service`, and + + ```rust + let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_service(/* tower::Service */).layer(/* layer */); + let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_handler(/* closure */).layer(/* layer */); + + let app = PokemonService::builder_without_plugins() + .get_pokemon_species_operation(operation) + /* other setters */ + .build() + .unwrap(); + ``` + + to add a `tower::Layer` (acting on HTTP requests/responses post-routing) to a single operation. + + We have seen little adoption of this API and for this reason we have opted instead to introduce a new setter, accepting a `tower::Service`, on the service builder: + + ```rust + let app = PokemonService::builder_without_plugins() + .get_pokemon_species_service(/* tower::Service */) + /* other setters */ + .build() + .unwrap(); + ``` + + Applying a `tower::Layer` to a _subset_ of operations is should now be done through the `Plugin` API via `filter_by_operation_id` + + ```rust + use aws_smithy_http_server::plugin::{PluginLayer, filter_by_operation_name, IdentityPlugin}; + + let plugin = PluginLayer(/* layer */); + let scoped_plugin = filter_by_operation_name(plugin, |id| id == GetPokemonSpecies::ID); + + let app = PokemonService::builder_with_plugins(scoped_plugin, IdentityPlugin) + .get_pokemon_species(/* handler */) + /* other setters */ + .build() + .unwrap(); + ``` + + or the new `Scoped` `Plugin` introduced below. + + # Addition of `Scoped` + + Currently, users can selectively apply a `Plugin` via the `filter_by_operation_id` function + + ```rust + use aws_smithy_http_server::plugin::filter_by_operation_id; + // Only apply `plugin` to `CheckHealth` and `GetStorage` operation + let filtered_plugin = filter_by_operation_id(plugin, |name| name == CheckHealth::ID || name == GetStorage::ID); + ``` + + In addition to this, we now provide `Scoped`, which selectively applies a `Plugin` at _compiletime_. Users should prefer this to `filter_by_operation_id` when applicable. + + ```rust + use aws_smithy_http_server::plugin::Scoped; + use pokemon_service_server_sdk::scoped; + + scope! { + /// Includes only the `CheckHealth` and `GetStorage` operation. + struct SomeScope { + includes: [CheckHealth, GetStorage] + } + } + let scoped_plugin = Scoped::new::(plugin); + ``` + +- ⚠ (all, [smithy-rs#2675](https://github.com/awslabs/smithy-rs/issues/2675)) Remove native-tls and add a migration guide. +- ⚠ (client, [smithy-rs#2671](https://github.com/awslabs/smithy-rs/issues/2671))

+ Breaking change in how event stream signing works (click to expand more details) + + This change will only impact you if you are wiring up their own event stream signing/authentication scheme. If you're using `aws-sig-auth` to use AWS SigV4 event stream signing, then this change will **not** impact you. + + Previously, event stream signing was configured at codegen time by placing a `new_event_stream_signer` method on the `Config`. This function was called at serialization time to connect the signer to the streaming body. Now, instead, a special `DeferredSigner` is wired up at serialization time that relies on a signing implementation to be sent on a channel by the HTTP request signer. To do this, a `DeferredSignerSender` must be pulled out of the property bag, and its `send()` method called with the desired event stream signing implementation. + + See the changes in https://github.com/awslabs/smithy-rs/pull/2671 for an example of how this was done for SigV4. +
+- ⚠ (all, [smithy-rs#2673](https://github.com/awslabs/smithy-rs/issues/2673)) For event stream operations, the `EventStreamSender` in inputs/outputs now requires the passed in `Stream` impl to implement `Sync`. +- ⚠ (server, [smithy-rs#2539](https://github.com/awslabs/smithy-rs/issues/2539)) Code generation will abort if the `ignoreUnsupportedConstraints` codegen flag has no effect, that is, if all constraint traits used in your model are well-supported. Please remove the flag in such case. +- ⚠ (client, [smithy-rs#2728](https://github.com/awslabs/smithy-rs/issues/2728), [smithy-rs#2262](https://github.com/awslabs/smithy-rs/issues/2262), [aws-sdk-rust#2087](https://github.com/awslabs/aws-sdk-rust/issues/2087)) The property bag type for Time is now `SharedTimeSource`, not `SystemTime`. If your code relies on setting request time, use `aws_smithy_async::time::SharedTimeSource`. +- ⚠ (server, [smithy-rs#2676](https://github.com/awslabs/smithy-rs/issues/2676), [smithy-rs#2685](https://github.com/awslabs/smithy-rs/issues/2685)) Bump dependency on `lambda_http` by `aws-smithy-http-server` to 0.8.0. This version of `aws-smithy-http-server` is only guaranteed to be compatible with 0.8.0, or semver-compatible versions of 0.8.0 of the `lambda_http` crate. It will not work with versions prior to 0.8.0 _at runtime_, making requests to your smithy-rs service unroutable, so please make sure you're running your service in a compatible configuration +- ⚠ (server, [smithy-rs#2457](https://github.com/awslabs/smithy-rs/issues/2457), @hlbarber) Remove `PollError` from an operations `Service::Error`. + + Any [`tower::Service`](https://docs.rs/tower/latest/tower/trait.Service.html) provided to + [`Operation::from_service`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/struct.Operation.html#method.from_service) + no longer requires `Service::Error = OperationError`, instead requiring just `Service::Error = Op::Error`. +- ⚠ (client, [smithy-rs#2742](https://github.com/awslabs/smithy-rs/issues/2742)) A newtype wrapper `SharedAsyncSleep` has been introduced and occurrences of `Arc` that appear in public APIs have been replaced with it. +- ⚠ (all, [smithy-rs#2893](https://github.com/awslabs/smithy-rs/issues/2893)) Update MSRV to Rust 1.69.0 +- ⚠ (server, [smithy-rs#2678](https://github.com/awslabs/smithy-rs/issues/2678)) `ShapeId` is the new structure used to represent a shape, with its absolute name, namespace and name. + `OperationExtension`'s members are replaced by the `ShapeId` and operations' names are now replced by a `ShapeId`. + + Before you had an operation and an absolute name as its `NAME` member. You could apply a plugin only to some selected operation: + + ``` + filter_by_operation_name(plugin, |name| name != Op::ID); + ``` + + Your new filter selects on an operation's absolute name, namespace or name. + + ``` + filter_by_operation_id(plugin, |id| id.name() != Op::ID.name()); + ``` + + The above filter is applied to an operation's name, the one you use to specify the operation in the Smithy model. + + You can filter all operations in a namespace or absolute name: + + ``` + filter_by_operation_id(plugin, |id| id.namespace() != "namespace"); + filter_by_operation_id(plugin, |id| id.absolute() != "namespace#name"); + ``` +- ⚠ (client, [smithy-rs#2758](https://github.com/awslabs/smithy-rs/issues/2758)) The occurrences of `Arc` have now been replaced with `SharedEndpointResolver` in public APIs. +- ⚠ (server, [smithy-rs#2740](https://github.com/awslabs/smithy-rs/issues/2740), [smithy-rs#2759](https://github.com/awslabs/smithy-rs/issues/2759), [smithy-rs#2779](https://github.com/awslabs/smithy-rs/issues/2779), @hlbarber) Remove `filter_by_operation_id` and `plugin_from_operation_id_fn` in favour of `filter_by_operation` and `plugin_from_operation_fn`. + + Previously, we provided `filter_by_operation_id` which filtered `Plugin` application via a predicate over the Shape ID. + + ```rust + use aws_smithy_http_server::plugin::filter_by_operation_id; + use pokemon_service_server_sdk::operation_shape::CheckHealth; + + let filtered = filter_by_operation_id(plugin, |name| name != CheckHealth::NAME); + ``` + + This had the problem that the user is unable to exhaustively match over a `&'static str`. To remedy this we have switched to `filter_by_operation` which is a predicate over an enum containing all operations contained in the service. + + ```rust + use aws_smithy_http_server::plugin::filter_by_operation_id; + use pokemon_service_server_sdk::service::Operation; + + let filtered = filter_by_operation(plugin, |op: Operation| op != Operation::CheckHealth); + ``` + + Similarly, `plugin_from_operation_fn` now allows for + + ```rust + use aws_smithy_http_server::plugin::plugin_from_operation_fn; + use pokemon_service_server_sdk::service::Operation; + + fn map(op: Operation, inner: S) -> PrintService { + match op { + Operation::CheckHealth => PrintService { name: op.shape_id().name(), inner }, + Operation::GetPokemonSpecies => PrintService { name: "hello world", inner }, + _ => todo!() + } + } + + let plugin = plugin_from_operation_fn(map); + ``` +- ⚠ (client, [smithy-rs#2783](https://github.com/awslabs/smithy-rs/issues/2783)) The naming `make_token` for fields and the API of `IdempotencyTokenProvider` in service configs and their builders has now been updated to `idempotency_token_provider`. +- ⚠ (client, [smithy-rs#2845](https://github.com/awslabs/smithy-rs/issues/2845)) `aws_smithy_async::future::rendezvous::Sender::send` no longer exposes `tokio::sync::mpsc::error::SendError` for the error of its return type and instead exposes a new-type wrapper called `aws_smithy_async::future::rendezvous::error::SendError`. In addition, the `aws_smithy_xml` crate no longer exposes types from `xmlparser`. +- ⚠ (client, [smithy-rs#2848](https://github.com/awslabs/smithy-rs/issues/2848)) The implementation `From` for `aws_smithy_http::event_stream::RawMessage` has been removed. +- ⚠ (server, [smithy-rs#2865](https://github.com/awslabs/smithy-rs/issues/2865)) The `alb_health_check` module has been moved out of the `plugin` module into a new `layer` module. ALB health checks should be enacted before routing, and plugins run after routing, so the module location was misleading. Examples have been corrected to reflect the intended application of the layer. +- ⚠ (client, [smithy-rs#2873](https://github.com/awslabs/smithy-rs/issues/2873)) The `test-util` feature in aws-smithy-client has been split to include a separate `wiremock` feature. This allows test-util to be used without a Hyper server dependency making it usable in webassembly targets. +- ⚠ (client) The entire architecture of generated clients has been overhauled. See the [upgrade guide](https://github.com/awslabs/smithy-rs/discussions/2887) to get your code working again. + +**New this release:** +- 🎉 (all, [smithy-rs#2647](https://github.com/awslabs/smithy-rs/issues/2647), [smithy-rs#2645](https://github.com/awslabs/smithy-rs/issues/2645), [smithy-rs#2646](https://github.com/awslabs/smithy-rs/issues/2646), [smithy-rs#2616](https://github.com/awslabs/smithy-rs/issues/2616), @thomas-k-cameron) Implement unstable serde support for the `Number`, `Blob`, `Document`, `DateTime` primitives +- 🎉 (client, [smithy-rs#2652](https://github.com/awslabs/smithy-rs/issues/2652), @thomas-k-cameron) Add a `send_with` function on `-Input` types for sending requests without fluent builders +- (client, [smithy-rs#2791](https://github.com/awslabs/smithy-rs/issues/2791), @davidsouther) Add accessors to Builders +- (all, [smithy-rs#2786](https://github.com/awslabs/smithy-rs/issues/2786), @yotamofek) Avoid intermediate vec allocations in AggregatedBytes::to_vec. +- 🐛 (server, [smithy-rs#2733](https://github.com/awslabs/smithy-rs/issues/2733), @thor-bjorgvinsson) Fix bug in AWS JSON 1.x routers where, if a service had more than 14 operations, the router was created without the route for the 15th operation. +- (client, [smithy-rs#2728](https://github.com/awslabs/smithy-rs/issues/2728), [smithy-rs#2262](https://github.com/awslabs/smithy-rs/issues/2262), [aws-sdk-rust#2087](https://github.com/awslabs/aws-sdk-rust/issues/2087)) Time is now controlled by the `TimeSource` trait. This facilitates testing as well as use cases like WASM where `SystemTime::now()` is not supported. +- 🐛 (client, [smithy-rs#2767](https://github.com/awslabs/smithy-rs/issues/2767), @mcmasn-amzn) Fix bug in client generation when using smithy.rules#endpointTests and operation and service shapes are in different namespaces. +- (client, [smithy-rs#2854](https://github.com/awslabs/smithy-rs/issues/2854)) Public fields in structs are no longer marked as `#[doc(hidden)]`, and they are now visible. +- (server, [smithy-rs#2866](https://github.com/awslabs/smithy-rs/issues/2866)) [RestJson1](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restjson1-protocol.html#operation-error-serialization) server SDKs now serialize only the [shape name](https://smithy.io/2.0/spec/model.html#shape-id) in operation error responses. Previously (from versions 0.52.0 to 0.55.4), the full shape ID was rendered. + Example server error response by a smithy-rs server version 0.52.0 until 0.55.4: + ``` + HTTP/1.1 400 Bad Request + content-type: application/json + x-amzn-errortype: com.example.service#InvalidRequestException + ... + ``` + Example server error response now: + ``` + HTTP/1.1 400 Bad Request + content-type: application/json + x-amzn-errortype: InvalidRequestException + ... + ``` + +**Contributors** +Thank you for your contributions! ❤ +- @davidsouther ([smithy-rs#2791](https://github.com/awslabs/smithy-rs/issues/2791)) +- @hlbarber ([smithy-rs#2457](https://github.com/awslabs/smithy-rs/issues/2457), [smithy-rs#2740](https://github.com/awslabs/smithy-rs/issues/2740), [smithy-rs#2759](https://github.com/awslabs/smithy-rs/issues/2759), [smithy-rs#2779](https://github.com/awslabs/smithy-rs/issues/2779), [smithy-rs#2827](https://github.com/awslabs/smithy-rs/issues/2827)) +- @mcmasn-amzn ([smithy-rs#2767](https://github.com/awslabs/smithy-rs/issues/2767)) +- @thomas-k-cameron ([smithy-rs#2616](https://github.com/awslabs/smithy-rs/issues/2616), [smithy-rs#2645](https://github.com/awslabs/smithy-rs/issues/2645), [smithy-rs#2646](https://github.com/awslabs/smithy-rs/issues/2646), [smithy-rs#2647](https://github.com/awslabs/smithy-rs/issues/2647), [smithy-rs#2652](https://github.com/awslabs/smithy-rs/issues/2652)) +- @thor-bjorgvinsson ([smithy-rs#2733](https://github.com/awslabs/smithy-rs/issues/2733)) +- @yotamofek ([smithy-rs#2786](https://github.com/awslabs/smithy-rs/issues/2786)) + + May 23rd, 2023 ============== **New this release:** diff --git a/CHANGELOG.next.toml b/CHANGELOG.next.toml index a99d1f3afb..fc4c4c2578 100644 --- a/CHANGELOG.next.toml +++ b/CHANGELOG.next.toml @@ -9,717 +9,4 @@ # message = "Fix typos in module documentation for generated crates" # references = ["smithy-rs#920"] # meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client | server | all"} -# author = "rcoh" - - [[aws-sdk-rust]] - message = "Automatically exclude X-Ray trace ID headers and authorization headers from SigV4 canonical request calculations." - references = ["smithy-rs#2815"] - meta = { "breaking" = false, "tada" = false, "bug" = true } - author = "relevantsam" - - [[aws-sdk-rust]] - message = "Add accessors to Builders" - references = ["smithy-rs#2791"] - meta = { "breaking" = false, "tada" = false, "bug" = false } - author = "davidsouther" - - [[smithy-rs]] - message = "Add accessors to Builders" - references = ["smithy-rs#2791"] - meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client"} - author = "davidsouther" - -[[smithy-rs]] -message = "Avoid intermediate vec allocations in AggregatedBytes::to_vec." -author = "yotamofek" -references = ["smithy-rs#2786"] -meta = { "breaking" = false, "tada" = false, "bug" = false } - -[[smithy-rs]] -message = "Fix bug in AWS JSON 1.x routers where, if a service had more than 14 operations, the router was created without the route for the 15th operation." -author = "thor-bjorgvinsson" -references = ["smithy-rs#2733"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "server" } - -[[aws-sdk-rust]] -message = "Remove native-tls and add a migration guide." -author = "82marbag" -references = ["smithy-rs#2675"] -meta = { "breaking" = true, "tada" = false, "bug" = false } - -[[smithy-rs]] -message = "Remove native-tls and add a migration guide." -author = "82marbag" -references = ["smithy-rs#2675"] -meta = { "breaking" = true, "tada" = false, "bug" = false } - -[[aws-sdk-rust]] -message = "Fix error message when `credentials-sso` feature is not enabled on `aws-config`. NOTE: if you use `no-default-features`, you will need to manually able `credentials-sso` after 0.55.*" -references = ["smithy-rs#2722", "aws-sdk-rust#703"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "rcoh" - -[[aws-sdk-rust]] -message = "`SsoCredentialsProvider`, `AssumeRoleProvider`, and `WebIdentityTokenCredentialsProvider` now use `NoCredentialsCache` internally when fetching credentials using an STS client. This avoids double-caching when these providers are wrapped by `LazyCredentialsCache` when a service client is created." -references = ["smithy-rs#2720"] -meta = { "breaking" = false, "tada" = false, "bug" = true } -author = "ysaito1001" - -[[smithy-rs]] -message = """ -
-Breaking change in how event stream signing works (click to expand more details) - -This change will only impact you if you are wiring up their own event stream signing/authentication scheme. If you're using `aws-sig-auth` to use AWS SigV4 event stream signing, then this change will **not** impact you. - -Previously, event stream signing was configured at codegen time by placing a `new_event_stream_signer` method on the `Config`. This function was called at serialization time to connect the signer to the streaming body. Now, instead, a special `DeferredSigner` is wired up at serialization time that relies on a signing implementation to be sent on a channel by the HTTP request signer. To do this, a `DeferredSignerSender` must be pulled out of the property bag, and its `send()` method called with the desired event stream signing implementation. - -See the changes in https://github.com/awslabs/smithy-rs/pull/2671 for an example of how this was done for SigV4. -
-""" -references = ["smithy-rs#2671"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } -author = "jdisanti" - -[[aws-sdk-rust]] -message = "For event stream operations such as S3 SelectObjectContent or Transcribe StartStreamTranscription, the `EventStreamSender` in the input now requires the passed in `Stream` impl to implement `Sync`." -references = ["smithy-rs#2673"] -meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "jdisanti" - -[[smithy-rs]] -message = "For event stream operations, the `EventStreamSender` in inputs/outputs now requires the passed in `Stream` impl to implement `Sync`." -references = ["smithy-rs#2673"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all" } -author = "jdisanti" - -[[aws-sdk-rust]] -message = "The `SigningInstructions` in the `aws-sigv4` module are now public. This allows them to be named in a function signature." -references = ["smithy-rs#2730"] -author = "cholcombe973" -meta = { "breaking" = false, "tada" = false, "bug" = true } - -[[smithy-rs]] -message = "Code generation will abort if the `ignoreUnsupportedConstraints` codegen flag has no effect, that is, if all constraint traits used in your model are well-supported. Please remove the flag in such case." -references = ["smithy-rs#2539"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } -author = "david-perez" - -[[smithy-rs]] -message = "Time is now controlled by the `TimeSource` trait. This facilitates testing as well as use cases like WASM where `SystemTime::now()` is not supported." -references = ["smithy-rs#2728", "smithy-rs#2262", "aws-sdk-rust#2087"] -meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } -author = "rcoh" - -[[smithy-rs]] -message = "The property bag type for Time is now `SharedTimeSource`, not `SystemTime`. If your code relies on setting request time, use `aws_smithy_async::time::SharedTimeSource`." -references = ["smithy-rs#2728", "smithy-rs#2262", "aws-sdk-rust#2087"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } -author = "rcoh" - -[[aws-sdk-rust]] -message = "Time is now controlled by the `TimeSource` trait. This facilitates testing as well as use cases like WASM where `SystemTime::now()` is not supported." -references = ["smithy-rs#2728", "smithy-rs#2262", "aws-sdk-rust#2087"] -meta = { "breaking" = false, "tada" = false, "bug" = false } -author = "rcoh" - -[[smithy-rs]] -message = "Bump dependency on `lambda_http` by `aws-smithy-http-server` to 0.8.0. This version of `aws-smithy-http-server` is only guaranteed to be compatible with 0.8.0, or semver-compatible versions of 0.8.0 of the `lambda_http` crate. It will not work with versions prior to 0.8.0 _at runtime_, making requests to your smithy-rs service unroutable, so please make sure you're running your service in a compatible configuration" -author = "david-perez" -references = ["smithy-rs#2676", "smithy-rs#2685"] -meta = { "breaking" = true, "tada" = false, "bug" = false, target = "server" } - -[[smithy-rs]] -message = """Remove `PollError` from an operations `Service::Error`. - -Any [`tower::Service`](https://docs.rs/tower/latest/tower/trait.Service.html) provided to -[`Operation::from_service`](https://docs.rs/aws-smithy-http-server/latest/aws_smithy_http_server/operation/struct.Operation.html#method.from_service) -no longer requires `Service::Error = OperationError`, instead requiring just `Service::Error = Op::Error`. -""" -references = ["smithy-rs#2457"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } -author = "hlbarber" - -[[aws-sdk-rust]] -message = "The SDK has added support for timestreamwrite and timestreamquery. Support for these services is considered experimental at this time. In order to use these services, you MUST call `.with_endpoint_discovery_enabled()` on the `Client` after construction." -meta = { "breaking" = false, "tada" = true, "bug" = false } -references = ["smithy-rs#2707", "aws-sdk-rust#114", "smithy-rs#2846"] -author = "rcoh" - -[[smithy-rs]] -message = "A newtype wrapper `SharedAsyncSleep` has been introduced and occurrences of `Arc` that appear in public APIs have been replaced with it." -references = ["smithy-rs#2742"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } -author = "ysaito1001" - -[[aws-sdk-rust]] -message = "A newtype wrapper `SharedAsyncSleep` has been introduced and occurrences of `Arc` that appear in public APIs have been replaced with it." -references = ["smithy-rs#2742"] -meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "ysaito1001" - -[[aws-sdk-rust]] -message = "Update MSRV to Rust 1.69.0" -references = ["smithy-rs#2893"] -meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "Velfi" - -[[smithy-rs]] -message = "Update MSRV to Rust 1.69.0" -references = ["smithy-rs#2893"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "all" } -author = "Velfi" - -[[smithy-rs]] -message = """`ShapeId` is the new structure used to represent a shape, with its absolute name, namespace and name. -`OperationExtension`'s members are replaced by the `ShapeId` and operations' names are now replced by a `ShapeId`. - -Before you had an operation and an absolute name as its `NAME` member. You could apply a plugin only to some selected operation: - -``` -filter_by_operation_name(plugin, |name| name != Op::ID); -``` - -Your new filter selects on an operation's absolute name, namespace or name. - -``` -filter_by_operation_id(plugin, |id| id.name() != Op::ID.name()); -``` - -The above filter is applied to an operation's name, the one you use to specify the operation in the Smithy model. - -You can filter all operations in a namespace or absolute name: - -``` -filter_by_operation_id(plugin, |id| id.namespace() != "namespace"); -filter_by_operation_id(plugin, |id| id.absolute() != "namespace#name"); -``` -""" -author = "82marbag" -references = ["smithy-rs#2678"] -meta = { "breaking" = true, "tada" = false, "bug" = false, target = "server" } - -[[smithy-rs]] -message = "The occurrences of `Arc` have now been replaced with `SharedEndpointResolver` in public APIs." -references = ["smithy-rs#2758"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } -author = "ysaito1001" - -[[smithy-rs]] -message = """The middleware system has been reworked as we push for a unified, simple, and consistent API. The following changes have been made in service of this goal: - -- A `ServiceShape` trait has been added. -- The `Plugin` trait has been simplified. -- The `HttpMarker` and `ModelMarker` marker traits have been added to better distinguish when plugins run and what they have access to. -- The `Operation` structure has been removed. -- A `Scoped` `Plugin` has been added. - -The `Plugin` trait has now been simplified and the `Operation` struct has been removed. - -## Addition of `ServiceShape` - -Since the [0.52 release](https://github.com/awslabs/smithy-rs/releases/tag/release-2022-12-12) the `OperationShape` has existed. - -```rust -/// Models the [Smithy Operation shape]. -/// -/// [Smithy Operation shape]: https://awslabs.github.io/smithy/1.0/spec/core/model.html#operation -pub trait OperationShape { - /// The ID of the operation. - const ID: ShapeId; - - /// The operation input. - type Input; - /// The operation output. - type Output; - /// The operation error. [`Infallible`](std::convert::Infallible) in the case where no error - /// exists. - type Error; -} -``` - -This allowed `Plugin` authors to access these associated types and constants. See the [`PrintPlugin`](https://github.com/awslabs/smithy-rs/blob/main/examples/pokemon-service/src/plugin.rs) as an example. - -We continue with this approach and introduce the following trait: - -```rust -/// Models the [Smithy Service shape]. -/// -/// [Smithy Service shape]: https://smithy.io/2.0/spec/service-types.html -pub trait ServiceShape { - /// The [`ShapeId`] of the service. - const ID: ShapeId; - - /// The version of the service. - const VERSION: Option<&'static str>; - - /// The [Protocol] applied to this service. - /// - /// [Protocol]: https://smithy.io/2.0/spec/protocol-traits.html - type Protocol; - - /// An enumeration of all operations contained in this service. - type Operations; -} -``` - -With the changes to `Plugin`, described below, middleware authors now have access to this information at compile time. - -## Simplication of the `Plugin` trait - -Previously, - -```rust -trait Plugin { - type Service; - type Layer; - - fn map(&self, input: Operation) -> Operation; -} -``` - -modified an `Operation`. - -Now, - -```rust -trait Plugin { - type Output; - - fn apply(&self, input: T) -> Self::Output; -} -``` - -maps a `tower::Service` to a `tower::Service`. This is equivalent to `tower::Layer` with two extra type parameters: `Service` and `Operation`, which implement `ServiceShape` and `OperationShape` respectively. - -Having both `Service` and `Operation` as type parameters also provides an even surface for advanced users to extend the codegenerator in a structured way. See [this issue](https://github.com/awslabs/smithy-rs/issues/2777) for more context. - -The following middleware setup - -```rust -pub struct PrintService { - inner: S, - name: &'static str, -} - -impl Service for PrintService -where - S: Service, -{ - async fn call(&mut self, req: R) -> Self::Future { - println!("Hi {}", self.name); - self.inner.call(req) - } -} - -pub struct PrintLayer { - name: &'static str, -} - -impl Layer for PrintLayer { - type Service = PrintService; - - fn layer(&self, service: S) -> Self::Service { - PrintService { - inner: service, - name: self.name, - } - } -} - -pub struct PrintPlugin; - -impl Plugin for PrintPlugin -where - Op: OperationShape, -{ - type Service = S; - type Layer = Stack; - - fn map(&self, input: Operation) -> Operation { - input.layer(PrintLayer { name: Op::NAME }) - } -} -``` - -now becomes - -```rust -pub struct PrintService { - inner: S, - name: &'static str, -} - -impl Service for PrintService -where - S: Service, -{ - async fn call(&mut self, req: R) -> Self::Future { - println!("Hi {}", self.name); - self.inner.call(req) - } -} - -pub struct PrintPlugin; - -impl Plugin for PrintPlugin -where - Op: OperationShape, -{ - type Output = PrintService; - - fn apply(&self, inner: T) -> Self::Output { - PrintService { inner, name: Op::ID.name() } - } -} - -impl HttpMarker for PrintPlugin { } -``` - -Alternatively, using the new `ServiceShape`, implemented on `Ser`: - -```rust -impl Plugin for PrintPlugin -where - Ser: ServiceShape, -{ - type Service = PrintService; - - fn apply(&self, inner: T) -> Self::Service { - PrintService { inner, name: Ser::ID.name() } - } -} -``` - -A single `Plugin` can no longer apply a `tower::Layer` on HTTP requests/responses _and_ modelled structures at the same time (see middleware positions [C](https://awslabs.github.io/smithy-rs/design/server/middleware.html#c-operation-specific-http-middleware) and [D](https://awslabs.github.io/smithy-rs/design/server/middleware.html#d-operation-specific-model-middleware). Instead one `Plugin` must be specified for each and passed to the service builder constructor separately: - -```rust -let app = PokemonService::builder_with_plugins(/* HTTP plugins */, /* model plugins */) - /* setters */ - .build() - .unwrap(); -``` - -To better distinguish when a plugin runs and what it has access to, `Plugin`s now have to additionally implement the `HttpMarker` marker trait, the `ModelMarker` marker trait, or both: - -- A HTTP plugin acts on the HTTP request before it is deserialized, and acts on the HTTP response after it is serialized. -- A model plugin acts on the modeled operation input after it is deserialized, and acts on the modeled operation output or the modeled operation error before it is serialized. - -The motivation behind this change is to simplify the job of middleware authors, separate concerns, accomodate common cases better, and to improve composition internally. - -Because `Plugin` is now closer to `tower::Layer` we have two canonical converters: - -```rust -use aws_smithy_http_server::plugin::{PluginLayer, LayerPlugin}; - -// Convert from `Layer` to `Plugin` which applies uniformly across all operations -let layer = /* some layer */; -let plugin = PluginLayer(layer); - -// Convert from `Plugin` to `Layer` for some fixed protocol and operation -let plugin = /* some plugin */; -let layer = LayerPlugin::new::(plugin); -``` - -## Removal of `PluginPipeline` - -Since plugins now come in two flavors (those marked with `HttpMarker` and those marked with `ModelMarker`) that shouldn't be mixed in a collection of plugins, the primary way of concatenating plugins, `PluginPipeline` has been removed in favor of the `HttpPlugins` and `ModelPlugins` types, which eagerly check that whenever a plugin is pushed, it is of the expected type. - -This worked before, but you wouldn't be able to do apply this collection of plugins anywhere; if you tried to, the compilation error messages would not be very helpful: - -```rust -use aws_smithy_http_server::plugin::PluginPipeline; - -let pipeline = PluginPipeline::new().push(http_plugin).push(model_plugin); -``` - -Now collections of plugins must contain plugins of the same flavor: - -```rust -use aws_smithy_http_server::plugin::{HttpPlugins, ModelPlugins}; - -let http_plugins = HttpPlugins::new() - .push(http_plugin) - // .push(model_plugin) // This fails to compile with a helpful error message. - .push(&http_and_model_plugin); -let model_plugins = ModelPlugins::new() - .push(model_plugin) - .push(&http_and_model_plugin); -``` - -In the above example, `&http_and_model_plugin` implements both `HttpMarker` and `ModelMarker`, so we can add it to both collections. - -## Removal of `Operation` - -The `aws_smithy_http_server::operation::Operation` structure has now been removed. Previously, there existed a `{operation_name}_operation` setter on the service builder, which accepted an `Operation`. This allowed users to - -```rust -let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_service(/* tower::Service */); - -let app = PokemonService::builder_without_plugins() - .get_pokemon_species_operation(operation) - /* other setters */ - .build() - .unwrap(); -``` - -to set an operation with a `tower::Service`, and - -```rust -let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_service(/* tower::Service */).layer(/* layer */); -let operation /* : Operation<_, _> */ = GetPokemonSpecies::from_handler(/* closure */).layer(/* layer */); - -let app = PokemonService::builder_without_plugins() - .get_pokemon_species_operation(operation) - /* other setters */ - .build() - .unwrap(); -``` - -to add a `tower::Layer` (acting on HTTP requests/responses post-routing) to a single operation. - -We have seen little adoption of this API and for this reason we have opted instead to introduce a new setter, accepting a `tower::Service`, on the service builder: - -```rust -let app = PokemonService::builder_without_plugins() - .get_pokemon_species_service(/* tower::Service */) - /* other setters */ - .build() - .unwrap(); -``` - -Applying a `tower::Layer` to a _subset_ of operations is should now be done through the `Plugin` API via `filter_by_operation_id` - -```rust -use aws_smithy_http_server::plugin::{PluginLayer, filter_by_operation_name, IdentityPlugin}; - -let plugin = PluginLayer(/* layer */); -let scoped_plugin = filter_by_operation_name(plugin, |id| id == GetPokemonSpecies::ID); - -let app = PokemonService::builder_with_plugins(scoped_plugin, IdentityPlugin) - .get_pokemon_species(/* handler */) - /* other setters */ - .build() - .unwrap(); -``` - -or the new `Scoped` `Plugin` introduced below. - -# Addition of `Scoped` - -Currently, users can selectively apply a `Plugin` via the `filter_by_operation_id` function - -```rust -use aws_smithy_http_server::plugin::filter_by_operation_id; -// Only apply `plugin` to `CheckHealth` and `GetStorage` operation -let filtered_plugin = filter_by_operation_id(plugin, |name| name == CheckHealth::ID || name == GetStorage::ID); -``` - -In addition to this, we now provide `Scoped`, which selectively applies a `Plugin` at _compiletime_. Users should prefer this to `filter_by_operation_id` when applicable. - -```rust -use aws_smithy_http_server::plugin::Scoped; -use pokemon_service_server_sdk::scoped; - -scope! { - /// Includes only the `CheckHealth` and `GetStorage` operation. - struct SomeScope { - includes: [CheckHealth, GetStorage] - } -} -let scoped_plugin = Scoped::new::(plugin); -``` - -""" -references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779", "smithy-rs#2827"] -meta = { "breaking" = true, "tada" = true, "bug" = false, target = "server" } -author = "hlbarber" - -[[smithy-rs]] -message = "Implement unstable serde support for the `Number`, `Blob`, `Document`, `DateTime` primitives" -author = "thomas-k-cameron" -meta = { "breaking" = false, "tada" = true, "bug" = false, target = "all" } -references = [ - "smithy-rs#2647", - "smithy-rs#2645", - "smithy-rs#2646", - "smithy-rs#2616", -] - -[[aws-sdk-rust]] -message = "Implement unstable serde support for the `Number`, `Blob`, `Document`, `DateTime` primitives" -author = "thomas-k-cameron" -meta = { "breaking" = false, "tada" = true, "bug" = false } -references = [ - "smithy-rs#2647", - "smithy-rs#2645", - "smithy-rs#2646", - "smithy-rs#2616", -] - -[[smithy-rs]] -message = "Add a `send_with` function on `-Input` types for sending requests without fluent builders" -author = "thomas-k-cameron" -references = ["smithy-rs#2652"] -meta = { "breaking" = false, "tada" = true, "bug" = false, target = "client" } - -[[aws-sdk-rust]] -message = "Add a `send_with` function on `-Input` types for sending requests without fluent builders" -author = "thomas-k-cameron" -references = ["smithy-rs#2652"] -meta = { "breaking" = false, "tada" = true, "bug" = false } - -[[smithy-rs]] -message = """Remove `filter_by_operation_id` and `plugin_from_operation_id_fn` in favour of `filter_by_operation` and `plugin_from_operation_fn`. - -Previously, we provided `filter_by_operation_id` which filtered `Plugin` application via a predicate over the Shape ID. - -```rust -use aws_smithy_http_server::plugin::filter_by_operation_id; -use pokemon_service_server_sdk::operation_shape::CheckHealth; - -let filtered = filter_by_operation_id(plugin, |name| name != CheckHealth::NAME); -``` - -This had the problem that the user is unable to exhaustively match over a `&'static str`. To remedy this we have switched to `filter_by_operation` which is a predicate over an enum containing all operations contained in the service. - -```rust -use aws_smithy_http_server::plugin::filter_by_operation_id; -use pokemon_service_server_sdk::service::Operation; - -let filtered = filter_by_operation(plugin, |op: Operation| op != Operation::CheckHealth); -``` - -Similarly, `plugin_from_operation_fn` now allows for - -```rust -use aws_smithy_http_server::plugin::plugin_from_operation_fn; -use pokemon_service_server_sdk::service::Operation; - -fn map(op: Operation, inner: S) -> PrintService { - match op { - Operation::CheckHealth => PrintService { name: op.shape_id().name(), inner }, - Operation::GetPokemonSpecies => PrintService { name: "hello world", inner }, - _ => todo!() - } -} - -let plugin = plugin_from_operation_fn(map); -``` -""" -references = ["smithy-rs#2740", "smithy-rs#2759", "smithy-rs#2779"] -meta = { "breaking" = true, "tada" = false, "bug" = false, target = "server" } -author = "hlbarber" - -[[smithy-rs]] -message = "Fix bug in client generation when using smithy.rules#endpointTests and operation and service shapes are in different namespaces." -author = "mcmasn-amzn" -references = ["smithy-rs#2767"] -meta = { "breaking" = false, "tada" = false, "bug" = true, "target" = "client" } - -[[smithy-rs]] -message = "The naming `make_token` for fields and the API of `IdempotencyTokenProvider` in service configs and their builders has now been updated to `idempotency_token_provider`." -references = ["smithy-rs#2783"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } -author = "ysaito1001" - -[[aws-sdk-rust]] -message = "The naming `make_token` for fields and the API of `IdempotencyTokenProvider` in service configs and their builders has now been updated to `idempotency_token_provider`." -references = ["smithy-rs#2783"] -meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "ysaito1001" - -[[smithy-rs]] -message = "`aws_smithy_async::future::rendezvous::Sender::send` no longer exposes `tokio::sync::mpsc::error::SendError` for the error of its return type and instead exposes a new-type wrapper called `aws_smithy_async::future::rendezvous::error::SendError`. In addition, the `aws_smithy_xml` crate no longer exposes types from `xmlparser`." -references = ["smithy-rs#2845"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } -author = "ysaito1001" - -[[aws-sdk-rust]] -message = "The implementation `From` for `aws_http::user_agent::UserAgentStageError` has been removed." -references = ["smithy-rs#2845"] -meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "ysaito1001" - -[[smithy-rs]] -message = "The implementation `From` for `aws_smithy_http::event_stream::RawMessage` has been removed." -references = ["smithy-rs#2848"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client" } -author = "ysaito1001" - -[[smithy-rs]] -message = "Public fields in structs are no longer marked as `#[doc(hidden)]`, and they are now visible." -references = ["smithy-rs#2854"] -meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "client" } -author = "ysaito1001" - -[[aws-sdk-rust]] -message = "The AppName property can now be set with `sdk_ua_app_id` in profile files. The old field, `sdk-ua-app-id`, is maintained for backwards compatibility." -references = ["smithy-rs#2724"] -meta = { "breaking" = false, "tada" = false, "bug" = false } -author = "rcoh" - -[[smithy-rs]] -message = "The `alb_health_check` module has been moved out of the `plugin` module into a new `layer` module. ALB health checks should be enacted before routing, and plugins run after routing, so the module location was misleading. Examples have been corrected to reflect the intended application of the layer." -references = ["smithy-rs#2865"] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "server" } -author = "david-perez" - -[[aws-sdk-rust]] -message = "**Behavior change**: Credential providers now share the HTTP connector used by the SDK. If you want to keep a separate connector for clients, use `::ConfigBuilder::http_connector` when constructing the client." -references = ["aws-sdk-rust#579", "aws-sdk-rust#338"] -meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "rcoh" - -[[smithy-rs]] -message = """ -[RestJson1](https://awslabs.github.io/smithy/2.0/aws/protocols/aws-restjson1-protocol.html#operation-error-serialization) server SDKs now serialize only the [shape name](https://smithy.io/2.0/spec/model.html#shape-id) in operation error responses. Previously (from versions 0.52.0 to 0.55.4), the full shape ID was rendered. -Example server error response by a smithy-rs server version 0.52.0 until 0.55.4: -``` -HTTP/1.1 400 Bad Request -content-type: application/json -x-amzn-errortype: com.example.service#InvalidRequestException -... -``` -Example server error response now: -``` -HTTP/1.1 400 Bad Request -content-type: application/json -x-amzn-errortype: InvalidRequestException -... -``` -""" -references = ["smithy-rs#2866"] -meta = { "breaking" = false, "tada" = false, "bug" = false, "target" = "server" } -author = "david-perez" - - -[[smithy-rs]] -message = "The `test-util` feature in aws-smithy-client has been split to include a separate `wiremock` feature. This allows test-util to be used without a Hyper server dependency making it usable in webassembly targets." -meta = { "breaking" = true, "tada" = false , "bug" = false, "target" = "client" } -author = "rcoh" -references = ["smithy-rs#2873"] - -[[aws-sdk-rust]] -message = """The `doc(hidden)` `time_source` in `aws-credential-types` was removed. Use `aws_smithy_async::time` instead.""" -meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "rcoh" -references = ["smithy-rs#2877"] - -[[aws-sdk-rust]] -message = """The `doc(hidden)` `with_env` in `ProviderConfig` was removed.""" -meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "rcoh" -references = ["smithy-rs#2877"] - -[[aws-sdk-rust]] -message = "The underlying architecture of the SDK clients has been overhauled. This shouldn't require any changes for most projects, but will affect projects that customize the SDK middleware. More details are available in the [upgrade guide](https://github.com/awslabs/aws-sdk-rust/discussions/853) if you are effected by these changes." -references = [] -meta = { "breaking" = true, "tada" = false, "bug" = false } -author = "jdisanti" - -[[smithy-rs]] -message = "The entire architecture of generated clients has been overhauled. See the [upgrade guide](https://github.com/awslabs/smithy-rs/discussions/2887) to get your code working again." -references = [] -meta = { "breaking" = true, "tada" = false, "bug" = false, "target" = "client"} -author = "jdisanti" +# author = "rcoh" \ No newline at end of file diff --git a/aws/SDK_CHANGELOG.next.json b/aws/SDK_CHANGELOG.next.json index 1beee31b1e..cb5a407d32 100644 --- a/aws/SDK_CHANGELOG.next.json +++ b/aws/SDK_CHANGELOG.next.json @@ -5,150 +5,6 @@ { "smithy-rs": [], "aws-sdk-rust": [ - { - "message": "Integrate Endpoints 2.0 into the Rust SDK. Endpoints 2.0 enables features like S3 virtual addressing & S3\nobject lambda. As part of this change, there are several breaking changes although efforts have been made to deprecate\nwhere possible to smooth the upgrade path.\n1. `aws_smithy_http::endpoint::Endpoint` and the `endpoint_resolver` methods have been deprecated. In general, these usages\n should be replaced with usages of `endpoint_url` instead. `endpoint_url` accepts a string so an `aws_smithy_http::Endpoint`\n does not need to be constructed. This structure and methods will be removed in a future release.\n2. The `endpoint_resolver` method on `::config::Builder` now accepts a service specific endpoint resolver instead\n of an implementation of `ResolveAwsEndpoint`. Most users will be able to replace these usages with a usage of `endpoint_url`.\n3. `ResolveAwsEndpoint` has been deprecated and will be removed in a future version of the SDK.\n4. The SDK does not support \"pseudo regions\" anymore. Specifically, regions like `iam-fips` will no longer resolve to a FIPS endpoint.\n", - "meta": { - "bug": false, - "breaking": true, - "tada": true - }, - "author": "rcoh", - "references": [ - "smithy-rs#1784", - "smithy-rs#2074" - ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 - }, - { - "message": "Add additional configuration parameters to `aws_sdk_s3::Config`.\n\nThe launch of endpoints 2.0 includes more configuration options for S3. The default behavior for endpoint resolution has\nbeen changed. Before, all requests hit the path-style endpoint. Going forward, all requests that can be routed to the\nvirtually hosted bucket will be routed there automatically.\n- `force_path_style`: Requests will now default to the virtually-hosted endpoint `.s3..amazonaws.com`\n- `use_arn_region`: Enables this client to use an ARN’s region when constructing an endpoint instead of the client’s configured region.\n- `accelerate`: Enables this client to use S3 Transfer Acceleration endpoints.\n\nNote: the AWS SDK for Rust does not currently support Multi Region Access Points (MRAP).\n", - "meta": { - "bug": false, - "breaking": true, - "tada": true - }, - "author": "rcoh", - "references": [ - "smithy-rs#1784", - "smithy-rs#2074" - ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 - }, - { - "message": "Move types for AWS SDK credentials to a separate crate.\nA new AWS runtime crate called `aws-credential-types` has been introduced. Types for AWS SDK credentials have been moved to that crate from `aws-config` and `aws-types`. The new crate is placed at the top of the dependency graph among AWS runtime crates with the aim of the downstream crates having access to the types defined in it.\n", - "meta": { - "bug": false, - "breaking": true, - "tada": false - }, - "author": "ysaito1001", - "references": [ - "smithy-rs#2108" - ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 - }, - { - "message": "Add support for overriding profile name and profile file location across all providers. Prior to this change, each provider needed to be updated individually.\n\n### Before\n```rust\nuse aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider};\nuse aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind};\n\nlet profile_files = ProfileFiles::builder()\n .with_file(ProfileFileKind::Credentials, \"some/path/to/credentials-file\")\n .build();\nlet credentials_provider = ProfileFileCredentialsProvider::builder()\n .profile_files(profile_files.clone())\n .build();\nlet region_provider = ProfileFileRegionProvider::builder()\n .profile_files(profile_files)\n .build();\n\nlet sdk_config = aws_config::from_env()\n .credentials_provider(credentials_provider)\n .region(region_provider)\n .load()\n .await;\n```\n\n### After\n```rust\nuse aws_config::profile::{ProfileFileCredentialsProvider, ProfileFileRegionProvider};\nuse aws_config::profile::profile_file::{ProfileFiles, ProfileFileKind};\n\nlet profile_files = ProfileFiles::builder()\n .with_file(ProfileFileKind::Credentials, \"some/path/to/credentials-file\")\n .build();\nlet sdk_config = aws_config::from_env()\n .profile_files(profile_files)\n .load()\n .await;\n/// ```\n", - "meta": { - "bug": false, - "breaking": false, - "tada": false - }, - "author": "rcoh", - "references": [ - "smithy-rs#2152" - ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 - }, - { - "message": "`aws_config::profile::retry_config` && `aws_config::environment::retry_config` have been removed. Use `aws_config::default_provider::retry_config` instead.", - "meta": { - "bug": false, - "breaking": true, - "tada": false - }, - "author": "rcoh", - "references": [ - "smithy-rs#2162" - ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 - }, - { - "message": "Add support for resolving FIPS and dual-stack endpoints.\n\nFIPS and dual-stack endpoints can each be configured in multiple ways:\n1. Automatically from the environment and AWS profile\n2. Across all clients loaded from the same `SdkConfig` via `from_env().use_dual_stack(true).load().await`\n3. At a client level when constructing the configuration for an individual client.\n\nNote: Not all services support FIPS and dual-stack.\n", - "meta": { - "bug": false, - "breaking": false, - "tada": true - }, - "author": "rcoh", - "references": [ - "smithy-rs#2168" - ], - "since-commit": "40da9a32b38e198da6ca2223b86c314b26438230", - "age": 5 - }, - { - "message": "Improve SDK credentials caching through type safety. `LazyCachingCredentialsProvider` has been renamed to `LazyCredentialsCache` and is no longer treated as a credentials provider. Furthermore, you do not create a `LazyCredentialsCache` directly, and instead you interact with `CredentialsCache`. This introduces the following breaking changes.\n\nIf you previously used `LazyCachingCredentialsProvider`, you can replace it with `CredentialsCache`.\n
\nExample\n\nBefore:\n```rust\nuse aws_config::meta::credentials::lazy_caching::LazyCachingCredentialsProvider;\nuse aws_types::provider::ProvideCredentials;\n\nfn make_provider() -> impl ProvideCredentials {\n // --snip--\n}\n\nlet credentials_provider =\n LazyCachingCredentialsProvider::builder()\n .load(make_provider())\n .build();\n\nlet sdk_config = aws_config::from_env()\n .credentials_provider(credentials_provider)\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\nAfter:\n```rust\nuse aws_credential_types::cache::CredentialsCache;\nuse aws_types::provider::ProvideCredentials;\n\nfn make_provider() -> impl ProvideCredentials {\n // --snip--\n}\n\n// Wrapping a result of `make_provider` in `LazyCredentialsCache` is done automatically.\nlet sdk_config = aws_config::from_env()\n .credentials_cache(CredentialsCache::lazy()) // This line can be omitted because it is on by default.\n .credentials_provider(make_provider())\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\nIf you previously configured a `LazyCachingCredentialsProvider`, you can use the builder for `LazyCredentialsCache` instead.\n\nBefore:\n```rust\nuse aws_config::meta::credentials::lazy_caching::LazyCachingCredentialsProvider;\nuse aws_types::provider::ProvideCredentials;\nuse std::time::Duration;\n\nfn make_provider() -> impl ProvideCredentials {\n // --snip--\n}\n\nlet credentials_provider =\n LazyCachingCredentialsProvider::builder()\n .load(make_provider())\n .load_timeout(Duration::from_secs(60)) // Configures timeout.\n .build();\n\nlet sdk_config = aws_config::from_env()\n .credentials_provider(credentials_provider)\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\nAfter:\n```rust\nuse aws_credential_types::cache::CredentialsCache;\nuse aws_types::provider::ProvideCredentials;\nuse std::time::Duration;\n\nfn make_provider() -> impl ProvideCredentials {\n // --snip--\n}\n\nlet sdk_config = aws_config::from_env()\n .credentials_cache(\n CredentialsCache::lazy_builder()\n .load_timeout(Duration::from_secs(60)) // Configures timeout.\n .into_credentials_cache(),\n )\n .credentials_provider(make_provider())\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\nThe examples above only demonstrate how to use `credentials_cache` and `credentials_provider` methods on `aws_config::ConfigLoader` but the same code update can be applied when you interact with `aws_types::sdk_config::Builder` or the builder for a service-specific config, e.g. `aws_sdk_s3::config::Builder`.\n\n
\n\n\nIf you previously configured a `DefaultCredentialsChain` by calling `load_timeout`, `buffer_time`, or `default_credential_expiration` on its builder, you need to call the same set of methods on the builder for `LazyCredentialsCache` instead.\n
\nExample\n\nBefore:\n```rust\nuse aws_config::default_provider::credentials::DefaultCredentialsChain;\nuse std::time::Duration;\n\nlet credentials_provider = DefaultCredentialsChain::builder()\n .buffer_time(Duration::from_secs(30))\n .default_credential_expiration(Duration::from_secs(20 * 60))\n .build()\n .await;\n\nlet sdk_config = aws_config::from_env()\n .credentials_provider(credentials_provider)\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\nAfter:\n```rust\nuse aws_config::default_provider::credentials::default_provider;\nuse aws_credential_types::cache::CredentialsCache;\nuse std::time::Duration;\n\n// Previously used methods no longer exist on the builder for `DefaultCredentialsChain`.\nlet credentials_provider = default_provider().await;\n\nlet sdk_config = aws_config::from_env()\n .credentials_cache(\n CredentialsCache::lazy_builder()\n .buffer_time(Duration::from_secs(30))\n .default_credential_expiration(Duration::from_secs(20 * 60))\n .into_credentials_cache(),\n )\n .credentials_provider(credentials_provider)\n .load()\n .await;\n\nlet client = aws_sdk_s3::Client::new(&sdk_config);\n```\n\n
\n", - "meta": { - "bug": false, - "breaking": true, - "tada": false - }, - "author": "ysaito1001", - "references": [ - "smithy-rs#2122", - "smithy-rs#2227" - ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 5 - }, - { - "message": "The introduction of `CredentialsCache` comes with an accompanying type `SharedCredentialsCache`, which we will store in the property bag instead of a `SharedCredentialsProvider`. As a result, `aws_http::auth:set_provider` has been updated to `aws_http::auth::set_credentials_cache`.\n\nBefore:\n```rust\nuse aws_credential_types::Credentials;\nuse aws_credential_types::provider::SharedCredentialsProvider;\nuse aws_http::auth::set_provider;\nuse aws_smithy_http::body::SdkBody;\nuse aws_smithy_http::operation;\n\nlet mut req = operation::Request::new(http::Request::new(SdkBody::from(\"some body\")));\nlet credentials = Credentials::new(\"example\", \"example\", None, None, \"my_provider_name\");\nset_provider(\n &mut req.properties_mut(),\n SharedCredentialsProvider::new(credentials),\n);\n```\n\nAfter:\n```rust\nuse aws_credential_types::Credentials;\nuse aws_credential_types::cache::{CredentialsCache, SharedCredentialsCache};\nuse aws_credential_types::provider::SharedCredentialsProvider;\nuse aws_http::auth::set_credentials_cache;\nuse aws_smithy_http::body::SdkBody;\nuse aws_smithy_http::operation;\n\nlet mut req = operation::Request::new(http::Request::new(SdkBody::from(\"some body\")));\nlet credentials = Credentials::new(\"example\", \"example\", None, None, \"my_provider_name\");\nlet credentials_cache = CredentialsCache::lazy_builder()\n .into_credentials_cache()\n .create_cache(SharedCredentialsProvider::new(credentials));\nset_credentials_cache(\n &mut req.properties_mut(),\n SharedCredentialsCache::new(credentials_cache),\n);\n```\n", - "meta": { - "bug": false, - "breaking": true, - "tada": false - }, - "author": "ysaito1001", - "references": [ - "smithy-rs#2122", - "smithy-rs#2227" - ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 5 - }, - { - "message": "Fix endpoint for s3.write_get_object_response(). This bug was introduced in 0.53.", - "meta": { - "bug": true, - "breaking": false, - "tada": false - }, - "author": "rcoh", - "references": [ - "smithy-rs#2204" - ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 5 - }, - { - "message": "Add `with_test_defaults()` and `set_test_defaults()` to `::Config`. These methods fill in defaults for configuration that is mandatory to successfully send a request.", - "meta": { - "bug": false, - "breaking": false, - "tada": false - }, - "author": "rcoh", - "references": [ - "smithy-rs#2204" - ], - "since-commit": "48ce90d3a32cc87337d87d1f291b41fc64f1e5bd", - "age": 5 - }, { "message": "Request IDs can now be easily retrieved on successful responses. For example, with S3:\n```rust\n// Import the trait to get the `request_id` method on outputs\nuse aws_sdk_s3::types::RequestId;\nlet output = client.list_buckets().send().await?;\nprintln!(\"Request ID: {:?}\", output.request_id());\n```\n", "meta": { @@ -162,7 +18,7 @@ "smithy-rs#2129" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "Retrieving a request ID from errors now requires importing the `RequestId` trait. For example, with S3:\n```rust\nuse aws_sdk_s3::types::RequestId;\nprintln!(\"Request ID: {:?}\", error.request_id());\n```\n", @@ -177,7 +33,7 @@ "smithy-rs#2129" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "The `message()` and `code()` methods on errors have been moved into `ProvideErrorMetadata` trait. This trait will need to be imported to continue calling these.", @@ -192,7 +48,7 @@ "smithy-rs#2129" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "The `*Error` and `*ErrorKind` types have been combined to make error matching simpler.\n
\nExample with S3\n**Before:**\n```rust\nlet result = client\n .get_object()\n .bucket(BUCKET_NAME)\n .key(\"some-key\")\n .send()\n .await;\nmatch result {\n Ok(_output) => { /* Do something with the output */ }\n Err(err) => match err.into_service_error() {\n GetObjectError { kind, .. } => match kind {\n GetObjectErrorKind::InvalidObjectState(value) => println!(\"invalid object state: {:?}\", value),\n GetObjectErrorKind::NoSuchKey(_) => println!(\"object didn't exist\"),\n }\n err @ GetObjectError { .. } if err.code() == Some(\"SomeUnmodeledError\") => {}\n err @ _ => return Err(err.into()),\n },\n}\n```\n**After:**\n```rust\n// Needed to access the `.code()` function on the error type:\nuse aws_sdk_s3::types::ProvideErrorMetadata;\nlet result = client\n .get_object()\n .bucket(BUCKET_NAME)\n .key(\"some-key\")\n .send()\n .await;\nmatch result {\n Ok(_output) => { /* Do something with the output */ }\n Err(err) => match err.into_service_error() {\n GetObjectError::InvalidObjectState(value) => {\n println!(\"invalid object state: {:?}\", value);\n }\n GetObjectError::NoSuchKey(_) => {\n println!(\"object didn't exist\");\n }\n err if err.code() == Some(\"SomeUnmodeledError\") => {}\n err @ _ => return Err(err.into()),\n },\n}\n```\n
\n", @@ -208,7 +64,7 @@ "smithy-rs#2075" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "`aws_smithy_types::Error` has been renamed to `aws_smithy_types::error::ErrorMetadata`.", @@ -223,7 +79,7 @@ "smithy-rs#2129" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "Fluent builder methods on the client are now marked as deprecated when the related operation is deprecated.", @@ -237,7 +93,7 @@ "aws-sdk-rust#740" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "`SdkError` variants can now be constructed for easier unit testing.", @@ -252,7 +108,7 @@ "smithy-rs#2208" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "Add more client re-exports. Specifically, it re-exports `aws_smithy_http::body::SdkBody`, `aws_smithy_http::byte_stream::error::Error`, and `aws_smithy_http::operation::{Request, Response}`.", @@ -267,7 +123,7 @@ "aws-sdk-rust#600" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "Enable presigning for S3's `HeadObject` operation.", @@ -282,7 +138,7 @@ "smithy-rs#2451" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "The modules in the SDK crates have been reorganized. See the [SDK Crate Reorganization Upgrade Guidance](https://github.com/awslabs/aws-sdk-rust/discussions/752) to see how to fix your code after this change.", @@ -296,7 +152,7 @@ "smithy-rs#2433" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "Reconnect on transient errors.\n\nIf a transient error (timeout, 500, 503, 503) is encountered, the connection will be evicted from the pool and will not\nbe reused. This is enabled by default for all AWS services. It can be disabled by setting `RetryConfig::with_reconnect_mode`\n\nAlthough there is no API breakage from this change, it alters the client behavior in a way that may cause breakage for customers.\n", @@ -311,7 +167,7 @@ "smithy-rs#2445" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "Update MSRV to 1.66.1", @@ -325,7 +181,7 @@ "smithy-rs#2467" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "Default connector provided by `aws-config` now respects `ConnectorSettings`.\n\nPreviously, it used the timeout settings provided by aws-config. A test from @Oliboy50 has been incorporated to verify this behavior.\n\n**Behavior Change**: Prior to this change, the Hyper client would be shared between all service clients. After this change, each service client will use its own Hyper Client.\nTo revert to the previous behavior, set `HttpConnector::Prebuilt` on `SdkConfig::http_connector`.\n", @@ -341,7 +197,7 @@ "smithy-rs#2151" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "Remove deprecated `ResolveAwsEndpoint` interfaces.\n[For details see the longform changelog entry](https://github.com/awslabs/aws-sdk-rust/discussions/755).\n", @@ -356,7 +212,7 @@ "smithy-rs#1784" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "Increase Tokio version to 1.23.1 for all crates. This is to address [RUSTSEC-2023-0001](https://rustsec.org/advisories/RUSTSEC-2023-0001)", @@ -370,7 +226,7 @@ "smithy-rs#2474" ], "since-commit": "562e196bbfb5c57270b2855479a5c365ba3d2dff", - "age": 3 + "age": 4 }, { "message": "Implement std::error::Error#source() properly for the service meta Error enum.", @@ -384,7 +240,7 @@ "aws-sdk-rust#784" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "The outputs for event stream operations (for example, S3's SelectObjectContent) now implement the `Sync` auto-trait.", @@ -398,7 +254,7 @@ "smithy-rs#2496" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "The AWS SDK now compiles for the `wasm32-unknown-unknown` and `wasm32-wasi` targets when no default features are enabled. WebAssembly is not officially supported yet, but this is a great first step towards it!", @@ -412,7 +268,7 @@ "smithy-rs#2254" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "S3's `GetObject` will no longer panic when checksum validation is enabled and the target object was uploaded as a multi-part upload.\nHowever, these objects cannot be checksum validated by the SDK due to the way checksums are calculated for multipart uploads.\nFor more information, see [this page](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#large-object-checksums).\n", @@ -426,7 +282,7 @@ "aws-sdk-rust#764" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "`AppName` is now configurable from within `ConfigLoader`.", @@ -440,7 +296,7 @@ "smithy-rs#2513" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "Add support for omitting session token in canonical requests for SigV4 signing.", @@ -454,7 +310,7 @@ "smithy-rs#2473" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "Add `into_segments` method to `AggregatedBytes`, for zero-copy conversions.", @@ -468,7 +324,7 @@ "smithy-rs#2525" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "Fix bug where an incorrect endpoint was produced for `WriteGetObjectResponse`", @@ -483,7 +339,7 @@ "aws-sdk-rust#781" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "Update the `std::fmt::Debug` implementation for `aws-sigv4::SigningParams` so that it will no longer print sensitive information.", @@ -497,7 +353,7 @@ "smithy-rs#2562" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "`aws_smithy_types::date_time::Format` has been re-exported in SDK crates.", @@ -511,7 +367,7 @@ "smithy-rs#2534" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "Reduce several instances of credential exposure in the SDK logs:\n- IMDS now suppresses the body of the response from logs\n- `aws-sigv4` marks the `x-amz-session-token` header as sensitive\n- STS & SSO credentials have been manually marked as sensitive which suppresses logging of response bodies for relevant operations\n", @@ -525,7 +381,7 @@ "smithy-rs#2603" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "Update MSRV to Rust 1.67.1", @@ -539,7 +395,7 @@ "smithy-rs#2611" ], "since-commit": "9201176c9876c9f7bf6599f8a93fe69d25ee0f03", - "age": 2 + "age": 3 }, { "message": "Avoid extending IMDS credentials' expiry unconditionally, which may incorrectly extend it beyond what is originally defined; If returned credentials are not stale, use them as they are.", @@ -554,8 +410,295 @@ "smithy-rs#2694" ], "since-commit": "3b5fc51a41700c88270145e38fa708eca72dc414", + "age": 2 + }, + { + "message": "Automatically exclude X-Ray trace ID headers and authorization headers from SigV4 canonical request calculations.", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "relevantsam", + "references": [ + "smithy-rs#2815" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "Add accessors to Builders", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "davidsouther", + "references": [ + "smithy-rs#2791" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "Remove native-tls and add a migration guide.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "82marbag", + "references": [ + "smithy-rs#2675" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "Fix error message when `credentials-sso` feature is not enabled on `aws-config`. NOTE: if you use `no-default-features`, you will need to manually able `credentials-sso` after 0.55.*", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "rcoh", + "references": [ + "smithy-rs#2722", + "aws-sdk-rust#703" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "`SsoCredentialsProvider`, `AssumeRoleProvider`, and `WebIdentityTokenCredentialsProvider` now use `NoCredentialsCache` internally when fetching credentials using an STS client. This avoids double-caching when these providers are wrapped by `LazyCredentialsCache` when a service client is created.", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "ysaito1001", + "references": [ + "smithy-rs#2720" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "For event stream operations such as S3 SelectObjectContent or Transcribe StartStreamTranscription, the `EventStreamSender` in the input now requires the passed in `Stream` impl to implement `Sync`.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "jdisanti", + "references": [ + "smithy-rs#2673" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "The `SigningInstructions` in the `aws-sigv4` module are now public. This allows them to be named in a function signature.", + "meta": { + "bug": true, + "breaking": false, + "tada": false + }, + "author": "cholcombe973", + "references": [ + "smithy-rs#2730" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "Time is now controlled by the `TimeSource` trait. This facilitates testing as well as use cases like WASM where `SystemTime::now()` is not supported.", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "rcoh", + "references": [ + "smithy-rs#2728", + "smithy-rs#2262", + "aws-sdk-rust#2087" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "The SDK has added support for timestreamwrite and timestreamquery. Support for these services is considered experimental at this time. In order to use these services, you MUST call `.with_endpoint_discovery_enabled()` on the `Client` after construction.", + "meta": { + "bug": false, + "breaking": false, + "tada": true + }, + "author": "rcoh", + "references": [ + "smithy-rs#2707", + "aws-sdk-rust#114", + "smithy-rs#2846" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "A newtype wrapper `SharedAsyncSleep` has been introduced and occurrences of `Arc` that appear in public APIs have been replaced with it.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "ysaito1001", + "references": [ + "smithy-rs#2742" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "Update MSRV to Rust 1.69.0", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "Velfi", + "references": [ + "smithy-rs#2893" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "Implement unstable serde support for the `Number`, `Blob`, `Document`, `DateTime` primitives", + "meta": { + "bug": false, + "breaking": false, + "tada": true + }, + "author": "thomas-k-cameron", + "references": [ + "smithy-rs#2647", + "smithy-rs#2645", + "smithy-rs#2646", + "smithy-rs#2616" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "Add a `send_with` function on `-Input` types for sending requests without fluent builders", + "meta": { + "bug": false, + "breaking": false, + "tada": true + }, + "author": "thomas-k-cameron", + "references": [ + "smithy-rs#2652" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "The naming `make_token` for fields and the API of `IdempotencyTokenProvider` in service configs and their builders has now been updated to `idempotency_token_provider`.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "ysaito1001", + "references": [ + "smithy-rs#2783" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "The implementation `From` for `aws_http::user_agent::UserAgentStageError` has been removed.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "ysaito1001", + "references": [ + "smithy-rs#2845" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "The AppName property can now be set with `sdk_ua_app_id` in profile files. The old field, `sdk-ua-app-id`, is maintained for backwards compatibility.", + "meta": { + "bug": false, + "breaking": false, + "tada": false + }, + "author": "rcoh", + "references": [ + "smithy-rs#2724" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "**Behavior change**: Credential providers now share the HTTP connector used by the SDK. If you want to keep a separate connector for clients, use `::ConfigBuilder::http_connector` when constructing the client.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "rcoh", + "references": [ + "aws-sdk-rust#579", + "aws-sdk-rust#338" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "The `doc(hidden)` `time_source` in `aws-credential-types` was removed. Use `aws_smithy_async::time` instead.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "rcoh", + "references": [ + "smithy-rs#2877" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "The `doc(hidden)` `with_env` in `ProviderConfig` was removed.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "rcoh", + "references": [ + "smithy-rs#2877" + ], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", + "age": 1 + }, + { + "message": "The underlying architecture of the SDK clients has been overhauled. This shouldn't require any changes for most projects, but will affect projects that customize the SDK middleware. More details are available in the [upgrade guide](https://github.com/awslabs/aws-sdk-rust/discussions/853) if you are effected by these changes.", + "meta": { + "bug": false, + "breaking": true, + "tada": false + }, + "author": "jdisanti", + "references": [], + "since-commit": "6eaacaa96684f662b7d355eea94a526c0b465e7f", "age": 1 } ], "aws-sdk-model": [] -} +} \ No newline at end of file From 234b8e6c01442576d61c5d2890fd93ad1f8e708f Mon Sep 17 00:00:00 2001 From: John DiSanti Date: Thu, 3 Aug 2023 08:25:14 -0700 Subject: [PATCH 249/253] Add max length check for changelog entries (#2896) _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- .../smithy-rs-tool-common/src/changelog.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tools/ci-build/smithy-rs-tool-common/src/changelog.rs b/tools/ci-build/smithy-rs-tool-common/src/changelog.rs index 579bae2b5a..5f358be99d 100644 --- a/tools/ci-build/smithy-rs-tool-common/src/changelog.rs +++ b/tools/ci-build/smithy-rs-tool-common/src/changelog.rs @@ -146,10 +146,16 @@ impl HandAuthoredEntry { if !self.author.chars().all(|c| c.is_alphanumeric() || c == '-') { bail!("Author must be valid GitHub username: [a-zA-Z0-9\\-]") } - // TODO(enableNewSmithyRuntimeCleanup): Re-add this validation - // if self.references.is_empty() { - // bail!("Changelog entry must refer to at least one pull request or issue"); - // } + if self.references.is_empty() { + bail!("Changelog entry must refer to at least one pull request or issue"); + } + if self.message.len() > 800 { + bail!( + "Your changelog entry is too long. Post long-form change log entries in \ + the GitHub Discussions under the Changelog category, and link to them from \ + the changelog." + ); + } Ok(()) } From 71c5788398e3b245d24232a6212d8a9c368f8ccb Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 3 Aug 2023 13:39:33 -0400 Subject: [PATCH 250/253] Default collections RFC (#2900) [Rendered](https://github.com/awslabs/smithy-rs/blob/flatten-collections-rfc/design/src/rfcs/rfc0035_collection_defaults.md) Could use a bit more color but I think it gets the point across. _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti --- .../src/rfcs/rfc0035_collection_defaults.md | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 design/src/rfcs/rfc0035_collection_defaults.md diff --git a/design/src/rfcs/rfc0035_collection_defaults.md b/design/src/rfcs/rfc0035_collection_defaults.md new file mode 100644 index 0000000000..a1aa389b4e --- /dev/null +++ b/design/src/rfcs/rfc0035_collection_defaults.md @@ -0,0 +1,82 @@ + +RFC: Collection Defaults +============= + + +> Status: RFC +> +> Applies to: client + + +For a summarized list of proposed changes, see the [Changes Checklist](#changes-checklist) section. + + +This RFC proposes a **breaking change** to how generated clients automatically provide default values for collections. Currently the SDK generated fields for `List` generate optional values: +```rust + ///

Container for elements related to a particular part. + pub fn parts(&self) -> Option<&[crate::types::Part]> { + self.parts.as_deref() + } +``` +This is _almost never_ what users want and leads to code noise when using collections: +```rust +async fn get_builds() { + let project = codebuild + .list_builds_for_project() + .project_name(build_project) + .send() + .await?; + let build_ids = project + .ids() + .unwrap_or_default(); + // ^^^^^^^^^^^^^^^^^^ this is pure noise +} +``` + +This RFC proposes unwrapping into default values in our accessor methods. + + +Terminology +----------- + +- **Accessor**: The Rust SDK defines accessor methods on modeled structures for fields to make them more convenient for users +- **Struct field**: The accessors point to concrete fields on the struct itself. + + +The user experience if this RFC is implemented +---------------------------------------------- + +In the current version of the SDK, users must call `.unwrap_or_default()` frequently. +Once this RFC is implemented, users will be able to use these accessors directly. In the rare case where users need to +distinguish be `None` and `[]`, we will direct users towards `model..is_some()`. + +```rust +async fn get_builds() { + let project = codebuild + .list_builds_for_project() + .project_name(build_project) + .send() + .await?; + let build_ids = project.ids(); + // Goodbye to this line: + // .unwrap_or_default(); +} +``` + + +How to actually implement this RFC +---------------------------------- + +In order to implement this feature, we need update the code generate accessors for lists and maps to add `.unwrap_or_default()`. Because we are returning slices `unwrap_or_default()` does not produce any additional allocations for empty collection. + +### Could this be implemented for `HashMap`? +This works for lists because we are returning a slice (allowing a statically owned `&[]` to be returned.) If we want to support HashMaps in the future this _is_ possible by using `OnceCell` to create empty HashMaps for requisite types. This would allow us to return references to those empty maps. + +### Isn't this handled by the default trait? +No, many existing APIs don't have the default trait. + +Changes checklist +----------------- +Estimated total work: 2 days +- [ ] Update accessor method generation to auto flatten lists +- [ ] Update docs for accessors to guide users to `.field.is_some()` if they MUST determine if the field was set. From c7fe610ffdc69e02853155ae68850388fcee93b9 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Thu, 3 Aug 2023 16:26:27 -0400 Subject: [PATCH 251/253] Add an RFC for limiting the HTTP public dependency (#2899) ## Motivation and Context [Rendered](https://github.com/awslabs/smithy-rs/blob/d3eaf5eea561af90146b7aa80da0307d49f63506/design/src/rfcs/rfc0036_http_dep_elimination.md) ## Description An RFC describing the need and proposed path for limiting the scope of our HTTP dependency. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --------- Co-authored-by: John DiSanti --- .../src/rfcs/rfc0036_http_dep_elimination.md | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 design/src/rfcs/rfc0036_http_dep_elimination.md diff --git a/design/src/rfcs/rfc0036_http_dep_elimination.md b/design/src/rfcs/rfc0036_http_dep_elimination.md new file mode 100644 index 0000000000..355d0f92dd --- /dev/null +++ b/design/src/rfcs/rfc0036_http_dep_elimination.md @@ -0,0 +1,108 @@ + +RFC: Eliminating Public `http` dependencies +============= + +> Status: RFC +> +> Applies to: client + + +For a summarized list of proposed changes, see the [Changes Checklist](#changes-checklist) section. + + +This RFC defines how we plan to refactor the SDK to allow the SDK to consume a `1.0` version of `hyper`, `http-body`, +and `http` at a later date. Currently, `hyper` is `0.14.x` and a `1.0` release candidate series is in progress. However, +there are [open questions](https://github.com/orgs/hyperium/projects/1/views/4) that may significantly delay the launch +of +these three crates. We do not want to tie the `1.0` of the Rust SDK to these crates. + + +Terminology +----------- + +- **http-body**: A crate (and trait) defining how HTTP bodies work. Notably, the change from `0.*` to `1.0` changes `http-body` + to operate on frames instead of having separate methods. +- `http` (crate): a low level crate of `http` primitives (no logic, just requests and responses) +- ossified dependency: An ossified dependency describes a dependency that, when a new version is released, cannot be utilized without breaking changes. For example, if the `mutate_request` function on every operation operates on `&mut http::Request` where `http = 0.2`, that dependency is "ossified." Compare this to a function that offers the ability to convert something into an `http = 0.2` request—since http=1 and http=0.2 are largely equivalent, the + existence of this function does not prevent us from using http = 1 in the future. In general terms, **functions that operate on references are much more likely to ossify—There is no practical way for someone to mutate an `http = 0.2` request if you have an `http = 1` request other than a time-consuming clone, and reconversion process. + + + +## Why is this important? + +**Performance**: +At some point in the Future, `hyper = 1`, `http = 1` and `http-body = 1` will be released. It takes ~1-2 microseconds to rebuild an HTTP request. If we assume that `hyper = 1` will only operate on `http = 1` requests, then if we can't use `http = 1` requests internally, our only way of supporting `hyper = 1` will be to convert the HTTP request at dispatch time. Besides pinning us to a potentially unsupported version of the HTTP crate, this will prevent us from directly dispatching requests in an efficient manner. With a total overhead of 20µs for the SDK, 1µs is not insignificant. Furthermore, it grows as the number of request headers grow. A benchmark should be run for a realistic HTTP request e.g. one that we send to S3. + +**Hyper Upgrade**: +Hyper 1 is significantly more flexible than Hyper 0.14.x, especially WRT to connection management & pooling. If we don't make these changes, the upgrade to Hyper 1.x could be significantly more challenging. + +**Security Fixes**: +If we're still on `http = 0.*` and a vulnerability is identified, we may end up needing to manually contribute the patch. The `http` crate is not trivial and contains parsing logic and optimized code (including a non-trivial amount of `unsafe`). [See this GitHub issue](https://github.com/hyperium/http/issues/412). Notable is that one issue may be unsound and result in changing the public API. + +**API Friendliness** +If we ship with an API that public exposes customers to `http = 0.*`, we have the API forever. We have to consider that we aren't shipping the Rust SDK for this month or even this year but probably the Rust SDK for the next 5-10 years. + +The user experience if this RFC is implemented +---------------------------------------------- +Customers are impacted in 3 main locations: +1. HTTP types in Interceptors +2. HTTP types in `customize(...)` +3. HTTP types in Connectors + +In all three of these cases, users would interact with our `http` wrapper types instead. + + +In the current version of the SDK, we expose public dependencies on the `http` crate in several key places: + +1. The `sigv4` crate. The `sigv4` crate currently operates directly on many types from the `http` crate. This is unnecessary and actually makes the crate more difficult to use. Although `http` may be used internally, `http` will be removed from the public API of this crate. +2. Interceptor Context: `interceptor`s can mutate the HTTP request through an unshielded interface. This requires creating a [wrapper layer around `http::Request`](#http-request-wrapper) and updating already written interceptors. +3. `aws-config`: `http::Response` and `uri` +4. A long tail of exposed requests and responses in the runtime crates. Many of these crates will be removed post-orchestrator so this can be temporarily delayed. + + +How to actually implement this RFC +---------------------------------- + +### Enabling API evolution +One key mechanism that we SHOULD use for allowing our APIs to evolve in the future is usage of `~` version bounds for the runtime crates after releasing 1.0. + +### Http Request Wrapper + +In order to enable HTTP evolution, we will create a set of wrapper structures around `http::Request` and `http::Response`. These will use `http = 0` internally. Since the HTTP crate itself is quite small, including private dependencies on both versions of the crate is a workable solution. In general, we will aim for an API that is close to drop-in compatible to the HTTP crate while ensuring that a different crate could be used as the backing storage. + +```rust +// since it's our type, we can default `SdkBody` +pub struct Request { + // this uses the http = 0.2 request. In the future, we can make an internal enum to allow storing an http = 1 + http_0: http::Request +} +``` + +**Conversion to/from `http::Request`** +One key property here is that although converting to/from an `http::Request` **can** be expensive, this is *not* ossification of the API. This is because the API can support converting from/to both `http = 0` and `http = 1` in the future—because it offers mutation of the request via a unified interface, the request would only need to be converted once for dispatch if there was a mismatch (instead of repeatedly). At some point in the future, the `http = 0` representation could be deprecated and removed or feature gated. + +**Challenges** +1. Creating an HTTP API which is forwards compatible, idiomatic and "truthful" without relying on existing types from Hyper—e.g. when adding a header, we need to account for the possibility that a header is invalid. +2. Allow for future forwards-compatible evolution in the API—A lot of thought went into the `http` crate API w.r.t method parameters, types, and generics. Although we can aim for a simpler solution in some cases (e.g. accepting `&str` instead of `HeaderName`), we need to be careful that we do so while allowing API evolution. + +### Removing the SigV4 HTTP dependency +The SigV4 crate signs a number of `HTTP` types directly. We should change it to accept strings, and when appropriate, iterators of strings for headers. + +### Removing the HTTP dependency from generated clients +Generated clients currently include a public HTTP dependency in `customize`. This should be changed to accept our `HTTP` wrapper type instead or be restricted to a subset of operations (e.g. `add_header`) while forcing users to add an interceptor if they need full control. + + +In order to implement this feature, we need to add X and update Y... + + +Changes checklist +----------------- + +- [ ] Create the `http::Request` wrapper. Carefully audit for compatibility without breaking changes. 5 Days. +- [ ] Refactor currently written interceptors to use the wrapper: 2 days. +- [ ] Refactor the SigV4 crate to remove the HTTP dependency from the public interface: 2 days. +- [ ] Add / validate support for SdkBody `http-body = 1.0rc.2` either in a PR or behind a feature gate. Test this to + ensure it works with Hyper. +- [ ] Remove `http::Response` and `Uri` from the public exposed types in `aws-config`: 1-4 days. +- [ ] Long tail of other usages: 1 week +- [ ] Implement `~` versions for SDK Crate => runtime crate dependencies: 1 week From 4615785c7fdcd873afa3b2a214ac77bbdee18359 Mon Sep 17 00:00:00 2001 From: 82marbag <69267416+82marbag@users.noreply.github.com> Date: Thu, 3 Aug 2023 21:27:21 +0100 Subject: [PATCH 252/253] Add TLS docs page (#2898) ## Motivation and Context The TLS connector page is missing ## Description Render the TLS connector documentation page ## Checklist - [ ] I have updated `CHANGELOG.next.toml` if I made changes to the smithy-rs codegen or runtime crates - [ ] 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._ --------- Signed-off-by: Daniele Ahmed Co-authored-by: david-perez --- design/src/SUMMARY.md | 1 + design/src/transport/connector.md | 44 +++---------------- examples/pokemon-service-tls/Cargo.toml | 3 +- .../pokemon-service-tls/tests/common/mod.rs | 30 +++++++++++++ .../tests/custom_connectors.rs | 29 ++++++++++++ examples/pokemon-service-tls/tests/http2.rs | 14 ------ 6 files changed, 68 insertions(+), 53 deletions(-) create mode 100644 examples/pokemon-service-tls/tests/custom_connectors.rs delete mode 100644 examples/pokemon-service-tls/tests/http2.rs diff --git a/design/src/SUMMARY.md b/design/src/SUMMARY.md index ea3bcb65c9..34933fca19 100644 --- a/design/src/SUMMARY.md +++ b/design/src/SUMMARY.md @@ -6,6 +6,7 @@ - [Transport](transport/overview.md) - [HTTP Operations](transport/operation.md) - [HTTP Middleware](transport/middleware.md) + - [TLS Connector](transport/connector.md) - [Smithy](./smithy/overview.md) - [Simple Shapes](./smithy/simple_shapes.md) diff --git a/design/src/transport/connector.md b/design/src/transport/connector.md index cadbcb6dd5..edb50d4412 100644 --- a/design/src/transport/connector.md +++ b/design/src/transport/connector.md @@ -1,39 +1,7 @@ -The Smithy client provides a default TLS connector, but a custom one can be plugged in. -`rustls` is enabled with the feature flag `rustls`. +The Smithy client provides a default TLS connector, but a custom one can be +plugged in. `rustls` is enabled with the feature flag `rustls`. -The client had previously supported `native-tls`. You can use your custom connector like this. - -Create your connector: - -```rust -/// A `hyper` connector that uses the `native-tls` crate for TLS. To use this in a smithy client, -/// wrap it in a [hyper_ext::Adapter](crate::hyper_ext::Adapter). -pub type NativeTls = hyper_tls::HttpsConnector; - -pub fn native_tls() -> NativeTls { - let mut tls = hyper_tls::native_tls::TlsConnector::builder(); - let tls = tls - .min_protocol_version(Some(hyper_tls::native_tls::Protocol::Tlsv12)) - .build() - .unwrap_or_else(|e| panic!("Error while creating TLS connector: {}", e)); - let mut http = hyper::client::HttpConnector::new(); - http.enforce_http(false); - hyper_tls::HttpsConnector::from((http, tls.into())) -} -``` - -Plug the connector in the client: -```rust -let mut builder = hyper::client::Builder::default(); -builder.pool_max_idle_per_host(70); -let connector = aws_smithy_client::erase::DynConnector::new( - aws_smithy_client::hyper_ext::Adapter::builder() - .hyper_builder(builder) - .connector_settings(std::default::Default::default()) - .build(native_tls()), -); -let raw_client = aws_smithy_client::builder::Builder::new() - .connector(connector) - .middleware_fn(...) - .build_dyn(); -``` +The client had previously (prior to version 0.56.0) supported `native-tls`. You +can continue to use a client whose TLS implementation is backed by `native-tls` +by passing in a custom connector. Check out the `custom_connectors.rs` tests in +the `pokemon-service-tls` example crate. diff --git a/examples/pokemon-service-tls/Cargo.toml b/examples/pokemon-service-tls/Cargo.toml index e1c3b3a082..9f11a904fc 100644 --- a/examples/pokemon-service-tls/Cargo.toml +++ b/examples/pokemon-service-tls/Cargo.toml @@ -26,8 +26,9 @@ pokemon-service-common = { path = "../pokemon-service-common/" } assert_cmd = "2.0" serial_test = "1.0.0" -# This dependency is only required for testing the `pokemon-service-tls` program. +# These dependencies are only required for testing the `pokemon-service-tls` program. hyper-rustls = { version = "0.24", features = ["http2"] } +hyper-tls = { version = "0.5" } # Local paths aws-smithy-client = { path = "../../rust-runtime/aws-smithy-client/", features = ["rustls"] } diff --git a/examples/pokemon-service-tls/tests/common/mod.rs b/examples/pokemon-service-tls/tests/common/mod.rs index c56c5fe59f..0d69407cf7 100644 --- a/examples/pokemon-service-tls/tests/common/mod.rs +++ b/examples/pokemon-service-tls/tests/common/mod.rs @@ -49,3 +49,33 @@ pub fn client_http2_only() -> Client { .build(); Client::from_conf(config) } + +/// A `hyper` connector that uses the `native-tls` crate for TLS. To use this in a smithy client, +/// wrap it in a [aws_smithy_client::hyper_ext::Adapter]. +pub type NativeTlsConnector = hyper_tls::HttpsConnector; + +fn native_tls_connector() -> NativeTlsConnector { + let cert = hyper_tls::native_tls::Certificate::from_pem( + std::fs::read_to_string(DEFAULT_TEST_CERT) + .expect("could not open certificate") + .as_bytes(), + ) + .expect("could not parse certificate"); + + let tls_connector = hyper_tls::native_tls::TlsConnector::builder() + .min_protocol_version(Some(hyper_tls::native_tls::Protocol::Tlsv12)) + .add_root_certificate(cert) + .build() + .unwrap_or_else(|e| panic!("error while creating TLS connector: {}", e)); + let mut http_connector = hyper::client::HttpConnector::new(); + http_connector.enforce_http(false); + hyper_tls::HttpsConnector::from((http_connector, tls_connector.into())) +} + +pub fn native_tls_client() -> Client { + let config = Config::builder() + .http_connector(Adapter::builder().build(native_tls_connector())) + .endpoint_url(format!("https://{DEFAULT_DOMAIN}:{DEFAULT_PORT}")) + .build(); + Client::from_conf(config) +} diff --git a/examples/pokemon-service-tls/tests/custom_connectors.rs b/examples/pokemon-service-tls/tests/custom_connectors.rs new file mode 100644 index 0000000000..12c82d55cb --- /dev/null +++ b/examples/pokemon-service-tls/tests/custom_connectors.rs @@ -0,0 +1,29 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +pub mod common; + +use serial_test::serial; + +#[tokio::test] +#[serial] +// This test invokes an operation with a client that can only send HTTP2 requests and whose TLS +// implementation is backed by `rustls`. +async fn test_check_health_http2_rustls_connector() { + let _child = common::run_server().await; + let client = common::client_http2_only(); + + let _check_health = client.check_health().send().await.unwrap(); +} + +#[tokio::test] +#[serial] +// This test invokes an operation with a client whose TLS implementation is backed by `native_tls`. +async fn test_check_health_native_tls_connector() { + let _child = common::run_server().await; + let client = common::native_tls_client(); + + let _check_health = client.check_health().send().await.unwrap(); +} diff --git a/examples/pokemon-service-tls/tests/http2.rs b/examples/pokemon-service-tls/tests/http2.rs deleted file mode 100644 index 32c0fba08c..0000000000 --- a/examples/pokemon-service-tls/tests/http2.rs +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * SPDX-License-Identifier: Apache-2.0 - */ - -pub mod common; - -#[tokio::test] -async fn test_check_health_http2() { - let _child = common::run_server().await; - let client = common::client_http2_only(); - - let _check_health = client.check_health().send().await.unwrap(); -} From 54c3ede81803afaa3b9f37b243fdcdf71a2a47a6 Mon Sep 17 00:00:00 2001 From: Zelda Hessler Date: Fri, 4 Aug 2023 10:30:06 -0500 Subject: [PATCH 253/253] Update changelog icons to use github emoji shortcodes (#2901) I noticed these were switched for emojis but they should be shortcodes. ---- _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._ --- tools/ci-build/changelogger/src/render.rs | 16 ++++++++-------- tools/ci-build/changelogger/tests/e2e_test.rs | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/ci-build/changelogger/src/render.rs b/tools/ci-build/changelogger/src/render.rs index 4dd8e22c1e..445d2e8d70 100644 --- a/tools/ci-build/changelogger/src/render.rs +++ b/tools/ci-build/changelogger/src/render.rs @@ -175,13 +175,13 @@ fn to_md_link(reference: &Reference) -> String { fn render_entry(entry: &HandAuthoredEntry, mut out: &mut String) { let mut meta = String::new(); if entry.meta.bug { - meta.push('🐛'); + meta.push_str(":bug:"); } if entry.meta.breaking { - meta.push('⚠'); + meta.push_str(":warning:"); } if entry.meta.tada { - meta.push('🎉'); + meta.push_str(":tada:"); } if !meta.is_empty() { meta.push(' '); @@ -562,11 +562,11 @@ message = "Some API change" v0.3.0 (January 4th, 2022) ========================== **Breaking Changes:** -- ⚠ (all, [smithy-rs#445](https://github.com/awslabs/smithy-rs/issues/445)) I made a major change to update the code generator +- :warning: (all, [smithy-rs#445](https://github.com/awslabs/smithy-rs/issues/445)) I made a major change to update the code generator **New this release:** -- 🎉 (all, [smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator -- 🎉 (all, [smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator +- :tada: (all, [smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator +- :tada: (all, [smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator **Update guide:** blah blah @@ -586,10 +586,10 @@ Thank you for your contributions! ❤ v0.1.0 (January 4th, 2022) ========================== **Breaking Changes:** -- ⚠ ([smithy-rs#445](https://github.com/awslabs/smithy-rs/issues/445)) I made a major change to update the AWS SDK +- :warning: ([smithy-rs#445](https://github.com/awslabs/smithy-rs/issues/445)) I made a major change to update the AWS SDK **New this release:** -- 🎉 ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator +- :tada: ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator **Service Features:** - `aws-sdk-ec2` (0.12.0): Some API change diff --git a/tools/ci-build/changelogger/tests/e2e_test.rs b/tools/ci-build/changelogger/tests/e2e_test.rs index 7f1a292df8..302607704a 100644 --- a/tools/ci-build/changelogger/tests/e2e_test.rs +++ b/tools/ci-build/changelogger/tests/e2e_test.rs @@ -393,7 +393,7 @@ fn render_aws_sdk() { January 1st, 1970 ================= **New this release:** -- 🐛 ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567), @test-dev) Some other change +- :bug: ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567), @test-dev) Some other change **Service Features:** - `aws-sdk-ec2` (0.12.0): Some API change @@ -414,7 +414,7 @@ Old entry contents r#"{ "tagName": "release-1970-01-01", "name": "January 1st, 1970", - "body": "**New this release:**\n- 🐛 ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567), @test-dev) Some other change\n\n**Service Features:**\n- `aws-sdk-ec2` (0.12.0): Some API change\n\n**Contributors**\nThank you for your contributions! ❤\n- @test-dev ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567))\n\n", + "body": "**New this release:**\n- :bug: ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567), @test-dev) Some other change\n\n**Service Features:**\n- `aws-sdk-ec2` (0.12.0): Some API change\n\n**Contributors**\nThank you for your contributions! ❤\n- @test-dev ([aws-sdk-rust#234](https://github.com/awslabs/aws-sdk-rust/issues/234), [smithy-rs#567](https://github.com/awslabs/smithy-rs/issues/567))\n\n", "prerelease": true }"#, release_manifest @@ -511,7 +511,7 @@ author = "rcoh" January 1st, 1970 ================= **Breaking Changes:** -- ⚠ (all, [smithy-rs#3](https://github.com/awslabs/smithy-rs/issues/3)) Third change - empty +- :warning: (all, [smithy-rs#3](https://github.com/awslabs/smithy-rs/issues/3)) Third change - empty **New this release:** - (server, [smithy-rs#1](https://github.com/awslabs/smithy-rs/issues/1), @server-dev) First change - server